AOP技術本質:java
AOP(Aspect-Oriented Programming,面向方面編程),能夠說是OOP(Object-Oriented Programming,面向對象編程)的補充和完善。OOP引入封裝、繼承和多態性等概念來創建一種對象層次結構,用以模擬公共行爲的一個集合。當咱們須要爲分散的對象引入公共行爲的時候,OOP則顯得無能爲力。也就是說,OOP容許你定義從上到下的關係,但並不適合定義從左到右的關係。例如日誌功能,日誌代碼每每水平地散佈在全部對象層次中,而與它所散佈到的對象的核心功能毫無關係。對於其餘類型的代碼,如安全性、異常處理和透明的持續性也是如此。這種散佈在各處的無關的代碼被稱爲橫切(cross-cutting)代碼,在OOP設計中,它致使了大量代碼的重複,而不利於各個模塊的重用。正則表達式
而AOP技術則偏偏相反,他利用一種稱爲"橫切"的技術,剖解開封裝的對象內部,並將哪些影響了多個類的公共行爲封裝到一個可重用模塊,並將其命名爲"Aspect",即方面。所謂"方面",簡單地說,就是將那些與業務無關,卻爲業務模塊所共同調用的邏輯或責任封裝起來,便於減小系統的重複代碼,下降模塊間的耦合度,並有利於將來的可操做性和可維護性。AOP表明的是一個橫向的關係,若是說"對象"是一個空心的圓柱體,其中封裝的是對象的屬性和行爲,那麼面向方面編程的方法就彷彿一把利刃,把這些空心圓柱體剖開,以得到其內部的消息。而剖開的切面,也就是所謂的"方面"了。而後它又以巧奪天功的妙手將這些剖開的切面復原,不留痕跡。spring
使用"橫切"技術,AOP把軟件系統分爲兩個部分:核心關注點和橫切關注點。業務處理的主要流程是核心關注點,與之關係不大的部分是橫切關注點。橫切關注點的一個特色是,它們常常發生在覈心關注點的多處,而各處都基本類似。好比權限認證、日誌、事務處理。AOP的做用在於分離系統中的各類關注點,將核心關注點和橫切關注點分離開來。編程
AOP的核心思想就是"將應用程序中的商業邏輯同其提供支持的通用服務進行分離"。安全
實現AOP的技術,主要分爲兩大類:一是採用動態代理技術,利用截取消息的方式,對該消息進行裝飾,以取代原有對象行爲的執行;二是採用靜態織入的方式,引入特定的語法建立"方面",從而使得編譯器能夠在編譯期間織入有關"方面"的代碼。然而異曲同工,實現AOP的技術特性倒是相同的,分別爲:性能優化
上述的技術特性組成了基本的AOP技術,大多數AOP工具均實現了這些技術。它們也能夠是研究AOP技術的基本術語。app
上圖:ide
"橫切"是AOP的專有名詞。它是一種蘊含強大力量的相對簡單的設計和編程技術,尤爲是用於創建鬆散耦合的、可擴展的企業系統時。橫切技術可使得AOP在一個給定的編程模型中穿越既定的職責部分(好比日誌記錄和性能優化)的操做。工具
若是不使用橫切技術,軟件開發是怎樣的情形呢?在傳統的程序中,因爲橫切行爲的實現是分散的,開發人員很難對這些行爲進行邏輯上的實現或更改。例如,用於日誌記錄的代碼和主要用於其餘職責的代碼纏繞在一塊兒。根據所解決的問題的複雜程度和做用域的不一樣,所引發的混亂可大可小。更改一個應用程序的日誌記錄策略可能涉及數百次編輯——即便可行,這也是個使人頭疼的任務。性能
在AOP中,咱們將這些具備公共邏輯的,與其餘模塊的核心邏輯糾纏在一塊兒的行爲稱爲"橫切關注點(Crosscutting Concern)",由於它跨越了給定編程模型中的典型職責界限。
下面咱們經過一個小的Demo,演示一下AOP在Spring中的具體應用:
首先新建一個Java Project:aopproxy,增長對Spring的支持。選擇Spring2.5,庫選擇:Spring 2.5 AOP Libraries、Spring 2.5 Core Libraries。點擊Finish。
新建包lee,在下面新建六個類:
package lee; public interface Person { void info(); void run(); } --------------------------------------------------------------------------------------------- package lee; public class PersonImpl implements Person { private String name; private int age; public void setName(String name) { this.name = name; } public void setAge(int age) { this.age = age; } @Override public void info() { System.out.println("個人名字是: " + name + ", 今年年齡爲: " + age); } @Override public void run() { if(age < 45) System.out.println("我還年輕,奔跑迅速..."); else System.out.println("我年老體弱,只能慢跑..."); } } ---------------------------------------------------------------------------------------------------------- package lee; import java.lang.reflect.Method; import org.springframework.aop.MethodBeforeAdvice; public class MyBeforeAdvisor implements MethodBeforeAdvice { @Override public void before(Method method, Object[] args, Object target) throws Throwable { System.out.println("方法調用以前..."); System.out.println("下面是方法調用的信息:"); System.out.println("所執行的方法是:" + method); System.out.println("調用方法的參數是: " + args); System.out.println("目標對象是: " + target); } } ------------------------------------------------------------------------------------------------------- package lee; import java.lang.reflect.Method; import org.springframework.aop.AfterReturningAdvice; public class MyAfterAdvisor implements AfterReturningAdvice { @Override public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable { System.out.println("方法調用結束..."); System.out.println("目標方法的返回值是:"); System.out.println("目標方法是:" + method); System.out.println("目標方法的參數是: " + args); System.out.println("目標對象是: " + target); } } ----------------------------------------------------------------------------------------------------- package lee; import org.aopalliance.intercept.MethodInterceptor; import org.aopalliance.intercept.MethodInvocation; public class MyAroundInterceptor implements MethodInterceptor { @Override public Object invoke(MethodInvocation invocation) throws Throwable { System.out.println("調用方法以前:invocation對象:[" + invocation + "]"); Object rval = invocation.proceed(); System.out.println("調用結束..."); return rval; } } ---------------------------------------------------------------------------------------------------- package lee; import org.springframework.beans.factory.xml.XmlBeanFactory; import org.springframework.core.io.ClassPathResource; public class BeanTest { public static void main(String[] args) throws Exception { XmlBeanFactory factory = new XmlBeanFactory(new ClassPathResource("applicationContext.xml")); Person p = (Person) factory.getBean("person"); // p.info(); p.run(); } }
下面是src下的配置文件applicationContext.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:p="http://www.springframework.org/schema/p" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd"> <bean id="personTarget" class="lee.PersonImpl"> <property name="name"> <value>Wawa</value> </property> <property name="age"> <value>51</value> </property> </bean> <bean id="myAdvisor" class="lee.MyBeforeAdvisor" /> <bean id="myAroundInterceptor" class="lee.MyAroundInterceptor" /> <bean id="runAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor"> <!-- advice屬性肯定處理bean --> <property name="advice"> <!-- 此處的處理bean定義採用嵌套bean, 也可引用容器的另外一個bean --> <bean class="lee.MyAfterAdvisor" /> </property> <!-- patterns肯定正則表達式模式 --> <property name="patterns"> <list> <!-- 肯定正則表達式列表 --> <value>.*run.*</value> </list> </property> </bean> <bean id="person" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="proxyInterfaces"> <value>lee.Person</value> </property> <property name="target"> <ref local="personTarget"/> </property> <property name="interceptorNames"> <list> <value>runAdvisor</value> <value>myAdvisor</value> <value>myAroundInterceptor</value> </list> </property> </bean> </beans>
運行BeanTest類的run方法的結果:
log4j:WARN No appenders could be found for logger (org.springframework.beans.factory.xml.XmlBeanDefinitionReader). log4j:WARN Please initialize the log4j system properly. 方法調用以前... 下面是方法調用的信息: 所執行的方法是:public abstract void lee.Person.run() 調用方法的參數是: [Ljava.lang.Object;@48ff2413 目標對象是: lee.PersonImpl@7b36a43c 調用方法以前:invocation對象:[ReflectiveMethodInvocation: public abstract void lee.Person.run(); target is of class [lee.PersonImpl]] 我年老體弱,只能慢跑... 調用結束... 方法調用結束... 目標方法的返回值是: 目標方法是:public abstract void lee.Person.run() 目標方法的參數是: [Ljava.lang.Object;@669980d5 目標對象是: lee.PersonImpl@7b36a43c
運行info方法的結果:
log4j:WARN No appenders could be found for logger (org.springframework.beans.factory.xml.XmlBeanDefinitionReader). log4j:WARN Please initialize the log4j system properly. 方法調用以前... 下面是方法調用的信息: 所執行的方法是:public abstract void lee.Person.info() 調用方法的參數是: [Ljava.lang.Object;@bd10a5c 目標對象是: lee.PersonImpl@736921fd 調用方法以前:invocation對象:[ReflectiveMethodInvocation: public abstract void lee.Person.info(); target is of class [lee.PersonImpl]] 個人名字是: Wawa, 今年年齡爲: 51 調用結束...
接下來,再看一個實例:使用Spring進行權限驗證。
首先新建一個Java Project:authority,增長對Spring的支持。選擇Spring2.5,庫選擇:Spring 2.5 AOP Libraries、Spring 2.5 Core Libraries。點擊Finish。
新建包lee,在下面新建六個類:
package lee; public interface TestService { void view(); void modify(); } ---------------------------------------------------------------------------------------------------------- package lee; public class TestServiceImpl implements TestService { @Override public void view() { System.out.println("用戶查看數據"); } @Override public void modify() { System.out.println("用戶修改數據"); } } --------------------------------------------------------------------------------------------------------- package lee; public interface TestAction { public void modify2(); public void view2(); } ------------------------------------------------------------------------------------------------------ package lee; public class TestActionImpl implements TestAction { private TestService ts; public void setTs(TestService ts) { this.ts = ts; } @Override public void modify2() { ts.modify(); } @Override public void view2() { ts.view(); } } ------------------------------------------------------------------------------------------------------ package lee; import org.aopalliance.intercept.MethodInterceptor; import org.aopalliance.intercept.MethodInvocation; public class AuthorityInterceptor implements MethodInterceptor { private String user; public void setUser(String user) { this.user = user; } @Override public Object invoke(MethodInvocation invocation) throws Throwable { System.out.println("================="); String methodName = invocation.getMethod().getName(); if(!user.equals("admin") && !user.equals("registerUser")) { System.out.println("您無權執行該方法"); return null; // 直接返回,再也不執行後續方法。 } else if(user.equals("registerUser") && methodName.equals("modify")) { System.out.println(methodName); System.out.println("您不是管理員,沒法修改數據"); return null; } else { return invocation.proceed(); // 代理事後,真實對象的方法獲得執行。 } } } ---------------------------------------------------------------------------------------------------------- package lee; import org.springframework.beans.factory.xml.XmlBeanFactory; import org.springframework.core.io.ClassPathResource; public class BeanTest { public static void main(String[] args) throws Exception { XmlBeanFactory factory = new XmlBeanFactory(new ClassPathResource("applicationContext.xml")); TestAction ta = (TestAction) factory.getBean("testAction"); ta.view2(); // 不一樣於TestService接口的方法名,以示區別。 ta.modify2(); } }
下面是ApplicationContext.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:p="http://www.springframework.org/schema/p" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"> <bean id="serviceTarget" class="lee.TestServiceImpl" /> <bean id="authorityInterceptor" class="lee.AuthorityInterceptor"> <property name="user"> <value>registerUser</value> <!-- 測試:admin,aa,registerUser --> </property> </bean> <bean id="service" class="org.springframework.aop.framework.ProxyFactoryBean"> <!-- 也可使用不借助接口的實現方式,直接做用於類,後面會給出相應的xml文件 --> <property name="proxyInterfaces"> <value>lee.TestService</value> </property> <property name="target"> <ref local="serviceTarget"/> </property> <property name="interceptorNames"> <list> <value>authorityInterceptor</value> </list> </property> </bean> <bean id="testAction" class="lee.TestActionImpl"> <property name="ts"> <ref local="service"/> </property> </bean> </beans>
經過修改ApplicationContext.xml中的bean-authorityInterceptor的user屬性,能夠看到權限驗證起做用了。
此外,咱們也能夠直接代理實現類類,而不是接口,它自己不是Java提供的支持,而是Spring的AOP三方庫提供的支持,經過直接修改Class文件的方式來達到目的。xml中相關部分以下:
<bean id="service" class="org.springframework.aop.framework.ProxyFactoryBean"> <!-- 也可使用不借助接口的實現方式,直接做用於類,後面會給出相應的xml文件 --> <property name="proxyTargetClass"> <value>true</value> </property> <property name="target"> <ref local="serviceTarget"/> </property> <property name="interceptorNames"> <list> <value>authorityInterceptor</value> </list> </property> </bean>
刪除TestAction接口,對TestActionImpl修改以下:
package lee; public class TestActionImpl implements TestAction { private TestServiceImpl ts; public void setTs(TestServiceImpl ts) { this.ts = ts; } @Override public void modify2() { ts.modify(); } @Override public void view2() { ts.view(); } }
運行main方法,可獲得相同的結果。