Spring AOP之---基於ProxyFactory的類編碼方式和XML配置方式實現AOP
前一篇文章Spring AOP之—基於JDK動態代理和CGLib動態代理的AOP實現 介紹了AOP的底層實現,基於JDK動態代理和CGLib動態代理。手工編碼的方式很繁瑣,本文介紹通過ProxyFactory和配置的方式實現AOP,方便快捷。
一、Spring支援的5種增強型別
增強是織入目標類連線點上的一段程式程式碼。
- 前置增強:MethodBeforeAdvice方法執行前實施增強
- 後置增強:AfterReturningAdvice方法執行後實施增強
- 環繞增強: MethodInterceptor方法執行前後實施增強
- 異常丟擲增強: ThrowsAdvice方法丟擲異常後實施增強,最適合的應用場景就是事物管理。
- 引介增強:IntroductionInterceptor在目標類中新增一些新的方法和屬性
二、類編碼的方式實現增強織入
1、目標類需要實現的介面 — Waiter
package com.mistra.aop.createAdvice;
/**
* @author Mistra-WangRui
* @create 2018/3/28 15:50
* @desc 目標類要實現的介面
*/
public interface Waiter {
void greetTo(String name);
void serveTo(String name);
}
2、目標類 — NaiveWaiter
package com.mistra.aop.createAdvice;
/**
* @author Mistra-WangRui
* @create 2018/3/28 15:51
* @desc 目標類
*/
public class NaiveWaiter implements Waiter{
public void greetTo(String name) {
System.out.println("greet to + "+name+"...");
}
public void serveTo(String name) {
System.out.println("serving to + " +name+"...");
}
}
3、前置增強類 — GreetingBeforeAdvice
package com.mistra.aop.createAdvice;
import org.springframework.aop.MethodBeforeAdvice;
import java.lang.reflect.Method;
/**
* @author Mistra-WangRui
* @create 2018/3/28 15:55
* @desc 前置增強類
*/
public class GreetingBeforeAdvice implements MethodBeforeAdvice {
public void before(Method method, Object[] objects, Object o) throws Throwable {
String clientName = (String)objects[0];
System.out.println("How are you! Mr."+clientName+".");
}
}
MethodBeforeAdvice 介面唯一的方法before(),method為目標類的方法,objects為method的入參,o為目標類例項。
4、類編碼方式實現增強織入
package com.mistra.aop.createAdvice;
import org.springframework.aop.BeforeAdvice;
import org.springframework.aop.framework.ProxyFactory;
import org.testng.annotations.Test;
/**
* @author Mistra-WangRui
* @create 2018/3/28 15:58
* @desc 編碼方式實現橫切邏輯增強
*/
public class BeforeAdviceTest {
@Test
public void before(){
Waiter waiter = new NaiveWaiter();
BeforeAdvice advice = new GreetingBeforeAdvice();
ProxyFactory pf = new ProxyFactory();//Spring提供的代理工廠
//pf.setInterfaces(waiter.getClass().getInterfaces());
//pf.setOptimize(true);
pf.setTarget(waiter);//設定代理目標
pf.addAdvice(advice);//為代理目標新增增強
Waiter proxy = (Waiter)pf.getProxy();//生成代理例項
proxy.greetTo("古天樂");
proxy.serveTo("吳彥祖");
}
}
執行結果:
5、ProxyFactory 解析
在BeforeAdviceTest中使用ProxyFactory代理工廠將GreetingBeforeAdvice的增強織入目標類NaiveWaiter中。ProxyFactory 內部就是使用JDK或CGLib動態代理技術將增強應用到目標類中的。
setInterfaces()指定目標介面進行代理,則ProxyFactory使用JDK動態代理(實現類:JdkDynamicAopProxy)技術建立代理。如果針對類的代理,則使用CGLib動態代理(實現類:Cglib2AopProxy)。
setOptimize(true)是啟動優化處理方式,這樣針對介面的代理也會使用CGLib動態代理。
可以為該方法新增多個增強,形成一個增強鏈,它們的呼叫順序和新增順序一致。可以通過addAdvice(int,Advice)方法將增強新增到增強鏈具體位置(第一個位置為0)。
三、Spring配置的方式實現增強織入
包結構目錄放在前面:
1、配置檔案 — 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:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="greetingAdvice" class="com.mistra.aop.createAdvice.GreetingBeforeAdvice"/>
<bean id="target" class="com.mistra.aop.createAdvice.NaiveWaiter"/>
<bean id="waiter" class="org.springframework.aop.framework.ProxyFactoryBean"
p:proxyInterfaces="com.mistra.aop.createAdvice.Waiter"
p:interceptorNames="greetingAdvice"
p:target-ref="target"
/>
</beans>
ProxyFactoryBean是FactoryBean介面的實現類,FactoryBean負責例項化一個Bean,ProxyFactoryBean負責為其他Bean建立代理例項,內部使用ProxyFactory來完成這項工作,ProxyFactoryBean的幾個常用配置屬性:
- target:代理的目標物件
- proxyInterfaces:代理所需實現的介面,可以是多個
- interceptorNames:需要織入目標物件的Bean列表,配置中的順序對應呼叫的順序
- singleton:返回的代理例項是否是單例項,預設為單例項
- optimize:設定為true時,強制使用CGLib動態代理(對於singleton的代理用CGLib,其他作用域的用JDK),CGLib代理時速度慢,但是建立的代理物件執行效率高,JDK反之
- proxyTargetClass:是否對類進行代理,設定為true時使用CGLib代理
2、配置方式實現橫切邏輯增強 — BeforeAdviceTest2
package com.mistra.aop.createAdvice;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.testng.annotations.Test;
/**
* @author Mistra-WangRui
* @create 2018/3/29 11:11
* @desc 配置方式實現橫切邏輯增強
*/
public class BeforeAdviceTest2 {
@Test
public void test(){
String configPath = "com.mistra/aop/createAdvice/beans.xml";
ApplicationContext ctx = new ClassPathXmlApplicationContext(configPath);
Waiter waiter = (Waiter)ctx.getBean("waiter");
waiter.greetTo("古天樂");
}
}
本文只介紹了前置增強,其他的增強都大同小異。
本文這樣實施增強的話是把增強織入了目標類的所有方法中,呼叫目標類的所有方法時都會執行增強邏輯程式碼。有時候希望有選擇的把增強織入目標類的特定方法中,就需要使用切點進行目標類連線點的定位。
四、Spring AOP其他
1、定義切面Advisor:有選擇性的織入增強到某些類的某些方法
2、自動建立代理:上文都是通過ProxyFactoryBean建立的目標類代理,Spring AOP通過BeanPostProcessor可以自動為目標類建立代理
3、無法實現增強的問題:在JDK動態代理中通過介面來實現方法攔截,必須確保要攔截的目標方法在介面中有定義。在CGLib動態代理中通過動態代理子類來實現方法攔截,必須確保要攔截的目標方法可被子類訪問,也就是目標方法不能被定義為final。
五、基於@AspectJ和Schema的AOP
@AspectJ和Schema都是實現AOP的方式,@AspectJ支援編碼的方式和配置的方式實現AOP,Schema就是運用
< aop:config>< /aop:config>標籤配置實現AOP。難點就是@AspectJ的語法基礎和其中的配置切點表示式函式吧。無論用哪種方式實現AOP,底層原理都是運用JDK或者是CGLib動態代理技術。在執行期動態生成目標類的代理類實現的。