Spring AOP即Aspect-oriented programming,面向切面編程,是做爲面向對象編程的一種補充,專門用於處理系統中分佈於各個模塊(不一樣方法)中的交叉關注點的問題。簡單地說,就是一個攔截器(interceptor)攔截一些處理過程。例如,當一個method被執行,Spring AOP可以劫持正在運行的method,在method執行前或者後加入一些額外的功能。java
在Spring AOP中,支持4中類型的通知(Advice)web
Before advice ——method執行前通知spring
After returning advice ——method返回一個結果後通知編程
After throwing advice – method拋出異常後通知app
Around advice – 環繞通知,結合了以上三種this
下邊這個例子解釋Spring AOP怎樣工做。url
首先一個簡單的不使用AOP的例子。代理
先建立一個簡單的Service,爲了稍後演示,這個類中加了幾個簡單的打印method。code
CustomerService.java以下:xml
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/
*************************
建立一個實現了接口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方法。
建立一個實現了接口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運行返回結果後,都將再執行HijackAfterMethod的afterReturning方法。可是執行到cust.printThrowException()後,直接拋出異常,方法沒有正常執行完畢(或者說沒有返回結果),因此不運行切入的afterReturning方法。
建立一個實現了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,則不能被截獲。
結合了以上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,即切入點和通知,之後的章節中會逐漸介紹。