Spring基础学习教程(Spring AOP总结)-09

来源:互联网 发布:淘宝卖家id可以改吗 编辑:程序博客网 时间:2024/05/17 17:43

 spring AOP即Aspect-oriented programming,面向切面编程,是作为面向对象编程的一种补充,专门用于处理系统中分布于各个模块(不同方法)中的交叉关注点的问题。简单地说,就是一个拦截器(interceptor)拦截一些处理过程。例如,当一个method被执行,Spring AOP能够劫持正在运行的method,在method执行前或者后加入一些额外的功能。

 

在Spring AOP中,支持4中类型的通知(Advice)

Before advice      ——method执行前通知

After returning advice ——method返回一个结果后通知

After throwing advice – method抛出异常后通知

Around advice – 环绕通知,结合了以上三种

 

下边这个例子解释Spring AOP怎样工作。

首先一个简单的不使用AOP的例子。

先创建一个简单的Service,为了稍后演示,这个类中加了几个简单的打印method。

CustomerService.Java如下:

复制代码
package com.lei.demo.aop.advice;public class CustomerService {    private String name;    private String url;     public void setName(String name) {        this.name = name;    }     public void setUrl(String url) {        this.url = url;    }     public void printName() {        System.out.println("Customer name : " + this.name);    }     public void printURL() {        System.out.println("Customer website : " + this.url);    }     public void printThrowException() {        throw new IllegalArgumentException();    }}
复制代码

 

Xml配置文件Apring-AOP-Advice.xml如下:

复制代码
<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-2.5.xsd">     <bean id="customerService" class="com.lei.demo.aop.advice.CustomerService">        <property name="name" value="LeiOOLei" />        <property name="url" value="http://www.cnblogs.com/leiOOlei/" />    </bean> </beans>
复制代码

 

 运行以下代码App.java:

复制代码
package com.lei.demo.aop.advice;import org.springframework.context.ApplicationContext;import org.springframework.context.support.ClassPathXmlApplicationContext;public class App {    public static void main(String[] args) {        ApplicationContext appContext = new ClassPathXmlApplicationContext(                new String[] { "Spring-AOP-Advice.xml" });         CustomerService cust = (CustomerService) appContext.getBean("customerService");         System.out.println("*************************");        cust.printName();        System.out.println("*************************");        cust.printURL();        System.out.println("*************************");        try {            cust.printThrowException();        } catch (Exception e) {         }     }}
复制代码

 

运行结果:

*************************

Customer name : LeiOOLei

*************************

Customer website : http://www.cnblogs.com/leiOOlei/

*************************

 

1.      Before Advice

创建一个实现了接口MethodBeforeAdvice的class,method运行前,将运行下边的代码

HijackBeforeMethod.java如下:

复制代码
package com.lei.demo.aop.advice;import java.lang.reflect.Method;import org.springframework.aop.MethodBeforeAdvice;public class HijackBeforeMethod implements MethodBeforeAdvice {    public void before(Method arg0, Object[] args, Object target)            throws Throwable {        System.out.println("HijackBeforeMethod : Before method hijacked!");            }}
复制代码

 

 在配置文件中加入新的bean配置HijackBeforeMethod,然后创建一个新的代理(proxy),命名为customerServiceProxy

“target”定义你想劫持哪个bean;

“interceptorNames”定义你想用哪个class(advice)劫持target。

Apring-AOP-Advice.xml如下:

复制代码
<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-2.5.xsd">     <bean id="customerService" class="com.lei.demo.aop.advice.CustomerService">        <property name="name" value="LeiOOLei" />        <property name="url" value="http://www.cnblogs.com/leiOOlei/" />    </bean>        <bean id="hijackBeforeMethodBean" class="com.lei.demo.aop.advice.HijackBeforeMethod" />     <bean id="customerServiceProxy" class="org.springframework.aop.framework.ProxyFactoryBean">        <property name="target" ref="customerService" />        <property name="interceptorNames">            <list>                <value>hijackBeforeMethodBean</value>            </list>        </property>    </bean> </beans>
复制代码

 

注意:

用Spring proxy之前,必须添加CGLIB2类库,,以下是pom.xml依赖

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

 

运行如下代码,注意代理

App.java如下

复制代码
package com.lei.demo.aop.advice;import org.springframework.context.ApplicationContext;import org.springframework.context.support.ClassPathXmlApplicationContext;public class App {    public static void main(String[] args) {        ApplicationContext appContext = new ClassPathXmlApplicationContext(                new String[] { "Spring-AOP-Advice.xml" });         CustomerService cust = (CustomerService) appContext.getBean("customerServiceProxy");         System.out.println("使用Spring AOP 如下");        System.out.println("*************************");        cust.printName();        System.out.println("*************************");        cust.printURL();        System.out.println("*************************");                try {            cust.printThrowException();        } catch (Exception e) {         }     }}
复制代码

 

