AOP爲Aspect Oriented Programming的縮寫,意爲:面向切面編程,通過預編譯方式和運行期動態代理實現程序功能的統一維護的一種技術。面向切面編程(AOP)通過提供另外一種思考程序結構的途經來彌補面向對象編程(OOP)的不足。在OOP中模塊化的關鍵單元是類(classes),而在AOP中模塊化的單元則是切面。切面能對關注點進行模塊化,例如橫切多個類型和對象的事務管理。
1. AOP的編程步驟
(1)定義接口
(2)編寫對象(被代理對象=目標對象)
(3)編寫通知
(4)配置beans.xml文件
1)配置被代理對象(目標對象)
2)配置通知
3)配置代理對象,是ProxyFactoryBean的對象實例
(代理接口集,把通知織入到代理對象,配置被代理對象)
2. 通知類型
名稱 | 通知類型 | 接口 | 描述 |
---|---|---|---|
前置通知 | Before | org.springframework.aop.MethodBeforeAdvice | 在目標方法調用前調用 |
後置通知 | After | org.springframework.aop.AfterReturningAdvice | 在目標方法調用後調用 |
環繞通知 | Around | org.aopalliance.intercept.MethodInterceptor | 攔截對目標方法調用 |
異常通知 | Throws | org.springframework.aop.ThrowsAdvice | 當目標方法拋出異常時調用 |
3. AOP編程實例
(1)定義接口
package com.aop;
public interface TestServiceInterface {
public void sayHello();
}
package com.aop;
public interface TestServiceInterface2 {
public void sayBye();
}
(2)編寫對象(被代理對象)
package com.aop;
public class Test1Service implements TestServiceInterface,TestServiceInterface2 {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void sayHello() {
System.out.println("hello "+name);
}
@Override
public void sayBye() {
System.out.println("byebye "+name);
}
}
(3)編寫通知
- 前置通知
package com.aop;
import java.lang.reflect.Method;
import org.springframework.aop.MethodBeforeAdvice;
public class MyMethodBeforeAdvice implements MethodBeforeAdvice {
/*
* method:被調用方法名字
* args:給method傳遞的參數
* target:目標對象
*
*/
public void before(Method method, Object[] args, Object target)
throws Throwable {
System.out.println("記錄日誌..."+method.getName());
}
}
- 後置通知
package com.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[] args,
Object target) throws Throwable {
// TODO Auto-generated method stub
System.out.println("關閉資源...");
System.out.println("***************************");
}
}
- 環繞通知
package com.aop;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
public class MyMethodInterceptor implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation arg0) throws Throwable {
// TODO Auto-generated method stub
System.out.println("調用方法前...");
Object obj=arg0.proceed();
System.out.println("調用方法後...");
return obj;
}
}
- 異常通知
package com.aop;
import java.lang.reflect.Method;
import org.springframework.aop.ThrowsAdvice;
public class MyThrowsAdvice implements ThrowsAdvice {
public void afterThrowing(Method m, Object[] args, Object target, Exception e) {
// Do something with all arguments
System.out.println("發生異常:"+e.getMessage());
}
}
(4)配置beans.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="test1Service" class="com.aop.Test1Service">
<property name="name" value="Elana"></property>
</bean>
<bean id="test2Service" class="com.aop.Test2Service">
<property name="name" value="Damon"></property>
</bean>
<!-- 配置前置通知 -->
<bean id="myMethodBeforeAdvice" class="com.aop.MyMethodBeforeAdvice">
</bean>
<!-- 配置後置通知 -->
<bean id="myAfterReturningAdvice" class="com.aop.MyAfterReturningAdvice"></bean>
<!-- 配置環繞通知 -->
<bean id="myMethodInterceptor" class="com.aop.MyMethodInterceptor"></bean>
<!-- 配置異常通知 -->
<bean id="myThrowsAdvice" class="com.aop.MyThrowsAdvice"></bean>
<!-- 定義前置通知的切入點 -->
<bean id="myMethodBeforeAdviceFilter" class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor">
<property name="advice" ref="myMethodBeforeAdvice"></property>
<property name="mappedNames">
<list>
<value>say*</value><!-- 即只對以say開頭的方法進行前置通知處理,支持正則表達式過濾 -->
</list>
</property>
</bean>
<!-- 配置代理對象 -->
<bean id="proxyFactoryBean" class="org.springframework.aop.framework.ProxyFactoryBean">
<!-- 代理接口集 -->
<property name="proxyInterfaces">
<list>
<value>com.aop.TestServiceInterface</value>
<value>com.aop.TestServiceInterface2</value>
</list>
</property>
<!-- 把通知織入到代理對象 -->
<property name="interceptorNames">
<list>
<!-- 織入前置通知,相當於把myMethodBeforeAdvice前置通知和代理對象關聯起來,也可以把通知看成攔截器,Struts2核心攔截器 -->
<value>myMethodBeforeAdvice</value>
<!-- 使用自定義切入點來控制前置通知 -->
<!-- <value>myMethodBeforeAdviceFilter</value>-->
<!-- 織入後置通知 -->
<value>myAfterReturningAdvice</value>
<!-- 織入環繞通知 -->
<value>myMethodInterceptor</value>
<!-- 織入異常通知 -->
<value>myThrowsAdvice</value>
</list>
</property>
<!-- 配置被代理對象(可以指定) -->
<property name="target" ref="test1Service">
</property>
</bean>
</beans>
(5)測試
package com.aop;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Test {
public static void main(String[] args) {
// TODO Auto-generated method stub
ApplicationContext ac=new ClassPathXmlApplicationContext("/com/aop/beans.xml");
//獲取代理對象
//一個類實現多個接口,接口之間可以互相轉換
TestServiceInterface ts=(TestServiceInterface)ac.getBean("proxyFactoryBean");
//System.out.println("ts的類型是:"+ts);//ts的類型是被代理對象(目標對象)的類型,即Test1Service,而不是代理對象的類型ProxyFactoryBean
ts.sayHello();
((TestServiceInterface2)ts).sayBye();
}
}
運行結果如下: