美文网首页
Spring Aop:一、四种advice

Spring Aop:一、四种advice

作者: weihy | 来源:发表于2019-10-31 18:16 被阅读0次

    本文参考实验楼教程:https://www.shiyanlou.com/courses/578/learning/?id=1940

      Spring Aop即 Aspect-Oriented Programming,面向切面编程。是用于处理系统中各个模块(不同方法)之间的交叉关注的问题。
      简单地说,就是一个拦截器,拦截一些处理过程。
      例如:当一个method被执行,Spring AOP能够劫持正在运行的method,在method执行前后加入一些额外的功能。(日志记录、性能统计、权限控制等)

    实验环境:
      JDK1.8
      idea 开发环境

    一、Spring AOP 支持4种类型的通知(advice)

    • Before advice --- method 执行前通知
    • After returning advice --- method 执行完毕或者返回一个结果后通知
    • After throwing advice --- method 抛出异常后通知
    • Around advice --- 环绕通知,结合了以上三种

    下面来学习如何使用这四种通知。
    以下是整个实验项目结构:

    project.png

    1、准备环境,创建一个maven工程SpringAop

     1)、添加maven依赖,对应的pom.xml文件如下:

    <?xml version="1.0" encoding="UTF-8"?>
    
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
      <modelVersion>4.0.0</modelVersion>
    
      <groupId>com.shiyanlou.spring</groupId>
      <artifactId>SpringAopTest</artifactId>
      <version>1.0-SNAPSHOT</version>
    
      <name>SpringAopTest</name>
      <!-- FIXME change it to the project's website -->
      <url>http://www.example.com</url>
    
      <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>1.7</maven.compiler.source>
        <maven.compiler.target>1.7</maven.compiler.target>
        <spring.version>5.1.1.RELEASE</spring.version>
      </properties>
    
      <dependencies>
        <dependency>
          <groupId>junit</groupId>
          <artifactId>junit</artifactId>
          <version>4.11</version>
          <scope>test</scope>
        </dependency>
        <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-core</artifactId>
          <version>${spring.version}</version>
        </dependency>
        <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-context</artifactId>
          <version>${spring.version}</version>
        </dependency>
        <dependency>
          <groupId>org.aspectj</groupId>
          <artifactId>aspectjweaver</artifactId>
          <version>1.9.2</version>
        </dependency>
        <dependency>
          <groupId>org.aspectj</groupId>
          <artifactId>aspectjtools</artifactId>
          <version>1.9.2</version>
        </dependency>
        <dependency>
          <groupId>org.aspectj</groupId>
          <artifactId>aspectjrt</artifactId>
          <version>1.9.2</version>
        </dependency>
        <dependency>
          <groupId>cglib</groupId>
          <artifactId>cglib</artifactId>
          <version>3.2.9</version>
        </dependency>
      </dependencies>
    
    </project>
    

     2)、创建类 CustomerService.java 如下:

    package com.shiyanlou.spring.aop.advice;
    
    import org.springframework.stereotype.Service;
    
    /**
     * Created by Administrator on 2019/10/30.
     */
    
    public class CustomerService {
        private String name;
        private String url;
    
        public CustomerService() {
        }
    
        public CustomerService(String name, String url) {
            this.name = name;
            this.url = url;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public String getUrl() {
            return url;
        }
    
        public void setUrl(String url) {
            this.url = url;
        }
    
        public void printName(){
            System.out.println("Customer name: " + name);
        }
    
        public void printURL(){
            System.out.println("Customer URL: " + url);
        }
    
        public void printThrowException(){
            throw new IllegalArgumentException();
        }
    }
    
    

     3)、在src/main/resources/下新建 Xml 配置文件 SpringAopAdvice.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"
           xsi:schemaLocation="
                                http://www.springframework.org/schema/beans
                                http://www.springframework.org/schema/beans/spring-beans.xsd">
    
        <bean id="customerService" class="com.shiyanlou.spring.aop.advice.CustomerService">
            <property name="name" value="weihouye"/>
            <property name="url" value="www.weihouyeaiyangzhan.com"/>
        </bean>
    
    </beans>
    

     4)、在com.shiyanlou.spring包下的App.java中编写如下:

    package com.shiyanlou.spring;
    
    import com.shiyanlou.spring.aop.advice.CustomerService;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    public class App
    {
        private static ApplicationContext context;
    
        public static void main( String[] args ) {
    
            context = new ClassPathXmlApplicationContext("SpringAopAdvice.xml");
    
            CustomerService cust = (CustomerService) context.getBean("customerService");
    
            System.out.println("*************************");
            cust.printName();
            System.out.println("*************************");
            cust.printURL();
            System.out.println("*************************");
            try {
                cust.printThrowException();
            } catch (IllegalArgumentException e){
            }
    
        }
    }
    
    

     5)、运行App.java文件,结果如下:

    
    *************************
    Customer name: weihouye
    *************************
    Customer URL: www.weihouyeaiyangzhan.com
    *************************
    
    

    2、Before advice

     1)、定义org.springframework.aop.MethodBeforeAdvice的实现类WeiBeforeMethod,重写before方法,该方法将在被代理的类的方法之前运行。代码如下:

    package com.shiyanlou.spring.aop.advice;
    
    import org.springframework.aop.MethodBeforeAdvice;
    
    import java.lang.reflect.Method;
    
    /**
     * Created by Administrator on 2019/10/30.
     */
    public class WeiBeforeMethod implements MethodBeforeAdvice {
        @Override
        public void before(Method method, Object[] args, Object target) throws Throwable {
            System.out.println("WeiBeforeMethod: Before method hi wei!!!");
        }
    }
    
    

     2)、在SpringAopAdvice.xml配置文件中加入新的bean配置weiBeforeMethod,然后创建一个代理bean(Proxy),命名为customerServiceProxy
      代理bean中target定义了被代理(被劫持)的类,拦截器interceptorNames则定义了想要用哪个类(advice)劫持target,拦截器可以有多个,所以写在<list>中。配置如下:

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="
                                http://www.springframework.org/schema/beans
                                http://www.springframework.org/schema/beans/spring-beans.xsd">
    
        <bean id="customerService" class="com.shiyanlou.spring.aop.advice.CustomerService">
            <property name="name" value="weihouye"/>
            <property name="url" value="www.weihouyeaiyangzhan.com"/>
        </bean>
    
        <bean id="weiBeforeMethod" class="com.shiyanlou.spring.aop.advice.WeiBeforeMethod"/>
    
        <bean id="customerServiceProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
            <property name="target" ref="customerService"/>
            <property name="interceptorNames">
                <list>
                    <value>weiBeforeMethod</value>
                </list>
            </property>
        </bean>
    </beans>
    

    用 Spring proxy 之前,必须添加 CGLIB 类库,在之前的 pom.xml 文件中,已经添加到了其中,以下是 pom.xml 依赖:

            <dependency>
                <groupId>cglib</groupId>
                <artifactId>cglib</artifactId>
                <version>3.2.9</version>
            </dependency>
    

     3)、App.java如下

    package com.shiyanlou.spring;
    
    import com.shiyanlou.spring.aop.advice.CustomerService;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    /**
     * Hello world!
     *
     */
    public class App
    {
        private static ApplicationContext context;
    
        public static void main( String[] args ) {
    
            context = new ClassPathXmlApplicationContext("SpringAopAdvice.xml");
    
            CustomerService cust = (CustomerService) context.getBean("customerServiceProxy");
    
            System.out.println("*************************");
            cust.printName();
            System.out.println("*************************");
            cust.printURL();
            System.out.println("*************************");
            try {
                cust.printThrowException();
            } catch (IllegalArgumentException e){
            }
        }
    }
    

    ps:注意 App.java中获取的是代理bean,即customerServiceProxy,由代理bean去执行原来的功能。

     4)、运行结果如下:

    
    *************************
    WeiBeforeMethod: Before method hi wei!!!
    Customer name: weihouye
    *************************
    WeiBeforeMethod: Before method hi wei!!!
    Customer URL: www.weihouyeaiyangzhan.com
    *************************
    WeiBeforeMethod: Before method hi wei!!!
    
    

      可以看到,被代理类CustomerServiceWeiBeforeMethod劫持,被代理类中的方法被执行前,均先执行WeiBeforeMethod中的before方法。

    3、After Returning Advice

     1)、创建一个org.springframework.aop.AfterReturningAdvice接口的实现类WeiAfterMethod.java,重写afterReturning方法,用于劫持被代理类;被代理类中的方法执行完毕或者返回结果后,将执行WeiAfterMethod中的afterReturning方法,若中途异常退出,则不会执行该方法。
    WeiAfterMethod.java代码如下:

    package com.shiyanlou.spring.aop.advice;
    
    import org.springframework.aop.AfterReturningAdvice;
    
    import java.lang.reflect.Method;
    
    /**
     * Created by Administrator on 2019/10/30.
     */
    public class WeiAfterMethod implements AfterReturningAdvice {
        @Override
        public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
            System.out.println("WeiAfterMethod: After Method hi wei!!");
        }
    }
    

     2)、修改bean配置xml文件 SpringAopAdvice.xml,加入weiAfterMethod配置,如下:

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="
                                http://www.springframework.org/schema/beans
                                http://www.springframework.org/schema/beans/spring-beans.xsd">
    
        <bean id="customerService" class="com.shiyanlou.spring.aop.advice.CustomerService">
            <property name="name" value="weihouye"/>
            <property name="url" value="www.weihouyeaiyangzhan.com"/>
        </bean>
    
        <bean id="weiBeforeMethod" class="com.shiyanlou.spring.aop.advice.WeiBeforeMethod"/>
        <bean id="weiAfterMethod" class="com.shiyanlou.spring.aop.advice.WeiAfterMethod"/>
    
        <bean id="customerServiceProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
            <property name="target" ref="customerService"/>
            <property name="interceptorNames">
                <list>
                    <!--<value>weiBeforeMethod</value>-->
                    <value>weiAfterMethod</value>
                </list>
            </property>
        </bean>
    </beans>
    

     3)、同样在App.java中获取并运行代理bean,结果如下:

    
    *************************
    Customer name: weihouye
    WeiAfterMethod: After Method hi wei!!
    *************************
    Customer URL: www.weihouyeaiyangzhan.com
    WeiAfterMethod: After Method hi wei!!
    *************************
    
    

    &emps;可以看到,在CustomerService.java中的每个方法执行完毕后,均会执行WeiAfterMethod.javaafterReturning方法,抛出异常除外。

    4、Afetr Throwing Advice

     1)、创建一个实现org.springframework.aop.ThrowsAdvice接口的实现类WeiThrowExceptionMethod.java,劫持IllegalArgumentException异常,当被劫持的类抛出该异常时,将会执行afterThrowing方法;代码如下:

    package com.shiyanlou.spring.aop.advice;
    
    import org.springframework.aop.ThrowsAdvice;
    
    /**
     * Created by Administrator on 2019/10/30.
     */
    public class WeiThrowExceptionMethod implements ThrowsAdvice {
    
        public void afterThrowing(Exception ex){
            System.out.println("WeiThrowException : Throw exception hi wei!!");
        }
    }
    

     2)、修改bean配置xml文件 SpringAopAdvice.xml,加入weiThrowExceptionMethod配置,如下:

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="
                                http://www.springframework.org/schema/beans
                                http://www.springframework.org/schema/beans/spring-beans.xsd">
    
        <bean id="customerService" class="com.shiyanlou.spring.aop.advice.CustomerService">
            <property name="name" value="weihouye"/>
            <property name="url" value="www.weihouyeaiyangzhan.com"/>
        </bean>
    
        <bean id="weiBeforeMethod" class="com.shiyanlou.spring.aop.advice.WeiBeforeMethod"/>
        <bean id="weiAfterMethod" class="com.shiyanlou.spring.aop.advice.WeiAfterMethod"/>
        <bean id="weiThrowExceptionMethod" class="com.shiyanlou.spring.aop.advice.WeiThrowExceptionMethod"/>
        <bean id="weiAroundMethod" class="com.shiyanlou.spring.aop.advice.WeiAroundMethod"/>
    
        <bean id="customerServiceProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
            <property name="target" ref="customerService"/>
            <property name="interceptorNames">
                <list>
                    <!--<value>weiBeforeMethod</value>-->
                    <!--<value>weiAfterMethod</value>-->
                    <value>weiThrowExceptionMethod</value>
                </list>
            </property>
        </bean>
    </beans>
    

     3)、同样在App.java中获取并运行代理bean,结果如下:

    
    *************************
    Customer name: weihouye
    *************************
    Customer URL: www.weihouyeaiyangzhan.com
    *************************
    WeiThrowException : Throw exception hi wei!!
    
    

     可以看到当执行到CustomerService.javaprintThrowException方法时,我们抛出了IllegalArgumentException异常,异常抛出后执行了拦截器WeiThrowExceptionMethod.javaafterThrowing方法。

    5、Around advice

     1)、结合上面3种形式的advice,创建一个org.aopalliance.intercept.MethodInterceptor接口的实现类WeiAroundMethod.java,重写public Object invoke(MethodInvocation invocation) throws Throwable方法;
     必须通过方法调用对象invocation.proceed()来调用原来的被代理的方法,当然也可以不调用;代码如下,注意注释中包含十分重要的信息。

    package com.shiyanlou.spring.aop.advice;
    
    
    import org.aopalliance.intercept.MethodInterceptor;
    import org.aopalliance.intercept.MethodInvocation;
    
    import java.util.Arrays;
    
    /**
     * Created by Administrator on 2019/10/30.
     */
    public class WeiAroundMethod implements MethodInterceptor {
        @Override
        public Object invoke(MethodInvocation invocation) throws Throwable {
    
            //Method调用的处理
            System.out.println("Method Name: " + invocation.getMethod().getName());
            System.out.println("Method Arguments: " + Arrays.toString(invocation.getArguments()));
            //相当于MethodBeforeAdvice
            System.out.println("WeiBeforeMethod: Before method hi wei!!!");
    
            try {
                //调用原方法,即调用CustomerService中的方法
                Object result = invocation.proceed();
    
                //相当于AfterReturningAdvice
                System.out.println("WeiAfterMethod: After Method hi wei!!");
    
                return result;
            } catch (IllegalArgumentException e) {
                //相当于ThrowsAdvice
                System.out.println("WeiThrowException : Throw exception hi wei!!");
    
                throw e; //将异常抛出
            }
        }
    }
    
    

     2)、修改bean配置xml文件 SpringAopAdvice.xml,加入weiAroundMethod配置,如下:

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="
                                http://www.springframework.org/schema/beans
                                http://www.springframework.org/schema/beans/spring-beans.xsd">
    
        <bean id="customerService" class="com.shiyanlou.spring.aop.advice.CustomerService">
            <property name="name" value="weihouye"/>
            <property name="url" value="www.weihouyeaiyangzhan.com"/>
        </bean>
    
        <bean id="weiBeforeMethod" class="com.shiyanlou.spring.aop.advice.WeiBeforeMethod"/>
        <bean id="weiAfterMethod" class="com.shiyanlou.spring.aop.advice.WeiAfterMethod"/>
        <bean id="weiThrowExceptionMethod" class="com.shiyanlou.spring.aop.advice.WeiThrowExceptionMethod"/>
        <bean id="weiAroundMethod" class="com.shiyanlou.spring.aop.advice.WeiAroundMethod"/>
    
        <bean id="customerServiceProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
            <property name="target" ref="customerService"/>
            <property name="interceptorNames">
                <list>
                    <!--<value>weiBeforeMethod</value>-->
                    <!--<value>weiAfterMethod</value>-->
                    <!--<value>weiThrowExceptionMethod</value>-->
                    <value>weiAroundMethod</value>
                </list>
            </property>
        </bean>
    </beans>
    

     3)、运行App.java结果如下:

    
    *************************
    Method Name: printName
    Method Arguments: []
    WeiBeforeMethod: Before method hi wei!!!
    Customer name: weihouye
    WeiAfterMethod: After Method hi wei!!
    *************************
    Method Name: printURL
    Method Arguments: []
    WeiBeforeMethod: Before method hi wei!!!
    Customer URL: www.weihouyeaiyangzhan.com
    WeiAfterMethod: After Method hi wei!!
    *************************
    Method Name: printThrowException
    Method Arguments: []
    WeiBeforeMethod: Before method hi wei!!!
    WeiThrowException : Throw exception hi wei!!
    
    

     可以看出Around advice 结合了前面三种advice。

    6、改进

     在上边的结果中,CustomerService.java的全部方法均被拦截,下面展示如何使用Pointcuts只拦截printName()
     在Spring AOP中,有3个常用的名词,Advices、Pointcuts、Advisor,解释如下:

    • Advices:表示一个方法执行前或者执行后的动作;
    • Pointcuts:表示根据一个方法的名字或者正则表达式去拦截一个方法;
    • Advisor:Advices和Pointcuts组成的独立的单元,且可以传给proxy factory对象。

     我们可以用名字匹配法或者正则表达式去匹配要拦截的方法。



     1)、Pointcuts ——Name match example (根据方法名匹配)

    SpringAopAdvice.xml中,创建一个NameMatchMethodPointcut的 Bean,并将想要拦截的方法名printName注入到属性mappedName中。配置如下:

        <bean id="customerPointcut" class="org.springframework.aop.support.NameMatchMethodPointcut">
            <property name="mappedName" value="printName"/>
        </bean>
    



     创建一个DefaultPointcutAdvisor的 Advisor bean,将 Pointcut 和 Advice 注入到 Advisor中。如下:

        <bean id="customerAdvisor" class="org.springframework.aop.support.DefaultPointcutAdvisor">
            <property name="pointcut" ref="customerPointcut"/>
            <property name="advice" ref="weiAroundMethod"/>
        </bean>
    



     更改代理customerServiceProxyinterceptorNames中的值,将上面的Advisor代替原来的weiAroundMethod,所有的配置如下:

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="
                                http://www.springframework.org/schema/beans
                                http://www.springframework.org/schema/beans/spring-beans.xsd">
    
        <bean id="customerService" class="com.shiyanlou.spring.aop.advice.CustomerService">
            <property name="name" value="weihouye"/>
            <property name="url" value="www.weihouyeaiyangzhan.com"/>
        </bean>
    
        <!--<bean id="weiBeforeMethod" class="com.shiyanlou.spring.aop.advice.WeiBeforeMethod"/>-->
        <!--<bean id="weiAfterMethod" class="com.shiyanlou.spring.aop.advice.WeiAfterMethod"/>-->
        <!--<bean id="weiThrowExceptionMethod" class="com.shiyanlou.spring.aop.advice.WeiThrowExceptionMethod"/>-->
        <bean id="weiAroundMethod" class="com.shiyanlou.spring.aop.advice.WeiAroundMethod"/>
    
        <bean id="customerPointcut" class="org.springframework.aop.support.NameMatchMethodPointcut">
            <property name="mappedName" value="printName"/>
        </bean>
    
        <bean id="customerAdvisor" class="org.springframework.aop.support.DefaultPointcutAdvisor">
            <property name="pointcut" ref="customerPointcut"/>
            <property name="advice" ref="weiAroundMethod"/>
        </bean>
    
        <bean id="customerServiceProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
            <property name="target" ref="customerService"/>
            <property name="interceptorNames">
                <list>
                    <!--<value>weiBeforeMethod</value>-->
                    <!--<value>weiAfterMethod</value>-->
                    <!--<value>weiThrowExceptionMethod</value>-->
                    <value>customerAdvisor</value>
                </list>
            </property>
        </bean>
    </beans>
    



     运行一下App.java,发现只有个printName方法被拦截了。结果输出如下:

    *************************
    Method Name: printName
    Method Arguments: []
    WeiBeforeMethod: Before method hi wei!!!
    Customer name: weihouye
    WeiAfterMethod: After Method hi wei!!
    *************************
    Customer URL: www.weihouyeaiyangzhan.com
    *************************
    



     以上 Poincut 和 Advisor可以一起配置,即不用单独配置 customerPointcut 和 customerAdvisor,只要在配置 customerAdvisor 时选择 NameMatchMethodPointcutAdvisor,如下:

        <!--pointcut和advisor可以一起配置,只要在配置customerAdvisor时,选择NameMatchMethodPointcutAdvisor-->
        <bean id="customerAdvisor" class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor">
            <property name="mappedName" value="printName"/>
            <property name="advice" ref="weiAroundMethod"/>
        </bean>
    



     2)、Pointcuts ——Regular expression match example (根据正则表达式匹配)

     可以使用正则表达式匹配拦截方法,Advisor 配置如下:

        <bean id="customerAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
            <property name="patterns">
                <list>
                    <value>.*URL.*</value>
                </list>
            </property>
            <property name="advice" ref="weiAroundMethod"/>
        </bean>
    

     运行结果:只有个printURL方法被拦截

    *************************
    Customer name: weihouye
    *************************
    Method Name: printURL
    Method Arguments: []
    WeiBeforeMethod: Before method hi wei!!!
    Customer URL: www.weihouyeaiyangzhan.com
    WeiAfterMethod: After Method hi wei!!
    *************************
    

    相关文章

      网友评论

          本文标题:Spring Aop:一、四种advice

          本文链接:https://www.haomeiwen.com/subject/tpravctx.html