天天看点

AspectJ注解开启切面支持切面 @Aspectpointcut表达式通知@Order

文章目录

  • 开启切面支持
  • 切面 @Aspect
  • pointcut表达式
    • execute
    • within
    • this
    • target
    • args
    • @target
    • @args
    • @within
    • @annotation
    • bean
  • 通知
    • @Pointcut
    • @Before
    • @After
    • @AfterReturning
    • @AfterThrowing
    • @Around
  • @Order

常见AspectJ的注解:

  • @Aspect 把当前类标识为一个切面
  • @Pointcut 植入Advice(通知)的触发条件。
  • @Before 前置增强,相当于BeforeAdvice,目标方法执行前执行
  • @After final增强,不管是抛出异常或者正常退出都会执行
  • @AfterReturning 后置增强,相当于AfterReturningAdvice,方法正常退出时执行
  • @AfterThrowing 异常抛出增强,相当于ThrowsAdvice,目标方法抛出异常后执行
  • @Around 环绕增强,相当于MethodInterceptor

开启切面支持

注解:

@Configuration
@EnableAspectJAutoProxy
public class AppConfig {

}
           

xml:

切面 @Aspect

配置value的知识了解:https://www.cnblogs.com/heliusKing/p/11845948.html

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Aspect {

    /**
     * 子句前缀表达式,默认为单列切面,"perthis(...)"就,针对多线程,实际业务没用过
     * 配置如value="perthis(execution(* com.helius.service.impl.*.*(..)))"
     */
    public String value() default "";
}
           

用例

@Aspect
@Component
public class PerformanceAspect {

}
           

pointcut表达式

标准Aspectj Aop 还是Spring Aop自增,参看:https://blog.csdn.net/f641385712/article/details/83543270

execute

execution([修饰符] 方法返回值 包名.[.]类名.方法名(参数) [异常])

/**
 * execution(): 表达式主体
 * 第一个*号:表示返回类型,*号表示所有的类型
 * 包名:表示需要拦截的包名,后面的两个句点表示当前包和当前包的所有子包,com.example.lx包、子孙包
 * 第二个*号:表示类名,*号表示所有的类。
 * *(..):第三个*号表示方法名,*号表示所有的方法,(..)表示方法参数,两个句点表示任何参数
 */
execution (* com.example.lx..*.*(..))
           

拦截所有public方法

拦截以save开头的所有方法

拦截UserService类或者接口中所有方法

拦截UserService类和UserService子类所有方法

拦截包中定义的所有方法,不包含子包中的方法

拦截包和子包中定义的所有方法

@Pointcut("exection(* com.example.lx..*(..))")
//包括下面两个路径
@Pointcut("exection(* com.example.lx.*(..))")
@Pointcut("exection(* com.example.lx.a.*(..))")
           

通过方法参数拦截

可以使用“”和“ …”通配符,其中“”表示任意类型的参数,而“…”表示任意类型参数且参数个数不限

参数类型是java.lang包下的类,直接使用类名,否则用全限定类名,

@Pointcut("execution(* save*(String, int))")
//必须使用全限定类名
@Pointcut("execution(* save*(java.util.List, int))")
//第二参数是任意类型,但是只匹配两个参数的
@Pointcut("execution(* save*(String, *))")
//第二参数是任意类型,匹配第一个参数为String的,没有第二个参数或者多个参数均匹配
@Pointcut("execution(* save*(String, ..))")
//只有一个参数,Object类型
@Pointcut("execution(* save*(Object))")
//只有一个参数,Object类型或者Object子类
@Pointcut("execution(* save*(Object+))")
           

within

within( 包名.[.]*)

拦截包中所有方法

拦截包和子包中的所有方法

@Pointcut("within(com.xyz.service..*)")
//包括下面两个路径
@Pointcut("within(com.xyz.service.*)")
@Pointcut("within(com.xyz.service.a.*)")
           

this

代理对象匹配时才会被拦截

spring的对象如果实现了接口,默认使用jdk动态代理;如果没有实现接口,使用cglib动态代理

指定都用cglib动态代理 设置@EnableAspectJAutoProxy(proxyTargetClass = true)

