摘要: Spring中的AspectJ切点表达式函数 切点表达式函数就像我们的GPS导航软件。通过切点表达式函数,再配合通配符和逻辑运算符的灵活运用,我们能很好定位到我们需要织入增强的连接点上。经过上面的铺垫,下面来看看Springz中支持的切点表
Spring中的AspectJ切点表达式函数
切点表达式函数就像我们的GPS导航软件。通过切点表达式函数,再配合通配符和逻辑运算符的灵活运用,我们能很好定位到我们需要织入增强的连接点上。经过上面的铺垫,下面来看看Springz中支持的切点表达式函数。
1. 方法切点函数
函数
入参
说明
示例
execution()
方法匹配字符串
满足某一匹配模式的的所有目标类方法连接点
execution(* com.yc.service.*.*(..))在配置service层的事务管理时常用,定位于任意返回类型(第一个”*”) 在com.yc.service包下的所有类(第二个”*”)下的所有方法(第三个”*”),且这个方法的入参为任意类型、数量(体现在 “(..)“)
@annotation()
方法注解类名
标注了特定注解的目标方法连接点上
@anntation(com.yc.controller.needRecord),定位于controller层中任何添加@needRecord的方法,这可以方便地对控制层中某些方法被调用(如某人某时间登陆、进入后台管理界面)添加日志记录。
1. execution详解
execution的语法表达式如下:execution(<修饰符> <返回类型> <类路径> <方法名>(<参数列表>) <异常模式> ) 
其中,修饰符和异常是可选的,如果不加类路径,则默认对所有的类生效。它常用实例如下:
1. 通过方法签名、返回值定义切点:
- `execution(public * *Service(..))`:定位于所有类下返回值任意、方法入参类型、数量任意,public类型的方法
- `execution(public String *Service(..))`:定位于所有类下返回值为String、方法入参类型、数量任意,public类型的方法
2. 通过类包定义切点:
- `execution(* com.yc.controller.BaseController+.*(..))`:匹配任意返回类型,对应包下BaseController类及其子类等任意方法。
- `execution(* com.*.(..))`:匹配任意返回类型,com包下所有类的所有方法
- `execution(* com..*.(..))`:匹配任意返回类型,com包、子包下所有类的所有方法
注意.表示该包下所有类,..则涵括其子包。
3. 通过方法入参定义切点
- 这里“\*”表示任意类型的一个参数,“..”表示任意类型任意数量的参数
- `execution(* speak(Integer,*))`:匹配任意返回类型,所有类中只有两个入参,第一个入参为Integer,第二个入参任意的方法
- `execution(* speak(..,Integer,..))`:匹配任意返回类型,所有类中至少有一个Integer入参,但位置任意的方法。
2. annotation详解
此注解用于定位标注了某个注解的目标切点。下面我们来看一个模拟用户登录成功后日志记录用户名、时间和调用方法的示例,
1. 自定义注解
@Retention(RetentionPolicy.CLASS)//生命注释保留时长,这里无需反射使用,使用CLASS级别
@Target(ElementType.METHOD)//生命可以使用此注解的元素级别类型(如类、方法变量等)
public @interface NeedRecord {
}
关于自定义注解的更多属性与说明,可查看我的另一篇文章http://blog.csdn.net/qwe6112071/article/details/50949663
2. 定义切面(配置增强和定位切点)
@Aspect//将当前类标注成一个切面。
public class Annotation_aspect {
    @AfterReturning("@annotation(test.aop2.NeedRecord)")//这里指向注解类
    public void Record(JoinPoint joinPoint){//切点入参。
        System.out.println("日志记录:用户" +joinPoint.getArgs()[0] + "在" + new SimpleDateFormat("yyyy-MM-dd hh:mm;ss").format(new Date()) + "调用了"+ joinPoint.getSignature()+"方法" );
    }
}
3. 定义目标对象
@NeedRecord
public void login(String name){
    System.out.println("I'm "+name+" ,I'm logining");
}
4. 配置IOC容器
<aop:aspectj-autoproxy />   <!-- 使@AspectJ注解生效 -->
<bean class="test.aop2.Annotation_aspect" /><!-- 注册切面,使AOP自动识别并进行AOP方面的配置 -->
<bean id="userController" class="test.aop2.UserController" /><!-- 注册目标对象 -->
这里需注意: 
1. 和 标注在目标对象Annotation_aspect的注解@Aspect缺一不可,否则调用login方法时,Record方法不会被调用 
2. 必须在IOC容器中注册切面和目标对象,以便在下面测试中通过
5. 测试
public static void main(String args[]){
        ApplicationContext ac = new ClassPathXmlApplicationContext("classpath:test/aop2/aop.xml");
        UserController userController = (UserController) ac.getBean("userController");
        userController.login("zenghao");
    }
