精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

爐火純青,基于JDK和Cglib動態代理,實現AOP核心功能

開發 前端
在把 AOP 整個切面設計融合到 Spring 前,我們需要解決兩個問題,包括:如何給符合規則的方法做代理,以及做完代理方法的案例后,把類的職責拆分出來。而這兩個功能點的實現,都是以切面的思想進行設計和開發。

[[411087]]

本文轉載自微信公眾號「bugstack蟲洞棧」,作者小傅哥。轉載本文請聯系bugstack蟲洞棧公眾號。

目錄

  • 一、前言
  • 二、目標
  • 三、方案
  • 四、實現
    • 1. 工程結構
    • 2. 代理方法案例
    • 3. 切點表達式
    • 4. 包裝切面通知信息
    • 5. 代理抽象實現(JDK&Cglib)
  • 五、測試
    • 1. 事先準備
    • 2. 自定義攔截方法
    • 3. 單元測試
  • 六、總結

一、前言

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

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

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

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

二、目標

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

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

  1. @Test 
  2. public void test_proxy_class() { 
  3.     IUserService userService = (IUserService) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), new Class[]{IUserService.class}, (proxy, method, args) -> "你被代理了!"); 
  4.     String result = userService.queryUserInfo(); 
  5.     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. 工程結構

  1. small-spring-step-11 
  2. └── src 
  3.     ├── main 
  4.     │   └── java 
  5.     │       └── cn.bugstack.springframework 
  6.     │           ├── aop 
  7.     │           │   ├── aspectj 
  8.     │           │   │   └── AspectJExpressionPointcut.java 
  9.     │           │   ├── framework  
  10.     │           │   │   ├── AopProxy.java 
  11.     │           │   │   ├── Cglib2AopProxy.java 
  12.     │           │   │   ├── JdkDynamicAopProxy.java 
  13.     │           │   │   └── ReflectiveMethodInvocation.java 
  14.     │           │   ├── AdvisedSupport.java 
  15.     │           │   ├── ClassFilter.java 
  16.     │           │   ├── MethodMatcher.java 
  17.     │           │   ├── Pointcut.java 
  18.     │           │   └── TargetSource.java 
  19.     │           ├── beans 
  20.     │           │   ├── factory 
  21.     │           │   │   ├── config 
  22.     │           │   │   │   ├── AutowireCapableBeanFactory.java 
  23.     │           │   │   │   ├── BeanDefinition.java 
  24.     │           │   │   │   ├── BeanFactoryPostProcessor.java 
  25.     │           │   │   │   ├── BeanPostProcessor.java 
  26.     │           │   │   │   ├── BeanReference.java 
  27.     │           │   │   │   ├── ConfigurableBeanFactory.java 
  28.     │           │   │   │   └── SingletonBeanRegistry.java 
  29.     │           │   │   ├── support 
  30.     │           │   │   │   ├── AbstractAutowireCapableBeanFactory.java 
  31.     │           │   │   │   ├── AbstractBeanDefinitionReader.java 
  32.     │           │   │   │   ├── AbstractBeanFactory.java 
  33.     │           │   │   │   ├── BeanDefinitionReader.java 
  34.     │           │   │   │   ├── BeanDefinitionRegistry.java 
  35.     │           │   │   │   ├── CglibSubclassingInstantiationStrategy.java 
  36.     │           │   │   │   ├── DefaultListableBeanFactory.java 
  37.     │           │   │   │   ├── DefaultSingletonBeanRegistry.java 
  38.     │           │   │   │   ├── DisposableBeanAdapter.java 
  39.     │           │   │   │   ├── FactoryBeanRegistrySupport.java 
  40.     │           │   │   │   ├── InstantiationStrategy.java 
  41.     │           │   │   │   └── SimpleInstantiationStrategy.java   
  42.     │           │   │   ├── support 
  43.     │           │   │   │   └── XmlBeanDefinitionReader.java 
  44.     │           │   │   ├── Aware.java 
  45.     │           │   │   ├── BeanClassLoaderAware.java 
  46.     │           │   │   ├── BeanFactory.java 
  47.     │           │   │   ├── BeanFactoryAware.java 
  48.     │           │   │   ├── BeanNameAware.java 
  49.     │           │   │   ├── ConfigurableListableBeanFactory.java 
  50.     │           │   │   ├── DisposableBean.java 
  51.     │           │   │   ├── FactoryBean.java 
  52.     │           │   │   ├── HierarchicalBeanFactory.java 
  53.     │           │   │   ├── InitializingBean.java 
  54.     │           │   │   └── ListableBeanFactory.java 
  55.     │           │   ├── BeansException.java 
  56.     │           │   ├── PropertyValue.java 
  57.     │           │   └── PropertyValues.java  
  58.     │           ├── context 
  59.     │           │   ├── event 
  60.     │           │   │   ├── AbstractApplicationEventMulticaster.java  
  61.     │           │   │   ├── ApplicationContextEvent.java  
  62.     │           │   │   ├── ApplicationEventMulticaster.java  
  63.     │           │   │   ├── ContextClosedEvent.java  
  64.     │           │   │   ├── ContextRefreshedEvent.java  
  65.     │           │   │   └── SimpleApplicationEventMulticaster.java  
  66.     │           │   ├── support 
  67.     │           │   │   ├── AbstractApplicationContext.java  
  68.     │           │   │   ├── AbstractRefreshableApplicationContext.java  
  69.     │           │   │   ├── AbstractXmlApplicationContext.java  
  70.     │           │   │   ├── ApplicationContextAwareProcessor.java  
  71.     │           │   │   └── ClassPathXmlApplicationContext.java  
  72.     │           │   ├── ApplicationContext.java  
  73.     │           │   ├── ApplicationContextAware.java  
  74.     │           │   ├── ApplicationEvent.java  
  75.     │           │   ├── ApplicationEventPublisher.java  
  76.     │           │   ├── ApplicationListener.java  
  77.     │           │   └── ConfigurableApplicationContext.java 
  78.     │           ├── core.io 
  79.     │           │   ├── ClassPathResource.java  
  80.     │           │   ├── DefaultResourceLoader.java  
  81.     │           │   ├── FileSystemResource.java  
  82.     │           │   ├── Resource.java  
  83.     │           │   ├── ResourceLoader.java  
  84.     │           │   └── UrlResource.java 
  85.     │           └── utils 
  86.     │               └── ClassUtils.java 
  87.     └── test 
  88.         └── java 
  89.             └── cn.bugstack.springframework.test 
  90.                 ├── bean 
  91.                 │   ├── IUserService.java 
  92.                 │   ├── UserService.java 
  93.                 │   └── UserServiceInterceptor.java 
  94.                 └── ApiTest.java 

