您的位置:  首頁 > 技術 > java語言 > 正文

《Spring 手擼專欄》第 12 章:爐火純青,基于JDK和Cglib動態代理,實現AOP核心功能

2021-07-22 21:05 OSCHINA 小傅哥 次閱讀 條評論

目錄


  • 一、前言

  • 二、目標

  • 三、方案

  • 四、實現

    • 1. 工程結構

    • 2. 代理方法案例

    • 3. 切點表達式

    • 4. 包裝切面通知信息

    • 5. 代理抽象實現(JDK&Cglib)

  • 五、測試

    • 1. 事先準備

    • 2. 自定義攔截方法

    • 3. 單元測試

  • 六、總結

  • 七、系列推薦


?

一、前言

為什么,你的代碼總是糊到豬圈上?

🎙怎么辦,知道你在互聯網,不知道你在哪個大廠。知道你在加班,不知道你在和哪個產品爭辯。知道你在偷懶,不知道你要摸魚到幾點。知道你在搬磚,不知道你在蓋哪個豬圈。


當你特別辛苦夜以繼日的完成著,每天、每周、每月重復性的工作時,你能獲得的成長是最小,得到的回報也是少的。留著最多的汗、拿著最少的錢

可能你一激動開始看源碼,但不知道看完的源碼能用到什么地方??丛O計模式,看的時候懂,但改自己的代碼又下不去手。其實一方面是本身技術棧的知識面不足,另外一方面是自己儲備的代碼也不夠。最終也就導致根本沒法把一些列的知識串聯起來,就像你看了 HashMap,但也聯想不到分庫分表組件中的數據散列也會用到了 HashMap 中的擾動函數思想和泊松分布驗證、看了Spring 源碼,也讀不出來 Mybatis 是如何解決只定義 Dao 接口就能使用配置或者注解對數據庫進行 CRUD 操作、看來 JDK 的動態代理,也想不到 AOP 是如何設計的。所以成體系學習,加強技術棧知識的完整性,才能更好的用上這些學習到的編碼能力。

?

二、目標

到本章節我們將要從 IOC 的實現,轉入到關于 AOP(Aspect Oriented Programming) 內容的開發。在軟件行業,AOP 意為:面向切面編程,通過預編譯的方式和運行期間動態代理實現程序功能功能的統一維護。其實 AOP 也是 OOP 的延續,在 Spring 框架中是一個非常重要的內容,使用 AOP 可以對業務邏輯的各個部分進行隔離,從而使各模塊間的業務邏輯耦合度降低,提高代碼的可復用性,同時也能提高開發效率。

關于 AOP 的核心技術實現主要是動態代理的使用,就像你可以給一個接口的實現類,使用代理的方式替換掉這個實現類,使用代理類來處理你需要的邏輯。比如:

@Test
public?void?test_proxy_class()?{
????IUserService?userService?=?(IUserService)?Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),?new?Class[]{IUserService.class},?(proxy,?method,?args)?->?"你被代理了!");
????String?result?=?userService.queryUserInfo();
????System.out.println("測試結果:"?+?result);
}

?

代理類的實現基本都大家都見過,那么有了一個基本的思路后,接下來就需要考慮下怎么給方法做代理呢,而不是代理類。另外怎么去代理所有符合某些規則的所有類中方法呢。如果可以代理掉所有類的方法,就可以做一個方法攔截器,給所有被代理的方法添加上一些自定義處理,比如打印日志、記錄耗時、監控異常等。

?

三、方案

在把 AOP 整個切面設計融合到 Spring 前,我們需要解決兩個問題,包括:如何給符合規則的方法做代理以及做完代理方法的案例后,把類的職責拆分出來。而這兩個功能點的實現,都是以切面的思想進行設計和開發。如果不是很清楚 AOP 是啥,你可以把切面理解為用刀切韭菜,一根一根切總是有點慢,那么用手(代理)把韭菜捏成一把,用菜刀或者斧頭這樣不同的攔截操作來處理。而程序中其實也是一樣,只不過韭菜變成了方法,菜刀變成了攔截方法。整體設計結構如下圖:

  • 就像你在使用 Spring 的 AOP 一樣,只處理一些需要被攔截的方法。在攔截方法后,執行你對方法的擴展操作。
  • 那么我們就需要先來實現一個可以代理方法的 Proxy,其實代理方法主要是使用到方法攔截器類處理方法的調用 ? ? MethodInterceptor#invoke,而不是直接使用 invoke 方法中的入參 Method method 進行 ? ? method.invoke(targetObj, args) 這塊是整個使用時的差異。
  • 除了以上的核心功能實現,還需要使用到 ? ? org.aspectj.weaver.tools.PointcutParser 處理攔截表達式 ? ? "execution(* cn.bugstack.springframework.test.bean.IUserService.*(..))",有了方法代理和處理攔截,我們就可以完成設計出一個 AOP 的雛形了。

