第二十七部分_AOP詳解

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的技術特性倒是相同的,分別爲:性能優化

  • join point(鏈接點):是程序執行中的一個精確執行點,例如類中的一個方法。它是一個抽象的概念,在實現AOP時,並不須要去定義一個join point。
  • point cut(切入點):本質上是一個捕獲鏈接點的結構,在AOP中,能夠定義一個point cut,來捕獲相關方法的調用。
  • advice(通知):是point cut的執行代碼,是執行"方面"的具體邏輯。
  • aspect(方面):point cut和advice結合起來就是aspect,它相似於OOP中定義的一個類,但它表明的更可能是對象間橫向的關係。
  • introduce(引入):爲對象引入附加的方法或屬性,從而達到修改對象結構的目的。有的AOP工具又將其稱爲mixin。

上述的技術特性組成了基本的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方法,可獲得相同的結果。

相關文章
相關標籤/搜索