调用测试方法后,会打印信息: 
I’m zenghao ,I’m logining 
DEBUG: org.springframework.beans.factory.support.DefaultListableBeanFactory - Returning cached instance of singleton bean ‘test.aop2.Annotation_aspect#0’//log4j的日志记录打印 
日志记录:用户zenghao在2016-03-21 08:27;48调用了void test.aop2.UserController.login(String)方法
2. 方法入参切点函数
函数
入参
说明
示例
args()
类名
定位于入参为特定类型的的方法
如args(com.yc.model.User,com.yc.model.Article),我们要定位于所有以User,Article为入参的方法,需要注意的是,类型的个数、顺序必须都一一对应)
@args()
类型注解类名
定位于被特定注解的类作为方法入参的连接点
@args(com.yc.annotation.MyAnnotation)。MyAnnotation为自定义注解,标注在目标对象方法入参上,被标注的目标都会被匹配。,如方法public myMethod(@MyAnnotation String args);
1. args()详解:
args函数接受一个类名或变量名(对应与目标对象方法的入参),并将该类名绑定到增强方法入参中。对上一个实例,我们将切面改造成:
@After("args(name)")
public void Record(JoinPoint joinPoint,String name) throws Throwable{
    System.out.println("日志记录:用户" +name+ "在" + new SimpleDateFormat("yyyy-MM-dd hh:mm;ss").format(new Date()) + "调用了"+ joinPoint.getSignature().getDeclaringTypeName()+"方法" );
    /*控制台打印
    I'm zenghao ,I'm logining
    日志记录:用户zenghao在2016-03-21 09:32;31调用了test.aop2.UserController方法
    */
}
在这里有几点是值得注意的: 
1. 在本例中,我们不能使用args(String)来匹配,因为我们在方法入参中加入了变量name,必须通过args()绑定连接点入参的机制:通过在方法声明中定义入参String name,然后args会搜寻方法定义中命名参数来获取对应的参数类型(这里是String)。 
2. 如果我们使用args(name),但在方法定义体中没声明String name,则会报错 java.lang.IllegalArgumentException: warning no match for this type name: name [Xlint:invalidAbsoluteTypeName] 
3. 在这里如果要使用args(String)匹配我们的UserController中的方法,则必须去掉方法定义中的String name参数,或将注解改成@After(value = "args(name)",argNames = "name")方可。否则会抛出异常 java.lang.IllegalArgumentException: error at ::0 formal unbound in pointcut 
4. 如果我们需要配置多变量,我们可以使用args(name,age)来配置,对应对象方法Annotation_aspect.Record(String name,Integer age)和UserController.login(String name,Integer age)。 
5. 除了args(),this(),target(),@args(),@within(),@target()和@annotation等函数都可以指定参数名,来将目标连接点上的方法入参绑定到增强的方法中。
3. 目标类切点函数
函数
入参
说明
示例
within()
类名匹配串
定位于特定作用于下的所有连接点
within(com.yc.service.*ServiceImpl),可以通过此注解为特定包下的所有以ServiceImpl名字结尾的类里面的所有方法添加事务控制。
target()
类名
定位于指定类及其子类
target(com.yc.service.IUserService),则可定位到IUserService接口和它的实现类如UserServiceImpl
@within()
类型注解类名
定位与标注了特定注解的类及其实现类
@within(com.yc.controller.needRecord),比如我们可以在BaseController中标注@needRecord,则所有继承了BaseController的UserController、ArticleController等等都会被定位
@target()
类型注解类名
定位于标注了特定注解的目标类里所有方法
@target(com.yc.controller.needRecord),则可以在controller层中,为我们需要日志记录的类标注@needRecord。
1.    within()定位连接点的最细粒度是到类,相对于execution()可定位连接点大到包,小到方法入参的适用范围更窄。
2.    相对于@within,显然@target的耦合性更低,针对性更强。比如UserController,ArticleControoler,HomeController都需要继承BaseController,如果我们在BaseController中标注@needRecord,则三个子Controller都会被@within定位到织入增强,但实际上我们不想让HomeController织入增强,显然分别在UserController和ArticleController中标注@needRecord,然后利用@target来织入增强才满足要求。
3.    @within()如果标注在一个接口上,则不会匹配实现了该接口的子类。
4. 代理类切点函数
主要为this(),大多数情况使用方法与target()相同,区别在通过引介增强引入新接口方法时,新的接口方法同样会被this()定位,但target()则不会。

切点独立命名
在前面的讲解里,我们都是直接把切点配置到切点表达式函数里的,而这种切点叫做匿名切点。但假如我们有新的需求,要为相同的切点配置多个增强,这就需要我们在多个增强中配置相同的切点。为了提高重用性和降低维护成本,我们可以通过@Pointcut注解来单独命名切点。 
和前面相同的例子,我们将注解切面Annotation_aspect改造成如下:
@Aspect
public class Annotation_aspect {
 
    @Pointcut("execution(public * test.aop2.*.*(..))")
    private void ClassInTest_aop2(){}//修饰为private表示此切点只能在本类中使用,这里的返回值和方法题没有实际用途
 
    @Pointcut("args(String)")
    protected void MethodWithArgNames(){}//修饰为protected表示此切点只能在本类及其子类中使用
 