//如果UserServiceImpl实现IUserService时,默认jdk代理不会拦截
@Pointcut("this(com.example.lx.service.impl.UserServiceImpl)")
//@EnableAspectJAutoProxy(proxyTargetClass = true),指定cglib代理时会拦截
@Pointcut("this(com.example.lx.service.impl.UserServiceImpl)")
//指定代理对象为接口,会拦截
@Pointcut("this(com.example.lx.service.impl.IUserService)")

//如果UserServiceImpl没有实现IUserService,默认是cglib代理会拦截
@Pointcut("this(com.example.lx.service.impl.UserServiceImpl)")
           

target

目标对象为指定的类型被拦截

//UserServiceImpl实现或者没有实现IUserService,都会拦截
 @Pointcut("target(com.example.lx.service.impl.UserServiceImpl)")
           

this 和 target 的不同

  1. this作用于代理对象,target作用于目标对象
  2. this表示目标对象被代理之后生成的代理对象和指定类型匹配才会拦截,匹配的是代理对象
  3. target表示目标对象(也就是实现类)和指定的类型匹配才会拦截,匹配的是目标对象

args

匹配方法中的参数

匹配只有一个参数,且类型为com.example.lx.pojo.User

匹配多个参数

//..表示0个或者多个参数
@Pointcut("args(com.example.lx.pojo.User, ..)")
           

@target

匹配的目标对象的类有一个指定的注解

目标对象类注解上有指定注解,调用该目标对象的任意方法都会被拦截

示列

UserService 类的所有方法调用都会被拦截

@MyAnnotation
public class UserService {
	public void method() {
	}
}
           

@args

方法参数所属的类型类上有指定的注解

注意:是方法参数所属类型对应的类上面有指定的注解,不是方法参数中有注解

匹配一个参数,且参数对应类型类上有指定注解

示列

@MyAnnotation
public class User {
}
//User类上有@MyAnnotation注解,所以会被拦截
public void test(User user) {
      
}
           

匹配多个参数,且多个参数所属类型上都有对应的注解

//第一个参数类型有注解MyAnnotation, 第二个参数类型有注解MyAnnotation2
@Pointcut("@args(com.example.lx.MyAnnotation, com.example.lx.MyAnnotation2)")
           

匹配多个参数,只要求第一个参数所属类型有对应注解

@within

拦截方法对应类有指定类注解的方法

示列

UserService 类的所有方法调用都会被拦截

@MyAnnotation
public class UserService {
	public void method() {
	}
}
           

@target 和 @within 的不同

  1. @taget(注解A): 判断被调用的目标对象中是否声明了注解A,如果有,被拦截
  2. @within(注解A): 判断被调用的方法所属的类中声明了注解A,如果有,被拦截
  3. @target关注的是被调用的对象,@within关注的是调用的方法所在类

@annotation

匹配有指定注解的方法(注解作用在方法上面)

被调用的方法包含指定的注解MyAnnotation

bean

当调用的方法是指定的bean的方法时生效

spring aop自增

匹配id或者名字是userService的bean中所有方法

匹配id或者名字是user开头的bean中所有方法

通知

@Pointcut

切入点,和其他通知注解结合使用

支持的操作符

  • &&:与操作符
  • ||:或操作符
  • !:非操作符
@Pointcut("execution(* com.fsx.run.MessageSender.*(..))")
private void logSender(){}

@Pointcut("execution(* com.fsx.run.MessageReceiver.*(..))")
private void logReceiver(){}

//等同于上面两个方法一起
@Pointcut("logSender() || logReceiver()")
private void logMessage(){}

@Pointcut("execution(* com.abc.service.*.access*(..)) && args(String)")
private void logMessage(){}
           

@Before

前置增强,相当于BeforeAdvice,目标方法执行前执行

@Before("execution(* com.abc.service.*.many*(..))")
public void permissionCheck(JoinPoint point) {
    System.out.println("@Before:目标方法为:" + 
            point.getSignature().getDeclaringTypeName() + 
            "." + point.getSignature().getName());
    System.out.println("@Before:参数为:" + Arrays.toString(point.getArgs()));
    System.out.println("@Before:被织入的目标对象为:" + point.getTarget());
}
//写法二:
@Pointcut("execution(execution(* com.abc.service.*.many*(..))")
private void pointcut() {
    log.info("pointcut");
}
@Before(value = "pointcut()")
public void before(JoinPoint point) {
    log.info("before");
}
           

