Spring AOP 簡單理解AOP技術即(面向切面編程)技術是在面向對象編程基礎上的發展,AOP技術是對所有對象或一類對象編程。核心是在不增加代碼的基礎上,還增加了新的功能。AOP編程在開發框架本身用的比較多,而實際項目中,用的比較少。它是將分散在各個業務邏輯代碼中的相同代碼抽取出來形成一個獨立的模塊。
1、定義AOP術語
(1)切面(aspect):要實現的交叉功能,是系統模塊化的一個切面或領域。
(2)通知(advice):切面的具體實現,包含五類通知。
(3)連接點(jointpoint):應用程序執行過程中插入切面的地點。
(4)切點(cutpoint):定義通知應該應用哪些連接點。
(5)引入(introduction):為類添加新方法和屬性。
(6)目標對象(target):通知邏輯的織入目標類。
(7)代理(proxy):將通知應用到目標對象后創建的對象,應用系統的其他部分不用為了支持代理對象而改變
(8)織入(weaving):將切面應用到目標對象從而創建一個新代理對象的過程。
2、AOP原理和實例
(1)基礎接口和類的實現:
package com.jasson.aop;
public interface TestServiceInter1 {
public void sayHello();
}
package com.jasson.aop;
public interface TestServiceInter2 {
public void sayBye();
public void sayHi();
}
(2)實現類如下:
package com.jasson.aop;
public class TestService implements TestServiceInter1,TestServiceInter2 {
public void sayHello() {
System.out.println("sayHello() method ");
}
public void sayBye() {
System.out.println("sayBye() method");
}
public void sayHi() {
System.out.println("sayHi() method");
}
}
(1)前置通知:要求在每個方法調用前進行日志記錄,則用的前置通知,定義如下:
package com.jasson.aop;
import java.lang.reflect.Method;
import org.springframework.aop.MethodBeforeAdvice;
public class MyMethodBeforeAdvice
implements MethodBeforeAdvice {
/**
* method: 方法名
* args: 輸入參數
* target: 目標對象
*/ public void before(Method method, Object[] args, Object target)
throws Throwable {
System.out.println("前置通知調用 記錄日志

"+method.getName());
}
}
配置文件如下:
<?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:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd"
>
<!-- 配置被代理的對象,即目標對象 -->
<bean id="testService" class="com.jasson.aop.TestService" />
<!-- 配置前置通知 -->
<bean id="myMethodBeforeAdvice" class="com.jasson.aop.MyMethodBeforeAdvice" />
<!-- 配置代理對象 -->
<bean id="proxyFactoryBean" class="org.springframework.aop.framework.ProxyFactoryBean">
<!-- 代理接口集 -->
<property name="proxyInterfaces">
<list>
<value>com.jasson.aop.TestServiceInter1</value>
<value>com.jasson.aop.TestServiceInter2</value>
</list>
</property>
<!-- 把通知織入到代理對象 -->
<property name="interceptorNames">
<!-- 相當于包MyMethodBeforeAdvice前置通知和代理對象關聯,我們也
可以把通知看出攔截器,struts2核心攔截器 -->
<list>
<value>myMethodBeforeAdvice</value>
</list>
</property>
<!-- 配置被代理對象,即目標對象 -->
<property name="target" ref="testService"/>
</bean>
</beans>
package com.jasson.aop;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class App1 {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
ApplicationContext ac=new ClassPathXmlApplicationContext("com/jasson/aop/beans.xml");
TestServiceInter1 ts=(TestServiceInter1) ac.getBean("proxyFactoryBean");
ts.sayHello();
System.out.println("*******************************************");
((TestServiceInter2)ts).sayBye();
System.out.println("*******************************************");
((TestServiceInter2)ts).sayHi();
}
}
執行結果如下:
31-May-2012 18:19:53 org.springframework.beans.factory.support.DefaultListableBeanFactory preInstantiateSingletons
INFO: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@c8f6f8: defining beans [testService,myMethodBeforeAdvice,
myAfterReturningAdvice,myMethodInterceptor,myThrowsAdvice,proxyFactoryBean]; root of factory hierarchy
前置通知調用 記錄日志

