求求你,下次面試別再問我什麼是AOP了!

Aop相關閱讀

閱讀本文之前,需要先掌握下面3篇文章內容,不然會比較喫力。

  1. Spring系列第15篇:代理詳解(java動態代理&CGLIB代理)

  2. Spring系列第30篇:jdk動態代理和cglib代理

  3. Spring系列第31篇:Aop概念詳解

  4. Spring系列第32篇:AOP核心源碼、原理詳解

本文繼續Aop。

AOP創建代理的方式主要分爲2大類

手動方式

也稱爲手動的方式,需要通過硬編碼一個個創建代理。

自動化的方式

也稱爲批量的方式,批量的方式用在spring環境中,通過bean後置處理器來對符合條件的bean創建代理

手動的方式基本上是採用硬編碼的方式,相對來說更靈活一些,可以脫離spring環境使用,而自動化的方式主要用在spring環境中,和spring集成起來更容易一些,更強大一些。

AOP創建代理相關的類

左邊的ProxyCreatorSupport下面的都是手動的方式,有3個類。

右邊的AbstractAutoProxyCreator下面掛的都是自動創建代理的方式,主要有5個實現類。

手動3種方式

ProxyFactory方式

這種是硬編碼的方式,可以脫離spring直接使用,用到的比較多,自動化方式創建代理中都是依靠ProxyFactory來實現的,所以這種方式的原理大家一定要了解,上篇文章中已經有介紹過了,不清楚的可以去看一下:Spring系列第32篇:AOP核心源碼、原理詳解

AspectJProxyFactory方式

AspectJ是一個面向切面的框架,是目前最好用,最方便的AOP框架,Spring將其集成進來了,通過Aspectj提供的一些功能實現aop代理非常方便,下篇文章將詳解。

ProxyFactoryBean方式

Spring環境中給指定的bean創建代理的一種方式,本文主要介紹這個。

ProxyFactoryBean

這個類實現了一個接口FactoryBeanFactoryBean不清楚的可以看一下:Spring系列第5篇:創建bean實例這些方式你們都知道?

ProxyFactoryBean就是通過FactoryBean的方式來給指定的bean創建一個代理對象。

創建代理,有3個信息比較關鍵:

  1. 需要增強的功能,這個放在通知(Advice)中實現

  2. 目標對象(target):表示你需要給哪個對象進行增強

  3. 代理對象(proxy):將增強的功能和目標對象組合在一起,然後形成的一個代理對象,通過代理對象來訪問目標對象,起到對目標對象增強的效果。

使用ProxyFactoryBean也是圍繞着3部分來的,ProxyFactoryBean使用的步驟:

1.創建ProxyFactoryBean對象
2.通過ProxyFactoryBean.setTargetName設置目標對象的bean名稱,目標對象是spring容器中的一個bean
3.通過ProxyFactoryBean。setInterceptorNames添加需要增強的通知
4.將ProxyFactoryBean註冊到Spring容器,假設名稱爲proxyBean
5.從Spring查找名稱爲proxyBean的bean,這個bean就是生成好的代理對象

上案例。

來個類Service1

package com.javacode2018.aop.demo8.test1;

public class Service1 {

    public void m1() {
        System.out.println("我是 m1 方法");
    }

    public void m2() {
        System.out.println("我是 m2 方法");
    }
}

需求

在spring容器中註冊上面這個類的bean,名稱爲service1,通過代理的方式來對這個bean進行增強,來2個通知

一個前置通知:在調用service1中的任意方法之前,輸出一條信息:準備調用xxxx方法

一個環繞通知:複製統計所有方法的耗時。

下面是代碼的實現

package com.javacode2018.aop.demo8.test1;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.MethodBeforeAdvice;
import org.springframework.aop.framework.ProxyFactoryBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.lang.Nullable;

import java.lang.reflect.Method;

@Configuration
public class MainConfig1 {
    //註冊目標對象
    @Bean
    public Service1 service1() {
        return new Service1();
    }

