精通Spring+4.x++企業開發與實踐之SpringAOP基礎

AOP概述

AOP的簡稱"Aspect Oriented Programing"的簡稱———面向切面編程。java

AOP術語

鏈接點

一個類或者一段程序代碼用於一些具備邊界性質的特性的特定點。這些代碼的特定點就被成爲"鏈接點"。Spring只支持方法的鏈接點,即僅能在方法調用前,方法調用後,方法拋出異常時及方法調用後這些程序執行點織入加強。鏈接點由兩個信息肯定: 1.用方法表示的程序執行點 2.用相對於位置表示方位。 例如: demo.foo()方法執行器的鏈接點,執行點未demo.foo(),方位未改方法執行前的位置。Spring使用切點對執行點進行定位,而方位裝載加強類型中定義。正則表達式

切點(Pointcut)

每一個程序類都用於多個連繫欸但,若是一個用於兩個方法的類,這兩個方法都是鏈接點,鏈接點是客觀存在的事務。AOP經過"切點"定位特定的鏈接點。切點和鏈接點是一對多的關係(就像數據庫查詢條件和記錄的關係)。在Spring中,切點使用org.springframework.aop.Pointcut接口進行描述,它使用類和方法做爲鏈接點的查詢條件,Spring AOP的規則系欸性能引發負責系欸性能切點所設定的查詢條件,找到對應的鏈接點。鏈接點是方法執行前,執行後等包括方位信息的具體程序執行點,而切點值定位到某個方法上,因此若是但願定位到具體的鏈接點上,還須要提供方位信息.spring

加強(Advice)

加強是織入目標類鏈接點上的一段程序代碼,結合執行段的方位信息和切點信息,就能夠找到特定的鏈接。真正的加強及包括用於添加到目標鏈接點上的一段執行邏輯,由包含用於定位鏈接點的方位信息,因此Spring所提供的加強接口的都是帶方位名的。因此Spring所提供的加強接口都是帶方位名的,例如BeforeAdvice,AfterReturningAdvice,ThrowsAdvice等。只有結合了切點和加強,才能肯定特定的鏈接點並實施加強邏輯。數據庫

目標對象(Target)

加強邏輯的織入目標類。若是沒有AOP,那麼目標業務須要本身實現全部的邏輯,在AOP的幫助下,那些非橫切的邏輯的程序邏輯,而性能監視和事務管理等這些橫切的邏輯則可使用AOP的動態織入特定的鏈接點上。編程

引介(Introduction)

引介是一種特護的加強,它爲類添加一些屬性和方法。這樣,幾時一個業務類本來沒有實現某個接口,經過AOP的引介功能,也能夠動態地位改業務類添加接口地實現邏輯,讓業務類成爲這個接口地實現類。框架

織入(Weaving)

織入是將加強添加到慕白哦類地具體鏈接點上。AOP就像一臺織布機,將目標類,曾倩或者引介完美無缺地編織到一塊兒。AOP有三種織入方式: 1.編譯期織入,這個要求使用特殊地Java編譯器。 2.類裝載期織入,這個要求使用特殊地類裝器。 動態代理,在運行期位目標類添加加強生產子類地方式。 Spring採用了動態代理地織入,而AspectJ採用編譯期織入和類裝載期織入。ide

代理(Proxy)

一個類被AOP織入加強以後,就產生了一個結果類,它是融合了袁磊和曾倩邏輯地代理類。根據不一樣地代理方式,代理多是ihe袁磊具備相同接口地類,也多是原類地子類。,因此能夠採用於調用原類相同地方式調用代理類。性能

切面(Aspect)

切面有切點和曾倩(引介)組成,它既包括橫切邏輯地定義,也包括了鏈接點地定義。Spring AOP就是負責實施切面地框架,它將切面所定義地橫切邏輯織入切面所指定的鏈接點中。測試

SpringAOP涉及的java基礎知識

JDK動態代理

JDK的動態代理主題要涉及java.lang.reflect包下面的java.lang.reflect.Proxy和java.lang.reflect.InvocationHandler。其中,java.lang.reflect.InvocationHandler是一個接口,能夠i經過實現接口定義很且邏輯,並經過反射機制調用目標類代碼,動態地將橫切邏輯和業務邏輯編制在一塊兒。Proxy利用了InvocationHandler動態建立一個符合某一接口地實例. 例子: 實現InvocationHandlerflex

public class JdkProxy implements InvocationHandler {
	private Object target;

	public JdkProxy(Object target) {
		this.target = target;
	}

	[@Override](https://my.oschina.net/u/1162528)
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		PerformanceMonitor.begin(target.getClass().getName()+"."+method.getName());
		Object invoke = method.invoke(target, args);
		PerformanceMonitor.end();
		return invoke;
	}
}

測試代碼: System.out.println("-------------------使用jdk動態代理------------------"); JdkProxy jdkProxy = new JdkProxy(forumService); ForumService instance = (ForumService) Proxy.newProxyInstance(Demo.class.getClassLoader(), new Class[]{ForumService.class}, jdkProxy); instance.removeForum(1); 執行結果:

CGLib動態代理

使用JDK建立地代理有一個限制,它只能位接口建立一個代理實例,這一點能夠從Proxy地接口方法public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces, InvocationHandler h)中看出來。第二個參數就是接口須要代理實例實現的接口列表。CGLib採用了底層的字節碼技術,能夠位一個類建立一個子類,在子類中採用方法攔擊的技術攔截因此父類方法調用並順勢織入很且邏輯。下面採用CGLib技術編寫一個能夠位任何類建立織入性能鍵事橫切邏輯代理對象的代理建立器。

實例:

public class CglibProxy implements MethodInterceptor {
	Enhancer enhancer = new Enhancer();

	public Object getProxy(Class clazz) {
		enhancer.setSuperclass(clazz);
		enhancer.setCallback(this);
		return enhancer.create();
	}

	[@Override](https://my.oschina.net/u/1162528)
	public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
	  PerformanceMonitor.begin(o.getClass().getName()+"."+method.getName());
		Object invoke = methodProxy.invokeSuper(o, objects);
		PerformanceMonitor.end();
		return invoke;
	}
}

用戶經過getProxy(Class clazz)爲一個類建立代理對象,該對象擴展clazz實現代理,在這個代理對象中,織入性能監視得橫切邏輯。intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy)是CGLib定義的intercept接口方法,它攔截了因此目標類方法的調用。o:目標類實例,method:目標了方法的反射對象,args:方法的動態入參,proxy:代理實例。

測試代碼:

System.out.println("-------------------使用CGLib動態代理------------------");
    CglibProxy cglibProxy = new CglibProxy();
    ForumServiceImpl forumService1 = (ForumServiceImpl)cglibProxy.getProxy(ForumServiceImpl.class);
    forumService1.removeForum(1);

執行結果:

AOP聯盟

AOP聯盟是衆多開源開源AOP項目的聯合組織,該組織的目的是爲了制定一套規範的AOP標準,定義標準的AOP接口。以便遵照標準的具體實現能夠互相調用。

SpringAOP的加強類型

建立加強

Spring只支持方法的加強,加強既包含橫切邏輯,又包括部分連接點的信息。

加強類型

AOP聯盟爲加強定義了Advice接口,Sring支持5種類型的加強,加強的接口集成關係圖以下:

1.前置加強:org.springframework.aop.BeforeAdvice是爲擴展而用,org.springframework.aop.MethodBeforeAdvice是目前可用的前置加強。

2.後置加強:org.springframework.aop.AfterReturningAdvice表明後置加強,表示在目標方法執行後實施加強。

3.環繞加強:org.aopalliance.intercept.MethodInterceptor表明環繞加強,表示在目標方法的執行先後實施加強。

4.異常拋出加強:org.springframework.aop.ThrowsAdvice表明拋出異常加強,表示在目標類種添加一些新的方法和屬性。

5.引介加強:org.springframework.aop.IntroductionInterceptor表明引介加強,表示在目標類種添加一些新的方法和屬性。

前置加強

MethodBeforeAdvice是BeforeAdvice前置的加強接口子類,BeforeAdvice存在的意義是爲了後續的擴展。 void before(Method method, Object[] args, Object target) throws Throwable是MethodBeforeAdvice接口的惟一方法,method是目標放啊,args目標方法的參數,target爲目標類實例。該方法發生異常的時候將阻止目標類方法執行。

實例: Waiter.java普通服務員

public interface Waiter {
void greetTo(String name);
void serveTo(String name);
}

PoliteWaiter.java禮貌地服務員