@After

final增强,不管是抛出异常或者正常退出都会执行

@After("execution(* com.abc.service.*.many*(..))")
public void releaseResource(JoinPoint point) {
    System.out.println("@After:目标方法为:" + 
            point.getSignature().getDeclaringTypeName() + 
            "." + point.getSignature().getName());
    System.out.println("@After:参数为:" + Arrays.toString(point.getArgs()));
    System.out.println("@After:被织入的目标对象为:" + point.getTarget());
}
//写法二:
@Pointcut("execution(execution(* com.abc.service.*.many*(..))")
private void pointcut() {
    log.info("pointcut");
}
@After(value = "pointcut()")
public void after(JoinPoint point) {
    log.info("after");
}
           

@AfterReturning

后置增强,相当于AfterReturningAdvice,方法正常退出时执行

@AfterReturning(pointcut="execution(* com.abc.service.*.many*(..))", 
    returning="returnValue")
public void log(JoinPoint point, Object returnValue) {
    System.out.println("@AfterReturning:目标方法为:" + 
            point.getSignature().getDeclaringTypeName() + 
            "." + point.getSignature().getName());
    System.out.println("@AfterReturning:参数为:" + 
            Arrays.toString(point.getArgs()));
    System.out.println("@AfterReturning:返回值为:" + returnValue);
    System.out.println("@AfterReturning:被织入的目标对象为:" + point.getTarget());
}
//写法二:
@Pointcut("execution(execution(* com.abc.service.*.many*(..))")
private void pointcut() {
    log.info("pointcut");
}
@AfterReturning(value = "pointcut()")
public void afterReturning(JoinPoint point, Object returnValue) {
    log.info("afterReturning");
}
           

@AfterThrowing

异常抛出增强,相当于ThrowsAdvice,目标方法抛出异常后执行

@AfterThrowing(value = "target(net.deniro.spring4.aspectj.CookA)",throwing = "e")
public void bind(Exception e){
    System.out.println("绑定异常【开始】");
    System.out.println("e:" + e.getMessage());
    System.out.println("绑定异常【结束】");
}
//写法二:
@Pointcut("execution(execution(* com.abc.service.*.many*(..))")
private void pointcut() {
    log.info("pointcut");
}
@AfterThrowing(value = "pointcut()")
public void afterThrowing(Exception e) {
    log.info("afterThrowing");
}    
           

@Around

环绕增强,相当于MethodInterceptor

@Around("execution(* com.abc.service.*.many*(..))")
public Object process(ProceedingJoinPoint point) throws Throwable {
    //访问目标方法的参数:
    Object[] args = point.getArgs();
    if (args != null && args.length > 0 && args[0].getClass() == String.class) {
        args[0] = "改变后的参数1";
    }
    //用改变后的参数执行目标方法
    Object returnValue = point.proceed(args);
    System.out.println("@Around:执行目标方法之后...");
    System.out.println("@Around:被织入的目标对象为:" + point.getTarget());
    return "原返回值:" + returnValue + ",这是返回结果的后缀";
}
//写法二:
@Pointcut("execution(execution(* com.abc.service.*.many*(..))")
private void pointcut() {
    log.info("pointcut");
}
@Around(value = "pointcut()")
public void around(ProceedingJoinPoint point) {
    log.info("around");
}
           

@Order

同一个方法被多个Aspect类拦截,通过@Order设置优先级,值越小优先级越高

优先级为1的切面类Bean1包含了@Before,优先级为2的切面类Bean2包含了@Around,虽然@Around优先级高于@Before,但由于Bean1的优先级高于Bean2的优先级,因此Bean1中的@Before先被织入

@Aspect
@Component
@Order(value = 1)
public class Aspect1 {
...
}

@Aspect
@Component
@Order(value = 2)
public class Aspect2 {
...
}
           
AspectJ注解开启切面支持切面 @Aspectpointcut表达式通知@Order