工程源碼:公眾號「bugstack蟲洞棧」,回復:Spring 專欄,獲取完整源碼

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

圖 12-2

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

2. 代理方法案例

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

單元測試

  1. @Test 
  2. public void test_proxy_method() { 
  3.     // 目標對象(可以替換成任何的目標對象) 
  4.     Object targetObj = new UserService(); 
  5.     // AOP 代理 
  6.     IUserService proxy = (IUserService) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), targetObj.getClass().getInterfaces(), new InvocationHandler() { 
  7.         // 方法匹配器 
  8.         MethodMatcher methodMatcher = new AspectJExpressionPointcut("execution(* cn.bugstack.springframework.test.bean.IUserService.*(..))"); 
  9.         @Override 
  10.         public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 
  11.             if (methodMatcher.matches(method, targetObj.getClass())) { 
  12.                 // 方法攔截器 
  13.                 MethodInterceptor methodInterceptor = invocation -> { 
  14.                     long start = System.currentTimeMillis(); 
  15.                     try { 
  16.                         return invocation.proceed(); 
  17.                     } finally { 
  18.                         System.out.println("監控 - Begin By AOP"); 
  19.                         System.out.println("方法名稱:" + invocation.getMethod().getName()); 
  20.                         System.out.println("方法耗時:" + (System.currentTimeMillis() - start) + "ms"); 
  21.                         System.out.println("監控 - End\r\n"); 
  22.                     } 
  23.                 }; 
  24.                 // 反射調用 
  25.                 return methodInterceptor.invoke(new ReflectiveMethodInvocation(targetObj, method, args)); 
  26.             } 
  27.             return method.invoke(targetObj, args); 
  28.         } 
  29.     }); 
  30.     String result = proxy.queryUserInfo(); 
  31.     System.out.println("測試結果:" + result); 

首先整個案例的目標是給一個 UserService 當成目標對象,對類中的所有方法進行攔截添加監控信息打印處理。

從案例中你可以看到有代理的實現 Proxy.newProxyInstance,有方法的匹配 MethodMatcher,有反射的調用 invoke(Object proxy, Method method, Object[] args),也用用戶自己攔截方法后的操作。這樣一看其實和我們使用的 AOP 就非常類似了,只不過你在使用 AOP 的時候是框架已經提供更好的功能,這里是把所有的核心過程給你展示出來了。

測試結果

  1. 監控 - Begin By AOP 
  2. 方法名稱:queryUserInfo 
  3. 方法耗時:86ms 
  4. 監控 - End 
  5.  
  6. 測試結果:小傅哥,100001,深圳 
  7.  
  8. Process finished with exit code 0 
  • 從測試結果可以看到我們已經對 UserService#queryUserInfo 方法進行了攔截監控操作,其實后面我們實現的 AOP 就是現在體現出的結果,只不過我們需要把這部分測試的案例解耦為更具有擴展性的各個模塊實現。