public class PoliteWaiter implements Waiter {
	[@Override](https://my.oschina.net/u/1162528)
	public void greetTo(String name) {
		System.out.println("greet to "+name+"....");
	}

	[@Override](https://my.oschina.net/u/1162528)
	public void serveTo(String name) {
		System.out.println("serve to "+name+"....");

	}
}

GreetBeforeAdvice.java 前置加強

public class GreetBeforeAdvice implements MethodBeforeAdvice {
	[@Override](https://my.oschina.net/u/1162528)
	public void before(Method method, Object[] objects, Object o) throws Throwable {
		String clientName = (String)objects[0];
		System.out.println("How are you! Mr."+clientName);
	}
}

測試代碼:

Waiter target = new PoliteWaiter();
    BeforeAdvice advice = new GreetBeforeAdvice();
    //Spring提供的代理工廠
    ProxyFactory proxyFactory = new ProxyFactory();
    //設置代理目標
    proxyFactory.setTarget(target);
    proxyFactory.addAdvice(advice);
    Waiter proxy = (Waiter) proxyFactory.getProxy();
    proxy.greetTo("John");
    proxy.serveTo("jack");

執行結果:

ProxyFactory 在上面的例子中咱們使用了ProxyFactory,ProxyFactory實際上使用的是JDK和CGLib動態代理技術將加強應用到目標類中。 ![] CglibAopProxy使用的是CGLib動態代理技術,JdkDynamicAopProxy使用的是JDK動態代理技術建立代理。若是經過ProxyFactory的setInterfaces(Class[] interfaces)方法指定目標接口進行代理,就是要JdkDynamicAopProxy,若是針對的是類的代理,就使用CglibAopProxy,可使用ProxyFactory的setOptimize(true)方法讓ProxyFactory啓動u歐化代理的方式,此時針對接口的代理也是使用CglibAopProxy。

//使用CGLib代理的方式
    proxyFactory.setOptimize(true);

//使用的jdk代理的方式  proxyFactory.setInterfaces(target.getClass().getInterfaces());

使用配置文件的方式:

<bean id="greetingAdvice" class="com.flexible.beforeadvice.GreetBeforeAdvice"></bean>
	<bean id="target" class="com.flexible.beforeadvice.PoliteWaiter"></bean>

	<bean id="waiter" class="org.springframework.aop.framework.ProxyFactoryBean"
	p:proxyInterfaces="com.flexible.beforeadvice.Waiter"
	p:interceptorNames="greetingAdvice"
	p:target-ref="target">
	</bean>

測試代碼:

@Test
	public void testMethod1(){
		ApplicationContext context = new ClassPathXmlApplicationContext("classpath:proxyfactory/beans");
		Waiter waiter = (Waiter) context.getBean("waiter");
		waiter.greetTo("zhangsgan ");
		waiter.serveTo("李四");
	}

ProxyFactoryBean是FactoryBean接口的實現類,它負責實例化一個Bean。ProxyFactoryBean負責爲其餘的Bean建立代理實例,它在內部使用ProxyFactory來完成這項工做。ProxyFactoryBean的幾個經常使用的可配置屬性。

1.target:代理的目標對象。

2.proxyInterfaces:代理所須要實現的接口,能夠是多個接口。該屬性還有一個別名屬性interfaces

3.interceptorNames:須要織入目標對象的Bean列表,採用Bean的名曾指定,這些Bean必須實現了org.springframework.cglib.proxy.MethodInterceptor接口或者org.springframework.aop.Advisor的Bean,配置中的順序的應調用的順序。

4.sigleton:返回的代理是不是單實例,默認爲單實例。

5.optimize:默認是false,使用的是jdk動態代理建立代理,若是設置true,強制使用CGLib動態代理,對於singleton代理,推薦使用CGLib。

6.proxyTargetClass:是否對類進行代理(而不是對接口進行代理)。設置爲true時,使用的CGLib動態代理。

後置加強

後置加強在目標類方法調用後執行。

GreetAfterAdvice.java

public class GreetAfterAdvice implements AfterReturningAdvice {

	@Override
	public void afterReturning(Object o, Method method, Object[] objects, Object o1) throws Throwable {
		System.out.println("please enjoy yourself....");
	}
}

beans.xml

<bean id="target2" class="com.flexible.afteradvice.PoliteWaiter"></bean>
	<bean id="greetingAdvice2" class="com.flexible.afteradvice.GreetBeforeAdvice"></bean>
	<bean id="greetingAfterAdvice" class="com.flexible.afteradvice.GreetAfterAdvice"></bean>

	<bean id="waiter2" class="org.springframework.aop.framework.ProxyFactoryBean"
		  p:proxyInterfaces="com.flexible.afteradvice.Waiter"
		  p:interceptorNames="greetingAdvice2,greetingAfterAdvice"
		  p:optimize="true"
		  p:target-ref="target2">
	</bean>

測試代碼:

@Test
	public void testMethod1(){
		ApplicationContext context = new ClassPathXmlApplicationContext("classpath:proxyfactory/beans");
		Waiter waiter = (Waiter) context.getBean("waiter2");
		waiter.greetTo("zhangsgan ");
		waiter.serveTo("李四");
	}

執行結果:

How are you! Mr.zhangsgan greet to zhangsgan .... please enjoy yourself.... How are you! Mr.李四 serve to 李四.... please enjoy yourself....

環繞加強

環繞加強容許在目標類方法調用先後織入橫切邏輯,綜合了前置和後置加強功能。

例子: GreetingInterceptor.java

public class GreetingInterceptor implements MethodInterceptor {
	@Override
	public Object invoke(MethodInvocation methodInvocation) throws Throwable {
		Object[] args =methodInvocation.getArguments();
		String clientNme = (String) args[0];
		System.out.println("How are you Mr."+clientNme+"....");
		Object proceed = methodInvocation.proceed();
		System.out.println("Please enjoy yourself....");
		return proceed;
	}
}

bean.xml

<bean id="target3" class="com.flexible.aroundadvice.PoliteWaiter"></bean>
	<bean id="greetingAdvice3" class="com.flexible.aroundadvice.GreetingInterceptor"></bean>

	<bean id="waiter3" class="org.springframework.aop.framework.ProxyFactoryBean"
		  p:proxyInterfaces="com.flexible.aroundadvice.Waiter"
		  p:interceptorNames="greetingAdvice3"
		  p:target-ref="target3">
	</bean>

測試代碼:

@Test
	public void testMethod1(){
		ApplicationContext context = new ClassPathXmlApplicationContext("classpath:proxyfactory/beans");
		Waiter waiter = (Waiter) context.getBean("waiter3");
		waiter.greetTo("zhangsgan ");
		waiter.serveTo("李四");
	}

測試結果

How are you Mr.zhangsgan ....
greet to zhangsgan ....
Please enjoy yourself....
How are you Mr.李四....
serve to 李四....
Please enjoy yourself....

異常拋出加強

異常拋出加強最適合的應用場景時事務管理,當參與事務的某個DAO發生異常時,事務管理器就必須回滾事務。要定義異常加強須要實現業務ThrowsAdvice加強,該接口只是一個標籤接口,在運行期間,Spring使用反射機制自行判斷,必須使用下面的形式定義方法

public void afterThrowing(Method method,Object[] args,Object target,Exception ex){

PayService.java 業務類 /** * 模擬業務的類,支付的時候拋出異常 */ public class PayService {

public void payMony(String from, double money, String to) {
	//TODO -- TODO some thing
		throw new RuntimeException("測試拋出運行時異常....");
	}
}

beans.xml 配置

<bean id="transactionManager" class="com.flexible.throwadvice.TransactionManager"></bean>
<bean id="payTarget" class="com.flexible.throwadvice.PayService"></bean>

<bean id="ins" class="org.springframework.aop.framework.ProxyFactoryBean"
      p:interceptorNames="transactionManager"
      p:target-ref="payTarget"
      p:proxyTargetClass="true">
</bean>

測試代碼:

@Test
public void testMethod1(){
    ApplicationContext context = new ClassPathXmlApplicationContext("classpath:proxyfactory/beans");
    PayService payService = (PayService) context.getBean("ins");
    payService.payMony("1",100.00,"2");
}

測試結果:


method:payMony 拋出異常:測試拋出運行時異常.... 成功回滾事務

引介加強

引介加強時一種比較特殊的加強類型,它不是在目標方法加強,而是爲目標類建立新的方法和屬性,因此介加強的鏈接點是類級別的。而非方法級別的。經過引介加強,能夠爲目標類添加一個接口的實現,即原來的目標類的爲實現某個接口,經過引介加強能夠爲目標了常見實現某個接口的代理。Spring 定義了引介加強接口IntroductionInterceptor,該接口沒有定義任何方法,Spring爲該接口提供了DelegatingIntroductionInterceptor實現類。通常狀況下,經過擴展該實現類定義本身引介加強類。

建立切面

若是咱們須要有選擇地織入目標類地某些特定地方法中,就須要使用切點進行目標鏈接點的定位。描述連繫欸可是進行AOP編程最主要的工做。加強的提供了連繫欸但方法爲信息,切點進一步的描述了織入哪些類的哪些方法上。 Spirng經過org.springframework.aop.Pointcut接口秒速和切點,其中org.springframework.aop.Pointcut是由ClassFilter和MethodMatcher構成,經過ClassFilter定位到某些特定類上,經過MethodMatcher定位到某些特定方法上。這樣org.springframework.aop.Pointcut就可以定位某些類的某些方法上的能力。org.springframework.aop.Pointcut的關係結構圖以下:

ClassFilter只定義了一個方法matcher(Class clazz),其參數表明一個被檢測類,改方法判別被u檢測的類是否匹配過濾條件。 Spring支持兩種匹配器:

1.靜態匹配器(僅須要匹配一次,對方法簽名進行匹配)

2.動態匹配器(由於可能每次入參都不同,須要每次調用都檢測,影響性能,通常不常使用),方法匹配器的類型由isRuntime()方法返回值決定,false表示靜態匹配器,true表示動態匹配器。

切點的類型

1.靜態方法切點:org.springframework.aop.support.StaticMethodMatcherPointcut是靜態方法切點的抽象類,默認狀況下它匹配因此的類。org.springframework.aop.support.StaticMethodMatcherPointcut的子類有:org.springframework.aop.support.NameMatchMethodPointcut和org.springframework.aop.support.AbstractRegexpMethodPointcut,NameMatchMethodPointcut提供簡單字符串匹配簽名,AbstractRegexpMethodPointcut使用正則表達式匹配放啊簽名。

2.動態方法切點:org.springframework.aop.support.DynamicMethodMatcherPointcut是動態方法切點的抽象基類,默認狀況下匹配全部類。

3.註解切點:org.springframework.aop.support.annotation.AnnotationMatchingPointcut實現類表示註解其欸但。使用AnnotationMatchingPointcut支持在BEAN中直接經過Java5.0註解標籤訂義的切點。

4.表達式切點:org.springframework.aop.support.ExpressionPointcut接口主要爲了支持AspectJ切點表達式語法而定義的接口。

5.流程切點:org.springframework.aop.support.ControlFlowPointcut實現類表示控制流程切點。ControlFlowPointcut是一種特殊的的切點,它根據程序執行堆棧信息的查看目標方法是否由某一個方法直接或者看法的發起調用,以此判斷是否爲匹配的鏈接點。

6.複合切點:org.springframework.aop.support.ComposablePointcut實現類是爲了建立多個切點而提供的方便操做類。它全部的方法都返回ComposablePointcut類,這樣就可使用連接表達式對切點進行操做:Pointcut pc = new ComposablePointcut().union(classFilter).intersetion(methodMatcher).intersetion(pointcut).

切面的類型

因爲加強既包含橫切代碼,又包含部分鏈接點的部分信息(方法前,方法後主方爲信息),能夠進經過加強類生成一個切面。可是切點僅表明目標類連繫欸但那的部分信息(類和方法的定位),全部僅有切點沒法制做出一個切面,必須結合加強才能製做出切面。Spring使用org.springframework.aop.Advisor接口比埃是切面的概念,一個切面同時包含橫切代碼和鏈接點信息。切面能夠分爲三類:

1.Advisor:表明通常切面,僅包含一個Advice。由於Advice包含了橫切代碼和鏈接點信息,全部Advice自己就是一個簡單的切面,它表明的橫切的鏈接點是全部目標類的全部方法,由於這個橫切面很寬泛,通常狀況下不使用。

2.PointcutAdvisor:表明具備切點的切面,包含Advice和Pointcut兩個類,這樣就能夠經過類,方法名及方法方位等信息靈活的定義切面的連接點,提供更具使用行的切面。

PointcutAdvisor主要有6個具體的實現類。 1.DefaultPointcutdvisor:最經常使用的切面類型,它能夠經過任意Pointcut和Advice定義一個切面,惟一不支持的是引介的切面類型,通常能夠經過擴展改類實現自定義的的切面。

2.NameMatchMethodPointcutAdvisor:經過類能夠定義按方法名定義切點的切面。

     3.RegexpMethodPointcutAdvisor:對於按正則表達式匹配方法名進行切點定義的切面,能夠經過擴展改實現類進行操做。其內部是經過jdkRegexpMethodPoint構造出正則表達式方法名稱切點。

     4.StaticMethodMatcherPointcutAdvisor:靜態方法匹配切點定義的切面,默認狀況下匹配全部的目標類。

    5.AspectJExpressionPointcutAdvisor:用於AspectJ切點表達式定義切點的切面。

    6.AspectJPointcutAdvisor:用於AspectJ語法定義切點的切切面。

3.IntroductionAdvisor:表明引介切面。引介切面是對應引介加強的特殊的切面,它應用於類層面上,全部切點使用ClassFilter進行定義。

靜態普通方法名匹配切面

StaticMethodMatcherPointAdvisor表明一個靜態方法匹配切面,它經過StaticMethodMatcherPointcut來定義切點,並經過類過濾和方法名來匹配所定義的切點。 例子: Waiter.java

public class Waiter {
void greetTo(String name){
	System.out.println("Waiter greet to "+name+"...");
}
void serveTo(String name){
	System.out.println("waiter sering "+name+"...");
}
}

Seller.java

public class Seller {
public void greetTo(String name){
	System.out.println("Waiter greet to "+name+"...");
}
}

GreetBeforeAdvice.java(前置加強)

public class GreetBeforeAdvice implements MethodBeforeAdvice {
	@Override
	public void before(Method method, Object[] objects, Object o) throws Throwable {
		String clientName = (String)objects[0];
		System.out.println("How are you! Mr."+clientName);
	}
}

GreetingAdvisor.java(切面匹配,這裏限制只能匹配Waiter的greetTo())

public class GreetingAdvisor extends StaticMethodMatcherPointcutAdvisor {
	@Override
	public boolean matches(Method method, Class<?> targetClass) {
		return "greetTo".equals(method.getName());
	}
	public ClassFilter getClassFilter() {
		return new ClassFilter() {
			@Override
			public boolean matches(Class<?> clazz) {
				return Waiter.class.isAssignableFrom(clazz);
			}
		};
	}
}

beans.xml

<!--普通靜態方法切面匹配-->

<bean id="waiterTarget" class="com.flexible.advisormatch.staticmethodadvisormatch.Waiter"></bean>
<bean id="sellerTarget" class="com.flexible.advisormatch.staticmethodadvisormatch.Seller"></bean>

<bean id="advisormatchGreetingAdvice"
      class="com.flexible.advisormatch.staticmethodadvisormatch.GreetBeforeAdvice"></bean>

<bean id="greetAdvisor" class="com.flexible.advisormatch.staticmethodadvisormatch.GreetingAdvisor"
      p:advice-ref="advisormatchGreetingAdvice"></bean>  <!--這裏注入一個前置切面-->
<!--經過一個父類定義一個公共的配置信息-->
<bean id="parent" abstract="true" class="org.springframework.aop.framework.ProxyFactoryBean"
      p:interceptorNames="greetAdvisor"
      p:proxyTargetClass="true"
></bean>
<bean id="advisormatchWaiter" parent="parent" p:target-ref="waiterTarget"></bean>
<bean id="advisormatchSeller" parent="parent" p:target-ref="sellerTarget"></bean>

測試代碼:

ApplicationContext context = new ClassPathXmlApplicationContext("classpath:proxyfactory/beans");
    Waiter waiter = (Waiter) context.getBean("advisormatchWaiter");
    Seller seller = (Seller) context.getBean("advisormatchSeller");
    waiter.greetTo("zhangsan");
    waiter.serveTo("zhangsan");
    seller.greetTo("李四");

執行結果:

靜態正則表達式方法匹配切面

RegexpMethodPointcutAdvisor是正則表達式方法匹配的切面實現類,改類已是功能齊全的實現類,能夠直接拿來使用。

例子: 與上一個例子差異主要再配置文件,beans.xml配置文件的內容以下:

<bean id="waiterTarget2" class="com.flexible.advisormatch.regexpadvisormatch.Waiter"></bean>
<bean id="sellerTarget2" class="com.flexible.advisormatch.regexpadvisormatch.Seller"></bean>

<bean id="advisormatchGreetingAdvice2"
      class="com.flexible.advisormatch.regexpadvisormatch.GreetBeforeAdvice"></bean>

<bean id="greetAdvisor2" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor"
      p:advice-ref="advisormatchGreetingAdvice2">
    <property name="patterns">
        <list>
            <value>.*greet.*</value>
        </list>
    </property>
</bean>
<!--經過一個父類定義一個公共的配置信息-->
<bean id="parent2" abstract="true" class="org.springframework.aop.framework.ProxyFactoryBean"
      p:interceptorNames="greetAdvisor2"
      p:proxyTargetClass="true"
></bean>
<bean id="advisormatchWaiter2" parent="parent2" p:target-ref="waiterTarget2"></bean>
<bean id="advisormatchSeller2" parent="parent2" p:target-ref="sellerTarget2"></bean>

測試執行結果:

動態切面

DynamicMethodMatcherPointcut抽象類,是Spring用於建立動態切面你的抽象類,改出響雷默認匹配全部的類和方法,所以默認須要擴展該類編寫符合要求的動態的切點。 例子: GreetingDynamicPointcut.java

public class GreetingDynamicPointcut extends DynamicMethodMatcherPointcut {
	private static List<String> specialClientList = new ArrayList<>();
	static {
		specialClientList.add("John");
		specialClientList.add("Tom");
	}
	//    對類進行靜態切點檢查
	@Override
	public ClassFilter getClassFilter() {
		return new ClassFilter() {
			@Override
			public boolean matches(Class<?> clazz) {
				System.out.println("調用getClassFilter()對" + clazz.getName() + "作靜態檢查.");
				return Waiter.class.isAssignableFrom(clazz);
			}
		};
	}
	//    對方法進行靜態切點檢測
	@Override
	public boolean matches(Method method, Class<?> targetClass) {
		return super.matches(method, targetClass);
	}

	// 對方法進行動態切點檢測
	@Override
	public boolean matches(Method method, Class<?> targetClass, Object... args) {
		System.out.println("調用matches(Method method, Class<?> targetClass, Object... args)"
				+ targetClass.getName() + "." + method.getName()+"作動態檢測");
		String clientName = (String) args[0];
		return specialClientList.contains(clientName);
	}
}

beans.xml

<bean id="waiterTarget3" class="com.flexible.advisormatch.dynamicmatch.Waiter"></bean>
	<bean id="dynamicAdvisor" class="org.springframework.aop.support.DefaultPointcutAdvisor">
		<property name="pointcut">
			<bean class="com.flexible.advisormatch.dynamicmatch.GreetingDynamicPointcut"></bean>
		</property>
		<property name="advice">
			<bean class="com.flexible.advisormatch.dynamicmatch.GreetBeforeAdvice"></bean>
		</property>
	</bean>
	<bean id="waiter4" class="org.springframework.aop.framework.ProxyFactoryBean"
		  p:interceptorNames="dynamicAdvisor"
		  p:target-ref="waiterTarget3"
		  p:proxyTargetClass="true"></bean>

測試代碼:

ApplicationContext context = new ClassPathXmlApplicationContext("classpath:proxyfactory/beans");
		Waiter waiter = (Waiter) context.getBean("waiter4");
		waiter.greetTo("John");
		waiter.greetTo("John");
		waiter.serveTo("zhangsan");
		waiter.serveTo("zhangsan");

執行結果:

從執行的結果能夠看出,Spring會建立代理織入切面時,對目標類中的全部方法進行了靜態切點檢查;生成了的植入切面的代理對象後,第一次調用代理類的每個方法都會進行一次靜態的檢查,若是本次檢查就能從候選者列表中將該方法排除,則之後對該方法的調用就再也不執行靜態檢查;對於靜態匹配的方法,後續的調用該方法都會進行動態的檢查。

流程切面

Spring 的流程切面由DefaultPointcutAdvisor和ControlPointcut是實現。流程切點表明由某個方法直接或者簡潔的發起調用的其餘方法。

例子: WaiterDelegate.java

public class WaiterDelegate {

	private Waiter waiter;

	/**
	 * 該方法發起調用的其餘方法都織入GreetBeforeAdvice加強,
	 * 要完成該功能就須要使用到流程切面。
	 *
	 * @param clientName
	 */
	public void service(String clientName) {
		waiter.greetTo("service" + clientName);
		waiter.serveTo("service" + clientName);
	}

	public void setWaiter(Waiter waiter) {
		this.waiter = waiter;
	}
}

beans.xml

<!--流程切面-->
	<bean id="controlFlowGreetingAdvice" class="com.flexible.advisormatch.controflowmatch.GreetBeforeAdvice"></bean>

	<bean id="controlFlowWaiterTarget" class="com.flexible.advisormatch.controflowmatch.Waiter"></bean>

	<bean id="controlFlowPointcut" class="org.springframework.aop.support.ControlFlowPointcut">
		<constructor-arg type="java.lang.Class"
						 value="com.flexible.advisormatch.controflowmatch.WaiterDelegate"></constructor-arg>
		<constructor-arg type="java.lang.String" value="service"></constructor-arg>
	</bean>

	<bean id="controlFlowAdvisor" class="org.springframework.aop.support.DefaultPointcutAdvisor"
		  p:pointcut-ref="controlFlowPointcut"
		  p:advice-ref="controlFlowGreetingAdvice">
	</bean>

	<bean id="controlFlowWaiter1" class="org.springframework.aop.framework.ProxyFactoryBean"
		  p:interceptorNames="controlFlowAdvisor"
		  p:target-ref="controlFlowWaiterTarget"
		  p:proxyTargetClass="true">
	</bean>

測試代碼:

ApplicationContext context = new ClassPathXmlApplicationContext("classpath:proxyfactory/beans");
		Waiter waiter = (Waiter) context.getBean("controlFlowWaiter1");
		WaiterDelegate delegate = new WaiterDelegate();
		delegate.setWaiter(waiter);
		waiter.greetTo("zhangsan");
		waiter.serveTo("zhangsan");
		delegate.service("zhangsan");

執行結果:

複合切點切面

若是一個切點難以描述目標鏈接點的信息,好比再前面流程切面的例子中,在上面的例子中,若是調用service(String clientName)就會發現該方法調用的兩個方法都會織入加強,若是隻但願加強其中一個,那麼這個切點就是符合切點。

例子:

GreetingComposablePointcut.java

public class GreetingComposablePointcut {
	public Pointcut getIntersectionPointcut(){
		ComposablePointcut cp = new ComposablePointcut();
		Pointcut pt1 = new ControlFlowPointcut(WaiterDelegate.class,"service");
		NameMatchMethodPointcut pt2 = new NameMatchMethodPointcut();
		pt2.addMethodName("greetTo");
		return cp.intersection(pt1).intersection((Pointcut)pt2);
	}
}

beans.xml

<!--複合切點-->
	<bean id="composableGreetingAdvice" class="com.flexible.advisormatch.composablepointcutmatch.GreetBeforeAdvice"></bean>
	<bean id="gcp" class="com.flexible.advisormatch.composablepointcutmatch.GreetingComposablePointcut"></bean>
	<bean id="composableAdvisor" class="org.springframework.aop.support.DefaultPointcutAdvisor"
	p:pointcut="#{gcp.intersectionPointcut}"
	p:advice-ref="composableGreetingAdvice"
	></bean>
	<bean id="composableWaiterTarget" class="com.flexible.advisormatch.composablepointcutmatch.Waiter"></bean>
	<bean id="composableProxyWaiter" class="org.springframework.aop.framework.ProxyFactoryBean"
	p:interceptorNames="composableAdvisor"
	p:target-ref="composableWaiterTarget"
	p:proxyTargetClass="true"></bean>

測試代碼:

ApplicationContext context = new ClassPathXmlApplicationContext("classpath:proxyfactory/beans");
		Waiter waiter = (Waiter) context.getBean("composableProxyWaiter");
		WaiterDelegate delegate = new WaiterDelegate();
		delegate.setWaiter(waiter);
		waiter.serveTo("zhansagn");
		waiter.greetTo("zhangsan");
		delegate.service("zhangsgan");

執行結果:

自動建立代理

以前的例子都是經過ProxyFactoryBean建立織入切面的代理,每一個須要被代理的Bean都須要配置,這樣就會很麻煩,所以Spring提供了自動代理的機制,讓容器自動生成代理,把開發從繁瑣的配置工做中解放出來。而這些都是Spring內部使用BeanPostProcessor自動完成這項工做。

BaeanPostProcessor實現類的介紹

基於BeanPostProcessor自動代理建立器的實現類有三類: 1.BeanNameAutoProxyCreator:基於Bean配置名規則的自動代理建立器,容許爲一組特定的配置名的Bean自動建立代理實例的代理建立器。

例子:

beans.xml

<!--BeanNameAutoProxyCreator自動建立代理類-->
<bean id="autoWaiter" class="com.flexible.autoprxycreator.Waiter"></bean>
<bean id="autoSeller" class="com.flexible.autoprxycreator.Seller"></bean>
<bean id="autoGreetAdvice" class="com.flexible.autoprxycreator.GreetingBeforeAdvice"></bean>

<bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator"
//這裏能夠配置多個須要代理的baean
p:beanNames="autoWaiter,autoSeller"
p:interceptorNames="autoGreetAdvice"
//p:optimize="true"須要配置,實現使用CGLib生成代理
p:optimize="true"></bean>

測試代碼:

ApplicationContext context = new ClassPathXmlApplicationContext("classpath:proxyfactory/beans");
		Waiter waiter = (Waiter) context.getBean("autoWaiter");
		Seller seller = (Seller) context.getBean("autoSeller");
		waiter.greetTo("John");
		seller.greetTo("Tom");

執行結果:

com.flexible.autoprxycreator.Waiter.greetTo How are you!Mr.John. waiter greet to John... com.flexible.autoprxycreator.Seller.greetTo How are you!Mr.Tom. seller greet to Tom...

2.DefaultAdvisorAutoProxyCreator:基於Advisor匹配機制的自動代理建立器,他會對容器中的全部的Advisor進行掃描,自動將這些切面應用到匹配的Bean中(爲目標Bean建立代理實例)

beans.xml配置

<bean id="autoWaiter2" class="com.flexible.autoprxycreator.defaultautoproxycreator.Waiter"></bean>
	<bean id="autoSeller2" class="com.flexible.autoprxycreator.defaultautoproxycreator.Seller"></bean>
	<bean id="autoGreetAdvice2" class="com.flexible.autoprxycreator.defaultautoproxycreator.GreetingBeforeAdvice"></bean>
	<bean id="regexpMethodPointcutAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor"
		  p:advice-ref="autoGreetAdvice2">
		<property name="patterns">
			<list>
				<value>.*greet.*</value>
			</list>
		</property>
	</bean>
	<!--會開啓掃描全部的切面 這個地須要配置使用CGLib動態代理-->
	<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"  p:proxyTargetClass="true"></bean>

測試代碼:

ApplicationContext context = new ClassPathXmlApplicationContext("classpath:autoproxycreator/beans");
		Waiter waiter = (Waiter) context.getBean("autoWaiter2");
		Seller seller = (Seller) context.getBean("autoSeller2");
		waiter.greetTo("John");
		seller.greetTo("Tom");

執行結果:

com.flexible.autoprxycreator.defaultautoproxycreator.Waiter.greetTo How are you!Mr.John. waiter greet to John... com.flexible.autoprxycreator.defaultautoproxycreator.Seller.greetTo How are you!Mr.Tom. seller greet to Tom...

3.AnnotationAwareAspectJAutoProxyCreator:基於Bean中AspectJ註解標籤的自動代理建立器,爲包含AspectJ註解的Bean自動建立代理實例。

Spring AOP的切面類型

經過自動代理技術建立切面

相關文章
相關標籤/搜索