    //註冊一個前置通知
    @Bean
    public MethodBeforeAdvice beforeAdvice() {
        MethodBeforeAdvice advice = new MethodBeforeAdvice() {
            @Override
            public void before(Method method, Object[] args, @Nullable Object target) throws Throwable {
                System.out.println("準備調用:" + method);
            }
        };
        return advice;
    }

    //註冊一個後置通知
    @Bean
    public MethodInterceptor costTimeInterceptor() {
        MethodInterceptor methodInterceptor = new MethodInterceptor() {
            @Override
            public Object invoke(MethodInvocation invocation) throws Throwable {
                long starTime = System.nanoTime();
                Object result = invocation.proceed();
                long endTime = System.nanoTime();
                System.out.println(invocation.getMethod() + ",耗時(納秒):" + (endTime - starTime));
                return result;
            }
        };
        return methodInterceptor;
    }

    //註冊ProxyFactoryBean
    @Bean
    public ProxyFactoryBean service1Proxy() {
        //1.創建ProxyFactoryBean
        ProxyFactoryBean proxyFactoryBean = new ProxyFactoryBean();
        //2.設置目標對象的bean名稱
        proxyFactoryBean.setTargetName("service1");
        //3.設置攔截器的bean名稱列表,此處2個(advice1和advice2)
        proxyFactoryBean.setInterceptorNames("beforeAdvice", "costTimeInterceptor");
        return proxyFactoryBean;
    }
}

下面啓動spring容器,並獲取代理對象

AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MainConfig1.class);
//獲取代理對象,代理對象bean的名稱爲註冊ProxyFactoryBean的名稱,即:service1Proxy
Service1 bean = context.getBean("service1Proxy", Service1.class);
System.out.println("----------------------");
//調用代理的方法
bean.m1();
System.out.println("----------------------");
//調用代理的方法
bean.m2();

運行輸出

----------------------
準備調用:public void com.javacode2018.aop.demo8.test1.Service1.m1()
我是 m1 方法
public void com.javacode2018.aop.demo8.test1.Service1.m1(),耗時(納秒):8680400
----------------------
準備調用:public void com.javacode2018.aop.demo8.test1.Service1.m2()
我是 m2 方法
public void com.javacode2018.aop.demo8.test1.Service1.m2(),耗時(納秒):82400

從輸出中可以看到,目標對象service1已經被增強了。

ProxyFactoryBean中的interceptorNames

interceptorNames用來指定攔截器的bean名稱列表,常用的2種方式。

  • 批量的方式

  • 非批量的方式

批量的方式

使用方法

proxyFactoryBean.setInterceptorNames("需要匹配的bean名稱*");

需要匹配的bean名稱後面跟上一個*,可以用來批量的匹配,如:interceptor*,此時spring會從容器中找到下面2中類型的所有bean,bean名稱以interceptor開頭的將作爲增強器

org.springframework.aop.Advisor
org.aopalliance.intercept.Interceptor

這個地方使用的時候需要注意,批量的方式註冊的時候,如果增強器的類型不是上面2種類型的,比如下面3種類型通知,我們需要將其包裝爲Advisor纔可以,而MethodInterceptorInterceptor類型的,可以不用包裝爲Advisor類型的。

MethodBeforeAdvice(方法前置通知)
AfterReturningAdvice(方法後置通知)
ThrowsAdvice(異常通知)

下面來個案例感受一下。

案例

下面批量註冊2個增強器。

package com.javacode2018.aop.demo8.test2;

import com.javacode2018.aop.demo8.test1.Service1;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.Advisor;
import org.springframework.aop.MethodBeforeAdvice;
import org.springframework.aop.framework.ProxyFactoryBean;
import org.springframework.aop.support.DefaultPointcutAdvisor;
import org.springframework.context.annotation.Bean;
import org.springframework.lang.Nullable;

import java.lang.reflect.Method;

public class MainConfig2 {
    //註冊目標對象
    @Bean
    public Service1 service1() {
        return new Service1();
    }

    //定義一個增強器:interceptor1,內部是一個前置通知,需要將其包裝爲Advisor類型的
    @Bean
    public Advisor interceptor1() {
        MethodBeforeAdvice advice = new MethodBeforeAdvice() {
            @Override
            public void before(Method method, Object[] args, @Nullable Object target) throws Throwable {
                System.out.println("準備調用:" + method);
            }
        };
        DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor();
        advisor.setAdvice(advice);
        return advisor;
    }

    //定義一個增強器:interceptor2
    @Bean
    public MethodInterceptor interceptor2() {
        MethodInterceptor methodInterceptor = new MethodInterceptor() {
            @Override
            public Object invoke(MethodInvocation invocation) throws Throwable {
                long starTime = System.nanoTime();
                Object result = invocation.proceed();
                long endTime = System.nanoTime();
                System.out.println(invocation.getMethod() + ",耗時(納秒):" + (endTime - starTime));
                return result;
            }
        };
        return methodInterceptor;
    }

    //註冊ProxyFactoryBean
    @Bean
    public ProxyFactoryBean service1Proxy() {
        //1.創建ProxyFactoryBean
        ProxyFactoryBean proxyFactoryBean = new ProxyFactoryBean();
        //2.設置目標對象的bean名稱
        proxyFactoryBean.setTargetName("service1");
        //3.設置攔截器的bean名稱列表,此處批量註冊
        proxyFactoryBean.setInterceptorNames("interceptor*"); //@1
        return proxyFactoryBean;
    }
}

上面定義了2個增強器:

interceptor1:前置通知,包裝爲Advisor類型了

interceptor2:環繞通知,MethodInterceptor類型的

測試代碼

@Test
public void test2() {
    AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MainConfig2.class);
    //獲取代理對象,代理對象bean的名稱爲註冊ProxyFactoryBean的名稱,即:service1Proxy
    Service1 bean = context.getBean("service1Proxy", Service1.class);
    System.out.println("----------------------");
    //調用代理的方法
    bean.m1();
    System.out.println("----------------------");
    //調用代理的方法
    bean.m2();
}

運行輸出

----------------------
準備調用:public void com.javacode2018.aop.demo8.test1.Service1.m1()
我是 m1 方法
public void com.javacode2018.aop.demo8.test1.Service1.m1(),耗時(納秒):10326200
----------------------
準備調用:public void com.javacode2018.aop.demo8.test1.Service1.m2()
我是 m2 方法
public void com.javacode2018.aop.demo8.test1.Service1.m2(),耗時(納秒):52000

非批量的方式

用法

非批量的方式,需要註冊多個增強器,需明確的指定多個增強器的bean名稱,多個增強器按照參數中指定的順序執行,如

proxyFactoryBean.setInterceptorNames("advice1","advice2");

advice1、advice2對應的bean類型必須爲下面列表中指定的類型,類型這塊比匹配的方式範圍廣一些

MethodBeforeAdvice(方法前置通知)
AfterReturningAdvice(方法後置通知)
ThrowsAdvice(異常通知)
org.aopalliance.intercept.MethodInterceptor(環繞通知)
org.springframework.aop.Advisor(顧問)

下面來個案例。

案例

這次給service1來3個通知:前置、環繞、後置

package com.javacode2018.aop.demo8.test3;

import com.javacode2018.aop.demo8.test1.Service1;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.AfterReturningAdvice;
import org.springframework.aop.MethodBeforeAdvice;
import org.springframework.aop.framework.ProxyFactoryBean;
import org.springframework.context.annotation.Bean;
import org.springframework.lang.Nullable;

import java.lang.reflect.Method;

public class MainConfig3 {
    //註冊目標對象
    @Bean
    public Service1 service1() {
        return new Service1();
    }

    //定義一個前置通知
    @Bean
    public MethodBeforeAdvice methodBeforeAdvice() {
        MethodBeforeAdvice methodBeforeAdvice = new MethodBeforeAdvice() {
            @Override
            public void before(Method method, Object[] args, @Nullable Object target) throws Throwable {
                System.out.println("準備調用:" + method);
            }
        };
        return methodBeforeAdvice;
    }

    //定義一個環繞通知
    @Bean
    public MethodInterceptor methodInterceptor() {
        MethodInterceptor methodInterceptor = new MethodInterceptor() {
            @Override
            public Object invoke(MethodInvocation invocation) throws Throwable {
                long starTime = System.nanoTime();
                Object result = invocation.proceed();
                long endTime = System.nanoTime();
                System.out.println(invocation.getMethod() + ",耗時(納秒):" + (endTime - starTime));
                return result;
            }
        };
        return methodInterceptor;
    }

    //定義一個後置通知
    @Bean
    public AfterReturningAdvice afterReturningAdvice() {
        AfterReturningAdvice afterReturningAdvice = new AfterReturningAdvice() {
            @Override
            public void afterReturning(@Nullable Object returnValue, Method method, Object[] args, @Nullable Object target) throws Throwable {
                System.out.println(method + ",執行完畢!");
            }
        };
        return afterReturningAdvice;
    }

    //註冊ProxyFactoryBean
    @Bean
    public ProxyFactoryBean service1Proxy() {
        //1.創建ProxyFactoryBean
        ProxyFactoryBean proxyFactoryBean = new ProxyFactoryBean();
        //2.設置目標對象的bean名稱
        proxyFactoryBean.setTargetName("service1");
        //3.設置攔截器的bean名稱列表,此處批量註冊
        proxyFactoryBean.setInterceptorNames("methodBeforeAdvice", "methodInterceptor", "afterReturningAdvice");
        return proxyFactoryBean;
    }
}

測試代碼

@Test
public void test3() {
    AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MainConfig3.class);
    //獲取代理對象,代理對象bean的名稱爲註冊ProxyFactoryBean的名稱,即:service1Proxy
    Service1 bean = context.getBean("service1Proxy", Service1.class);
    System.out.println("----------------------");
    //調用代理的方法
    bean.m1();
    System.out.println("----------------------");
    //調用代理的方法
    bean.m2();
}

運行輸出

----------------------
準備調用:public void com.javacode2018.aop.demo8.test1.Service1.m1()
我是 m1 方法
public void com.javacode2018.aop.demo8.test1.Service1.m1(),執行完畢!
public void com.javacode2018.aop.demo8.test1.Service1.m1(),耗時(納秒):12724100
----------------------
準備調用:public void com.javacode2018.aop.demo8.test1.Service1.m2()
我是 m2 方法
public void com.javacode2018.aop.demo8.test1.Service1.m2(),執行完畢!
public void com.javacode2018.aop.demo8.test1.Service1.m2(),耗時(納秒):76700

源碼解析

重點在於下面這個方法

org.springframework.aop.framework.ProxyFactoryBean#getObject

源碼:

public Object getObject() throws BeansException {
    //初始化advisor(攔截器)鏈
    initializeAdvisorChain();
    //是否是單例
    if (isSingleton()) {
        //創建單例代理對象
        return getSingletonInstance();
    }
    else {
        //創建多例代理對象
        return newPrototypeInstance();
    }
}

initializeAdvisorChain方法,用來初始化advisor(攔截器)鏈,是根據interceptorNames配置,找到spring容器中符合條件的攔截器,將其放入創建aop代理的配置中

private synchronized void initializeAdvisorChain() throws AopConfigException, BeansException {
    if (!ObjectUtils.isEmpty(this.interceptorNames)) {
        // 輪詢 interceptorNames
        for (String name : this.interceptorNames) {
            //批量註冊的方式:判斷name是否以*結尾
            if (name.endsWith(GLOBAL_SUFFIX)) {
                //@1:從容器中匹配查找匹配的增強器,將其添加到aop配置中
                addGlobalAdvisor((ListableBeanFactory) this.beanFactory,
                                 name.substring(0, name.length() - GLOBAL_SUFFIX.length()));
            }
            else {
                //非匹配的方式:按照name查找bean,將其包裝爲Advisor丟到aop配置中
                Object advice;
                //從容器中查找bean
                advice = this.beanFactory.getBean(name);
                //@2:將advice添加到攔截器列表中
                addAdvisorOnChainCreation(advice, name);
            }
        }
    }
}

@1:addGlobalAdvisor批量的方式Advisor,看一下源碼,比較簡單

/**
 * 添加所有全局攔截器和切入點,
 * 容器中所有類型爲Advisor/Interceptor的bean,bean名稱prefix開頭的都會將其添加到攔截器鏈中
 */
private void addGlobalAdvisor(ListableBeanFactory beanFactory, String prefix) {
    //獲取容器中所有類型爲Advisor的bean
    String[] globalAdvisorNames =
        BeanFactoryUtils.beanNamesForTypeIncludingAncestors(beanFactory, Advisor.class);
    //獲取容器中所有類型爲Interceptor的bean
    String[] globalInterceptorNames =
        BeanFactoryUtils.beanNamesForTypeIncludingAncestors(beanFactory, Interceptor.class);
    List<Object> beans = new ArrayList<>(globalAdvisorNames.length + globalInterceptorNames.length);
    Map<Object, String> names = new HashMap<>(beans.size());
    for (String name : globalAdvisorNames) {
        Object bean = beanFactory.getBean(name);
        beans.add(bean);
        names.put(bean, name);
    }
    for (String name : globalInterceptorNames) {
        Object bean = beanFactory.getBean(name);
        beans.add(bean);
        names.put(bean, name);
    }
    //對beans進行排序,可以實現Ordered接口,排序規則:order asc
    AnnotationAwareOrderComparator.sort(beans);
    for (Object bean : beans) {
        String name = names.get(bean);
        //判斷bean是否已prefix開頭
        if (name.startsWith(prefix)) {
            //將其添加到攔截器鏈中
            addAdvisorOnChainCreation(bean, name);
        }
    }
}

@2:addAdvisorOnChainCreation

private void addAdvisorOnChainCreation(Object next, String name) {
    //namedBeanToAdvisor用來將bean轉換爲advisor
    Advisor advisor = namedBeanToAdvisor(next);
    //將advisor添加到攔截器鏈中
    addAdvisor(advisor);
}

namedBeanToAdvisor方法

private AdvisorAdapterRegistry advisorAdapterRegistry = new DefaultAdvisorAdapterRegistry();

private Advisor namedBeanToAdvisor(Object next) {
    //將對象包裝爲Advisor對象
    return this.advisorAdapterRegistry.wrap(next);
}

AdvisorAdapterRegistry不清楚的,看一下上一篇文章:Spring系列第32篇:AOP核心源碼、原理詳解

advisorAdapterRegistry#wrap方法將adviceObject包裝爲Advisor對象,代碼如下,比較簡單

public Advisor wrap(Object adviceObject) throws UnknownAdviceTypeException {
    if (adviceObject instanceof Advisor) {
        return (Advisor) adviceObject;
    }
    if (!(adviceObject instanceof Advice)) {
        throw new UnknownAdviceTypeException(adviceObject);
    }
    Advice advice = (Advice) adviceObject;
    if (advice instanceof MethodInterceptor) {
        return new DefaultPointcutAdvisor(advice);
    }
    for (AdvisorAdapter adapter : this.adapters) {
        if (adapter.supportsAdvice(advice)) {
            return new DefaultPointcutAdvisor(advice);
        }
    }
    throw new UnknownAdviceTypeException(advice);
}

總結

  1. Spring中創建代理主要分爲2種:手動方式和自動化的方式

  2. 手動方式採用硬編碼的方式,一次只能給一個目標對象創建代理對象,相對來說靈活一下,對開發者來說更靈活一些,通常可以獨立spring環境使用;自動化的方式主要在spring環境中使用,通常是匹配的方式來爲符合條件的目標bean創建代理,使用起來更簡單一些

  3. 本文介紹的ProxyFactoryBean用來在spring環境中給指定的bean創建代理對象,用到的不是太多,大家可以作爲了解即可

案例源碼

https://gitee.com/javacode2018/spring-series

路人甲java所有案例代碼以後都會放到這個上面,大家watch一下,可以持續關注動態。

Spring系列

  1. Spring系列第1篇:爲何要學spring?

  2. Spring系列第2篇:控制反轉(IoC)與依賴注入(DI)

  3. Spring系列第3篇:Spring容器基本使用及原理

  4. Spring系列第4篇:xml中bean定義詳解(-)

  5. Spring系列第5篇:創建bean實例這些方式你們都知道?

  6. Spring系列第6篇:玩轉bean scope,避免跳坑裏!

  7. Spring系列第7篇:依賴注入之手動注入

  8. Spring系列第8篇:自動注入(autowire)詳解,高手在於堅持

  9. Spring系列第9篇:depend-on到底是幹什麼的?

  10. Spring系列第10篇:primary可以解決什麼問題?

  11. Spring系列第11篇:bean中的autowire-candidate又是幹什麼的?

  12. Spring系列第12篇:lazy-init:bean延遲初始化

  13. Spring系列第13篇:使用繼承簡化bean配置(abstract & parent)

  14. Spring系列第14篇:lookup-method和replaced-method比較陌生,怎麼玩的?

  15. Spring系列第15篇:代理詳解(Java動態代理&cglib代理)?

  16. Spring系列第16篇:深入理解java註解及spring對註解的增強(預備知識)

  17. Spring系列第17篇:@Configration和@Bean註解詳解(bean批量註冊)

  18. Spring系列第18篇:@ComponentScan、@ComponentScans詳解(bean批量註冊)

  19. Spring系列第18篇:@import詳解(bean批量註冊)

  20. Spring系列第20篇:@Conditional通過條件來控制bean的註冊

  21. Spring系列第21篇:註解實現依賴注入(@Autowired、@Resource、@Primary、@Qulifier)

  22. Spring系列第22篇:@Scope、@DependsOn、@ImportResource、@Lazy 詳解

  23. Spring系列第23篇:Bean生命週期詳解

  24. Spring系列第24篇:父子容器詳解

  25. Spring系列第25篇:@Value【用法、數據來源、動態刷新】

  26. Spring系列第26篇:國際化詳解

  27. Spring系列第27篇:spring事件機制詳解

  28. Spring系列第28篇:Bean循環依賴詳解

  29. Spring系列第29篇:BeanFactory擴展(BeanFactoryPostProcessor、BeanDefinitionRegistryPostProcessor)

  30. Spring系列第30篇:jdk動態代理和cglib代理

  31. Spring系列第31篇:aop概念詳解

更多好文章

  1. Java高併發系列(共34篇)

  2. MySql高手系列(共27篇)

  3. Maven高手系列(共10篇)

  4. Mybatis系列(共12篇)

  5. 聊聊db和緩存一致性常見的實現方式

  6. 接口冪等性這麼重要,它是什麼?怎麼實現?

  7. 泛型,有點難度,會讓很多人懵逼,那是因爲你沒有看這篇文章!

世界上最好的關係是相互成就,點贊轉發 感恩開心????

路人甲java

▲長按圖片識別二維碼關注

路人甲Java:工作10年的前阿里P7,所有文章以系列的方式呈現,帶領大家成爲java高手,目前已出:java高併發系列、mysql高手系列、Maven高手系列、mybatis系列、spring系列,正在連載springcloud系列,歡迎關注!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章