前言
标准的AspectJ Aop的pointcut的表达式类型是很丰富的,但是Spring Aop只支持其中的9种,外加Spring Aop自己扩充的一种一共是11(10+1)种类型的表达式,分别如下:
-
execution
:一般用于指定方法
的执行,用的最多 -
within
:是用来匹配类的,指定类中的所有方法将被拦截 ,也可用来指定一个包 -
this
:代理对象
为指定的类型会被拦截 -
target
:目标对象
为指定的类型被拦截 -
args
:用来匹配方法参数的。 -
@target
:匹配的目标对象的类有一个指定的注解,在接口上声明的对它不起作用 -
@args
:方法参数所属的类型上有指定的注解,被匹配 -
@within
:判断被调用的方法所属的类中是否声明了注解,如果有,会被拦截 -
@annotation
:用于匹配方法上拥有指定注解的情况。
Pointcut定义时,还可以使用&&、||、! 这三个运算。进行逻辑运算。可以把各种条件组合起来使用
execution
语法:注解? 修饰符? 返回值类型
类路径匹配 ?方法名
(参数列表
) 异常列表?
注解:`可选`,方法上持有的注解,如@Deprecated;
修饰符:`可选`,如public、protected;
返回值类型:`必填`,可以是任何类型模式;* 表示所有类型;
类路径匹配:`可选`,可以是任何类型模式;
方法名:`必填`,可以使用 * 进行模式匹配;
参数列表:`必填`, 其规则如下
() 表示方法没有任何参数;
(..) 表示匹配接受任意个参数的方法,
(..,java.lang.String) 表示匹配接受java.lang.String类型的参数结束,且其前边可以接受有任意个参数的方法;
(java.lang.String,..) 表示匹配接受java.lang.String类型的参数开始,且其后边可以接受任意个参数的方法;
(*,java.lang.String) 表示匹配接受java.lang.String类型的参数结束,且其前边接受有一个任意类型参数的方法;
异常列表;可选,以“throws 异常全限定名列表”声明,异常全限定名列表如有多个以“,”分割
类型匹配语法
*:匹配任何数量字符;
..:匹配任何数量字符的重复,如在类路径匹配中匹配任何数量子包;而在方法参数模式中匹配任何数量参数。
+:匹配指定类型的子类型;仅能作为后缀放在类型模式后边。
案例
1、execution(public * *(..))
拦截任意公共方法
2、execution(* set*(..))
拦截以set开头的任意方法
3、execution(* com.xyz.service.AccountService.*(..))
拦截AccountService(类、接口)中定义的所有方法
4、execution(* com.xyz.service.*.*(..))
拦截com.xyz.service包中所有类中任意方法,不包含子包中的类
5、execution(* com.xyz.service..*.*(..))
拦截com.xyz.service包或者子包中定义的所有方法
within
作用:是用来指定类型的,指定类型中的所有方法将被拦截
案例
within(com.xyz.service..*)
拦截service包及子包中任意类的任意方法
this
作用:代理对象
为指定的类型会被拦截
案例
public interface HelloService { void hello();}
@Component
public class HelloServiceImpl implements HelloService {
public void hello() {
System.out.println("调用hello方法...");
}
}
@Aspect
@Component
public class MyAspect {
@Pointcut("this(aop.HelloServiceImpl)")
private void pointcut() { }
@Before(value="pointcut()")
public void before(JoinPoint joinPoint){
System.out.println("执行before方法....");
}
}
@ComponentScan(value = {"aop"})
@Configuration
@EnableAspectJAutoProxy
public class RootConfig { }
public class App {
public static void main( String[] args ) {
AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(RootConfig.class);
System.out.println(annotationConfigApplicationContext.getBean("helloServiceImpl").getClass());
HelloService helloService = ((HelloService)annotationConfigApplicationContext.getBean("helloServiceImpl"));
helloService.hello();
System.out.println(HelloServiceImpl.class.isAssignableFrom(helloService.getClass()));
}
}
运行输出:
class com.sun.proxy.$Proxy18
调用hello方法...
false
原因分析
@EnableAspectJAutoProxy的属性proxyTargetClass在默认情况下为fasle,其表示若spring创建的对象如果实现了接口,默认使用jdk动态>代理,如果没有实现接口,使用cglib创建代理对象
所以 helloService 是使用jdk
动态代理生成的对象,HelloServiceImpl.class.isAssignableFrom(helloService.getClass()
为 false
如果把配置类改成这样
@ComponentScan(value = {"aop"})
@Configuration
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class RootConfig {
}
运行输出:
class aop.HelloServiceImpl$$EnhancerBySpringCGLIB$$36334b64
执行before方法....
调用hello方法...
true
原因分析:helloService 为 HelloServiceImpl类型的对象,所以会被拦截
target
作用:目标对象
为指定的类型被拦截
案例
public interface HelloService { void hello();}
@Component
public class HelloServiceImpl implements HelloService {
public void hello() {
System.out.println("调用hello方法...");
}
}
@Aspect
@Component
public class MyAspect {
@Pointcut("target(aop.HelloServiceImpl)")
private void pointcut() { }
@Before(value="pointcut()")
public void before(JoinPoint joinPoint){
System.out.println("执行before方法....");
}
}
@ComponentScan(value = {"aop"})
@Configuration
@EnableAspectJAutoProxy
public class RootConfig { }
public class App {
public static void main( String[] args ) {
AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(RootConfig.class);
System.out.println(annotationConfigApplicationContext.getBean("helloServiceImpl").getClass());
HelloService helloService = ((HelloService)annotationConfigApplicationContext.getBean("helloServiceImpl"));
helloService.hello();
System.out.println(HelloServiceImpl.class.isAssignableFrom(helloService.getClass()));
}
}
运行输出:
class com.sun.proxy.$Proxy18
执行before方法....
调用hello方法...
false
this 和 target 的不同点
this作用于代理对象,target作用于目标对象
this表示目标对象被代理之后生成的代理对象和指定的类型匹配会被拦截,匹配的是代理对象
target表示目标对象和指定的类型匹配会被拦截,匹配的是目标对象
args
作用:用来匹配方法参数的。
案例
1、args()
匹配任何不带参数的方法。
2、args(java.lang.String)
匹配任何只带一个参数,而且这个参数的类型是String的方法。
3、args(…)
带任意参数的方法。
4、args(java.lang.String,…)
匹配带任意个参数,但是第一个参数的类型是String的方法。
5、args(…,java.lang.String)
匹配带任意个参数,但是最后一个参数的类型是String的方法。
@target
作用:匹配的目标对象的类有一个指定的注解,在接口上声明的对它不起作用
案例
1、@target(com.ms.aop.MyAnnotation)
匹配目前类上存在@MyAnnotation注解,调用该目标对象的任意方法都会被拦截
注意:如果父类匹配到了,不会影响到子类
@args
作用:方法参数所属的类型上有指定的注解,被匹配
案例
1、@args(com.ms.aop.jargs.demo1.Anno1)
匹配1个参数,且第1个参数所属的类中有Anno1注解
2、@args(com.ms.aop.jargs.demo1.Anno1,com.ms.aop.jargs.demo1.Anno2)
匹配2个参数,且2个参数所属的类型上都有指定的注解
3、@args(com.ms.aop.jargs.demo2.Anno1,…) 匹配多个参数,且第一个参数所属的类中有Anno1注解
@within
@within : 判断被调用的方法所属的类中是否声明了注解,如果有,会被拦截
比如有一个ClassA上使用了注解MyAnno标注,并且定义了一个方法a(),那么在调用ClassA.a()方法时将匹配该Pointcut;如果有一个ClassB上没有MyAnno注解,但是它继承自ClassA,同时它上面定义了一个方法b(),那么在调用ClassB().b()方法时不会匹配该Pointcut,但是在调用ClassB().a()时将匹配该方法调用,因为a()是定义在父类型ClassA上的,且ClassA上使用了MyAnno注解。但是如果子类ClassB覆写了父类ClassA的a()方法,则调用ClassB.a()方法时也不匹配该Pointcut。
案例
public interface HelloService {
void hello();
}
@AN
public class Father implements HelloService {
@Override
public void hello() {
System.out.println("调用hello方法...");
}
}
@Component
public class HelloServiceImpl extends Father implements HelloService {
}
@Aspect
@Component
public class MyAspect {
@Pointcut("@within(aop.AN)")
private void pointcut() { }
@Before(value="pointcut()")
public void before(JoinPoint joinPoint){
System.out.println("执行before方法....");
}
}
@ComponentScan(value = {"aop"})
@Configuration
@EnableAspectJAutoProxy
public class RootConfig {
}
public class App {
public static void main( String[] args ) {
AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(RootConfig.class);
System.out.println(annotationConfigApplicationContext.getBean("helloServiceImpl").getClass());
HelloService helloService = ((HelloService)annotationConfigApplicationContext.getBean("helloServiceImpl"));
helloService.hello();
}
}
运行输出
class com.sun.proxy.$Proxy19
执行before方法....
调用hello方法...
@target 和 @within 的不同点
@target(注解A):判断被调用的目标对象中是否声明了注解A,如果有,会被拦截
@within(注解A): 判断被调用的方法所属的类中是否声明了注解A,如果有,会被拦截
@target关注的是被调用的对象,@within关注的是调用的方法所在的类
@annotation
作用:用于匹配方法上拥有指定注解的情况。
案例
1、@annotation(com.fsx.run.anno.MyAnno)
方法上有@MyAnno注解的都会被匹配