?

四、實現

?

1. 工程結構

small-spring-step-11
└──?src
????├──?main
????│???└──?java
????│???????└──?cn.bugstack.springframework
????│???????????├──?aop
????│???????????│???├──?aspectj
????│???????????│???│???└──?AspectJExpressionPointcut.java
????│???????????│???├──?framework?
????│???????????│???│???├──?AopProxy.java
????│???????????│???│???├──?Cglib2AopProxy.java
????│???????????│???│???├──?JdkDynamicAopProxy.java
????│???????????│???│???└──?ReflectiveMethodInvocation.java
????│???????????│???├──?AdvisedSupport.java
????│???????????│???├──?ClassFilter.java
????│???????????│???├──?MethodMatcher.java
????│???????????│???├──?Pointcut.java
????│???????????│???└──?TargetSource.java
????│???????????├──?beans
????│???????????│???├──?factory
????│???????????│???│???├──?config
????│???????????│???│???│???├──?AutowireCapableBeanFactory.java
????│???????????│???│???│???├──?BeanDefinition.java
????│???????????│???│???│???├──?BeanFactoryPostProcessor.java
????│???????????│???│???│???├──?BeanPostProcessor.java
????│???????????│???│???│???├──?BeanReference.java
????│???????????│???│???│???├──?ConfigurableBeanFactory.java
????│???????????│???│???│???└──?SingletonBeanRegistry.java
????│???????????│???│???├──?support
????│???????????│???│???│???├──?AbstractAutowireCapableBeanFactory.java
????│???????????│???│???│???├──?AbstractBeanDefinitionReader.java
????│???????????│???│???│???├──?AbstractBeanFactory.java
????│???????????│???│???│???├──?BeanDefinitionReader.java
????│???????????│???│???│???├──?BeanDefinitionRegistry.java
????│???????????│???│???│???├──?CglibSubclassingInstantiationStrategy.java
????│???????????│???│???│???├──?DefaultListableBeanFactory.java
????│???????????│???│???│???├──?DefaultSingletonBeanRegistry.java
????│???????????│???│???│???├──?DisposableBeanAdapter.java
????│???????????│???│???│???├──?FactoryBeanRegistrySupport.java
????│???????????│???│???│???├──?InstantiationStrategy.java
????│???????????│???│???│???└──?SimpleInstantiationStrategy.java??
????│???????????│???│???├──?support
????│???????????│???│???│???└──?XmlBeanDefinitionReader.java
????│???????????│???│???├──?Aware.java
????│???????????│???│???├──?BeanClassLoaderAware.java
????│???????????│???│???├──?BeanFactory.java
????│???????????│???│???├──?BeanFactoryAware.java
????│???????????│???│???├──?BeanNameAware.java
????│???????????│???│???├──?ConfigurableListableBeanFactory.java
????│???????????│???│???├──?DisposableBean.java
????│???????????│???│???├──?FactoryBean.java
????│???????????│???│???├──?HierarchicalBeanFactory.java
????│???????????│???│???├──?InitializingBean.java
????│???????????│???│???└──?ListableBeanFactory.java
????│???????????│???├──?BeansException.java
????│???????????│???├──?PropertyValue.java
????│???????????│???└──?PropertyValues.java?
????│???????????├──?context
????│???????????│???├──?event
????│???????????│???│???├──?AbstractApplicationEventMulticaster.java?
????│???????????│???│???├──?ApplicationContextEvent.java?
????│???????????│???│???├──?ApplicationEventMulticaster.java?
????│???????????│???│???├──?ContextClosedEvent.java?
????│???????????│???│???├──?ContextRefreshedEvent.java?
????│???????????│???│???└──?SimpleApplicationEventMulticaster.java?
????│???????????│???├──?support
????│???????????│???│???├──?AbstractApplicationContext.java?
????│???????????│???│???├──?AbstractRefreshableApplicationContext.java?
????│???????????│???│???├──?AbstractXmlApplicationContext.java?
????│???????????│???│???├──?ApplicationContextAwareProcessor.java?
????│???????????│???│???└──?ClassPathXmlApplicationContext.java?
????│???????????│???├──?ApplicationContext.java?
????│???????????│???├──?ApplicationContextAware.java?
????│???????????│???├──?ApplicationEvent.java?
????│???????????│???├──?ApplicationEventPublisher.java?
????│???????????│???├──?ApplicationListener.java?
????│???????????│???└──?ConfigurableApplicationContext.java
????│???????????├──?core.io
????│???????????│???├──?ClassPathResource.java?
????│???????????│???├──?DefaultResourceLoader.java?
????│???????????│???├──?FileSystemResource.java?
????│???????????│???├──?Resource.java?
????│???????????│???├──?ResourceLoader.java?
????│???????????│???└──?UrlResource.java
????│???????????└──?utils
????│???????????????└──?ClassUtils.java
????└──?test
????????└──?java
????????????└──?cn.bugstack.springframework.test
????????????????├──?bean
????????????????│???├──?IUserService.java
????????????????│???├──?UserService.java
????????????????│???└──?UserServiceInterceptor.java
????????????????└──?ApiTest.java