    @After("ClassInTest_aop2() and MethodWithArgNames() ")
    public void Record(JoinPoint joinPoint) throws Throwable{
        System.out.println("日志记录:用户在" + new SimpleDateFormat("yyyy-MM-dd hh:mm;ss").format(new Date()) + "调用了"+ joinPoint.getSignature().getDeclaringTypeName()+"方法" );
    }
 
}
运行测试函数,控制台打印: 
I’m zenghao ,I’m logining 
日志记录:用户在2016-03-21 11:33;59调用了test.aop2.UserController方法
 

Spring—切点表达式的更多相关文章

  1. spring 切点表达式

    spring切点表达式: 1.*通配符:该通配符主要用于匹配单个单词. 例如:execution(* com.bonnie.Controller.TestController.*()) 上述表达式表示 ...

  2. Spring AOP切点表达式用法总结

    1. 简介        面向对象编程,也称为OOP(即Object Oriented Programming)最大的优点在于能够将业务模块进行封装,从而达到功能复用的目的.通过面向对象编程,不同的模 ...

  3. AOP (切点表达式讲解)

    Spring EL表达式:: 1.execution 表达式 语法格式: execution(返回类型.包名.类名.方法名(参数表)) exection(*.com.xxx.AService.*(.. ...

  4. AOP切点表达式

    Aspectj切入点语法定义 在使用spring框架配置AOP的时候,不管是通过XML配置文件还是注解的方式都需要定义pointcut"切入点" 例如定义切入点表达式  execu ...

  5. 21Spring重用切点表达式

    直接看代码: package com.cn.spring.aop.impl; //加减乘除的接口类 public interface ArithmeticCalculator { int add(in ...

  6. SpringBoot AOP中JoinPoint的用法和通知切点表达式

    前言 上一篇文章讲解了springboot aop 初步完整的使用和整合 这一篇讲解他的接口方法和类 JoinPoint和ProceedingJoinPoint对象 JoinPoint对象封装了Spr ...

  7. Java Spring cron表达式使用详解

    Java Spring cron表达式使用详解   By:授客 QQ:1033553122 语法格式 Seconds Minutes Hours DayofMonth Month DayofWeek ...

  8. spring cron表达式(定时器)

    转: spring cron表达式(定时器) 写定时器时用到,记录一下: Cron表达式是一个字符串,字符串以5或6个空格隔开,分开工6或7个域,每一个域代表一个含义,Cron有如下两种语法 格式:  ...

  9. AspectJ风格的Aop切点表达式

    execution(*com.aptech.jb.epet.dao.hibimpl.*.*(..)) 这样写应该就可以了,这是com.aptech.jb.epet.dao.hibimpl 包下所有的类 ...

随机推荐

  1. 超全面的JavaWeb笔记day17<JDBC>

    1.JDBC的原理 是由JavaEE提供的连接数据库的规范 需要由各大数据库的厂商提供对JDBC的实现类 2.四大核心类 3.四大参数 driverClassName url username pas ...

  2. 转的:burp suite小例子

    Web安全测试时经常会遇到一些蹩脚的注射点,而因各种原因利用注射又无法获取网站管理账号或拥有网站管理权限却迟迟不能upload一个shell的时候,可能会权衡一下web权限与数据库信息,哪个是我们所需 ...

  3. Oracle应用技术精华教程:管理还原段

    管理还原段 在oracle 9i 之后提供了两种方法来管理还原数据 自动的还原数据管理:oracle 自动管理还原段的创建.分配和优化 手动的还原数据管理:oracle 手动管理还原段的创建.分配和优 ...

  4. CentOS-6.3安装配置Nginx--【测试已OK】

    安装说明 系统环境:CentOS-6.3软件:nginx-1.2.6.tar.gz安装方式:源码编译安装 安装位置:/usr/local/nginx 下载地址:http://nginx.org/en/ ...

  5. 网络子系统45_ip协议tos处理

    //ip报头tos字段,一个字节 // 二进制位:[0 1 2] [3] [4] [5] [6] [7] // 1.[0 1 2] 表示优先级: // 000 路由 // 001 优先级 // 010 ...

  6. Delphi的打开文件对话框-TOpenDialog

    1.TOpenDialog组件的典型用法 “打开”对话框是用TOpenDialog组件实现的,TOpenDialog组件是非可视组件. Filter属性用于设置文件过滤器,让对话框只列出特定类型的文件 ...

  7. Android 7.1 SystemUI--任务管理--场景一:长按某个缩略图,拖动分屏的流程

    TaskView 类的长按事件 onLongClick 方法内发送了 DragStartEvent 事件消息,该 DragStartEvent 事件消息由 RecentsView,TaskStackV ...

  8. Python学习之k-近邻算法

    1. K-近邻算法 # coding=utf-8 from numpy import * import operator def createDataSet(): group = array([[1. ...

  9. EUI组件之BitmapLabel 位图字体

    一.制作文图字体文件 使用TextureMerger制作位图字体,具体查看 官方教程. 我们这里制作了一组位图字体. 二.导入位图字体 位图字体素材放入资源配置文件default.res.json 三 ...

  10. c# 计算文字高度

    SizeF sizeF = g.MeasureString(listBox1.Items[e.Index].ToString(), e.Font, listBox1.Width);