内容字号:默认大号超大号

段落设置:段首缩进取消段首缩进

字体设置:切换到微软雅黑切换到宋体

Spring AOP注解使用总结

2018-09-11 21:46 出处:清屏网 人气: 评论(0

Spring AOP基本概念

  1. 是一种动态编译期增强性AOP的实现
  2. 与IOC进行整合,不是全面的切面框架
  3. 与动态代理相辅相成
  4. 有两种实现:基于jdk动态代理、cglib

Spring AOP与AspectJ区别

  1. Spring的AOP是基于动态代理的,动态增强目标对象,而AspectJ是静态编译时增强,需要使用自己的编译器来编译,还需要织入器
  2. 使用AspectJ编写的java代码无法直接使用javac编译,必须使用AspectJ增强的ajc增强编译器才可以通过编译,写法不符合原生Java的语法;而Spring AOP是符合Java语法的,也不需要指定编译器去编译,一切都由Spring 处理。

JDK动态代理与Cglib的区别

  1. jdk的动态代理需要实现接口 InvocationHandler
  2. cglib无需实现接口,使用字节码技术去修改class文件使继承
  3. spring默认使用jdk动态代理,如果没有实现接口会使用cglib

使用步骤

  1. 定义业务组件
  2. 定义切点(重点)
  3. 定义增强处理方法(切面方法)

依赖

jar包依赖,除此以外还有spring依赖

  1. aspectjweaver.jar
  2. aspectjrt.jar
  3. aspectj.jar
  4. aopalliance.jar

maven依赖

<dependencies>
        <!-- 有此依赖会远程下载其它相关依赖 -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>4.2.9.RELEASE</version>
        </dependency>
        <!-- aspectJ AOP 织入器 -->
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.8.9</version>
        </dependency>

    </dependencies>

注解方式开发

  1. 扫描Aspect增强的类
<context:component-scan base-package="">
    <context:include-filter type="annotation"
        expression="org.aspectj.lang.annotation.Aspect"/>
</context:component-scan>
<aop:aspectj-autoproxy/>

常用注解的使用

  1. @Before:在切点方法前执行
    @Before("execution(* 包名.*.*(..))")
    

示例代码

@Aspect
public class AuthAspect {
    
    //定义切点
    @Pointcut("execution(* com.cnblogs.hellxz.service.*.*(..))")
    public void pointCut() {}
    
    //前置处理
    @Before("pointCut()")
    public void auth() {
        System.out.println("模拟权限检查……");
    }
}
  1. @After:在切点方法后执行
    • 用法同@Before
  2. @Around:在切点方法外环绕执行
    • 在增强的方法上 @Around("execution(* 包名.*(..))") 或使用切点 @Around("pointcut()")
    • 接收参数类型为 ProceedingJoinPoint ,必须有这个参数在切面方法的入参第一位
    • 返回值为Object
    • 需要执行ProceedingJoinPoint对象的proceed方法,在这个方法前与后面做环绕处理,可以决定何时执行与完全阻止方法的执行
    • 返回proceed方法的返回值
    • @Around相当于@Before和@AfterReturning功能的总和
    • 可以改变方法参数,在proceed方法执行的时候可以传入Object[]对象作为参数,作为目标方法的实参使用。
    • 如果传入Object[]参数与方法入参数量不同或类型不同,会抛出异常
    • 通过改变proceed()的返回值来修改目标方法的返回值

示例代码

@Aspect
public class TxAspect {
    
    //环绕处理
    @Around("execution(* com.cnblogs.hellxz.service.*.*(..))")
    Object auth(ProceedingJoinPoint point) {
        
        Object object = null;
        try {
            System.out.println("事务开启……");
            //放行
            object = point.proceed();
            System.out.println("事务关闭……");
        } catch (Throwable e) {
            e.printStackTrace();
        }
        
        return object;
    }
}
  1. @AfterRetruning: 在方法返回之前,获取返回值并进行记录操作
    • 和上边的方法不同的地方是该注解除了切点,还有一个返回值的对象名
    • 不同的两个注解参数:returning与pointcut,其中pointcut参数可以为切面表达式,也可为切点
    • returning定义的参数名作为切面方法的入参名,类型可以指定。如果切面方法入参类型指定Object则无限制,如果为其它类型,则当且仅当目标方法返回相同类型时才会进入切面方法,否则不会
    • 还有一个默认的value参数,如果指定了pointcut则会覆盖value的值
    • 与@After类似,但@AfterReturning只有方法成功完成才会被织入,而@After不管结果如何都会被织入

虽然可以拿到返回值,但无法改变返回值

示例代码

@Aspect
public class AfterReturningAspect {

    @AfterReturning(returning="rvt",
            pointcut = "execution(* com.cnblogs.hellxz.service.*.*(..))")
    //声明rvt时指定的类型会限定目标方法的返回值类型,必须返回指定类型或者没有返回值
    //rvt类型为Object则是不对返回值做限制
    public void log(Object rvt) {
        System.out.println("获取目标返回值:"+ rvt);
        System.out.println("假装在记录日志……");
    }
    
    /**
     * 这个方法可以看出如果目标方法的返回值类型与切面入参的类型相同才会执行此切面方法
     * @param itr
     */
    @AfterReturning(returning="itr", 
            pointcut="execution(* com.cnblogs.hellxz.service.*.*(..))")
    public void test(Integer itr) {
        System.out.println("故意捣乱……:"+ itr);
    }
}
  1. @AfterThrowing: 在异常抛出前进行处理,比如记录错误日志
    @AfterReturning
    @AfterReturning
    

示例代码

@Aspect
public class AfterThrowingAspect {

    @Pointcut("execution(* com.cnblogs.hellxz.test.*.*(..))")
    public void pointcut() {}
    
    /**
     * 如果抛出异常在切面中的几个异常类型都满足,那么这几个切面方法都会执行
     */
    @AfterThrowing(throwing="ex1", 
            pointcut="pointcut()")
    //无论异常还是错误都会记录
    //不捕捉错误可以使用Exception
    public void throwing(Throwable ex1) {
        System.out.println("出现异常:"+ex1);
    }
    
    @AfterThrowing(throwing="ex", 
            pointcut="pointcut()")
    //只管IOException的抛出
    public void throwing2(IOException ex) {
        System.out.println("出现IO异常: "+ex);
    }
}

pointcut定义的切点方法在@Before/@After/@Around需要写在双引号中,e.g. @Before("pointCut()")

JoinPoint的概念与方法说明

概念

  • 顾名思义,连接点,织入增强处理的连接点
  • 程序运行时的目标方法的信息都会封装到这个连接点对象中
  • 此连接点只读

方法说明

Object[] getArgs()
Signature getSignature()
Object getTarget()
Object getThis()

使用

  • 在@Before/@After/@AfterReturning/@AfterThrowing所修饰的切面方法的参数列表中加入JoinPoint对象,可以使用这个对象获得整个增强处理中的所有细节
  • 此方法不适用于@Around, 其可用ProceedingJoinPoint作为连接点

ProceedingJoinPoint的概念与方法说明

概念

  • 是JoinPoint的子类
  • 与JoinPoint概念基本相同,区别在于是可修改的
  • 使用@Around时,第一个入参必须为ProceedingJoinPoint类型
  • 在@Around方法内时需要执行proceed()或proceed(Object[] args)方法使方法继续,否则会一直处于阻滞状态

方法说明

ProceedingJoinPoint是JoinPoint的子类,包含其所有方法外,还有两个公有方法

  • Object proceed() :执行此方法才会执行目标方法
  • Object proceed(Object[] args) :执行此方法才会执行目标方法,而且会使用Object数组参数去代替实参,如果传入Object[]参数与方法入参数量不同或类型不同,会抛出异常

    通过修改proceed方法的返回值来修改目标方法的返回值

编入的优先级

优先级最高的会最先被织入,在退出连接点的时候,具有最高的优先级的最后被织入

当不同切面中两个增强处理切入同一连接点的时候,Spring AOP 会使用随机织入的方式

如果想要指定优先级,那么有两种方案:

org.springframework.core.Ordered
@Order

访问目标方法的形参

除了使用JoinPoint或ProceedingJoinPoint来获取目标方法的相关信息外(包括形参),如果只是简单访问形参,那么还有一种方法可以实现

  • 在pointcut的execution表达式之后加入 && args(arg0,arg1) 这种方式
@Aspect
public class AccessInputArgs {

    @Before("execution(* com.cnblogs.hellxz.test.*.*(..)) && args(arg0, arg1)")
    public void access(String arg0, String arg1){
        System.out.println("接收到的参数为arg0="+arg0+",arg1="+arg1);
    }
}

注意:通过这种方式会只匹配到方法只有指定形参 数量 的方法,并且,在切面方法中指定的 类型 会限制目标方法,不符合条件的不会进行织入增强

定义切入点

通过定义切入点,我们可以复用切点,减少重复定义切点表达式等

切入点定义包含两个部分:

  • 切入点表达式
  • 包含名字和任意参数的方法签名

使用@Pointcut注解进行标记一个无参无返回值的方法,加上切点表达式

@Pointcut("execution(* com.cnblogs.hellxz.test.*.*(..))")
    public void pointcut(){}

切入点指示符

Spring AOP 支持10种切点指示符:execution、within、this、target、args、@target、@args、@within、@annotation、bean下面做下简记(没有写@Pointcut(),请注意):

  • execution: 用来匹配执行方法的连接点的指示符。

    用法相对复杂,格式如下: execution(权限访问符 返回值类型 方法所属的类名包路径.方法名(形参类型) 异常类型)

    e.g. execution(public String com.cnblogs.hellxz.test.Test.access(String,String))

    权限修饰符和异常类型可省略,返回类型支持通配符,类名、方法名支持*通配,方法形参支持..通配

  • within: 用来限定连接点属于某个确定类型的类。

    within(com.cnblogs.hellxz.test.Test)

    within(com.cnblogs.hellxz.test.

    ) //包下类

    within(com.cnblogs.hellxz.test..

    ) //包下及子包下
  • this和target : this用于没有实现接口的Cglib代理类型,target用于实现了接口的JDK代理目标类型
    举例:this(com.cnblogs.hellxz.test.Foo) //Foo没有实现接口,使用Cglib代理,用this
    实现了个接口public class Foo implements Bar{...}
    target(com.cnblogs.hellxz.test.Test) //Foo实现了接口的情况
  • args : 对连接点的参数类型进行限制,要求参数类型是指定类型的实例。
    args(Long)
  • @target : 用于匹配 类头有指定注解 的连接点
    @target(org.springframework.stereotype.Repository)
  • @args : 用来匹配连接点的参数的,@args指出连接点在运行时传过来的参数的类必须要有指定的注解
    java @Pointcut("@args(org.springframework.web.bind.annotation.RequestBody)") public void methodsAcceptingEntities() {}
  • @within : 指定匹配必须包括某个注解的的类里的所有连接点
    @within(org.springframework.stereotype.Repository)
  • @annotation : 匹配那些有指定注解的连接点
    @annotation(org.springframework.stereotype.Repository)
  • bean: 用于匹配指定Bean实例内的连接点,传入bean的id或name,支持使用*通配符

    切点表达式组合

    使用&&、||、!、三种运算符来组合切点表达式,表示与或非的关系

    execution(* com.cnblogs.hellxz.test.*.*(..)) && args(arg0, arg1)

部分代码可以参考: 我的Github

分享给小伙伴们:
本文标签: SpringAOP

相关文章

发表评论愿您的每句评论,都能给大家的生活添色彩,带来共鸣,带来思索,带来快乐。

CopyRight © 2015-2016 QingPingShan.com , All Rights Reserved.

清屏网 版权所有 豫ICP备15026204号