• Spring AOP面向切面编程详解


    前言

    AOP即面向切面编程,是一种编程思想,OOP的延续。在程序开发中主要用来解决一些系统层面上的问题,比如日志,事务,权限等等。在阅读本文前希望您已经对Spring有一定的了解

    注:在能对代码进行添加注解方式实现AOP的话,并不推荐使用XML方式。换言之在XML方式配置更适用于不能对代码添加注解的情况下(注解配置方式推荐值>XML配置方式推荐值)

    AOP相关术语

    1.通知(Advice):在切面的某个特定的连接点上执行的动作,即当程序到达一个执行点后会执行相对应的一段代码,也称为增强处理。通知共有如下5种类型[前置通知 后置通知 返回通知 环绕通知 抛出异常后通知]
    
    2.连接点(JoinPoint):程序执行的某个特定位置,例如类初始化前,类初始化后,方法执行前,方法执行后,方法抛出异常时等,Spring只支持方法级别的连接点,即方法执行前,方法执行后,方法抛出异常时
    
    3.切入点(Pointcut):切入点是一个筛选连接点的过程,因为在你的工程中可能有很多连接点,你只是想让其中几个,在调用这几个方法之前、之后或者抛出异常时干点什么,那么就用切入点来定义这几个方法,让切点来筛选连接点,选中那几个你想要的方法
    
    4.切面(Aspect):切面通常是指一个类,是通知和切入点的结合。到这里会发现连接点就是为了让你好理解切点产生的。通俗来说切面的配置可以理解为:什么时候在什么地方做什么事。切入点说明了在哪里干(指定到方法),通知说明了什么时候干什么
    
    5.引入(Introduction):引入允许我们向现有的类添加新方法或属性
    
    6.织入(Weaving):把切面应用到目标对象来创建新的代理对象的过程,织入一般发生在如下几个时机:
        (1)编译时:当一个类文件被编译时进行织入,这需要特殊的编译器才可以做的到,例如AspectJ的织入编译器
        (2)类加载时:使用特殊的ClassLoader在目标类被加载到程序之前增强类的字节代码
        (3)运行时:切面在运行的某个时刻被织入,SpringAOP就是以这种方式织入切面的,原理应该是使用了JDK的动态代理技术

    基于XML方式配置AOP

    正常通知

    1.编写业务类

    public class HelloWorldBusiness {
        public String sayHelloWorld(String language) {
            String result = "Hello World " + language;
            System.out.println("真正的业务方法执行啦~~~");
            return result;
        }
    }

    2.编写切面类

    public class HelloWorldBusinessAspect {
        public void beforeSayHelloWorld(String language) {
            System.out.println("执行方法前运行,参数为:" + language);
        }
    
        public void afterSayHelloWorld(String language) {
            System.out.println("执行方法后运行,参数为:" + language);
        }
    
        public void afterReturningSayHelloWorld(String language, String result) {
            System.out.println("执行方法返回后运行,参数为:" + language + " 方法返回值为:" + result);
        }
    
        public void afterThrowingHelloWorld(String language, Throwable e) {
            System.out.println("执行方法抛出异常后运行,参数为:" + language + "异常为:" + e);
        }
    }

    3.编写配置文件

    <?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-4.3.xsd">
    
        <!-- 配置业务Bean -->
        <bean id="helloWorldBusiness" class="roberto.growth.process.aop.HelloWorldBusiness" />
    
        <!-- 配置切面Bean -->
        <bean id="helloWorldBusinessAspect" class="roberto.growth.process.aop.HelloWorldBusinessAspect" />
    
        <!-- 配置一个切面 -->
        <aop:config>
            <aop:aspect id="helloWorldAspect" ref="helloWorldBusinessAspect">
                <!-- 配置一个切点 -->
                <aop:pointcut id="sayHelloWorldPoint" expression="execution(public * roberto.growth.process.aop.HelloWorldBusiness.sayHelloWorld(..)) and args(language)" />
                <!-- 配置前置通知 -->
                <aop:before pointcut-ref="sayHelloWorldPoint" method="beforeSayHelloWorld" arg-names="language"/>
                <!-- 配置前置通知 -->
                <aop:after pointcut-ref="sayHelloWorldPoint" method="afterSayHelloWorld" arg-names="language"/>
                <!-- 配置后置返回通知 -->
                <aop:after-returning pointcut-ref="sayHelloWorldPoint" method="afterReturningSayHelloWorld" arg-names="language,result" returning="result" />
                <!-- 异常通知 -->
                <aop:after-throwing pointcut-ref="sayHelloWorldPoint" method="afterThrowingHelloWorld" arg-names="language,e" throwing="e" />
            </aop:aspect>
        </aop:config>
    </beans>

    4.运行HelloWorldBusiness的sayHelloWorld方法输出结果为

    执行方法前运行,参数为:JAVA
    真正的业务方法执行啦~~~
    执行方法后运行,参数为:JAVA
    执行方法返回后运行,参数为:JAVA 方法返回值为:Hello World JAVA

    环绕通知

    1.编写业务类

    public class HelloWorldBusiness {
        public String sayHelloWorld(String language) {
            String result = "Hello World " + language;
            System.out.println("真正的业务方法执行啦~~~");
            return result;
        }
    }

    2.编写切面类

    public class HelloWorldBusinessAspect {
        public void aroundSayHelloWorld(ProceedingJoinPoint joinPoint) {
            String language = (String) joinPoint.getArgs()[0];
            try {
                System.out.println("执行方法前运行,参数为:" + language);
                String result = (String) joinPoint.proceed();
                System.out.println("执行方法后运行,参数为:" + language + " 方法返回值为:" + result);
            } catch (Throwable e) {
                System.out.println("执行方法抛出异常后运行,参数为:" + language + "异常为:" + e);
            }
        }
    }

    3.编写配置文件

    <?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-4.3.xsd">
    
        <!-- 配置业务Bean -->
        <bean id="helloWorldBusiness" class="roberto.growth.process.aop.HelloWorldBusiness" />
    
        <!-- 配置切面Bean -->
        <bean id="helloWorldBusinessAspect" class="roberto.growth.process.aop.HelloWorldBusinessAspect" />
    
        <!-- 配置一个切面 -->
        <aop:config>
            <aop:aspect id="helloWorldAspect" ref="helloWorldBusinessAspect">
                <!-- 配置一个切点 -->
                <aop:pointcut id="sayHelloWorldPoint" expression="execution(public * roberto.growth.process.aop.HelloWorldBusiness.sayHelloWorld(..))" />
                <!-- 配置环绕通知 -->
                <aop:around pointcut-ref="sayHelloWorldPoint" method="aroundSayHelloWorld" />
            </aop:aspect>
        </aop:config>
    </beans>

    4.运行HelloWorldBusiness的sayHelloWorld方法输出结果为

    执行方法前运行,参数为:JAVA
    真正的业务方法执行啦~~~
    执行方法后运行,参数为:JAVA 方法返回值为:Hello World JAVA

    使用MethodInterceptor实现AOP

    1.编写业务类

    public class HelloWorldBusiness {
        public String sayHelloWorld(String language) {
            String result = "Hello World " + language;
            System.out.println("真正的业务方法执行啦~~~");
            return result;
        }
    }

    2.编写拦截器类 实现MethodInterceptor方法

    public class HelloWorldBusinessAspect implements MethodInterceptor {
        @Override
        public Object invoke(MethodInvocation invocation) throws Throwable {
            // 获取被增强对象参数列表
            String language = (String) invocation.getArguments()[0];
            // 获取被增强对象的方法
            Method method = invocation.getMethod();
            // 继续执行业务方法
            System.out.println("执行" + method.getName() + "方法前运行,参数为: " + language);
            Object result = invocation.proceed();
            System.out.println("执行方法返回后运行,参数为:" + language + " 方法返回值为:" + result);
            return result;
        }
    }

    3.编写配置文件

    <?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-4.3.xsd">
    
        <!-- 配置业务Bean -->
        <bean id="helloWorldBusiness" class="roberto.growth.process.aop.HelloWorldBusiness" />
    
        <!-- 配置切面Bean -->
        <bean id="helloWorldBusinessAspect" class="roberto.growth.process.aop.HelloWorldBusinessAspect" />
    
        <aop:config>
            <!-- 配置一个切点 -->
            <aop:pointcut id="sayHelloWorldPoint" expression="execution(public * roberto.growth.process.aop.HelloWorldBusiness.sayHelloWorld(..))" />
    
            <!-- 配置通知类 -->
            <aop:advisor advice-ref="helloWorldBusinessAspect" pointcut-ref="sayHelloWorldPoint" />
        </aop:config>
    </beans>

    4.运行HelloWorldBusiness的sayHelloWorld方法输出结果为

    执行sayHelloWorld方法前运行,参数为: JAVA
    真正的业务方法执行啦~~~
    执行方法返回后运行,参数为:JAVA 方法返回值为:Hello World JAVA

    基于注解方式配置AOP

    正常通知

    1.编写业务类

    @Component
    public class HelloWorldBusiness {
        public String sayHelloWorld(String language) {
            String result = "Hello World " + language;
            System.out.println("真正的业务方法执行啦~~~");
            return result;
        }
    }

    2.编写切面类

    @Aspect
    @Component
    public class HelloWorldBusinessAspect {
        @Pointcut("execution(public * roberto.growth.process.aop.HelloWorldBusiness.sayHelloWorld(..)) && args(language)")
        public void sysHelloWorldPointCut(String language) {
    
        }
    
        @Before("sysHelloWorldPointCut(language)")
        public void beforeSayHelloWorld(String language) {
            System.out.println("执行方法前运行,参数为:" + language);
        }
    
        @After("sysHelloWorldPointCut(language)")
        public void afterSayHelloWorld(String language) {
            System.out.println("执行方法后运行,参数为:" + language);
        }
    
        @AfterReturning(pointcut = "sysHelloWorldPointCut(language)", returning = "result")
        public void afterReturningSayHelloWorld(String language, String result) {
            System.out.println("执行方法返回后运行,参数为:" + language + " 方法返回值为:" + result);
        }
    
        @AfterThrowing(pointcut = "sysHelloWorldPointCut(language)", throwing = "e")
        public void afterThrowingHelloWorld(String language, Throwable e) {
            System.out.println("执行方法抛出异常后运行,参数为:" + language + "异常为:" + e);
        }
    }

    3.编写配置类(使用EnableAspectJAutoProxy注解启用自动代理功能,即aspectJ的cglib代理方式)

    @Configuration
    @EnableAspectJAutoProxy
    @ComponentScan(basePackages = "roberto.growth.process")
    public class ApplicationConfig {
    
    }

    4.运行HelloWorldBusiness的sayHelloWorld方法输出结果为

    执行方法前运行,参数为:JAVA
    真正的业务方法执行啦~~~
    执行方法后运行,参数为:JAVA
    执行方法返回后运行,参数为:JAVA 方法返回值为:Hello World JAVA

    环绕通知

    1.编写业务类

    @Component
    public class HelloWorldBusiness {
        public String sayHelloWorld(String language) {
            String result = "Hello World " + language;
            System.out.println("真正的业务方法执行啦~~~");
            return result;
        }
    }

    2.编写切面类

    @Aspect
    @Component
    public class HelloWorldBusinessAspect {
        @Pointcut("execution(public * roberto.growth.process.aop.HelloWorldBusiness.sayHelloWorld(..))")
        public void sysHelloWorldPointCut() {
    
        }
    
        @Around("sysHelloWorldPointCut()")
        public void aroundSayHelloWorld(ProceedingJoinPoint joinPoint) {
            String language = (String) joinPoint.getArgs()[0];
            try {
                System.out.println("执行方法前运行,参数为:" + language);
                String result = (String) joinPoint.proceed();
                System.out.println("执行方法后运行,参数为:" + language + " 方法返回值为:" + result);
            } catch (Throwable e) {
                System.out.println("执行方法抛出异常后运行,参数为:" + language + "异常为:" + e);
            }
        }
    }

    3.编写配置类(使用EnableAspectJAutoProxy注解启用自动代理功能,即aspectJ的cglib代理方式)

    @Configuration
    @EnableAspectJAutoProxy
    @ComponentScan(basePackages = "roberto.growth.process")
    public class ApplicationConfig {
    
    }

    4.运行HelloWorldBusiness的sayHelloWorld方法输出结果为

    执行方法前运行,参数为:JAVA
    真正的业务方法执行啦~~~
    执行方法后运行,参数为:JAVA 方法返回值为:Hello World JAVA

    转载:https://blog.csdn.net/RobertoHuang/article/details/70148474

  • 相关阅读:
    【DDD】领域驱动设计实践 —— 架构风格及架构实例
    【DDD】领域驱动设计精要
    Zynq UltraScale+ cross compiler
    Platform device/driver注册过程 (转)
    static inline extern等概念
    (int argc, char *argv[]) 指针数组
    linux man 1,2,3 命令
    指针左值错误
    arm ds5 编译选项
    在JTAG菊花链拓扑对设备编程
  • 原文地址:https://www.cnblogs.com/cainiao-Shun666/p/9176843.html
Copyright © 2020-2023  润新知