?

工程源碼公眾號「bugstack蟲洞?!?#xff0c;回復:Spring 專欄,獲取完整源碼

AOP 切點表達式和使用以及基于 JDK 和 CGLIB 的動態代理類關系,如圖 12-2


圖 12-2

  • 整個類關系圖就是 AOP 實現核心邏輯的地方,上面部分是關于方法的匹配實現,下面從 AopProxy 開始是關于方法的代理操作。
  • AspectJExpressionPointcut 的核心功能主要依賴于 aspectj 組件并處理 Pointcut、ClassFilter,、MethodMatcher 接口實現,專門用于處理類和方法的匹配過濾操作。
  • AopProxy 是代理的抽象對象,它的實現主要是基于 JDK 的代理和 Cglib 代理。在前面章節關于對象的實例化 CglibSubclassingInstantiationStrategy,我們也使用過 Cglib 提供的功能。

?

2. 代理方法案例

在實現 AOP 的核心功能之前,我們先做一個代理方法的案例,通過這樣一個可以概括代理方法的核心全貌,可以讓大家更好的理解后續拆解各個方法,設計成解耦功能的 AOP 實現過程。

單元測試

@Test
public?void?test_proxy_method()?{
????//?目標對象(可以替換成任何的目標對象)
????Object?targetObj?=?new?UserService();
????//?AOP?代理
????IUserService?proxy?=?(IUserService)?Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),?targetObj.getClass().getInterfaces(),?new?InvocationHandler()?{
????????//?方法匹配器
????????MethodMatcher?methodMatcher?=?new?AspectJExpressionPointcut("execution(*?cn.bugstack.springframework.test.bean.IUserService.*(..))");
????????@Override
????????public?Object?invoke(Object?proxy,?Method?method,?Object[]?args)?throws?Throwable?{
????????????if?(methodMatcher.matches(method,?targetObj.getClass()))?{
????????????????//?方法攔截器
????????????????MethodInterceptor?methodInterceptor?=?invocation?->?{
????????????????????long?start?=?System.currentTimeMillis();
????????????????????try?{
????????????????????????return?invocation.proceed();
????????????????????}?finally?{
????????????????????????System.out.println("監控?-?Begin?By?AOP");
????????????????????????System.out.println("方法名稱:"?+?invocation.getMethod().getName());
????????????????????????System.out.println("方法耗時:"?+?(System.currentTimeMillis()?-?start)?+?"ms");
????????????????????????System.out.println("監控?-?End\r\n");
????????????????????}
????????????????};
????????????????//?反射調用
????????????????return?methodInterceptor.invoke(new?ReflectiveMethodInvocation(targetObj,?method,?args));
????????????}
????????????return?method.invoke(targetObj,?args);
????????}
????});
????String?result?=?proxy.queryUserInfo();
????System.out.println("測試結果:"?+?result);
}

