0%

Spring中AspectJ的注解开发

@AspectJ注解于类名上来声明这个类是一个切面类

AspectJ 简介

  • AspectJ是一个基于Java语言的AOP框架
  • Spring2.0以后新增了对AspectJ切点表达式支持
  • @AspectJ 是AspectJ1.5新增功能,通过JDK5注解技术,允许直接在Bean类中定义切面
  • 新版本Spring框架,建议使用AspectJ方式来开发AOP
  • 使用AspectJ 需要导入Spring AOP和 AspectJ相关jar包
    • spring-aop-4.2.4.RELEASE.jar
    • com.springsource.org.aopalliance-1.0.0.jar
    • spring-aspects-4.2.4.RELEASE.jar
    • com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar

      .xml头配置

1
2
3
4
5
6
7
8
9
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<!--开启AspectJ的注解开发,自动代理-->
<aop:aspectj-autoproxy/>
</beans>

切入点表达式部分关键词

  1. execution:用于匹配子表达式

    1
    2
    3
    //匹配com.cjm.model包及其子包中所有类中的所有方法,返回类型任意,方法参数任意
    @Pointcut("execution(* com.cjm.model..*.*(..))")
    public void before(){}
  2. within:用于匹配连接点所在的Java类或者包

    1
    2
    3
    4
    5
    6
    //匹配Person类中的所有方法
    @Pointcut("within(com.cjm.model.Person)")
    public void before(){}
    //匹配com.cjm包及其子包中所有类中的所有方法
    @Pointcut("within(com.cjm..*)")
    public void before(){}
  3. this:用于向通知方法中传入代理对象的引用

    1
    2
    3
    4
    @Before("before() && this(proxy)")
    public void beforeAdvide(JoinPoint point, Object proxy){
    //处理逻辑
    }
  4. target:用于向通知方法中传入目标对象的引用

    1
    2
    3
    4
    @Before("before() && target(target)")
    public void beforeAdvide(JoinPoint point, Object proxy){
    //处理逻辑
    }
  5. args:用于将参数传入到通知方法中。

    1
    2
    3
    4
    @Before("before() && args(age,username)")
    public void beforeAdvide(JoinPoint point, int age, String username){
    //处理逻辑
    }

    @AspectJ的不同通知类型

  • 所有通知的value属性都是Pointcut,可以直接写表达式,也可以写成一个被@Pointcut注解过的方法,类似于下面的例字

    • 顺带介绍@Pointcut注解的格式:private void 无参数方法,方法名为切点名,且当通知多个切点时,可以使用|| 进行连接
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    @Pointcut(value = "execution(* com.xyz.aspectJ.ProductDao.save(..))")
    private void myPointcut() {
    }
    @Before(value = "aPointcut1()")
    public void before(JoinPoint joinPoint) {
    //doSomething
    }
    //---------------等效于----------------
    @Before(value = "execution(* com.xyz.aspectJ.ProductDao.save(..))")
    public void before(JoinPoint joinPoint) {
    //doSomething
    }
  • @Before 前置通知,相当于BeforeAdvice

    可以在before()方法中添加 JoinPoint joinPoint这样就可以在切面方法中获得切入点信息

    1
    2
    3
    4
    @Before(value = "myPointcut()")
    public void before(JoinPoint joinPoint) {
    //doSomething
    }
  • @AfterReturning 后置通知,相当于AfterReturningAdvice

    • 若目标方法有返回值,就需要给@AfterReturning注释中的 returning属性赋值,方法添加一个Object returnObj参数,这样可以在切面方法中获得目标方法的返回值
    • 这里returning = "returnObj""returnObj" 字符串的值可以任取,但要保证函数中传入的参数名与之相同
    1
    2
    3
    4
    @AfterReturning(value = "myPointcut()", returning = "returnObj")
    public void afterReturing(Object returnObj) {
    //doSomething
    }
  • @Around 环绕通知,相当于MethodInterceptor

    • 切面方法的返回值就是代理方法执行返回值,一般将返回值写为Object
  • 参数必须包含ProceedingJoinPoint,它的作用是调用拦截目标方法执行

    • 如果不调用 ProceedingJoinPointproceed方法,那么目标方法就不会被执行
  • ProceedingJoinPoint 对象的 proceed() 需要捕获异常,并且这里如果产生异常(即目标方法产生异常)

    1
    2
    3
    4
    5
    6
    7
    @Around(value = "myPointcut3()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
    //doSomething 假设这里开启事务
    Object obj = joinPoint.proceed(); // 执行目标方法 假设为数据库的操作
    //doSomething 假设这里提交事务
    return obj;
    }
  • @AfterThrowing异常抛出通知,相当于ThrowAdvice

    这里的throwing属性使用方法与@AfterReturning注释中的 returning属性类似,可以让代理方法得到目标方法所抛出的异常

    1
    2
    3
    4
      @AfterThrowing(value = "myPointcut4()", throwing = "e")
    public void afterThrowing(Throwable e) {
    //doSomething 假设回滚事务
    }
  • @After 最终final通知,不管是否异常,该通知都会执行

  • @DeclareParents 引介通知,相当于IntroductionInterceptor

通过Value属性定义切点(execution函数)

在通知中通过value属性定义切点,通过execution函数,可以定义切点的方法切入

1. 语法:

execution(<访问修饰符>? <返回类型> <方法名>(<参数>)<异常>)

2. 介绍这里使用的一些基本通配符的含义:

  • * 匹配任意数量的字符
  • +匹配制定数量的类及其子类,可用于表示实现此接口的所有类
  • .. 匹配本包及其子包
  • &&、||、! 切入点表达式支持逻辑运算符

3. 例:

  • 匹配所有类public方法

    execution(public * *(..))

  • 匹配指定包下所有类方法

    • execution(* com.imooc.dao.*(..)) 不包含子包
    • execution(* com.imooc.dao..*(..)) ..*表示包、子孙包下所有类
  • 匹配指定类所有方法

    execution(* com.imooc.service.UserService.*(..))

  • 匹配实现特定接口所有类方法

    execution(* com.imooc.dao.GenericDAO+.*(..))

  • 匹配所有save开头的方法

    execution(* save*(..))