sayHello
sayHello() method
*******************************************
前置通知調用 記錄日志

sayBye
sayBye() method
*******************************************
前置通知調用 記錄日志

sayHi
sayHi() method
(2)后置通知:要求在調用每個方法后執行的功能,例如在調用每個方法后關閉資源
package com.jasson.aop;
import java.lang.reflect.Method;
import org.springframework.aop.AfterReturningAdvice;
public class MyAfterReturningAdvice
implements AfterReturningAdvice {
@Override
public void afterReturning(Object returnValue, Method method, Object[] arg,
Object target)
throws Throwable {
// TODO Auto-generated method stub
System.out.println("后置通知調用,關閉資源

"+method.getName());
}
}
bean 配置如下:
<?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:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd"
>
<!-- 配置被代理的對象,即目標對象 -->
<bean id="testService" class="com.jasson.aop.TestService" />
<!-- 配置前置通知 -->
<bean id="myMethodBeforeAdvice" class="com.jasson.aop.MyMethodBeforeAdvice" />
<!-- 配置后置通知 -->
<bean id="myAfterReturningAdvice" class="com.jasson.aop.MyAfterReturningAdvice" />
<!-- 配置代理對象 -->
<bean id="proxyFactoryBean" class="org.springframework.aop.framework.ProxyFactoryBean">
<!-- 代理接口集 -->
<property name="proxyInterfaces">
<list>
<value>com.jasson.aop.TestServiceInter1</value>
<value>com.jasson.aop.TestServiceInter2</value>
</list>
</property>
<!-- 把通知織入到代理對象 -->
<property name="interceptorNames">
<!-- 相當于包MyMethodBeforeAdvice前置通知和代理對象關聯,我們也
可以把通知看出攔截器,struts2核心攔截器 -->
<list>
<value>myMethodBeforeAdvice</value>
<value>myAfterReturningAdvice</value>
</list>
</property>
<!-- 配置被代理對象,即目標對象 -->
<property name="target" ref="testService"/>
</bean>
</beans>
執行結果如下:
INFO: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@122cdb6: defining beans [testService,myMethodBeforeAdvice,myAfterReturningAdvice,myMethodInterceptor,myThrowsAdvice,proxyFactoryBean]; root of factory hierarchy
前置通知調用 記錄日志

sayHello
sayHello() method
后置通知調用,關閉資源

sayHello
*******************************************
前置通知調用 記錄日志

sayBye
sayBye() method
后置通知調用,關閉資源

sayBye
*******************************************
前置通知調用 記錄日志

sayHi
sayHi() method
后置通知調用,關閉資源
sayHi
(3)環繞通知:指在某個具體的方法中,添加相應的操作
package com.jasson.aop;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
public class MyMethodInterceptor implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation arg) throws Throwable {
// TODO Auto-generated method stub
System.out.println("環繞通知調用方法前");
Object obj = arg.proceed();
System.out.println("環繞通知調用方法后");
return obj;
}
}
配置文件如下:
<?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:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd"
>
<!-- 配置被代理的對象,即目標對象 -->
<bean id="testService" class="com.jasson.aop.TestService" />
<!-- 配置前置通知 -->
<bean id="myMethodBeforeAdvice" class="com.jasson.aop.MyMethodBeforeAdvice" />
<!-- 配置后置通知 -->
<bean id="myAfterReturningAdvice" class="com.jasson.aop.MyAfterReturningAdvice" />
<!-- 配置環繞通知 -->
<bean id="myMethodInterceptor" class="com.jasson.aop.MyMethodInterceptor" />
<!-- 配置代理對象 -->
<bean id="proxyFactoryBean" class="org.springframework.aop.framework.ProxyFactoryBean">
<!-- 代理接口集 -->
<property name="proxyInterfaces">
<list>
<value>com.jasson.aop.TestServiceInter1</value>
<value>com.jasson.aop.TestServiceInter2</value>
</list>
</property>
<!-- 把通知織入到代理對象 -->
<property name="interceptorNames">
<!-- 相當于包MyMethodBeforeAdvice前置通知和代理對象關聯,我們也
可以把通知看出攔截器,struts2核心攔截器 -->
<list>
<value>myMethodBeforeAdvice</value>
<value>myAfterReturningAdvice</value>
<value>myMethodInterceptor</value>
</list>
</property>
<!-- 配置被代理對象,即目標對象 -->
<property name="target" ref="testService"/>
</bean>
</beans>
執行結果如下:
INFO: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@1ce2dd4: defining beans [testService,myMethodBeforeAdvice,myAfterReturningAdvice,myMethodInterceptor,proxyFactoryBean]; root of factory hierarchy
前置通知調用 記錄日志