?

  • 首先整個案例的目標是給一個 UserService 當成目標對象,對類中的所有方法進行攔截添加監控信息打印處理。
  • 從案例中你可以看到有代理的實現 Proxy.newProxyInstance,有方法的匹配 MethodMatcher,有反射的調用 invoke(Object proxy, Method method, Object[] args),也用用戶自己攔截方法后的操作。這樣一看其實和我們使用的 AOP 就非常類似了,只不過你在使用 AOP 的時候是框架已經提供更好的功能,這里是把所有的核心過程給你展示出來了。

測試結果

監控?-?Begin?By?AOP
方法名稱:queryUserInfo
方法耗時:86ms
監控?-?End

測試結果:小傅哥,100001,深圳

Process?finished?with?exit?code?0

?

  • 從測試結果可以看到我們已經對 UserService#queryUserInfo 方法進行了攔截監控操作,其實后面我們實現的 AOP 就是現在體現出的結果,只不過我們需要把這部分測試的案例解耦為更具有擴展性的各個模塊實現。

拆解案例


圖 12-3

  • 拆解過程可以參考截圖 12-3,我們需要把代理對象拆解出來,因為它可以是 JDK 的實現也可以是 Cglib 的處理。
  • 方法匹配器操作其實已經是一個單獨的實現類了,不過我們還需要把傳入的目標對象、方法匹配、攔截方法,都進行統一的包裝,方便外部調用時進行一個入參透傳。
  • 最后其實是 ? ? ReflectiveMethodInvocation 的使用,它目前已經是實現 ? ? MethodInvocation 接口的一個包裝后的類,參數信息包括:調用的對象、調用的方法、調用的入參。

?

3. 切點表達式

定義接口

cn.bugstack.springframework.aop.Pointcut

public?interface?Pointcut?{

????/**
?????*?Return?the?ClassFilter?for?this?pointcut.
?????*?@return?the?ClassFilter?(never?<code>null</code>)
?????*/

????ClassFilter?getClassFilter();

????/**
?????*?Return?the?MethodMatcher?for?this?pointcut.
?????*?@return?the?MethodMatcher?(never?<code>null</code>)
?????*/

????MethodMatcher?getMethodMatcher();

}

?

  • 切入點接口,定義用于獲取 ClassFilter、MethodMatcher 的兩個類,這兩個接口獲取都是切點表達式提供的內容。

cn.bugstack.springframework.aop.ClassFilter

public?interface?ClassFilter?{

????/**
?????*?Should?the?pointcut?apply?to?the?given?interface?or?target?class?
?????*?@param?clazz?the?candidate?target?class
?????*?@return?whether?the?advice?should?apply?to?the?given?target?class
?????*/

????boolean?matches(Class<?>?clazz);

}

?

  • 定義類匹配類,用于切點找到給定的接口和目標類。

cn.bugstack.springframework.aop.MethodMatcher

public?interface?MethodMatcher?{

????/**
?????*?Perform?static?checking?whether?the?given?method?matches.?If?this
?????*?@return?whether?or?not?this?method?matches?statically
?????*/

????boolean?matches(Method?method,?Class<?>?targetClass);
????
}

?

  • 方法匹配,找到表達式范圍內匹配下的目標類和方法。在上文的案例中有所體現: ? ? methodMatcher.matches(method, targetObj.getClass()) ? ?

實現切點表達式類

public?class?AspectJExpressionPointcut?implements?Pointcut,?ClassFilter,?MethodMatcher?{

????private?static?final?Set<PointcutPrimitive>?SUPPORTED_PRIMITIVES?=?new?HashSet<PointcutPrimitive>();

????static?{
????????SUPPORTED_PRIMITIVES.add(PointcutPrimitive.EXECUTION);
????}

????private?final?PointcutExpression?pointcutExpression;

????public?AspectJExpressionPointcut(String?expression)?{
????????PointcutParser?pointcutParser?=?PointcutParser.getPointcutParserSupportingSpecifiedPrimitivesAndUsingSpecifiedClassLoaderForResolution(SUPPORTED_PRIMITIVES,?this.getClass().getClassLoader());
????????pointcutExpression?=?pointcutParser.parsePointcutExpression(expression);
????}

????@Override
????public?boolean?matches(Class<?>?clazz)?{
????????return?pointcutExpression.couldMatchJoinPointsInType(clazz);
????}

????@Override
????public?boolean?matches(Method?method,?Class<?>?targetClass)?{
????????return?pointcutExpression.matchesMethodExecution(method).alwaysMatches();
????}

????@Override
????public?ClassFilter?getClassFilter()?{
????????return?this;
????}

????@Override
????public?MethodMatcher?getMethodMatcher()?{
????????return?this;
????}

}

