小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

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

 小傅哥 2021-12-13

作者:小傅哥
博客:https://

?

沉淀、分享、成長,讓自己和他人都能有所收獲!??

?

目錄

  • 一、前言

  • 二、目標

  • 三、方案

  • 四、實現(xiàn)

    • 1. 工程結(jié)構(gòu)

    • 2. 代理方法案例

    • 3. 切點表達式

    • 4. 包裝切面通知信息

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

  • 五、測試

    • 1. 事先準備

    • 2. 自定義攔截方法

    • 3. 單元測試

  • 六、總結(jié)

  • 七、系列推薦

一、前言

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

??怎么辦,知道你在互聯(lián)網(wǎng),不知道你在哪個大廠。知道你在加班,不知道你在和哪個產(chǎn)品爭辯。知道你在偷懶,不知道你要摸魚到幾點。知道你在搬磚,不知道你在蓋哪個豬圈。


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

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

二、目標

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

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

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

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

三、方案

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

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

四、實現(xiàn)

1. 工程結(jié)構(gòu)

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蟲洞?!梗貜?fù):Spring 專欄,獲取完整源碼

AOP 切點表達式和使用以及基于 JDK 和 CGLIB 的動態(tài)代理類關(guān)系,如圖 12-2

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

2. 代理方法案例

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

單元測試

@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("監(jiān)控 - Begin By AOP");
                        System.out.println("方法名稱:" + invocation.getMethod().getName());
                        System.out.println("方法耗時:" + (System.currentTimeMillis() - start) + "ms");
                        System.out.println("監(jiān)控 - End\r\n");
                    }
                };
                // 反射調(diào)用
                return methodInterceptor.invoke(new ReflectiveMethodInvocation(targetObj, method, args));
            }
            return method.invoke(targetObj, args);
        }
    });
    String result = proxy.queryUserInfo();
    System.out.println("測試結(jié)果:" + result);
}
  • 首先整個案例的目標是給一個 UserService 當成目標對象,對類中的所有方法進行攔截添加監(jiān)控信息打印處理。
  • 從案例中你可以看到有代理的實現(xiàn) Proxy.newProxyInstance,有方法的匹配 MethodMatcher,有反射的調(diào)用 invoke(Object proxy, Method method, Object[] args),也用用戶自己攔截方法后的操作。這樣一看其實和我們使用的 AOP 就非常類似了,只不過你在使用 AOP 的時候是框架已經(jīng)提供更好的功能,這里是把所有的核心過程給你展示出來了。

測試結(jié)果

監(jiān)控 - Begin By AOP
方法名稱:queryUserInfo
方法耗時:86ms
監(jiān)控 - End

測試結(jié)果:小傅哥,100001,深圳

Process finished with exit code 0
  • 從測試結(jié)果可以看到我們已經(jīng)對 UserService#queryUserInfo 方法進行了攔截監(jiān)控操作,其實后面我們實現(xiàn)的 AOP 就是現(xiàn)在體現(xiàn)出的結(jié)果,只不過我們需要把這部分測試的案例解耦為更具有擴展性的各個模塊實現(xiàn)。

拆解案例

圖 12-3
  • 拆解過程可以參考截圖 12-3,我們需要把代理對象拆解出來,因為它可以是 JDK 的實現(xiàn)也可以是 Cglib 的處理。
  • 方法匹配器操作其實已經(jīng)是一個單獨的實現(xiàn)類了,不過我們還需要把傳入的目標對象、方法匹配、攔截方法,都進行統(tǒng)一的包裝,方便外部調(diào)用時進行一個入?yún)⑼競鳌?/section>
  • 最后其實是 ReflectiveMethodInvocation 的使用,它目前已經(jīng)是實現(xiàn) MethodInvocation 接口的一個包裝后的類,參數(shù)信息包括:調(diào)用的對象、調(diào)用的方法、調(diào)用的入?yún)ⅰ?/section>

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 的兩個類,這兩個接口獲取都是切點表達式提供的內(nèi)容。

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);
    
}
  • 方法匹配,找到表達式范圍內(nèi)匹配下的目標類和方法。在上文的案例中有所體現(xiàn):methodMatcher.matches(method, targetObj.getClass())

實現(xiàn)切點表達式類

public class AspectJExpressionPointcut implements PointcutClassFilterMethodMatcher {

    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;
    }

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

匹配驗證

@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
}
  • 這里單獨提供出來一個匹配方法的驗證測試,可以看看你攔截的方法與對應(yīng)的對象是否匹配。

4. 包裝切面通知信息

cn.bugstack.springframework.aop.AdvisedSupport

public class AdvisedSupport {

    // 被代理的目標對象
    private TargetSource targetSource;
    // 方法攔截器
    private MethodInterceptor methodInterceptor;
    // 方法匹配器(檢查目標方法是否符合通知條件)
    private MethodMatcher methodMatcher;
    
    // ...get/set
}
  • AdvisedSupport,主要是用于把代理、攔截、匹配的各項屬性包裝到一個類中,方便在 Proxy 實現(xiàn)類進行使用。這和你的業(yè)務(wù)開發(fā)中包裝入?yún)⑹且粋€道理
  • TargetSource,是一個目標對象,在目標對象類中提供 Object 入?yún)傩?,以及獲取目標類 TargetClass 信息。
  • MethodInterceptor,是一個具體攔截方法實現(xiàn)類,由用戶自己實現(xiàn) MethodInterceptor#invoke 方法,做具體的處理。像我們本文的案例中是做方法監(jiān)控處理
  • MethodMatcher,是一個匹配方法的操作,這個對象由 AspectJExpressionPointcut 提供服務(wù)。

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

定義接口

cn.bugstack.springframework.aop.framework

public interface AopProxy {

    Object getProxy();

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

cn.bugstack.springframework.aop.framework.JdkDynamicAopProxy

public class JdkDynamicAopProxy implements AopProxyInvocationHandler {

    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 實現(xiàn)的代理類,需要實現(xiàn)接口 AopProxy、InvocationHandler,這樣就可以把代理對象 getProxy 和反射調(diào)用方法 invoke 分開處理了。
  • getProxy 方法中的是代理一個對象的操作,需要提供入?yún)?ClassLoader、AdvisedSupport、和當前這個類 this,因為這個類提供了 invoke 方法。
  • invoke 方法中主要處理匹配的方法后,使用用戶自己提供的方法攔截實現(xiàn),做反射調(diào)用 methodInterceptor.invoke 。
  • 這里還有一個 ReflectiveMethodInvocation,其他它就是一個入?yún)⒌陌b信息,提供了入?yún)ο螅耗繕藢ο?、方法、入?yún)ⅰ?/section>

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 字節(jié)碼增強技術(shù)處理對象的代理對象生成,因此被代理類不需要實現(xiàn)任何接口。
  • 關(guān)于擴展進去的用戶攔截方法,主要是在 Enhancer#setCallback 中處理,用戶自己的新增的攔截處理。這里可以看到 DynamicAdvisedInterceptor#intercept 匹配方法后做了相應(yīng)的反射操作。

五、測試

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個不同方法,另外你還可以增加新的類來加入測試。后面我們的測試過程,會給這個兩個方法添加我們的攔截處理,打印方法執(zhí)行耗時。

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("監(jiān)控 - Begin By AOP");
            System.out.println("方法名稱:" + invocation.getMethod());
            System.out.println("方法耗時:" + (System.currentTimeMillis() - start) + "ms");
            System.out.println("監(jiān)控 - End\r\n");
        }
    }

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

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();
    // 測試調(diào)用
    System.out.println("測試結(jié)果:" + proxy_jdk.queryUserInfo());
    
    // 代理對象(Cglib2AopProxy)
    IUserService proxy_cglib = (IUserService) new Cglib2AopProxy(advisedSupport).getProxy();
    // 測試調(diào)用
    System.out.println("測試結(jié)果:" + proxy_cglib.register("花花"));
}
  • 整個案例測試了 AOP 在于 Spring 結(jié)合前的核心代碼,包括什么是目標對象、怎么組裝代理信息、如何調(diào)用代理對象。
  • AdvisedSupport,包裝了目標對象、用戶自己實現(xiàn)的攔截方法以及方法匹配表達式。
  • 之后就是分別調(diào)用 JdkDynamicAopProxy、Cglib2AopProxy,兩個不同方式實現(xiàn)的代理類,看看是否可以成功攔截方法

測試結(jié)果

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

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

測試結(jié)果:注冊用戶:花花 success!

Process finished with exit code 0
  • 如 AOP 功能定義一樣,我們可以通過這樣的代理方式、方法匹配和攔截后,在對應(yīng)的目標方法下,做了攔截操作進行監(jiān)控信息打印。

六、總結(jié)

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

七、系列推薦

    轉(zhuǎn)藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多