• Spring 基于 AspectJ 的 AOP 开发


    Spring 基于 AspectJ 的 AOP 开发

    在 Spring 的 aop 代理方式中, AspectJ 才是主流。


    1. AspectJ 简介

    • AspectJ 是一个基于 java 语言的 AOP 框架
    • Spring 2.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
    

    2. 注解开发:环境准备

    <?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"> <!-- bean definitions here -->
    
    </beans>
    

    3. @Aspect 提供不同的通知类型

    • @Before 前置通知,相当于 BeforeAdvice
    • @AfterReturning 后置通知,相当于 AfterReturning
    • @Around 环绕通知,相当于 MethodInterceptor
    • @After Throwing 异常抛出通知,相当于 ThrowAdvice
    • After 最终通知,不管是否异常,改通知都会执行
    • DeclareParents 引介通知,相当于 IntroductionInterceptor (不要求掌握)
    a. @Before 前置通知

    可以在方法中传入 JoinPoint 对象,用来获得切点信息

    // 要增强的代码
    @Before(value = "execution(* com.test.aspectJ.demo1.ProductDao.save(..))")
        public void before(JoinPoint joinPoint) {
            System.out.println("前置通知=========" + joinPoint);
        }
    
    b. @AfterReturning 后置通知

    通过 returning 属性,可以定义方法返回值,作为参数:

    // result 拿到返回值
    @AfterReturning(value = "execution(* com.test.aspectJ.demo1.ProductDao.update(..))", returning = "result")
    public void afterReturning(Object result) {
        System.out.println("后置通知=========="+result);
    }
    
    c. @Around 环绕通知
    • around 方法的返回值就是目标代理方法执行返回值
    • 参数为 ProceedingJoinPoint 可以调用拦截目标方法执行
    @Around(value = "execution(* com.test.aspectJ.demo1.ProductDao.delete(..))")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("环绕前通知============");
        Object obj = joinPoint.proceed(); //执行目标方法
        System.out.println("环绕后通知==========");
        return obj;
    }
    

    重点:如果不调用 ProceedingJoinPoint 的 proceed 方法,那么目标方法就被拦截了。

    d. @AfterThrowing 异常抛出通知

    通过设置 throwing 属性,可以设置发生异常对象参数

    @AfterThrowing(value = "execution(* com.test.aspectJ.demo1.ProductDao.find(..))", throwing = "e")
    public void afterThrowing(Throwable e) {
        System.out.println("异常抛出通知==========="+e.getMessage());
    }
    
    e. After 最终通知

    无论是否出现异常,最终通知总是会被执行

    @AfterThrowing(value = "execution(* com.test.aspectJ.demo1.ProductDao.findAll(..))", throwing = "e")
    public void afterThrowing(Throwable e) {
        System.out.println("异常抛出通知==========="+e.getMessage());
    }
    

    4. 在通知中通过 value 属性定义切点

    通过 execution 函数,可以定义切点的方式切入

    语法:

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

    例如:

    • 匹配所有类 public 方法:

      execution(public * * (..))
      
    • 匹配指定包下所有类方法:

      execution(* com.test.dao.*(.))    //不包含子包
      execuiton(* com.test.dao..*(..)) // ..* 表示包,子孙包下所有类
      
    • 匹配指定类所有方法:

      execution(* com.atest.service.UserService.*(..))
      
    • 匹配实现特定接口所有类方法:

      execution(* com.test.doa.GenericDao + .*(..))
      
    • 匹配所有 save 开头的方法:

      execution(* save*(..))
      

    5. 为目标类定义切面类

    定义切面类:

    @Aspect
    public class MyAspectAnno {}
    

    6. 通过 @Pointcut 为切点命名

    • 在每个通知内定义切点,会造成工作量大,不易维护,对于重复的切点,可以使用 @Point 进行定义
    • 切点方法:private void 无参数方法,方法名为切点名
    • 当通知多个切点时,可以使用 || 进行连接
    @Pointcut(value = "execution(* com.test.aspectJ.demo1.ProductDao.save(..))")
    private void myPointcut1() {}
    

    具体代码:SpringDemo1.demo1()


    基于 AsepctJ 的 XML 方法的 AOP 开发

    1. 编写切面类

    public class MyAspectXml {
    
        //前置通知
        public void before(JoinPoint joinPoint) {
            System.out.println("XML方法的前置通知=========="+joinPoint);
        }
    
        // 后置通知
        public void afterReturning(Object result) {
            System.out.println("XML方法的后置通知==========="+result);
        }
    
        //环绕通知
        public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
            System.out.println("XML方式的环绕前通知==========");
            Object obj = joinPoint.proceed();
            System.out.println("XML方式的环绕后通知");
            return obj;
        }
    
        // 异常抛出通知
        public void afterThrowing(Throwable e) {
            System.out.println("XML方法的异常抛出通知"+e.getMessage());
        }
    
        // 最终通知
        public void after() {
            System.out.println("XML方法的最终通知");
        }
    
    }
    

    2. 完成切面类的配置

    <!-- 配置切面类 -->
    <bean id="myAspectXml" class="com.test.aspectJ.demo2.MyAspectXml" />
    

    3. 配置 AOP 完成增强

    <!-- aop 的相关配置 -->
    <aop:config>
        <!-- 配置切入点 -->
        <aop:pointcut id="pointcut1" expression="execution(* com.test.aspectJ.demo2.CustomerDaoImpl.save())" />
        <aop:pointcut id="pointcut2" expression="execution(* com.test.aspectJ.demo2.CustomerDaoImpl.update())" />
        <aop:pointcut id="pointcut3" expression="execution(* com.test.aspectJ.demo2.CustomerDaoImpl.delete())" />
        <aop:pointcut id="pointcut4" expression="execution(* com.test.aspectJ.demo2.CustomerDaoImpl.findOne())" />
        <aop:pointcut id="pointcut5" expression="execution(* com.test.aspectJ.demo2.CustomerDaoImpl.findAll())" />
        <!-- 配置AOP的切面 -->
        <aop:aspect ref="myAspectXml">
            <!-- 配置前置通知 -->
            <aop:before method="before" pointcut-ref="pointcut1" />
            <!-- 配置后置通知 -->
            <aop:after-returning method="afterReturning" pointcut-ref="pointcut2" returning="result" />
            <!-- 配置环绕通知 -->
            <aop:around method="around" pointcut-ref="pointcut3" />
            <!-- 配置异常抛出通知 -->
            <aop:after-throwing method="afterThrowing" pointcut-ref="pointcut4" throwing="e"/>
            <!-- 配置最终通知 -->
            <aop:after method="after" pointcut-ref="pointcut5" />
        </aop:aspect>
    </aop:config>
    

    具体代码:SpringDemo2.demo1()

  • 相关阅读:
    k3sk8s集群-node节点设置不可调度或者删除node节点
    CentOS8防暴力破解fail2ban
    网站挂马情况处理
    linux给普通用户赋予sudo权限免密码
    Mockito (二十四)
    Mockito (二十三)
    Mockito (二十二)
    Mockito (二十一)
    Mockito (二十)
    Mockito (十九)
  • 原文地址:https://www.cnblogs.com/weixuqin/p/11068305.html
Copyright © 2020-2023  润新知