?

  • 切點表達式實現了 Pointcut、ClassFilter、MethodMatcher,三個接口定義方法,同時這個類主要是對 aspectj 包提供的表達式校驗方法使用。
  • 匹配 matches: ? ? pointcutExpression.couldMatchJoinPointsInType(clazz)、 ? ? pointcutExpression.matchesMethodExecution(method).alwaysMatches(),這部分內容可以單獨測試驗證。

匹配驗證

@Test
public?void?test_aop()?throws?NoSuchMethodException?{
????AspectJExpressionPointcut?pointcut?=?new?AspectJExpressionPointcut("execution(*?cn.bugstack.springframework.test.bean.UserService.*(..))");
????Class<UserService>?clazz?=?UserService.class;
????Method?method?=?clazz.getDeclaredMethod("queryUserInfo");???

????System.out.println(pointcut.matches(clazz));
????System.out.println(pointcut.matches(method,?clazz));??????????
????
????//?true、true
}

?

  • 這里單獨提供出來一個匹配方法的驗證測試,可以看看你攔截的方法與對應的對象是否匹配。

?

4. 包裝切面通知信息

cn.bugstack.springframework.aop.AdvisedSupport

public?class?AdvisedSupport?{

????//?被代理的目標對象
????private?TargetSource?targetSource;
????//?方法攔截器
????private?MethodInterceptor?methodInterceptor;
????//?方法匹配器(檢查目標方法是否符合通知條件)
????private?MethodMatcher?methodMatcher;
????
????//?...get/set
}

?

  • AdvisedSupport,主要是用于把代理、攔截、匹配的各項屬性包裝到一個類中,方便在 Proxy 實現類進行使用。 ? ? 這和你的業務開發中包裝入參是一個道理 ? ?
  • TargetSource,是一個目標對象,在目標對象類中提供 Object 入參屬性,以及獲取目標類 TargetClass 信息。
  • MethodInterceptor,是一個具體攔截方法實現類,由用戶自己實現 MethodInterceptor#invoke 方法,做具體的處理。 ? ? 像我們本文的案例中是做方法監控處理 ? ?
  • MethodMatcher,是一個匹配方法的操作,這個對象由 AspectJExpressionPointcut 提供服務。

?

5. 代理抽象實現(JDK&Cglib)

定義接口

cn.bugstack.springframework.aop.framework

public?interface?AopProxy?{

????Object?getProxy();

}

?

  • 定義一個標準接口,用于獲取代理類。因為具體實現代理的方式可以有 JDK 方式,也可以是 Cglib 方式,所以定義接口會更加方便管理實現類。

cn.bugstack.springframework.aop.framework.JdkDynamicAopProxy

public?class?JdkDynamicAopProxy?implements?AopProxy,?InvocationHandler?{

????private?final?AdvisedSupport?advised;

????public?JdkDynamicAopProxy(AdvisedSupport?advised)?{
????????this.advised?=?advised;
????}

????@Override
????public?Object?getProxy()?{
????????return?Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),?advised.getTargetSource().getTargetClass(),?this);
????}

????@Override
????public?Object?invoke(Object?proxy,?Method?method,?Object[]?args)?throws?Throwable?{
????????if?(advised.getMethodMatcher().matches(method,?advised.getTargetSource().getTarget().getClass()))?{
????????????MethodInterceptor?methodInterceptor?=?advised.getMethodInterceptor();
????????????return?methodInterceptor.invoke(new?ReflectiveMethodInvocation(advised.getTargetSource().getTarget(),?method,?args));
????????}
????????return?method.invoke(advised.getTargetSource().getTarget(),?args);
????}

}

?

  • 基于 JDK 實現的代理類,需要實現接口 AopProxy、InvocationHandler,這樣就可以把代理對象 getProxy 和反射調用方法 invoke 分開處理了。
  • getProxy 方法中的是代理一個對象的操作,需要提供入參 ClassLoader、AdvisedSupport、和當前這個類 this,因為這個類提供了 invoke 方法。
  • invoke 方法中主要處理匹配的方法后,使用用戶自己提供的方法攔截實現,做反射調用 methodInterceptor.invoke 。
  • 這里還有一個 ReflectiveMethodInvocation,其他它就是一個入參的包裝信息,提供了入參對象:目標對象、方法、入參。

