aop( aspect oriented programming ) 面向切麵編程,是對所有對象或者是一類對象編程 幾個重要的概念: 1.切麵(aspect):要實現的交叉功能,是系統模塊化的一個切麵或領域。如日誌記錄。 2.連接點:應用程式執行過程中插入切麵的地點,可以是方法調用,異常拋出,或者要 ...
aop( aspect oriented programming )
面向切麵編程,是對所有對象或者是一類對象編程
幾個重要的概念:
1.切麵(aspect):要實現的交叉功能,是系統模塊化的一個切麵或領域。如日誌記錄。
2.連接點:應用程式執行過程中插入切麵的地點,可以是方法調用,異常拋出,或者要修改的 欄位。
3.通知:切麵的實際實現,他通知系統新的行為。如在日誌通知包含了實 現日誌功能的代碼,如嚮日志文件寫日誌。通知在連接點插入到應用系統中。
4.切入點:定義了通知應該應用在哪些連接點,通知可以應用到AOP框架支持的任何連接點。
5.引入:為類添加新方法和屬性。
6.目標對象:被通知的對象。既可以是你編寫的類也可以是第三方類。
7.代理:將通知應用到目標對象後創建的對象,應用系統的其他部分不用為了支持代理對象而 改變。
8.織入:將切麵應用到目標對象從而創建一個新代理對象的過程。織入是一個過程。織入發生在目標 對象生命周期的多個點上:
編譯期:切麵在目標對象編譯時織入.這需要一個特殊的編譯器.
類裝載期:切麵在目標對象被載入JVM時織入.這需要一個特殊的類載入器.
運行期:切麵在應用系統運行時織入.
創建切麵的方式:
還有一種引用通知方式。總共五種類型,下麵一一舉例實現:
編程說明:
步驟:
1.定義介面
2.編寫對象(被代理對象=目標對象)
3.編寫通知(前置通知目標方法調用前調用)
4.在beans.xml文件配置
4.1 配置 被代理對象=目標對象
4.2 配置通知
4.3 配置代理對象 是 ProxyFactoryBean的對象實例
4.3.1 代理介面集
4.3.2 織入通知
4.3.3 配置被代理對象
一.定義介面:
介面1:
public interface TestServiceInter {
public void sayHello();
}
介面2:
public interface TestServiceInter2 {
public void sayBye();
}
二、編寫對象
public class Test1Service implements TestServiceInter,TestServiceInter2 {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void sayHello() {
System.out.println("hi "+name);
}
public void sayBye() {
System.out.println("bye "+name);
//int i=9/0;
}
}
三、編寫通知
1.前置通知
public class MyMethodBeforeAdvice implements MethodBeforeAdvice {
/**
* method: 被調用方法名字
* args: 給method傳遞的參數
* target: 目標對象
*/
public void before(Method method, Object[] args, Object target)
throws Throwable {
System.out.println("..............");
System.out.println("記錄日誌..."+method.getName());
}
}
該介面提供了獲得目標方法、參數和目標對象的機會。不能夠改變運行時參數,即不能替換參數對象和目標對象。
註意在方法結束後不返回任何值。原因是該介面返回後,目標方法將會被調用,應該返回目標對象的返回值。
該介面唯一能 阻止目標方法被調用的途徑是拋出異常或(System.exit())。
2.後置通知
與前置通知類似
public class MyAfterReturningAdvice implements AfterReturningAdvice {
@Override
public void afterReturning(Object returnValue, Method method,
Object[] args, Object target) throws Throwable
System.out.println("關閉資源。。。。");
}
}
3.環繞通知
public class MyMethodInterceptor implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation arg0) throws Throwable {
System.out.println("調用方法前。。。");
Object obj=arg0.proceed(); //目標對象方法的調用執行
System.out.println("調用方法後。。。");
return obj;
}
}
該介面同前兩種通知有兩個重要區別:
1.該通知能夠控制目標方法 是否真的被調用。通過invocation.proceed()方法來調用。
2.該通知可以控制返回的對象。可以返回一個與proceed()方法返回對象完全不同的對象。但要謹慎使用。
4.異常通知
public class MyThrowsAdvice implements ThrowsAdvice {
public void afterThrowing(Method m,Object[] os,Object target,Exception throwable){
System.out.println("出大事了"+throwable.getMessage());
}
}
public interface ThrowsAdvice{
}
該介面為標識性介面,沒有任何方法,但實現該介面的類必須要有如下形式的方法:
public void afterThrowing(Throwable throwable);
public void afterThrowing(Method m,Object[] os,Object target,Exception throwable);
第一個方法只接受一個參數:需要拋出的異常。 第二個方法接受異常、被調用的方法、參數以及目標對象。
5.引入通知
如果不能表達在應用系統的什麼地方應用 通知的話,通知將毫無用處,這就是切入點的用處。
切入點決定了一個特定的類的特定方法是否滿足一定的規則。若符合,通知就應用到該方法上。
引入通知只需要在beans.xml中自定義切入點來控制通知。
四、beans.xml配置
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"
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="test1Service" class="com.hsp.aop.Test1Service">
<property name="name" value="順平" />
</bean>
<!-- 配置前置通知 -->
<bean id="MyMethodBeforeAdvice" class="com.hsp.aop.MyMethodBeforeAdvice" />
<!-- 配置後置通知 -->
<bean id="myAfterReturningAdvice" class="com.hsp.aop.MyAfterReturningAdvice"/>
<!-- 配置環繞通知 -->
<bean id="myMethodInterceptor" class="com.hsp.aop.MyMethodInterceptor" />
<!-- 配置異常通知 -->
<bean id="myThrowsAdvice" class="com.hsp.aop.MyThrowsAdvice"/>
<!-- 定義前置通知的切入點 -->
<bean id="myMethodBeforeAdviceFilter" class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor" >
<property name="advice" ref="MyMethodBeforeAdvice" />
<property name="mappedNames">
<list>
<value>sayHello</value>
</list>
</property>
</bean>
<!-- 代理對象的實現原理.實現介面
proxyFactoryBean implements TestServiceInter,TestServiceInter2{
public void sayHello();
}
思考:多態下介面類型的轉換
interface Inter1{};
class A implements Inter1,Inter2{
}
Inter1 a=new A();
Inter2 b=(Inter2)a;
-->
<!-- 配置代理對象,只需配置而不要寫 -->
<bean id="proxyFactoryBean" class="org.springframework.aop.framework.ProxyFactoryBean">
<!-- 代理介面集 這裡name值是固定的-->
<property name="proxyInterfaces">
<list>
<value>com.hsp.aop.TestServiceInter</value>
<value>com.hsp.aop.TestServiceInter2</value>
</list>
</property>
<!-- 把通知織入到代理對象 這裡name值是固定的 -->
<property name="interceptorNames">
<list>
<!-- 相當於把MyMethodBeforeAdvice前置通知和代理對象關聯,我們也
可以把通知看成攔截器,struts2核心攔截器 -->
<!-- 相當於自定義切入點來控制前置通知的使用 -->
<value>myMethodBeforeAdviceFilter</value>
<!-- 織入後置通知 -->
<value>myAfterReturningAdvice</value>
<!-- 織入環繞通知 -->
<value>myMethodInterceptor</value>
<!-- 織入異常通知 -->
<value>myThrowsAdvice</value>
</list>
</property>
<!-- 配置被代理對象,可以指定 -->
<property name="target" ref="test1Service"/>
</bean>
</beans>
測試:
public static void main(String[] args) {
ApplicationContext ac=new ClassPathXmlApplicationContext("com/hsp/aop/beans.xml");
TestServiceInter ts=(TestServiceInter) ac.getBean("proxyFactoryBean");
ts.sayHello();
((TestServiceInter2)ts).sayBye();
}
執行結果:
現在加入列印代理對象的類型的語句:
public static void main(String[] args) {
ApplicationContext ac=new ClassPathXmlApplicationContext("com/hsp/aop/beans.xml");
TestServiceInter ts=(TestServiceInter) ac.getBean("proxyFactoryBean");
System.out.println("ts的類型是"+ts);
ts.sayHello();
((TestServiceInter2)ts).sayBye();
}
執行結果:
ts是個代理對象,從中還可以看出只要代理對象被調用就會執行織入通知。
提問? 說spring的aop中,當你通過代理對象去實現aop的時候,獲取的ProxyFactoryBean是什麼類型?
答: 返回的是一個代理對象,如果目標對象實現了介面,則spring使用jdk 動態代理技術,如果目標對象沒有實現介面,則spring使用CGLIB技術.