sayHello
環繞通知調用方法前
sayHello() method
環繞通知調用方法后
后置通知調用,關閉資源

sayHello
*******************************************
前置通知調用 記錄日志

sayBye
環繞通知調用方法前
sayBye() method
環繞通知調用方法后
后置通知調用,關閉資源

sayBye
*******************************************
前置通知調用 記錄日志

sayHi
環繞通知調用方法前
sayHi() method
環繞通知調用方法后
后置通知調用,關閉資源
sayHi
(4)異常通知:當發生異常時,要執行的通知
package com.jasson.aop;
import java.lang.reflect.Method;
import org.springframework.aop.ThrowsAdvice;
public class MyThrowsAdvice implements ThrowsAdvice {
public void afterThrowing(Method method, Object[] os, Object target,
Exception exception) {
System.out.println("異常通知產生異常,進行處理" + exception.getMessage());
}
}
<?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:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd"
>
<!-- 配置被代理的對象,即目標對象 -->
<bean id="testService" class="com.jasson.aop.TestService" />
<!-- 配置前置通知 -->
<bean id="myMethodBeforeAdvice" class="com.jasson.aop.MyMethodBeforeAdvice" />
<!-- 配置后置通知 -->
<bean id="myAfterReturningAdvice" class="com.jasson.aop.MyAfterReturningAdvice" />
<!-- 配置環繞通知 -->
<bean id="myMethodInterceptor" class="com.jasson.aop.MyMethodInterceptor" />
<!-- 配置異常通知 -->
<bean id="myThrowsAdvice" class="com.jasson.aop.MyThrowsAdvice" />
<!-- 配置代理對象 -->
<bean id="proxyFactoryBean" class="org.springframework.aop.framework.ProxyFactoryBean">
<!-- 代理接口集 -->
<property name="proxyInterfaces">
<list>
<value>com.jasson.aop.TestServiceInter1</value>
<value>com.jasson.aop.TestServiceInter2</value>
</list>
</property>
<!-- 把通知織入到代理對象 -->
<property name="interceptorNames">
<!-- 相當于包MyMethodBeforeAdvice前置通知和代理對象關聯,我們也
可以把通知看出攔截器,struts2核心攔截器 -->
<list>
<value>myMethodBeforeAdvice</value>
<value>myAfterReturningAdvice</value>
<value>myMethodInterceptor</value>
<value>myThrowsAdvice</value>
</list>
</property>
<!-- 配置被代理對象,即目標對象 -->
<property name="target" ref="testService"/>
</bean>
package com.jasson.aop;
public class TestService implements TestServiceInter1,TestServiceInter2 {
public void sayHello() {
System.out.println("sayHello() method ");
}
public void sayBye() {
System.out.println("sayBye() method");
}
public void sayHi() {
int a =10/0;
System.out.println("sayHi() method");
}
}
執行結果如下:
INFO: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@1ce2dd4: defining beans [testService,myMethodBeforeAdvice,myAfterReturningAdvice,myMethodInterceptor,myThrowsAdvice,proxyFactoryBean]; root of factory hierarchy
前置通知調用 記錄日志

sayHello
環繞通知調用方法前
sayHello() method
環繞通知調用方法后
后置通知調用,關閉資源

sayHello
*******************************************
前置通知調用 記錄日志

sayBye
環繞通知調用方法前
sayBye() method
環繞通知調用方法后
后置通知調用,關閉資源

sayBye
*******************************************
前置通知調用 記錄日志

sayHi
環繞通知調用方法前
異常通知產生異常,進行處理/ by zero
Exception in thread "main" java.lang.ArithmeticException: / by zero
(5)上面的通知都是針對每個方法的,如果只是對單個或者一類的方法進行相應處理的時,可采用名字或者正則表達式的方式進行處理
配置如下:
<?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:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd"
>
<!-- 配置被代理的對象,即目標對象 -->
<bean id="testService" class="com.jasson.aop.TestService" />
<!-- 配置前置通知 -->
<bean id="myMethodBeforeAdvice" class="com.jasson.aop.MyMethodBeforeAdvice" />
<!-- 配置后置通知 -->
<bean id="myAfterReturningAdvice" class="com.jasson.aop.MyAfterReturningAdvice" />
<!-- 配置環繞通知 -->
<bean id="myMethodInterceptor" class="com.jasson.aop.MyMethodInterceptor" />
<!-- 配置異常通知 -->
<bean id="myThrowsAdvice" class="com.jasson.aop.MyThrowsAdvice" />
<!-- 通知與正則表達式切入點一起配置 -->
<!-- Advisor等于切入點加通知,所有say開頭的方法添加前置通知 -->
<bean id="regexpPointcutAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
<property name="advice" ref="myMethodBeforeAdvice"/>
<property name="patterns">
<list>
<value>.*say.*</value>
</list>
</property>
</bean>
<!-- 方法名匹配切入點配置器:只對 sayHello方法添加環繞通知-->
<bean id="namePointcutAdvisor" class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor">
<property name="advice" ref="myMethodInterceptor"/>
<property name="mappedNames">
<list>
<value>sayHello</value>
</list>
</property>
</bean>
<!-- 配置代理對象 -->
<bean id="proxyFactoryBean" class="org.springframework.aop.framework.ProxyFactoryBean">
<!-- 代理接口集 -->
<property name="proxyInterfaces">
<list>
<value>com.jasson.aop.TestServiceInter1</value>
<value>com.jasson.aop.TestServiceInter2</value>
</list>
</property>
<!-- 把通知織入到代理對象 -->
<property name="interceptorNames">
<!-- 相當于包MyMethodBeforeAdvice前置通知和代理對象關聯,我們也
可以把通知看出攔截器,struts2核心攔截器 -->
<list>
<value>namePointcutAdvisor</value>
<value>myAfterReturningAdvice</value>
<value>regexpPointcutAdvisor</value>
<value>myThrowsAdvice</value>
</list>
</property>
<!-- 配置被代理對象,即目標對象 -->
<property name="target" ref="testService"/>
</bean>
</beans>
執行結果如下:
INFO: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@1ef9157: defining beans
[testService,myMethodBeforeAdvice,myAfterReturningAdvice,myMethodInterceptor,myThrowsAdvice,regexpPointcutAdvisor,namePointcutAdvisor,proxyFactoryBean];
root of factory hierarchy
環繞通知調用方法前
前置通知調用 記錄日志

sayHello
sayHello() method
后置通知調用,關閉資源

sayHello
環繞通知調用方法后
*******************************************
前置通知調用 記錄日志

sayBye
sayBye() method
后置通知調用,關閉資源

sayBye
*******************************************
前置通知調用 記錄日志

sayHi
異常通知產生異常,進行處理/ by zero
Exception in thread "main" java.lang.ArithmeticException: / by zero
-----------------------------------------------------
Silence, the way to avoid many problems;
Smile, the way to solve many problems;