• 在Spring中使用AspectJ实现AOP


     在spring中使用aspectj有2种方式:

    • xml配置
    • 注解配置(推荐)

    这2种方式需要添加的jar包都一样(待修改):

    • spring-aop.RELEASE.jar
    • aspectjweaver.jar

    在spring中使用aspectj,不需要添加aspectjrt.jar,也不需要专门的ajc编译器,使用javac编译即可。


    xml配置方式

    (1)目标接口、目标类

    新建包com.chy.dao,包下新建接口UserDao、实现类UserDaoImpl:

    public interface UserDao {
        public void addUser();
        public void deleteUser();
    }
    public class UserDaoImpl implements UserDao{
        @Override
        public void addUser() {
            System.out.println("正在添加用户...");
        }
    
        @Override
        public void deleteUser() {
            System.out.println("正在删除用户...");
        }
    }

    (2)切面

    新建包com.chy.aspect,包下新建类UserDaoAspect:

    public class UserDaoAspect {
        //前置通知要调用的方法
        public void before(){
            System.out.println("正在执行前置通知...");
        }
    
        //后置通知要调用的方法
        public void after(){
            System.out.println("正在执行后置通知...");
        }
    
        //返回通知要调用的方法
        public void afterReturning(Object obj){
            System.out.println("正在执行返回通知...");
            System.out.println("目标方法的返回值是:"+obj);
        }
    
        // 异常通知要调用的方法
        public void afterThrowing(JoinPoint point,Exception e){
            System.out.println("异常信息:"+e.getMessage());
        }
    }

    (3)xml配置

    <?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 https://www.springframework.org/schema/aop/spring-aop.xsd">
    
        <!--目标对象-->
        <bean name="userDaoImpl" class="com.chy.dao.UserDaoImpl" />
    
        <!--切面-->
        <bean name="userDaoAspect" class="com.chy.aspect.UserDaoAspect" />
    
        <!--AOP配置-->
        <aop:config>
            <!--全局切入点,所有切面都可以引用。配置切入点只能用id,不能用name -->
    <!--    <aop:pointcut id="pointCut" expression="execution(* com.chy.dao.UserDaoImpl.*(..))" />     -->
            
            <!--一个<aop:aspect>配置一个切面-->
            <aop:aspect ref="userDaoAspect">
                <!--局部切入点,只能在此切面中引用-->
                <aop:pointcut id="pointCut" expression="execution(* com.chy.dao.UserDao.*(..))" />
                
                <!--前置通知-->
                <aop:before method="before" pointcut-ref="pointCut" />
                <!--可以引用切入点,也可以现配-->
    <!--        <aop:before method="before" pointcut="execution(* com.chy.dao.UserDao.*(..))"/>     -->
                
                <!--后置通知-->
                <aop:after method="after" pointcut-ref="pointCut" />
                
                <!--返回通知-->
                <!--如果要使用目标方法的返回值,可以用returning要将目标方法的返回值传递给返回通知要调用的方法的哪个形参-->
                <aop:after-returning method="afterReturning" pointcut-ref="pointCut" returning="obj"/>
                
                <!--异常通知-->
                <!--如果要使用捕获的异常对象,可以用throwing指定要将异常对象传递给哪个形参-->
                <aop:after-throwing method="afterThrowing" pointcut-ref="pointCut" throwing="e"/>
                
            </aop:aspect>
            
        </aop:config>
        
    </beans>

    5种通知对应的方法都可以传递JoinPoint型的参数,不用传递实参,只有目标方法的返回值、捕获的异常需要传实参。

    returning是返回通知的特有属性,throwing是异常通知的特有属性。

    环绕通知

    环绕通知与其它通知有重叠,通常不与其它通知一起使用。

        // 环绕通知
        public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
            //前增强
            System.out.println("正在执行前增强...");
            //调用目标方法
            Object object=proceedingJoinPoint.proceed();
            //后增强
            System.out.println("正在执行后增强...");
            return object;
        }
                <!--环绕通知-->
                <!--ProceedingJoinPoint是JoinPoint的子类,不必手动传参-->
                <aop:around method="around" pointcut-ref="pointCut" />    

     注解配置方式

    (1)目标接口、目标类

    public interface UserDao {
        public void addUser();
        public void deleteUser();
    }
    @Repository
    public class UserDaoImpl implements UserDao{
        @Override
        public void addUser() {
            System.out.println("正在添加用户...");
        }
    
        @Override
        public void deleteUser() {
            System.out.println("正在删除用户...");
        }
    }

     (2)切面

    @Component
    @Aspect
    public class UserDaoAspect {
        //配置切入点
        @Pointcut("execution(* com.chy.dao.UserDao.*(..))")
        private void pointCut(){}
        
        /*
        前置通知
        @Before(value="pointCut()"),只有一个属性时可以只写属性值
        @Before("execution(* com.chy.dao.UserDao.*(..))"),属性值可以引用切入点,也可以现配
         */
        @Before("pointCut()")
        public void before(){
            System.out.println("正在执行前置通知...");
        }
    
        //后置通知
        @After("pointCut()")
        public void after(){
            System.out.println("正在执行后置通知...");
        }
    
        //返回通知。可传递目标方法的返回值。
        @AfterReturning(value = "pointCut()",returning = "obj" )
        public void afterReturning(Object obj){
            System.out.println("正在执行返回通知...");
            System.out.println("目标方法的返回值是:"+obj);
        }
    
        // 异常通知。可传递异常对象。
        @AfterThrowing(value = "pointCut()",throwing = "e")
        public void afterThrowing(JoinPoint point,Exception e){
            System.out.println("异常信息:"+e.getMessage());
        }   
    }

    环绕通知:

         // 环绕通知
        @Around("pointCut()")
        public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
            //前增强
            System.out.println("正在执行前增强...");
            //调用目标方法
            Object object=proceedingJoinPoint.proceed();
            //后增强
            System.out.println("正在执行后增强...");
            return object;
        }

    (3)xml配置

    <?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"
           xmlns:context="http://www.springframework.org/schema/context"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
    
        <!--使用包扫描,要扫描多个包时用逗号分隔-->
        <context:component-scan base-package="com.chy.dao,com.chy.aspect" />
    
        <!--启用AspectJ的注解-->
        <aop:aspectj-autoproxy />
    </beans>

    使用注解十分方便,xml文件也更加简洁,推荐。


    使用

    不管是xml方式,还是注解方式,使用时都是一样的:

    1)如果目标类实现了接口,不管切入点配置为接口、还是实现类:

    execution(* com.chy.dao.UserDao.*(..))
    execution(* com.chy.dao.UserDaoImpl.*(..))

    使用时都只能使用接口(代理的是接口,不是具体的实现类):

        UserDao userDao=applicationContext.getBean("userDaoImpl", UserDao.class);
        userDao.addUser();

    红色标出的两处都只能使用接口。

    (2)如果目标类没有实现接口,那切入点只能配置为目标类:

    execution(* com.chy.dao.UserDaoImpl.*(..))

    使用时自然只能使用目标类:

        UserDaoImpl userDao=applicationContext.getBean("userDaoImpl", UserDaoImpl.class);
        userDao.addUser();    

    调用方法时会自动增强。


    AOP常见术语

    •  Joinpoint (连接点):目标类中的所有方法
    • Pointcut(切入点):目标类中被增强的方法。有时候我们只增强目标类的部分方法。
    • Weaving(织入):把增强应用到目标对象创建代理对象的过程。spring aop采用动态代理织入,aspectj采用在编译期、类装载期织入。
  • 相关阅读:
    如何快速理解复杂业务,系统思考问题?
    巧用API网关构建大型应用体系架构
    当我们谈论不可变基础设施时,我们在谈论什么
    浅谈 Node.js 热更新
    阿里云云原生一体化数仓 — 数据建模新能力解读
    一个开发者自述:我是如何设计针对冷热读写场景的 RocketMQ 存储系统
    谈谈讲清楚这件事的重要性
    全链路压测:影子库与影子表之争
    问题排查不再愁,Nacos 上线推送轨迹功能
    【20220112】缺乏自信的恐惧(一)
  • 原文地址:https://www.cnblogs.com/chy18883701161/p/11144903.html
Copyright © 2020-2023  润新知