输出结果:

使用Spring AOP 如下

*************************

HijackBeforeMethod : Before method hijacked!

Customer name : LeiOOLei

*************************

HijackBeforeMethod : Before method hijacked!

Customer website : http://www.cnblogs.com/leiOOlei/

*************************

HijackBeforeMethod : Before method hijacked!

 

每一个customerService的method运行前,都将先执行HijackBeforeMethod的before方法。

 

2.      After Returning Advice

创建一个实现了接口AfterReturningAdvice的class,method运行后,直到返回结果后,才运行下边的代码,如果没有返回结果,将不运行切入的代码。

HijackAfterMethod.java如下:

复制代码
package com.lei.demo.aop.advice;import java.lang.reflect.Method;import org.springframework.aop.AfterReturningAdvice;public class HijackAfterMethod implements AfterReturningAdvice {    public void afterReturning(Object returnValue, Method method, Object[] args,            Object target) throws Throwable {        System.out.println("HijackAfterMethod : After method hijacked!");    }}
复制代码

 

修改bean配置文件,加入hijackAfterMethodBean配置,Apring-AOP-Advice.xml如下:

复制代码
<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-2.5.xsd">     <bean id="customerService" class="com.lei.demo.aop.advice.CustomerService">        <property name="name" value="LeiOOLei" />        <property name="url" value="http://www.cnblogs.com/leiOOlei/" />    </bean>        <bean id="hijackBeforeMethodBean" class="com.lei.demo.aop.advice.HijackBeforeMethod" />    <bean id="hijackAfterMethodBean" class="com.lei.demo.aop.advice.HijackAfterMethod" />     <bean id="customerServiceProxy" class="org.springframework.aop.framework.ProxyFactoryBean">        <property name="target" ref="customerService" />        <property name="interceptorNames">            <list>                <value>hijackAfterMethodBean</value>            </list>        </property>    </bean> </beans>
复制代码

 

现在再运行App.java后输出如下:

使用Spring AOP 如下

*************************

Customer name : LeiOOLei

HijackAfterMethod : After method hijacked!

*************************

Customer website : http://www.cnblogs.com/leiOOlei/

HijackAfterMethod : After method hijacked!

*************************

 

可以看到输出结果,每一个customerService的method运行返回结果后,都将再执行HijackAfterMethodafterReturning方法。但是执行到cust.printThrowException()后,直接抛出异常,方法没有正常执行完毕(或者说没有返回结果),所以不运行切入的afterReturning方法。

 

3.      Afetr Throwing Advice

创建一个实现了ThrowsAdvice接口的class,劫持IllegalArgumentException异常,目标method运行时,抛出IllegalArgumentException异常后,运行切入的方法。

HijackThrowException.java如下:

复制代码
package com.lei.demo.aop.advice;import org.springframework.aop.ThrowsAdvice;import sun.awt.SunToolkit.IllegalThreadException;public class HijackThrowException implements ThrowsAdvice {    public void afterThrowing(IllegalArgumentException e) throws Throwable {        System.out.println("HijackThrowException : Throw exception hijacked!");    }}
复制代码

 

修改bean配置文件,加入了hijackThrowExceptionBean,Apring-AOP-Advice.xml如下:

复制代码
<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-2.5.xsd">     <bean id="customerService" class="com.lei.demo.aop.advice.CustomerService">        <property name="name" value="LeiOOLei" />        <property name="url" value="http://www.cnblogs.com/leiOOlei/" />    </bean>        <bean id="hijackBeforeMethodBean" class="com.lei.demo.aop.advice.HijackBeforeMethod" />    <bean id="hijackAfterMethodBean" class="com.lei.demo.aop.advice.HijackAfterMethod" />    <bean id="hijackThrowExceptionBean" class="com.lei.demo.aop.advice.HijackThrowException" />     <bean id="customerServiceProxy" class="org.springframework.aop.framework.ProxyFactoryBean">        <property name="target" ref="customerService" />        <property name="interceptorNames">            <list>                <value>hijackThrowExceptionBean</value>            </list>        </property>    </bean> </beans>
复制代码

 

运行结果如下:

使用Spring AOP 如下

*************************

Customer name : LeiOOLei

*************************

Customer website : http://www.cnblogs.com/leiOOlei/

*************************

HijackThrowException : Throw exception hijacked!

 

当运行CustomerService中的printThrowException方法时,认为的抛出IllegalArgumentException异常,被HijackThrowException截获,运行其中的afterThrowing方法。注意,如果抛出异常不是IllegalArgumentException,则不能被截获。

 

