javascript
Spring-AOP 动态切面
- 概述
- 實例
- 結論
概述
低版本中,Spring提供了用于創建動態切面的DynamicMethodMatcherPointcutAdvisor抽象類,這個抽象類在2.0已過時,現在可以使用DefaultPointcutAdvisor和DynamicMethodMatcherPointcut來完成相同的功能。
DynamicMethodMatcherPointcut是一個抽象類,它將 isRuntime()標識位final并返回true,這樣其子類就一定是一個動態切點。 該抽象類默認匹配所有的類和方法,因此需要擴展該類編寫符合要求的動態切點
實例
代碼已托管到Github—> https://github.com/yangshangwei/SpringMaster
目標:我們使用動態切面對特定的客戶進行織入前置增強的橫切代碼。
我們先看下動態切點
package com.xgj.aop.spring.advisor.DynamicAdvisor;import java.lang.reflect.Method; import java.util.ArrayList; import java.util.List;import org.springframework.aop.ClassFilter; import org.springframework.aop.support.DynamicMethodMatcherPointcut;public class GreetingDynamicPointcut extends DynamicMethodMatcherPointcut {public static List<String> specialClientList = new ArrayList<String>();static {specialClientList.add("XiaoGongJiangOne");specialClientList.add("XiaoGongJiangTwo");}/*** (1)對類進行靜態切點檢查*/public ClassFilter getClassFilter() {return new ClassFilter() {@Overridepublic boolean matches(Class<?> clazz) {System.out.println("調用getClassFilter()對 類【 " + clazz.getName()+ "】做靜態檢查\n");return Waiter.class.isAssignableFrom(clazz);}};}/*** (2)對方法進行靜態切點檢查*/@Overridepublic boolean matches(Method method, Class<?> targetClass) {System.out.println("調用matches(Method method, Class<?> targetClass),對方法【"+ targetClass.getName() + "." + method.getName()+ "】做靜態檢查\n");return "greetTo".equals(method.getName());}/*** (3)對方法進行動態切點檢查*/@Overridepublic boolean matches(Method method, Class<?> targetClass, Object... args) {System.out.println("調用matches(Method method, Class<?> targetClass, Object... args)對方法【"+ targetClass.getName()+ "."+ method.getName()+ "】做動態檢查\n");String clientName = (String) args[0];return specialClientList.contains(clientName);} }我們可以看到GreetingDynamicPointcut 類既有用于靜態切點檢查的方法,又有動態切點檢查的方法。
由于動態切點檢查會對性能造成很大的影響,所以應當盡量避免在運行時每次都對目標類的各個方法進行動態檢查。
Spring采用的機制如下: 在創建代理時對目標類的每個連接點使用靜態切點檢查,如果僅通過靜態切點檢查就知可以知道連接點是不匹配的,這在運行時就會進行動態檢查。 反之,則進行動態切點檢查
在動態切點類中定義靜態切點檢查的方法可以避免不必要的動態檢查操作,從而極大地提高運行效率。
我們在(3)處通過 matches(Method method, Class<?> targetClass, Object... args)定義了動態切點檢查的方法,結合(2)處只對目標方法為greetTo(clientName)且clientName為特殊客戶的方法啟用增強,通過specialClientList模擬特殊的客戶名單。
前置增強同之前的一樣,我們來看下
package com.xgj.aop.spring.advisor.DynamicAdvisor;import java.lang.reflect.Method;import org.springframework.aop.MethodBeforeAdvice;public class GreetingBeforeAdvice implements MethodBeforeAdvice {@Overridepublic void before(Method method, Object[] args, Object target)throws Throwable {// 輸出切點System.out.println("Pointcut:" + target.getClass().getName() + "."+ method.getName());String clientName = (String) args[0];System.out.println("How are you " + clientName + " ?");}}編寫好動態切點和增強后, 我們通過Spring配置文件中裝配出一個動態切面
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans"xmlns:p="http://www.springframework.org/schema/p"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"><!-- 目標對象 --><bean id="waiterTarget" class="com.xgj.aop.spring.advisor.DynamicAdvisor.Waiter"/><!-- 第一種寫法 <bean id="dynamicAdvisor" class="org.springframework.aop.support.DefaultPointcutAdvisor"><property name="pointcut"><bean class="com.xgj.aop.spring.advisor.DynamicAdvisor.GreetingDynamicPointcut"/></property><property name="advice"><bean class="com.xgj.aop.spring.advisor.DynamicAdvisor.GreetingBeforeAdvice"/></property></bean> --> <!-- 前置增強 --><bean id="greetBeforeAdvice" class="com.xgj.aop.spring.advisor.DynamicAdvisor.GreetingBeforeAdvice"/><!-- 切點 --><bean id="greetingDynamicPointcut" class="com.xgj.aop.spring.advisor.DynamicAdvisor.GreetingDynamicPointcut"/><!-- 切面 --> <bean id="dynamicAdvisor" class="org.springframework.aop.support.DefaultPointcutAdvisor"p:pointcut-ref="greetingDynamicPointcut"p:advice-ref="greetBeforeAdvice"/> <!-- 代理類 --><bean id="waiter" class="org.springframework.aop.framework.ProxyFactoryBean"p:interceptorNames="dynamicAdvisor"p:target-ref="waiterTarget"p:proxyTargetClass="true"/> </beans>我們可以看到動態切面的配置和靜態切面的配置沒有什么區別。 靜態切面的配置可以參考 http://blog.csdn.net/yangshangwei/article/details/77393421 比對下,一樣的。
使用org.springframework.aop.support.DefaultPointcutAdvisor定義切面,注入動態切點greetingDynamicPointcut,織入增強greetBeforeAdvice。 當然了DefaultPointcutAdvisor還有個order屬性,用于定義切面的織入順序。
現在我們寫個測試類,運行下
package com.xgj.aop.spring.advisor.DynamicAdvisor;import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext;public class DynamicAdvisorTest {@Testpublic void test() {ApplicationContext ctx = new ClassPathXmlApplicationContext("classpath:com/xgj/aop/spring/advisor/DynamicAdvisor/conf-dynamicAdvisor.xml");Waiter waiter = ctx.getBean("waiter", Waiter.class);// list中的特殊客戶waiter.greetTo("XiaoGongJiangOne");waiter.serverTo("XiaoGongJiangOne");// list中的特殊客戶waiter.greetTo("XiaoGongJiangTwo");waiter.serverTo("XiaoGongJiangTwo");// 不在list中的客戶waiter.greetTo("XiaoGongJiang1");waiter.serverTo("XiaoGongJiang1");} }運行結果:
2017-08-20 01:41:20,872 INFO [main] (AbstractApplicationContext.java:583) - Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@3b293677: startup date [Sun Aug 20 01:41:20 BOT 2017]; root of context hierarchy 2017-08-20 01:41:20,989 INFO [main] (XmlBeanDefinitionReader.java:317) - Loading XML bean definitions from class path resource [com/xgj/aop/spring/advisor/DynamicAdvisor/conf-dynamicAdvisor.xml] 調用getClassFilter()對 類【 com.xgj.aop.spring.advisor.DynamicAdvisor.Waiter】做靜態檢查調用matches(Method method, Class<?> targetClass),對方法【com.xgj.aop.spring.advisor.DynamicAdvisor.Waiter.greetTo】做靜態檢查調用getClassFilter()對 類【 com.xgj.aop.spring.advisor.DynamicAdvisor.Waiter】做靜態檢查調用matches(Method method, Class<?> targetClass),對方法【com.xgj.aop.spring.advisor.DynamicAdvisor.Waiter.serverTo】做靜態檢查調用getClassFilter()對 類【 com.xgj.aop.spring.advisor.DynamicAdvisor.Waiter】做靜態檢查調用matches(Method method, Class<?> targetClass),對方法【com.xgj.aop.spring.advisor.DynamicAdvisor.Waiter.toString】做靜態檢查調用getClassFilter()對 類【 com.xgj.aop.spring.advisor.DynamicAdvisor.Waiter】做靜態檢查調用matches(Method method, Class<?> targetClass),對方法【com.xgj.aop.spring.advisor.DynamicAdvisor.Waiter.clone】做靜態檢查調用getClassFilter()對 類【 com.xgj.aop.spring.advisor.DynamicAdvisor.Waiter】做靜態檢查調用matches(Method method, Class<?> targetClass),對方法【com.xgj.aop.spring.advisor.DynamicAdvisor.Waiter.greetTo】做靜態檢查調用matches(Method method, Class<?> targetClass, Object... args)對方法【com.xgj.aop.spring.advisor.DynamicAdvisor.Waiter.greetTo】做動態檢查Pointcut:com.xgj.aop.spring.advisor.DynamicAdvisor.Waiter.greetTo How are you XiaoGongJiangOne ? Waiter Greet To XiaoGongJiangOne 調用getClassFilter()對 類【 com.xgj.aop.spring.advisor.DynamicAdvisor.Waiter】做靜態檢查調用matches(Method method, Class<?> targetClass),對方法【com.xgj.aop.spring.advisor.DynamicAdvisor.Waiter.serverTo】做靜態檢查Waiter Server To XiaoGongJiangOne 調用matches(Method method, Class<?> targetClass, Object... args)對方法【com.xgj.aop.spring.advisor.DynamicAdvisor.Waiter.greetTo】做動態檢查Pointcut:com.xgj.aop.spring.advisor.DynamicAdvisor.Waiter.greetTo How are you XiaoGongJiangTwo ? Waiter Greet To XiaoGongJiangTwo Waiter Server To XiaoGongJiangTwo 調用matches(Method method, Class<?> targetClass, Object... args)對方法【com.xgj.aop.spring.advisor.DynamicAdvisor.Waiter.greetTo】做動態檢查Waiter Greet To XiaoGongJiang1 Waiter Server To XiaoGongJiang1我們來分析下:
System.out日志打印的前8行輸出信息反應了在織入切面Spring對目標類中所有的方法進行的靜態切點檢查。
接下來的幾行日志
調用getClassFilter()對 類【 com.xgj.aop.spring.advisor.DynamicAdvisor.Waiter】做靜態檢查調用matches(Method method, Class<?> targetClass),對方法【com.xgj.aop.spring.advisor.DynamicAdvisor.Waiter.greetTo】做靜態檢查調用matches(Method method, Class<?> targetClass, Object... args)對方法【com.xgj.aop.spring.advisor.DynamicAdvisor.Waiter.greetTo】做動態檢查Pointcut:com.xgj.aop.spring.advisor.DynamicAdvisor.Waiter.greetTo How are you XiaoGongJiangOne ? Waiter Greet To XiaoGongJiangOne對Waiter.greetTo(“XiaoGongJiangOne”)第一次調用greetTo方法時,執行靜態、動態切點檢查
接下來的日志
調用getClassFilter()對 類【 com.xgj.aop.spring.advisor.DynamicAdvisor.Waiter】做靜態檢查調用matches(Method method, Class<?> targetClass),對方法【com.xgj.aop.spring.advisor.DynamicAdvisor.Waiter.serverTo】做靜態檢查Waiter Server To XiaoGongJiangOne對Waiter.serverTo(“XiaoGongJiangOne”)第一次調用serverTo方法時,執行靜態切點檢查.
接下來的日志
調用matches(Method method, Class<?> targetClass, Object... args)對方法【com.xgj.aop.spring.advisor.DynamicAdvisor.Waiter.greetTo】做動態檢查Pointcut:com.xgj.aop.spring.advisor.DynamicAdvisor.Waiter.greetTo How are you XiaoGongJiangTwo ? Waiter Greet To XiaoGongJiangTwo對Waiter.greetTo(“XiaoGongJiangOne”)第二次調用greetTo方法時,僅僅執行動態切點檢查。
接下的日志
Waiter Server To XiaoGongJiangTwo對Waiter.serverTo(“XiaoGongJiangOne”)第二次調用serverTo方法時,不再執行靜態切點檢查.
接下來的日志
調用matches(Method method, Class<?> targetClass, Object... args)對方法【com.xgj.aop.spring.advisor.DynamicAdvisor.Waiter.greetTo】做動態檢查Waiter Greet To XiaoGongJiang1對waiter.greetTo(“XiaoGongJiang1”),第三次調greetTo方法時只執行動態切點檢查
最后一行日志
Waiter Server To XiaoGongJiang1第三次調用 serverTo,不再執行靜態切點檢查
通過上述的分析,結合GreetingDynamicPointcut切點類,可以很容易的發現,
Spring會在創建代理織入切面時,對目標類中的所有方法進行靜態切點檢查
在生成織入切面的代理對象后,第一次調用代理類的每一個方法都會進行一次靜態切點檢查,如果本次檢查就能夠從候選者列表中排除改方法,則以后對該方法就不會再執行靜態切點檢查
對于那些在靜態切點檢查時匹配的方法,在后續調用該方法時,將執行動態切點檢查
在我們的案例中,切點匹配的規則是:
1.目標類為com.xgj.aop.spring.advisor.DynamicAdvisor.Waiter或者其子類,
2.方法名為greetTo,
3.動態入參clientName必須是特殊名單中的客戶。
基于這條規則,serverTo()以及Object繼承而來的 toString和clone()方法通過靜態切點檢查就可以排除在候選者之外,只有greetTo方法是動態切點檢查的候選者,每次調用都會進行動態切點檢查。
如果我們將GreetingDynamicPointcut類中 對類和方法的靜態切點注釋掉,重新運行日志如下:
2017-08-20 02:20:53,908 INFO [main] (AbstractApplicationContext.java:583) - Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@3b293677: startup date [Sun Aug 20 02:20:53 BOT 2017]; root of context hierarchy 2017-08-20 02:20:54,004 INFO [main] (XmlBeanDefinitionReader.java:317) - Loading XML bean definitions from class path resource [com/xgj/aop/spring/advisor/DynamicAdvisor/conf-dynamicAdvisor.xml] 調用matches(Method method, Class<?> targetClass, Object... args)對方法【com.xgj.aop.spring.advisor.DynamicAdvisor.Waiter.greetTo】做動態檢查Pointcut:com.xgj.aop.spring.advisor.DynamicAdvisor.Waiter.greetTo How are you XiaoGongJiangOne ? Waiter Greet To XiaoGongJiangOne 調用matches(Method method, Class<?> targetClass, Object... args)對方法【com.xgj.aop.spring.advisor.DynamicAdvisor.Waiter.serverTo】做動態檢查Pointcut:com.xgj.aop.spring.advisor.DynamicAdvisor.Waiter.serverTo How are you XiaoGongJiangOne ? Waiter Server To XiaoGongJiangOne 調用matches(Method method, Class<?> targetClass, Object... args)對方法【com.xgj.aop.spring.advisor.DynamicAdvisor.Waiter.greetTo】做動態檢查Pointcut:com.xgj.aop.spring.advisor.DynamicAdvisor.Waiter.greetTo How are you XiaoGongJiangTwo ? Waiter Greet To XiaoGongJiangTwo 調用matches(Method method, Class<?> targetClass, Object... args)對方法【com.xgj.aop.spring.advisor.DynamicAdvisor.Waiter.serverTo】做動態檢查Pointcut:com.xgj.aop.spring.advisor.DynamicAdvisor.Waiter.serverTo How are you XiaoGongJiangTwo ? Waiter Server To XiaoGongJiangTwo 調用matches(Method method, Class<?> targetClass, Object... args)對方法【com.xgj.aop.spring.advisor.DynamicAdvisor.Waiter.greetTo】做動態檢查Waiter Greet To XiaoGongJiang1 調用matches(Method method, Class<?> targetClass, Object... args)對方法【com.xgj.aop.spring.advisor.DynamicAdvisor.Waiter.serverTo】做動態檢查Waiter Server To XiaoGongJiang1可以發現,每次調用代理對象的任何一個方法,都會執行動態切點檢查,這將導致很大的性能問題。
結論
在定義動態切點時,切勿忘記同時覆蓋getClassFilter()和 boolean matches(Method method, Class<?> targetClass)方法,通過靜態切點檢查排除大部分方法,從而提高程序運行效率
總結
以上是生活随笔為你收集整理的Spring-AOP 动态切面的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Spring-AOP 静态正则表达式方法
- 下一篇: Spring-AOP 流程切面