cn.bugstack.springframework.aop.framework.Cglib2AopProxy

public?class?Cglib2AopProxy?implements?AopProxy?{

????private?final?AdvisedSupport?advised;

????public?Cglib2AopProxy(AdvisedSupport?advised)?{
????????this.advised?=?advised;
????}

????@Override
????public?Object?getProxy()?{
????????Enhancer?enhancer?=?new?Enhancer();
????????enhancer.setSuperclass(advised.getTargetSource().getTarget().getClass());
????????enhancer.setInterfaces(advised.getTargetSource().getTargetClass());
????????enhancer.setCallback(new?DynamicAdvisedInterceptor(advised));
????????return?enhancer.create();
????}

????private?static?class?DynamicAdvisedInterceptor?implements?MethodInterceptor?{

????????@Override
????????public?Object?intercept(Object?o,?Method?method,?Object[]?objects,?MethodProxy?methodProxy)?throws?Throwable?{
????????????CglibMethodInvocation?methodInvocation?=?new?CglibMethodInvocation(advised.getTargetSource().getTarget(),?method,?objects,?methodProxy);
????????????if?(advised.getMethodMatcher().matches(method,?advised.getTargetSource().getTarget().getClass()))?{
????????????????return?advised.getMethodInterceptor().invoke(methodInvocation);
????????????}
????????????return?methodInvocation.proceed();
????????}
????}

????private?static?class?CglibMethodInvocation?extends?ReflectiveMethodInvocation?{

????????@Override
????????public?Object?proceed()?throws?Throwable?{
????????????return?this.methodProxy.invoke(this.target,?this.arguments);
????????}

????}

}

?

  • 基于 Cglib 使用 Enhancer 代理的類可以在運行期間為接口使用底層 ASM 字節碼增強技術處理對象的代理對象生成,因此被代理類不需要實現任何接口。
  • 關于擴展進去的用戶攔截方法,主要是在 Enhancer#setCallback 中處理,用戶自己的新增的攔截處理。這里可以看到 DynamicAdvisedInterceptor#intercept 匹配方法后做了相應的反射操作。

?

五、測試

?

1. 事先準備

public?class?UserService?implements?IUserService?{

????public?String?queryUserInfo()?{
????????try?{
????????????Thread.sleep(new?Random(1).nextInt(100));
????????}?catch?(InterruptedException?e)?{
????????????e.printStackTrace();
????????}
????????return?"小傅哥,100001,深圳";
????}

????public?String?register(String?userName)?{
????????try?{
????????????Thread.sleep(new?Random(1).nextInt(100));
????????}?catch?(InterruptedException?e)?{
????????????e.printStackTrace();
????????}
????????return?"注冊用戶:"?+?userName?+?" success!";
????}

}

?

  • 在 UserService 中提供了2個不同方法,另外你還可以增加新的類來加入測試。后面我們的測試過程,會給這個兩個方法添加我們的攔截處理,打印方法執行耗時。

?

2. 自定義攔截方法

public?class?UserServiceInterceptor?implements?MethodInterceptor?{

????@Override
????public?Object?invoke(MethodInvocation?invocation)?throws?Throwable?{
????????long?start?=?System.currentTimeMillis();
????????try?{
????????????return?invocation.proceed();
????????}?finally?{
????????????System.out.println("監控?-?Begin?By?AOP");
????????????System.out.println("方法名稱:"?+?invocation.getMethod());
????????????System.out.println("方法耗時:"?+?(System.currentTimeMillis()?-?start)?+?"ms");
????????????System.out.println("監控?- End\r\n");
????????}
????}

}

?

  • 用戶自定義的攔截方法需要實現 MethodInterceptor 接口的 invoke 方法,使用方式與 Spring AOP 非常相似,也是包裝 invocation.proceed() 放行,并在 finally 中添加監控信息。

?

3. 單元測試