拆解案例

圖 12-3

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

3. 切點表達式

定義接口

cn.bugstack.springframework.aop.Pointcut

  1. public interface Pointcut { 
  2.  
  3.     /** 
  4.      * Return the ClassFilter for this pointcut. 
  5.      * @return the ClassFilter (never <code>null</code>) 
  6.      */ 
  7.     ClassFilter getClassFilter(); 
  8.  
  9.     /** 
  10.      * Return the MethodMatcher for this pointcut. 
  11.      * @return the MethodMatcher (never <code>null</code>) 
  12.      */ 
  13.     MethodMatcher getMethodMatcher(); 
  14.  
  • 切入點接口,定義用于獲取 ClassFilter、MethodMatcher 的兩個類,這兩個接口獲取都是切點表達式提供的內容。

cn.bugstack.springframework.aop.ClassFilter

  1. public interface ClassFilter { 
  2.  
  3.     /** 
  4.      * Should the pointcut apply to the given interface or target class? 
  5.      * @param clazz the candidate target class 
  6.      * @return whether the advice should apply to the given target class 
  7.      */ 
  8.     boolean matches(Class<?> clazz); 
  9.  
  • 定義類匹配類,用于切點找到給定的接口和目標類。

cn.bugstack.springframework.aop.MethodMatcher

  1. public interface MethodMatcher { 
  2.  
  3.     /** 
  4.      * Perform static checking whether the given method matches. If this 
  5.      * @return whether or not this method matches statically 
  6.      */ 
  7.     boolean matches(Method method, Class<?> targetClass); 
  8.      
  • 方法匹配,找到表達式范圍內匹配下的目標類和方法。在上文的案例中有所體現:methodMatcher.matches(method, targetObj.getClass())

實現切點表達式類

  1. public class AspectJExpressionPointcut implements Pointcut, ClassFilter, MethodMatcher { 
  2.  
  3.     private static final Set<PointcutPrimitive> SUPPORTED_PRIMITIVES = new HashSet<PointcutPrimitive>(); 
  4.  
  5.     static { 
  6.         SUPPORTED_PRIMITIVES.add(PointcutPrimitive.EXECUTION); 
  7.     } 
  8.  
  9.     private final PointcutExpression pointcutExpression; 
  10.  
  11.     public AspectJExpressionPointcut(String expression) { 
  12.         PointcutParser pointcutParser = PointcutParser.getPointcutParserSupportingSpecifiedPrimitivesAndUsingSpecifiedClassLoaderForResolution(SUPPORTED_PRIMITIVES, this.getClass().getClassLoader()); 
  13.         pointcutExpression = pointcutParser.parsePointcutExpression(expression); 
  14.     } 
  15.  
  16.     @Override 
  17.     public boolean matches(Class<?> clazz) { 
  18.         return pointcutExpression.couldMatchJoinPointsInType(clazz); 
  19.     } 
  20.  
  21.     @Override 
  22.     public boolean matches(Method method, Class<?> targetClass) { 
  23.         return pointcutExpression.matchesMethodExecution(method).alwaysMatches(); 
  24.     } 
  25.  
  26.     @Override 
  27.     public ClassFilter getClassFilter() { 
  28.         return this; 
  29.     } 
  30.  
  31.     @Override 
  32.     public MethodMatcher getMethodMatcher() { 
  33.         return this; 
  34.     } 
  35.  
  • 切點表達式實現了 Pointcut、ClassFilter、MethodMatcher,三個接口定義方法,同時這個類主要是對 aspectj 包提供的表達式校驗方法使用。
  • 匹配 matches:pointcutExpression.couldMatchJoinPointsInType(clazz)、pointcutExpression.matchesMethodExecution(method).alwaysMatches(),這部分內容可以單獨測試驗證。

匹配驗證

  1. @Test 
  2. public void test_aop() throws NoSuchMethodException { 
  3.     AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut("execution(* cn.bugstack.springframework.test.bean.UserService.*(..))"); 
  4.     Class<UserService> clazz = UserService.class; 
  5.     Method method = clazz.getDeclaredMethod("queryUserInfo");    
  6.  
  7.     System.out.println(pointcut.matches(clazz)); 
  8.     System.out.println(pointcut.matches(method, clazz));           
  9.      
  10.     // truetrue 

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

4. 包裝切面通知信息

cn.bugstack.springframework.aop.AdvisedSupport

  1. public class AdvisedSupport { 
  2.  
  3.     // 被代理的目標對象 
  4.     private TargetSource targetSource; 
  5.     // 方法攔截器 
  6.     private MethodInterceptor methodInterceptor; 
  7.     // 方法匹配器(檢查目標方法是否符合通知條件) 
  8.     private MethodMatcher methodMatcher; 
  9.      
  10.     // ...get/set 
  • AdvisedSupport,主要是用于把代理、攔截、匹配的各項屬性包裝到一個類中,方便在 Proxy 實現類進行使用。這和你的業務開發中包裝入參是一個道理
  • TargetSource,是一個目標對象,在目標對象類中提供 Object 入參屬性,以及獲取目標類 TargetClass 信息。
  • MethodInterceptor,是一個具體攔截方法實現類,由用戶自己實現 MethodInterceptor#invoke 方法,做具體的處理。像我們本文的案例中是做方法監控處理
  • MethodMatcher,是一個匹配方法的操作,這個對象由 AspectJExpressionPointcut 提供服務。

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

定義接口

cn.bugstack.springframework.aop.framework

  1. public interface AopProxy { 
  2.  
  3.     Object getProxy(); 
  4.  
  • 定義一個標準接口,用于獲取代理類。因為具體實現代理的方式可以有 JDK 方式,也可以是 Cglib 方式,所以定義接口會更加方便管理實現類。

cn.bugstack.springframework.aop.framework.JdkDynamicAopProxy

  1. public class JdkDynamicAopProxy implements AopProxy, InvocationHandler { 
  2.  
  3.     private final AdvisedSupport advised; 
  4.  
  5.     public JdkDynamicAopProxy(AdvisedSupport advised) { 
  6.         this.advised = advised; 
  7.     } 
  8.  
  9.     @Override 
  10.     public Object getProxy() { 
  11.         return Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), advised.getTargetSource().getTargetClass(), this); 
  12.     } 
  13.  
  14.     @Override 
  15.     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 
  16.         if (advised.getMethodMatcher().matches(method, advised.getTargetSource().getTarget().getClass())) { 
  17.             MethodInterceptor methodInterceptor = advised.getMethodInterceptor(); 
  18.             return methodInterceptor.invoke(new ReflectiveMethodInvocation(advised.getTargetSource().getTarget(), method, args)); 
  19.         } 
  20.         return method.invoke(advised.getTargetSource().getTarget(), args); 
  21.     } 
  22.  
  • 基于 JDK 實現的代理類,需要實現接口 AopProxy、InvocationHandler,這樣就可以把代理對象 getProxy 和反射調用方法 invoke 分開處理了。
  • getProxy 方法中的是代理一個對象的操作,需要提供入參 ClassLoader、AdvisedSupport、和當前這個類 this,因為這個類提供了 invoke 方法。
  • invoke 方法中主要處理匹配的方法后,使用用戶自己提供的方法攔截實現,做反射調用 methodInterceptor.invoke 。
  • 這里還有一個 ReflectiveMethodInvocation,其他它就是一個入參的包裝信息,提供了入參對象:目標對象、方法、入參。

cn.bugstack.springframework.aop.framework.Cglib2AopProxy

  1. public class Cglib2AopProxy implements AopProxy { 
  2.  
  3.     private final AdvisedSupport advised; 
  4.  
  5.     public Cglib2AopProxy(AdvisedSupport advised) { 
  6.         this.advised = advised; 
  7.     } 
  8.  
  9.     @Override 
  10.     public Object getProxy() { 
  11.         Enhancer enhancer = new Enhancer(); 
  12.         enhancer.setSuperclass(advised.getTargetSource().getTarget().getClass()); 
  13.         enhancer.setInterfaces(advised.getTargetSource().getTargetClass()); 
  14.         enhancer.setCallback(new DynamicAdvisedInterceptor(advised)); 
  15.         return enhancer.create(); 
  16.     } 
  17.  
  18.     private static class DynamicAdvisedInterceptor implements MethodInterceptor { 
  19.  
  20.         @Override 
  21.         public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { 
  22.             CglibMethodInvocation methodInvocation = new CglibMethodInvocation(advised.getTargetSource().getTarget(), method, objects, methodProxy); 
  23.             if (advised.getMethodMatcher().matches(method, advised.getTargetSource().getTarget().getClass())) { 
  24.                 return advised.getMethodInterceptor().invoke(methodInvocation); 
  25.             } 
  26.             return methodInvocation.proceed(); 
  27.         } 
  28.     } 
  29.  
  30.     private static class CglibMethodInvocation extends ReflectiveMethodInvocation { 
  31.  
  32.         @Override 
  33.         public Object proceed() throws Throwable { 
  34.             return this.methodProxy.invoke(this.target, this.arguments); 
  35.         } 
  36.  
  37.     } 
  38.  

基于 Cglib 使用 Enhancer 代理的類可以在運行期間為接口使用底層 ASM 字節碼增強技術處理對象的代理對象生成,因此被代理類不需要實現任何接口。

關于擴展進去的用戶攔截方法,主要是在 Enhancer#setCallback 中處理,用戶自己的新增的攔截處理。這里可以看到 DynamicAdvisedInterceptor#intercept 匹配方法后做了相應的反射操作。

五、測試

1. 事先準備

  1. public class UserService implements IUserService { 
  2.  
  3.     public String queryUserInfo() { 
  4.         try { 
  5.             Thread.sleep(new Random(1).nextInt(100)); 
  6.         } catch (InterruptedException e) { 
  7.             e.printStackTrace(); 
  8.         } 
  9.         return "小傅哥,100001,深圳"
  10.     } 
  11.  
  12.     public String register(String userName) { 
  13.         try { 
  14.             Thread.sleep(new Random(1).nextInt(100)); 
  15.         } catch (InterruptedException e) { 
  16.             e.printStackTrace(); 
  17.         } 
  18.         return "注冊用戶:" + userName + " success!"
  19.     } 
  20.  
  • 在 UserService 中提供了2個不同方法,另外你還可以增加新的類來加入測試。后面我們的測試過程,會給這個兩個方法添加我們的攔截處理,打印方法執行耗時。

2. 自定義攔截方法

  1. public class UserServiceInterceptor implements MethodInterceptor { 
  2.  
  3.     @Override 
  4.     public Object invoke(MethodInvocation invocation) throws Throwable { 
  5.         long start = System.currentTimeMillis(); 
  6.         try { 
  7.             return invocation.proceed(); 
  8.         } finally { 
  9.             System.out.println("監控 - Begin By AOP"); 
  10.             System.out.println("方法名稱:" + invocation.getMethod()); 
  11.             System.out.println("方法耗時:" + (System.currentTimeMillis() - start) + "ms"); 
  12.             System.out.println("監控 - End\r\n"); 
  13.         } 
  14.     } 
  15.  
  • 用戶自定義的攔截方法需要實現 MethodInterceptor 接口的 invoke 方法,使用方式與 Spring AOP 非常相似,也是包裝 invocation.proceed() 放行,并在 finally 中添加監控信息。

3. 單元測試

  1. @Test 
  2. public void test_dynamic() { 
  3.     // 目標對象 
  4.     IUserService userService = new UserService();      
  5.  
  6.     // 組裝代理信息 
  7.     AdvisedSupport advisedSupport = new AdvisedSupport(); 
  8.     advisedSupport.setTargetSource(new TargetSource(userService)); 
  9.     advisedSupport.setMethodInterceptor(new UserServiceInterceptor()); 
  10.     advisedSupport.setMethodMatcher(new AspectJExpressionPointcut("execution(* cn.bugstack.springframework.test.bean.IUserService.*(..))")); 
  11.      
  12.     // 代理對象(JdkDynamicAopProxy) 
  13.     IUserService proxy_jdk = (IUserService) new JdkDynamicAopProxy(advisedSupport).getProxy(); 
  14.     // 測試調用 
  15.     System.out.println("測試結果:" + proxy_jdk.queryUserInfo()); 
  16.      
  17.     // 代理對象(Cglib2AopProxy) 
  18.     IUserService proxy_cglib = (IUserService) new Cglib2AopProxy(advisedSupport).getProxy(); 
  19.     // 測試調用 
  20.     System.out.println("測試結果:" + proxy_cglib.register("花花")); 
  • 整個案例測試了 AOP 在于 Spring 結合前的核心代碼,包括什么是目標對象、怎么組裝代理信息、如何調用代理對象。
  • AdvisedSupport,包裝了目標對象、用戶自己實現的攔截方法以及方法匹配表達式。
  • 之后就是分別調用 JdkDynamicAopProxy、Cglib2AopProxy,兩個不同方式實現的代理類,看看是否可以成功攔截方法

測試結果

  1. 監控 - Begin By AOP 
  2. 方法名稱:public abstract java.lang.String cn.bugstack.springframework.test.bean.IUserService.queryUserInfo() 
  3. 方法耗時:86ms 
  4. 監控 - End 
  5.  
  6. 測試結果:小傅哥,100001,深圳 
  7. 監控 - Begin By AOP 
  8. 方法名稱:public java.lang.String cn.bugstack.springframework.test.bean.UserService.register(java.lang.String) 
  9. 方法耗時:97ms 
  10. 監控 - End 
  11.  
  12. 測試結果:注冊用戶:花花 success! 
  13.  
  14. Process finished with exit code 0 

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

六、總結

從本文對 Proxy#newProxyInstance、MethodInterceptor#invoke,的使用驗證切面核心原理以及再把功能拆解到 Spring 框架實現中,可以看到一個貌似復雜的技術其實核心內容往往沒有太多,但因為需要為了滿足后續更多的擴展就需要進行職責解耦和包裝,通過這樣設計模式的使用,以此讓調用方能更加簡化,自身也可以不斷按需擴展。

AOP 的功能實現目前還沒有與 Spring 結合,只是對切面技術的一個具體實現,你可以先學習到如何處理代理對象、過濾方法、攔截方法,以及使用 Cglib 和 JDK 代理的區別,其實這與的技術不只是在 Spring 框架中有所體現,在其他各類需要減少人工硬編碼的場景下,都會用到。比如RPC、Mybatis、MQ、分布式任務

 

一些核心技術的使用上,都是具有很強的關聯性的,它們也不是孤立存在的。而這個能把整個技術棧串聯起來的過程,需要你來大量的學習、積累、由點到面的鋪設,才能在一個知識點的學習拓展到一個知識面和知識體系的建設。

 

責任編輯:武曉燕 來源: bugstack蟲洞棧
相關推薦

2023-12-06 08:23:44

代理模式設計模式

2022-09-01 10:40:29

SpringAOPJDK

2022-04-12 08:09:22

Nodejs前端面試題

2009-06-22 15:10:00

java 編程AOP

2020-08-27 08:54:02

腳本架構師Linux

2025-02-27 00:32:35

2012-12-27 10:50:42

2024-01-04 07:42:44

JavaCGLIBJDK

2022-02-22 22:44:46

接口源碼對象

2024-09-05 09:35:58

CGLIBSpring動態代理

2019-11-29 16:21:22

Spring框架集成

2012-09-28 10:20:14

IBMdw

2015-03-25 11:12:35

iOS開發

2020-08-18 18:11:54

架構師腳本語言

2010-01-27 17:38:58

Windows Emb

2009-11-18 13:11:29

PHP核心

2012-09-27 09:47:43

SpringJava面向對象

2015-06-10 11:15:42

iOS開發

2020-10-25 17:11:29

JDK代理監控

2010-09-22 15:31:05

OracleSPARCSolaris
點贊
收藏

51CTO技術棧公眾號

99久久综合狠狠综合久久止 | 精品久久久久久无码中文野结衣| av片免费播放| 国产亚洲欧洲| 最近2019年日本中文免费字幕| 网站在线你懂的| 人成在线免费网站| 国产欧美一区视频| 国产精品对白刺激久久久| av黄色在线播放| 亚洲欧美亚洲| 中文国产成人精品久久一| 麻豆精品国产传媒| 日本精品裸体写真集在线观看| 亚洲精品五月天| 色姑娘综合av| 天堂网在线资源| 精品一区二区三区av| 97视频免费在线看| 欧美h片在线观看| 亚洲丝袜啪啪| 精品日韩99亚洲| 超碰av在线免费观看| 波多野结依一区| 国产精品久久精品日日| 精品国产免费人成电影在线观... 精品国产免费久久久久久尖叫 | 亚洲女人在线观看| 欧洲成人一区| 都市激情亚洲色图| 成人午夜免费在线视频| 午夜伦理在线| 国产丝袜美腿一区二区三区| 好吊色欧美一区二区三区| 国产伦精品一区二区三区视频痴汉| 午夜亚洲性色福利视频| 韩剧1988免费观看全集| 午夜精品一区二区三级视频| 精品久久视频| 亚洲欧美日韩爽爽影院| 亚洲精品乱码久久久久久久| 91精品日本| 欧美一区二区黄| 亚洲男人天堂av在线| 成人看片网页| 欧美性色黄大片| 蜜臀av午夜一区二区三区 | 国产精品高潮在线| av中文在线播放| 在线亚洲免费| 91精品国产乱码久久久久久久久 | 色天使久久综合网天天| 日日摸日日碰夜夜爽无码| 久久免费电影| 亚洲在线视频网站| 日本中文字幕亚洲| 3344国产永久在线观看视频| 亚洲h在线观看| 日韩欧美精品免费| 国产粉嫩在线观看| 欧美视频一二三| 麻豆av免费在线| 久久99久久99精品免观看软件| 色婷婷香蕉在线一区二区| 日本黄网站免费| 成人国产精品入口免费视频| 欧美日韩电影一区| 日韩av片免费观看| 91蜜桃臀久久一区二区| 亚洲韩国欧洲国产日产av | 亚洲欧洲日韩av| 香蕉视频免费版| av手机免费在线观看| 偷拍与自拍一区| 欧美日韩第二页| 91亚洲精品| 日韩女优视频免费观看| 亚洲女则毛耸耸bbw| 欧美日韩精品一区二区三区在线观看| 精品丝袜一区二区三区| 少妇的滋味中文字幕bd| 91精品国产成人观看| 欧美精品18videos性欧美| 99精品视频99| 免费不卡在线视频| av一本久道久久波多野结衣| 性xxxx视频| 中文字幕欧美日韩一区| 中文字幕在线中文| 在线毛片观看| 欧美精品免费视频| 手机免费看av片| 精品视频97| 欧美俄罗斯性视频| 精品人妻一区二区三区潮喷在线 | 日本免费在线视频| 亚洲在线成人精品| 亚洲一区在线不卡| 哺乳一区二区三区中文视频 | 日本在线观看大片免费视频| 狠狠综合久久av一区二区小说| 99热手机在线| 精品资源在线| 久久国内精品一国内精品| 国产污视频在线看| 看电视剧不卡顿的网站| 激情五月综合色婷婷一区二区| 高清av电影在线观看| 亚洲一二三专区| 超碰超碰在线观看| 免费萌白酱国产一区二区三区| 日韩在线观看网站| 五月天婷婷激情| 国产精品亚洲一区二区三区在线| 久久久久久久久四区三区| 久久99精品久久久久久野外| 日韩欧美国产网站| 国产国语老龄妇女a片| 欧美在线电影| 欧美有码在线观看视频| 不卡的日韩av| 中文字幕中文字幕在线一区| av片中文字幕| 免费成人三级| 欧美区在线播放| 国产精品久久久久久免费播放| 久久久亚洲精品石原莉奈| 777av视频| 久久综合给合| 久久久精品国产一区二区| 中文字幕 人妻熟女| 99麻豆久久久国产精品免费优播| 桥本有菜av在线| 成人午夜亚洲| 中文字幕日韩在线观看| 成人免费毛片男人用品| 99国产精品99久久久久久| 女人被男人躁得好爽免费视频 | 国内成+人亚洲| 欧美人与性动交α欧美精品济南到| 欧美日韩在线直播| 18精品爽国产三级网站| 日韩av在线免费观看不卡| 欧美二区三区在线| 日韩成人动漫| 在线精品高清中文字幕| 在线观看国产区| 亚洲国产精品精华液ab| 少妇网站在线观看| 99成人在线视频| 成人网中文字幕| 二区三区四区高清视频在线观看| 欧美精品自拍偷拍| 岛国毛片在线观看| 国产福利一区在线| 国产www免费| 国产精品17p| 欧美在线观看网址综合| 毛片网站在线观看| 精品婷婷伊人一区三区三| 影音先锋男人在线| 捆绑紧缚一区二区三区视频| 中文字幕第一页亚洲| 午夜视频一区二区在线观看| 色综合天天狠天天透天天伊人| 国精品人妻无码一区二区三区喝尿| 亚洲午夜电影在线观看| 精品无码国产一区二区三区51安| 亚洲免费影视| 亚洲欧洲日韩精品| 久久gogo国模啪啪裸体| 久久久久久97| 人人九九精品| 欧美日韩免费一区二区三区| 波多野结衣家庭教师| 国产91高潮流白浆在线麻豆| 成熟了的熟妇毛茸茸| 欧美日韩激情| 91精品在线一区| 九九色在线视频| 亚洲精品一区中文字幕乱码| 久久久久久久久久一级| 亚洲黄色小视频| v8888av| 久久精品国产一区二区| 亚洲色婷婷久久精品av蜜桃| 日韩精品丝袜美腿| 国产自产女人91一区在线观看| 羞羞的视频在线看| 亚洲美女性视频| 国产精品久久久久久久久毛片 | 91精品国产精品| 天堂а√在线资源在线| 精品国产91洋老外米糕| 免费在线不卡av| 亚洲二区在线视频| 女人十八毛片嫩草av| 粉嫩久久99精品久久久久久夜| 国产二区视频在线播放| 久久久五月天| 欧美日韩日本网| 亚洲高清在线一区| 国产精品久久久久久久久久久不卡| caopon在线免费视频| 亚洲男人天堂视频| 亚洲a视频在线| 欧美综合天天夜夜久久| 国产污片在线观看| 亚洲男帅同性gay1069| 精品人妻互换一区二区三区| 国产成人精品影视| 男女视频在线看| 一区二区精品| 日韩成人手机在线| 91久久电影| 视频在线精品一区| 三级视频在线| 精品成在人线av无码免费看| 成人mm视频在线观看| 久久久久久久色| 黄色av电影在线播放| 国产亚洲人成网站在线观看| 日本韩国在线观看| 91精品国产综合久久福利软件| 精品视频一二三区| 狠狠色香婷婷久久亚洲精品| 欧美日韩中文视频| 亚洲欧美日韩成人高清在线一区| 丝袜美腿中文字幕| 不卡一区二区三区四区| 性生交大片免费看l| 韩国视频一区二区| 欧美特黄aaa| 蜜臀精品一区二区三区在线观看 | 成人毛片免费| 国产精品黄页免费高清在线观看| 日本不卡网站| 8090成年在线看片午夜| 阿v视频在线观看| 高清欧美性猛交| segui88久久综合| 欧美国产日韩二区| 羞羞的网站在线观看| 九九热这里只有精品免费看| 国产二区三区在线| 欧美另类交人妖| 日本色护士高潮视频在线观看| 麻豆国产va免费精品高清在线| 快射av在线播放一区| 久久精品一偷一偷国产| 国产在线更新| 九九久久久久99精品| 一区二区三区伦理| 欧美—级高清免费播放| 污视频免费在线观看| 欧美激情精品久久久久久黑人| dj大片免费在线观看| 九九九久久久久久| 好看的中文字幕在线播放| 国模gogo一区二区大胆私拍| av不卡高清| 日本在线观看天堂男亚洲| 激情开心成人网| 国产欧美日韩亚洲精品| 国产亚洲高清一区| 国产精品亚洲综合| 性人久久久久| 亚洲欧洲精品在线| 国产精品hd| 精品久久久久久久久久中文字幕| 国产日韩欧美一区在线| 青青在线免费观看视频| 久久精品国产秦先生| 超级砰砰砰97免费观看最新一期| 成人国产免费视频| 久久国产柳州莫菁门| 国产精品国产三级国产aⅴ无密码 国产精品国产三级国产aⅴ原创 | 国产午夜精品在线观看| 女同久久另类69精品国产| 一区二区三区在线看| 欧美啪啪小视频| 欧美裸体bbwbbwbbw| 亚洲毛片在线播放| 国产亚洲福利一区| 色婷婷av在线| 日韩美女在线观看| 国产午夜精品一区在线观看| 久久久久久久久一区| 忘忧草精品久久久久久久高清| 亚洲国产精品无码av| 国产精品日韩| www.成年人| 久久无码av三级| 国产在线一卡二卡| 日韩欧美在线视频观看| 99久久精品日本一区二区免费| 精品1区2区在线观看| av在线播放网| 高清一区二区三区日本久| 草民电影神马电影一区二区| 国产精品一区二区三区免费观看| 日韩大片在线| 国产深夜男女无套内射| 国产精品一级二级三级| 色一情一交一乱一区二区三区| 亚洲一二三四在线| 国产美女www爽爽爽视频| 日韩精品免费观看| 天堂av在线电影| 国产精品视频一区二区三区四| 第一区第二区在线| 潘金莲一级淫片aaaaa免费看| 久久黄色影院| 亚洲 欧美 日韩在线| 亚洲视频一二三| 一级黄色在线观看| 亚洲精品99久久久久中文字幕| 巨大荫蒂视频欧美另类大| 日韩美女视频在线观看| 精品国产一区二区三区成人影院| 日韩最新中文字幕| 免费观看日韩电影| a级大片在线观看| 精品高清美女精品国产区| 性生交大片免费看女人按摩| 日韩视频亚洲视频| 成人黄色免费网站| 日韩视频在线观看国产| 亚洲欧美成人综合| 亚洲av无码一区二区三区网址| 亚洲一区二区在线观看视频 | 99热播精品免费| 欧洲精品在线一区| 麻豆成人精品| 亚洲黄色在线网站| 精品国产户外野外| 日韩一级免费视频| 国内精品伊人久久| silk一区二区三区精品视频| 国风产精品一区二区| 国产一区二区三区四区五区美女| 国产麻豆a毛片| 欧美另类久久久品| 午夜视频在线看| 91美女福利视频高清| 五月久久久综合一区二区小说| 黄色一级片免费的| 一区在线观看免费| 99re只有精品| 欧美激情日韩图片| 红杏视频成人| 无码人妻精品一区二区三区在线| 91女厕偷拍女厕偷拍高清| 欧美 日韩 精品| 伊人久久免费视频| 先锋影音一区二区| 91麻豆天美传媒在线| 丁香一区二区三区| 久久夜靖品2区| 亚洲少妇激情视频| 国产极品一区| 日本黄网站色大片免费观看| 福利视频网站一区二区三区| 国产精品不卡av| 亚洲精品一区二区网址| 91在线成人| 老司机激情视频| 91网上在线视频| 一区二区自拍偷拍| 欧美精品免费看| 啪啪激情综合网| 免费看污污网站| 亚洲欧美日韩电影| 亚州av在线播放| 国产精品视频网站| 伊人久久大香线蕉综合热线| mm131美女视频| 欧美精品色综合| 9999精品成人免费毛片在线看| 久久99精品久久久久久秒播放器| 久久亚洲视频| 日日骚一区二区三区| 日韩av中文字幕在线免费观看| 一区二区视频免费完整版观看| 日韩人妻精品一区二区三区| 97精品电影院| 国产又大又黑又粗| 91国内在线视频| 日韩精品一区二区三区免费观看| youjizz.com日本| 在线观看一区日韩| 国产精品—色呦呦| 亚洲国产欧美不卡在线观看| 丁香六月综合激情| 中文字幕人妻丝袜乱一区三区| 欧美国产激情18| 大片网站久久| 熟妇人妻久久中文字幕| 欧美一区二区在线播放| 亚洲成a人片| 欧美又粗又长又爽做受|