4.      Around Advice

结合了以上3种形式的Advice,创建一个实现了接口MethodInterceptor的class,你必须通过methodInvocation.proceed()来调用原来的方法,即通过调用methodInvocation.proceed()来调用CustomerService中的每一个方法,当然也可以不调用原方法。

HijackAroundMethod.java如下:

复制代码
package com.lei.demo.aop.advice;import java.util.Arrays;import org.aopalliance.intercept.MethodInterceptor;import org.aopalliance.intercept.MethodInvocation;public class HijackAroundMethod implements MethodInterceptor {    public Object invoke(MethodInvocation methodInvocation) throws Throwable {        System.out.println("Method name : "                + methodInvocation.getMethod().getName());        System.out.println("Method arguments : "                + Arrays.toString(methodInvocation.getArguments()));         // 相当于  MethodBeforeAdvice        System.out.println("HijackAroundMethod : Before method hijacked!");         try {            // 调用原方法,即调用CustomerService中的方法            Object result = methodInvocation.proceed();             // 相当于 AfterReturningAdvice            System.out.println("HijackAroundMethod : After method hijacked!");             return result;         } catch (IllegalArgumentException e) {            // 相当于 ThrowsAdvice            System.out.println("HijackAroundMethod : Throw exception hijacked!");            throw e;        }    }}
复制代码

 

修改bean配置文件,加入了hijackAroundMethodBean,Apring-AOP-Advice.xml如下:

复制代码
<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-2.5.xsd">     <bean id="customerService" class="com.lei.demo.aop.advice.CustomerService">        <property name="name" value="LeiOOLei" />        <property name="url" value="http://www.cnblogs.com/leiOOlei/" />    </bean>        <bean id="hijackBeforeMethodBean" class="com.lei.demo.aop.advice.HijackBeforeMethod" />    <bean id="hijackAfterMethodBean" class="com.lei.demo.aop.advice.HijackAfterMethod" />    <bean id="hijackThrowExceptionBean" class="com.lei.demo.aop.advice.HijackThrowException" />    <bean id="hijackAroundMethodBean" class="com.lei.demo.aop.advice.HijackAroundMethod" />     <bean id="customerServiceProxy" class="org.springframework.aop.framework.ProxyFactoryBean">        <property name="target" ref="customerService" />        <property name="interceptorNames">            <list>                <value>hijackAroundMethodBean</value>            </list>        </property>    </bean> </beans>
复制代码

 

执行App.java,输出结果:

使用Spring AOP 如下

*************************

Method name : printName

Method arguments : []

HijackAroundMethod : Before method hijacked!

Customer name : LeiOOLei

HijackAroundMethod : After method hijacked!

*************************

Method name : printURL

Method arguments : []

HijackAroundMethod : Before method hijacked!

Customer website : http://www.cnblogs.com/leiOOlei/

HijackAroundMethod : After method hijacked!

*************************

Method name : printThrowException

Method arguments : []

HijackAroundMethod : Before method hijacked!

HijackAroundMethod : Throw exception hijacked!

 

CustomerService中每一个方法的调用,都会执行HijackAroundMethod中的invoke方法,可以看到整个切入点将目标around。

 

大多数的Spring开发者只用Around Advice,因为它能够实现所有类型的Advice。在实际的项目开发中,我们还是要尽量选择适合的Advice。

       在以上的例子中,CustomerService中的所有方法都被自动拦截,但是大多数情况下,我们不需要拦截一个class中的所有方法,而是拦截符合条件的方法。这时,我们就需要用到Pointcut and Advice,即切入点和通知

Spring AOP——Pointcut,Advisor

  上一篇的Spring AOP Advice例子中,Class(CustomerService)中的全部method都被自动的拦截了。但是大多情况下,你只需要一个方法去拦截一两个method。这样就引入了Pointcut(切入点)的概念,它允许你根据method的名字去拦截指定的method。另外,一个Pointcut必须结合一个Advisor来使用。

 

在Spring AOP中,有3个常用的概念,Advices、Pointcut、Advisor,解释如下,

Advices:表示一个method执行前或执行后的动作。

Pointcut:表示根据method的名字或者正则表达式去拦截一个method。

Advisor:Advice和Pointcut组成的独立的单元,并且能够传给proxy factory 对象。

 

下边来回顾一下上一篇例子中的代码

CustomerService.java

复制代码
package com.lei.demo.aop.advice;public class CustomerService {    private String name;    private String url;     public void setName(String name) {        this.name = name;    }     public void setUrl(String url) {        this.url = url;    }     public void printName() {        System.out.println("Customer name : " + this.name);    }     public void printURL() {        System.out.println("Customer website : " + this.url);    }     public void printThrowException() {        throw new IllegalArgumentException();    }}
复制代码

 

 配置文件Spring-AOP-Advice.xml:

复制代码
<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-2.5.xsd">     <bean id="customerService" class="com.lei.demo.aop.advice.CustomerService">        <property name="name" value="LeiOOLei" />        <property name="url" value="http://www.cnblogs.com/leiOOlei/" />    </bean>        <bean id="hijackAroundMethodBean" class="com.lei.demo.aop.advice.HijackAroundMethod" />     <bean id="customerServiceProxy" class="org.springframework.aop.framework.ProxyFactoryBean">        <property name="target" ref="customerService" />        <property name="interceptorNames">            <list>                <value>hijackAroundMethodBean</value>            </list>        </property>    </bean> </beans>
复制代码

 

HijackAroundMethod.java

复制代码
package com.lei.demo.aop.advice;import java.util.Arrays;import org.aopalliance.intercept.MethodInterceptor;import org.aopalliance.intercept.MethodInvocation;public class HijackAroundMethod implements MethodInterceptor {    public Object invoke(MethodInvocation methodInvocation) throws Throwable {        System.out.println("Method name : "                + methodInvocation.getMethod().getName());        System.out.println("Method arguments : "                + Arrays.toString(methodInvocation.getArguments()));         // 相当于  MethodBeforeAdvice        System.out.println("HijackAroundMethod : Before method hijacked!");         try {            // 调用原方法,即调用CustomerService中的方法            Object result = methodInvocation.proceed();             // 相当于 AfterReturningAdvice            System.out.println("HijackAroundMethod : After method hijacked!");             return result;         } catch (IllegalArgumentException e) {            // 相当于 ThrowsAdvice            System.out.println("HijackAroundMethod : Throw exception hijacked!");            throw e;        }    }}
复制代码

 

运行如下App.java

复制代码
package com.lei.demo.aop.advice;import org.springframework.context.ApplicationContext;import org.springframework.context.support.ClassPathXmlApplicationContext;public class App {    public static void main(String[] args) {        ApplicationContext appContext = new ClassPathXmlApplicationContext(                new String[] { "Spring-AOP-Advice.xml" });                System.out.println("使用Spring AOP 如下");        CustomerService cust = (CustomerService) appContext.getBean("customerServiceProxy");        System.out.println("*************************");        cust.printName();        System.out.println("*************************");        cust.printURL();        System.out.println("*************************");                try {            cust.printThrowException();        } catch (Exception e) {         }     }}
复制代码

 

 运行结果:

使用Spring AOP 如下

*************************

Method name : printName

Method arguments : []

HijackAroundMethod : Before method hijacked!

Customer name : LeiOOLei

HijackAroundMethod : After method hijacked!

*************************

Method name : printURL

Method arguments : []

HijackAroundMethod : Before method hijacked!

Customer website : http://www.cnblogs.com/leiOOlei/

HijackAroundMethod : After method hijacked!

*************************

Method name : printThrowException

Method arguments : []

HijackAroundMethod : Before method hijacked!

HijackAroundMethod : Throw exception hijacked!

 

上边的结果中,CustomerService.java中,全部的method方法全部被拦截了,下边我们将展示怎样利用Pointcuts只拦截printName()。

 

你可以用名字匹配法和正则表达式匹配法去匹配要拦截的method。

1.      Pointcut——Name match example

通过pointcut和advisor拦截printName()方法。

创建一个NameMatchMethodPointcut的bean,将你想拦截的方法的名字printName注入到属性mappedName,如下

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

 

 创建一个DefaultPointcutAdvisor的advisor bean,将pointcut和advice关联起来。

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

 

更改代理的interceptorNames值,将上边的advisor( customerAdvisor)替代原来的hijackAroundMethodBean。

复制代码
<bean id="customerServiceProxy"        class="org.springframework.aop.framework.ProxyFactoryBean">         <property name="target" ref="customerService" />         <property name="interceptorNames">            <list>                <value>customerAdvisor</value>            </list>        </property></bean>
复制代码

 

所有的配置文件如下:

复制代码
<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-2.5.xsd">     <bean id="customerService" class="com.lei.demo.aop.advice.CustomerService">        <property name="name" value="LeiOOLei" />        <property name="url" value="http://www.cnblogs.com/leiOOlei/" />    </bean>     <bean id="hijackAroundMethodBean" class="com.lei.demo.aop.advice.HijackAroundMethod" />     <bean id="customerServiceProxy" class="org.springframework.aop.framework.ProxyFactoryBean">         <property name="target" ref="customerService" />         <property name="interceptorNames">            <list>                <value>customerAdvisor</value>            </list>        </property>    </bean>     <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=" hijackAroundMethodBean " />    </bean> </beans>
复制代码

 

 再运行一下App.java,输出结果如下:

使用Spring AOP 如下

*************************

Method name : printName

Method arguments : []

HijackAroundMethod : Before method hijacked!

Customer name : LeiOOLei

HijackAroundMethod : After method hijacked!

*************************

Customer website : http://www.cnblogs.com/leiOOlei/

*************************

 

以上运行结果显示,只拦截了printName()方法。

 

注意:

以上配置中pointcut和advisor可以合并在一起配置,即不用单独配置customerPointcutcustomerAdvisor,只要配置customerAdvisor时class选择NameMatchMethodPointcutAdvisor如下:

<bean id="customerAdvisor" class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor">        <property name="mappedName" value="printName" />        <property name="advice" ref="hijackAroundMethodBean" /></bean>

 

这样,整个配置文件如下:

复制代码
<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-2.5.xsd">     <bean id="customerService" class="com.lei.demo.aop.advice.CustomerService">        <property name="name" value="LeiOOLei" />        <property name="url" value="http://www.cnblogs.com/leiOOlei/" />    </bean>        <bean id="hijackAroundMethodBean" class="com.lei.demo.aop.advice.HijackAroundMethod" />     <bean id="customerServiceProxy" class="org.springframework.aop.framework.ProxyFactoryBean">        <property name="target" ref="customerService" />        <property name="interceptorNames">            <list>                <value>customerAdvisor</value>            </list>        </property>    </bean>            <bean id="customerAdvisor" class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor">        <property name="mappedName" value="printName" />        <property name="advice" ref="hijackAroundMethodBean" />    </bean> </beans>
复制代码

 

  实际上这种做法将method名字与具体的advice捆绑在一起,有悖于Spring松耦合理念,如果将method名字单独配置成pointcut(切入点),advice和pointcut的结合会更灵活,使一个pointcut可以和多个advice结合。

2.      Pointcut——Regular exxpression match example

你可以配置用正则表达式匹配需要拦截的method,如下配置

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

 

现在,你可以拦截名字中包含URL字符的method了,在实际工作中,你可以用它来管理DAO层,例如,你可以用“.*DAO.*”来拦截所有DAO层中的相关业务。


Spring AOP——自动创建Proxy

上面的例子中,在配置文件中,你必须手动为每一个需要AOP的bean创建Proxy bean(ProxyFactoryBean)。

这不是一个好的体验,例如,你想让DAO层的所有bean都支持AOP,以便写SQL日志,那么你必须手工创建很多的ProxyFactoryBean,这样会直接导致你的xml配置文件内容成几何级的倍增,不利于xml配置维护。

幸运的是,Spring有两种方法,可以为你自动创建proxy。

 

1.       利用BeanNameAutoProxyCreator自动创建proxy

手工创建ProxyFactoryBean如下:

复制代码
<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-2.5.xsd">     <bean id="customerService" class=" com.lei.demo.aop.advice.CustomerService">        <property name="name" value="LeiOOLei" />        <property name="url" value="http://www.cnblogs.com/leiOOlei/" />    </bean>     <bean id="hijackAroundMethodBean" class=" com.lei.demo.aop.advice.HijackAroundMethod" />     <bean id="customerServiceProxy"         class="org.springframework.aop.framework.ProxyFactoryBean">        <property name="target" ref="customerService" />        <property name="interceptorNames">            <list>                <value>customerAdvisor</value>            </list>        </property>    </bean>     <bean id="customerAdvisor"    class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor">        <property name="mappedName" value="printName" />        <property name="advice" ref=" hijackAroundMethodBean " />    </bean></beans>
复制代码

 

配置完后要得到customerServiceProxy,需要如下代码

CustomerService cust = (CustomerService) appContext.getBean("customerServiceProxy");

 

在自动模式中,你需要创建BeanNameAutoProxyCreator,将所有的bean(通过名字或正则表达式匹配)和advisor形成一个独立的单元,配置如下:

复制代码
<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-2.5.xsd">     <bean id="customerService" class="com.lei.demo.aop.advice.CustomerService">        <property name="name" value="LeiOOLei" />        <property name="url" value="http://www.cnblogs.com/leiOOlei/" />    </bean>     <bean id="hijackAroundMethodBeanAdvice" class=" com.lei.demo.aop.advice.HijackAroundMethod" />    <bean    class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">        <property name="beanNames">            <list>                <value>*Service</value>            </list>        </property>        <property name="interceptorNames">            <list>                <value>customerAdvisor</value>            </list>        </property>    </bean><bean id="customerAdvisor"    class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor">        <property name="mappedName" value="printName" />        <property name="advice" ref="hijackAroundMethodBeanAdvice" /></bean></beans>
复制代码

 

 以上配置中只要bean的id符合*Service,就会自动创建proxy,所以,你可以用以下代码获得proxy。

CustomerService cust = (CustomerService) appContext.getBean("customerService");

 

2.      利用DefaultAdvisorAutoProxyCreator创建Proxy

这种方式利用DefaultAdvisorAutoProxyCreator实现自动创建Proxy,此种方式威力巨大,任何匹配Advisor的bean,都会自动创建Proxy实现AOP,所以慎用。

复制代码
<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-2.5.xsd">     <bean id="customerService" class="com.lei.demo.aop.advice.CustomerService">        <property name="name" value="LeiOOLei" />        <property name="url" value="http://www.cnblogs.com/leiOOlei/" />    </bean>     <bean id="hijackAroundMethodBeanAdvice" class="com.lei.demo.aop.advice.HijackAroundMethod" />     <bean id="customerAdvisor"    class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor">        <property name="mappedName" value="printName" />        <property name="advice" ref="hijackAroundMethodBeanAdvice" />    </bean>        <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" /> </beans>
复制代码

 

以上例子中,xml中任何bean,只要有method名字为printName,使用以下代码时,都会自动创建Proxy,来支持AOP。

CustomerService cust = (CustomerService) appContext.getBean("customerService");


 Spring AOP AspectJ

 

本文讲述使用AspectJ框架实现Spring AOP。

再重复一下Spring AOP中的三个概念,

  1. Advice:向程序内部注入的代码。
  2. Pointcut:注入Advice的位置,切入点,一般为某方法。
  3. Advisor:Advice和Pointcut的结合单元,以便将Advice和Pointcut分开实现灵活配置。

AspectJ是基于注释(Annotation)的,所以需要JDK5.0以上的支持。

AspectJ支持的注释类型如下:

  1. @Before
  2. @After
  3. @AfterReturning
  4. @AfterThrowing
  5. @Around

 

首先定义一个简单的bean,CustomerBo实现了接口ICustomerBo

ICustomerBo.java如下:

复制代码
package com.lei.demo.aop.aspectj;public interface ICustomerBo {    void addCustomer();    void deleteCustomer();    String AddCustomerReturnValue();    void addCustomerThrowException() throws Exception;    void addCustomerAround(String name);}
复制代码

 

 CustomerBo.java如下:

 

复制代码
package com.lei.demo.aop.aspectj;public class CustomerBo implements ICustomerBo {    public void addCustomer() {        System.out.println("addCustomer() is running ...");    }        public void deleteCustomer() {        System.out.println("deleteCustomer() is running ...");    }    public String AddCustomerReturnValue() {        System.out.println("AddCustomerReturnValue() is running ...");        return "abc";    }    public void addCustomerThrowException() throws Exception {        System.out.println("addCustomerThrowException() is running ...");        throw new Exception("Generic Error");    }    public void addCustomerAround(String name) {        System.out.println("addCustomerAround() is running ,args:"+name);    }}
复制代码

 

一、      简单的AspectJ,Advice和Pointcut结合在一起

首先没有引入Pointcut之前,Advice和Pointcut是混在一起的

步骤,只需要两步,如下:

  1. 创建一个Aspect类
  2. 配置Spring配置文件

 

第一步,创建Aspect类

LoggingAspect.java如下:

复制代码
package com.lei.demo.aop.aspectj;import org.aspectj.lang.JoinPoint;import org.aspectj.lang.annotation.After;import org.aspectj.lang.annotation.Aspect;import org.aspectj.lang.annotation.Before;@Aspectpublic class LoggingAspect {        @Before("execution(public * com.lei.demo.aop.aspectj.CustomerBo.addCustomer(..))")    public void logBefore(JoinPoint joinPoint){        System.out.println("logBefore() is running ...");        System.out.println("hijacked:"+joinPoint.getSignature().getName());        System.out.println("**********");    }        @After("execution(public * com.lei.demo.aop.aspectj.CustomerBo.deleteCustomer(..))")    public void logAfter(JoinPoint joinPoint){        System.out.println("logAfter() is running ...");        System.out.println("hijacked:"+joinPoint.getSignature().getName());        System.out.println("**********");    }}
复制代码

 

解释:

1.  必须使用@Aspect在LoggingAspect声明之前注释,以便被框架扫描到

2.  此例Advice和Pointcut结合在一起,类中的具体方法logBefore和logAfter即为Advice,是要注入的代码,Advice方法上的表达式为Pointcut表达式,即定义了切入点,上例中@Before注释的表达式代表执行CustomerBo.addCustomer方法时注入logBefore代码。

3.  在LoggingAspect方法上加入@Before或者@After等注释

4.  "execution(public * com.lei.demo.aop.aspectj.CustomerBo.addCustomer(..))"是Aspect的切入点表达式,其中,*代表返回类型,后边的就要定义要拦截的方法名,这里写的的是com.lei.demo.aop.aspectj.CustomerBo.addCustomer表示拦截CustomerBo中的addCustomer方法,(..)代表参数匹配,此处表示匹配任意数量的参数,可以是0个也可以是多个,如果你确定这个方法不需要使用参数可以直接用(),还可以使用(*)来匹配一个任意类型的参数,还可以使用 (* , String),这样代表匹配两个参数,第二个参数必须是String 类型的参数

5.  AspectJ表达式,可以对整个包定义,例如,execution(* com.lei.service..*.*(..))表示切入点是com.lei.sevice包中的任意一个类的任意方法,具体的表达式请自行百度。

 

第二步,配置Spring配置文件,

配置Spring-AOP-AspectJ.xml文件,如下:

复制代码
<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-3.0.xsd    http://www.springframework.org/schema/aop    http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">        <aop:aspectj-autoproxy/>        <bean id="customerBo" class="com.lei.demo.aop.aspectj.CustomerBo"/>        <bean id="logAspect" class="com.lei.demo.aop.aspectj.LoggingAspect" />    </beans>
复制代码

 

解释:

1.      <aop:aspectj-autoproxy/>启动AspectJ支持,这样Spring会自动寻找用@Aspect注释过的类,其他的配置与spring普通bean配置一样。

 

测试:

执行App.java如下:

复制代码
package com.lei.demo.aop.aspectj;import org.springframework.context.ApplicationContext;import org.springframework.context.support.ClassPathXmlApplicationContext;public class App {    public static void main(String[] args) {                ApplicationContext appContext = new ClassPathXmlApplicationContext(                new String[] { "Spring-AOP-AspectJ.xml" });        ICustomerBo customer=(ICustomerBo)appContext.getBean("customerBo");        customer.addCustomer();                System.out.println("-------------------------------------------");        customer.deleteCustomer();            }}
复制代码

 

结果:

logBefore() is running ...

hijacked:addCustomer

**********

addCustomer() is running ...

-------------------------------------------

deleteCustomer() is running ...

logAfter() is running ...

hijacked:deleteCustomer

**********

 

二、      将Advice和Pointcut分开

需要三步,

  1. 创建Pointcut
  2. 创建Advice
  3. 配置Spring的配置文件

 

第一步,PointcutsDefinition.java定义了Pointcut,如下

复制代码
package com.lei.demo.aop.aspectj;import org.aspectj.lang.annotation.Aspect;import org.aspectj.lang.annotation.Pointcut;@Aspectpublic class PointcutsDefinition {    @Pointcut("execution(* com.lei.demo.aop.aspectj.CustomerBo.*(..))")    public void customerLog() {    }}
复制代码

 

解释:

1. 类声明前加入@Aspect注释,以便被框架扫描到。

2. @Pointcut是切入点声明,指定需要注入的代码的位置,如上例中指定切入点为CustomerBo类中的所有方法,在实际业务中往往是指定切入点到一个逻辑层,例如 execution (* com.lei.business.service.*.*(..)),表示aop切入点为service包中所有类的所有方法,具体的表达式后边会有介绍。

3. 方法customerLog是一个签名,在Advice中可以用此签名代替切入点表达式,所以不需要在方法体内编写实际代码,只起到助记功能,例如此处代表操作CustomerBo类时需要的切入点。

 

第二步,创建Advice类

LoggingAspect.java如下:

复制代码
package com.lei.demo.aop.aspectj;import org.aspectj.lang.JoinPoint;import org.aspectj.lang.annotation.After;import org.aspectj.lang.annotation.Aspect;import org.aspectj.lang.annotation.Before;@Aspectpublic class LoggingAspect {        @Before("com.lei.demo.aop.aspectj.PointcutsDefinition.customerLog()")    public void logBefore(JoinPoint joinPoint){        System.out.println("logBefore() is running ...");        System.out.println("hijacked:"+joinPoint.getSignature().getName());        System.out.println("**********");    }    @After("com.lei.demo.aop.aspectj.PointcutsDefinition.customerLog()")    public void logAfter(JoinPoint joinPoint){        System.out.println("logAfter() is running ...");        System.out.println("hijacked:"+joinPoint.getSignature().getName());        System.out.println("**********");    }}
复制代码

 

注释:

1.       @Before和@After使用PointcutsDefinition中的方法签名代替Pointcut表达式找到相应的切入点,即通过签名找到PointcutsDefinition中customerLog签名上的Pointcut表达式,表达式指定切入点为CustomerBo类中的所有方法。所以此例中Advice类LoggingAdvice,为CustomerBo中的所有方法都加入了@Before和@After两种类型的两种操作。

2.       对于PointcutsDefinition来说,主要职责是定义Pointcut,可以在其中第一多个切入点,并且可以用便于记忆的方法签名进行定义。

3.       单独定义Pointcut的好处是,一是通过使用有意义的方法名,而不是难读的Pointcut表达式,使代码更加直观;二是Pointcut可以实现共享,被多个Advice直接调用。若有多个Advice调用某个Pointcut,而这个Pointcut的表达式在将来有改变时,只需修改一个地方,维护更加方便。

 

第三步,配置Spring配置文件,配置文件并没有改变

配置Spring-AOP-AspectJ.xml文件,如下:

复制代码
<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-3.0.xsd    http://www.springframework.org/schema/aop    http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">        <aop:aspectj-autoproxy/>        <bean id="customerBo" class="com.lei.demo.aop.aspectj.CustomerBo"/>        <bean id="logAspect" class="com.lei.demo.aop.aspectj.LoggingAspect" />    </beans>
复制代码

 

App.java不变,运行测试代码App.java

输出结果:

logBefore() is running ...

hijacked:addCustomer

**********

addCustomer() is running ...

logAfter() is running ...

hijacked:addCustomer

**********

-------------------------------------------

logBefore() is running ...

hijacked:deleteCustomer

**********

deleteCustomer() is running ...

logAfter() is running ...

hijacked:deleteCustomer

**********

 

三、     切入点表达式

Spring3.0.5帮助文档中的切入点表达式如下:

 

Some examples of common pointcut expressions are given below.

 

the execution of any public method:

execution(public * *(..))


the execution of any method with a name beginning with "set":

execution(* set*(..))


the execution of any method defined by the AccountService interface:

execution(* com.xyz.service.AccountService.*(..))


the execution of any method defined in the service package:

execution(* com.xyz.service.*.*(..))


the execution of any method defined in the service package or a sub-package:

execution(* com.xyz.service..*.*(..))


any join point (method execution only in Spring AOP) within the service package:

within(com.xyz.service.*)


any join point (method execution only in Spring AOP) within the service package or a sub-package:

within(com.xyz.service..*)


any join point (method execution only in Spring AOP) where the proxy implements the AccountService interface:

this(com.xyz.service.AccountService)

'this' is more commonly used in a binding form :- see the following section on advice for how to make the proxy object available in the advice body.

 

any join point (method execution only in Spring AOP) where the target object implements the AccountService interface:

target(com.xyz.service.AccountService)

'target' is more commonly used in a binding form :- see the following section on advice for how to make the target object available in the advice body.

 

any join point (method execution only in Spring AOP) which takes a single parameter, and where the argument passed at runtime is Serializable:

args(java.io.Serializable)
'args' is more commonly used in a binding form :- see the following section on advice for how to make the method arguments available in the advice body.

Note that the pointcut given in this example is different to execution(* *(java.io.Serializable)): the args version matches if the argument passed at runtime is Serializable, the execution version matches if the method signature declares a single parameter of type Serializable.

 

any join point (method execution only in Spring AOP) where the target object has an @Transactional annotation:

@target(org.springframework.transaction.annotation.Transactional)

'@target' can also be used in a binding form :- see the following section on advice for how to make the annotation object available in the advice body.

 

any join point (method execution only in Spring AOP) where the declared type of the target object has an @Transactional annotation:

@within(org.springframework.transaction.annotation.Transactional)

'@within' can also be used in a binding form :- see the following section on advice for how to make the annotation object available in the advice body.

 

any join point (method execution only in Spring AOP) where the executing method has an @Transactional annotation:

@annotation(org.springframework.transaction.annotation.Transactional)

'@annotation' can also be used in a binding form :- see the following section on advice for how to make the annotation object available in the advice body.

 

any join point (method execution only in Spring AOP) which takes a single parameter, and where the runtime type of the argument passed has the@Classified annotation:

@args(com.xyz.security.Classified)

'@args' can also be used in a binding form :- see the following section on advice for how to make the annotation object(s) available in the advice body.

 

any join point (method execution only in Spring AOP) on a Spring bean named 'tradeService':

bean(tradeService)


any join point (method execution only in Spring AOP) on Spring beans having names that match the wildcard expression '*Service':

bean(*Service)

原创粉丝点击