@Test
public?void?test_dynamic()?{
????//?目標對象
????IUserService?userService?=?new?UserService();?????

????//?組裝代理信息
????AdvisedSupport?advisedSupport?=?new?AdvisedSupport();
????advisedSupport.setTargetSource(new?TargetSource(userService));
????advisedSupport.setMethodInterceptor(new?UserServiceInterceptor());
????advisedSupport.setMethodMatcher(new?AspectJExpressionPointcut("execution(*?cn.bugstack.springframework.test.bean.IUserService.*(..))"));
????
????//?代理對象(JdkDynamicAopProxy)
????IUserService?proxy_jdk?=?(IUserService)?new?JdkDynamicAopProxy(advisedSupport).getProxy();
????//?測試調用
????System.out.println("測試結果:"?+?proxy_jdk.queryUserInfo());
????
????//?代理對象(Cglib2AopProxy)
????IUserService?proxy_cglib?=?(IUserService)?new?Cglib2AopProxy(advisedSupport).getProxy();
????//?測試調用
????System.out.println("測試結果:"?+?proxy_cglib.register("花花"));
}

?

  • 整個案例測試了 AOP 在于 Spring 結合前的核心代碼,包括什么是目標對象、怎么組裝代理信息、如何調用代理對象。
  • AdvisedSupport,包裝了目標對象、用戶自己實現的攔截方法以及方法匹配表達式。
  • 之后就是分別調用 JdkDynamicAopProxy、Cglib2AopProxy,兩個不同方式實現的代理類,看看是否可以成功攔截方法

測試結果

監控?-?Begin?By?AOP
方法名稱:public?abstract?java.lang.String?cn.bugstack.springframework.test.bean.IUserService.queryUserInfo()
方法耗時:86ms
監控?-?End

測試結果:小傅哥,100001,深圳
監控?-?Begin?By?AOP
方法名稱:public?java.lang.String?cn.bugstack.springframework.test.bean.UserService.register(java.lang.String)
方法耗時:97ms
監控?-?End

測試結果:注冊用戶:花花 success

Process?finished?with?exit?code?0

?

  • 如 AOP 功能定義一樣,我們可以通過這樣的代理方式、方法匹配和攔截后,在對應的目標方法下,做了攔截操作進行監控信息打印。

?

六、總結

  • 從本文對 Proxy#newProxyInstance、MethodInterceptor#invoke,的使用驗證切面核心原理以及再把功能拆解到 Spring 框架實現中,可以看到一個貌似復雜的技術其實核心內容往往沒有太多,但因為需要為了滿足后續更多的擴展就需要進行職責解耦和包裝,通過這樣設計模式的使用,以此讓調用方能更加簡化,自身也可以不斷按需擴展。
  • AOP 的功能實現目前還沒有與 Spring 結合,只是對切面技術的一個具體實現,你可以先學習到如何處理代理對象、過濾方法、攔截方法,以及使用 Cglib 和 JDK 代理的區別,其實這與的技術不只是在 Spring 框架中有所體現,在其他各類需要減少人工硬編碼的場景下,都會用到。 ? ? 比如RPC、Mybatis、MQ、分布式任務 ? ?
  • 一些核心技術的使用上,都是具有很強的關聯性的,它們也不是孤立存在的。而這個能把整個技術棧串聯起來的過程,需要你來大量的學習、積累、由點到面的鋪設,才能在一個知識點的學習拓展到一個知識面和知識體系的建設。? ? ??


  • 0
    感動
  • 0
    路過
  • 0
    高興
  • 0
    難過
  • 0
    搞笑
  • 0
    無聊
  • 0
    憤怒
  • 0
    同情
熱度排行
友情鏈接
18禁高潮出水呻吟娇喘mp3,日本熟妇乱人伦A片免费高清,成人午夜精品无码区,狠狠色噜噜色狠狠狠综合久久,麻豆一区二区99久久久久,年轻的妈妈4,少妇被又大又粗又爽毛片,护士张开腿让我爽了一夜,男男互攻互受h啪肉np文,你好神枪手电视剧免费观看啊,97人妻一区二区精品免费,久久久婷婷五月亚洲97号色,freegaysexvideos男男中国,国产精品国产三级国av麻豆,国产精品又黄又爽又色无遮挡网站,亚洲av无码一区二区三区网站,亚洲国产精品久久久久蜜桃,国产真人无码作爱视频免费,国产成人精品亚洲一区二区三区,亚洲欧洲日产最新,老司机带带我精彩免费,国产成人久久精品激情,日本最新av免费一区二区三区,边摸边吃奶又黄又激烈视频
<蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <文本链> <文本链> <文本链> <文本链> <文本链> <文本链>