手寫Spring---AOP面向切面編程(4)

接上一篇《手寫Spring---AOP面向切面編程(3)》繼續更新

補充:擴展的Advisor

在上一篇中已經講到了Advisor包下的Advisor.java,也就是一個通知者和一個基於AspectJ語法實現的Advisor,因爲通知者也可分爲許多不一樣種類的通知者,因此咱們在進行擴展的時候,須要經過不一樣的實現方式定義不一樣的通知者。java

如今看看這兩種不一樣的設計方式spring

第二種相比第一種多了一層抽象,把相同的屬性方法抽離掉而後減小了冗餘代碼, 此時pointcut的生成必須由抽象類AbstractPointcutAdvisor的子類去實現生成不一樣的 pointcut,是AspectJ的生成AspectJ的,是正則Reg的生成正則的。編程

1、織入Weaving的實現

① 織入要完成什麼?

將用戶提供的加強功能加到指定的方法上
複製代碼

② 在何時進行織入?

建立bean實例,在bean初始化後進行加強
複製代碼

③ 如何肯定bean須要加強

對bean類及方法進行遍歷,匹配用戶指定的切面
複製代碼

④ 如何進行織入

代理!
複製代碼

2、織入Weaving的設計

此時咱們回顧一下AOP的使用流程api

用戶---> 配置並註冊切面
咱們---> 織入流程:初始化bean---判斷是否須要加強 ? 代理加強---返回實例 : 返回實例
複製代碼

① 用戶到哪裏去註冊切面?

② 判斷匹配,織入邏輯寫在哪裏

③ 此時咱們是否須要在bean建立的過程當中加一層處理?

還記得咱們以前實現的DefaultBeanFactory嗎?
在它的doGetBean方法中,初始化Bean前增長一個加強邏輯便可
複製代碼

後續咱們在bean建立過程當中還會再加入更多不一樣的處理,若是直接在BeanFactory中實現,會致使BeanFactory代碼爆炸性增加,並且也不容易進行擴展,此時咱們該如何處理呢?緩存

此時咱們再回顧一下3周前的那篇《手寫Spring---IOC容器(1)》中講到的Bean的產出過程app

1.建立Bean定義---① 2.註冊Bean定義②---③ 3.建立Bean實例④---⑤ 4.初始化Bean實例⑥

在此4個節點中,每一項過程的先後階段均可能增長各類各樣的處理邏輯
爲了讓咱們更加靈活,設計好BeanFactory後不須要修改代碼,
咱們須要在各個節點中加入擴展點(使用了 **① ~ ⑥** 標識,
好比**①**的意思爲註冊bean定義前,**②**爲註冊Bean定義後)和註冊機制
複製代碼

此時咱們須要會想到觀察者模式(監聽模式),須要六個擴展點,也就是六個觀察者maven


3、應用觀察者模式加入咱們的AOP織入

此時咱們不推薦僅定義一個接口去完成全部擴展點的監聽,由於它們的用途不同且實現的時候須要一會兒實現6個功能,此時咱們定義一個監聽接口BeanPostProcessor來監聽Bean初始化先後過程,具體流程參照下圖:ide

① 監聽者模式實現AOP流程圖

② 代碼實現

1.BeanPostProcessor.java

這裏默認什麼都不幹,定義的是default默認方法,這裏是爲了方便選擇只進行某一個的擴展點的處理,好比咱們以後就只選擇進行初始化後的處理工具

public interface BeanPostProcessor {
    default Object postProcessBeforeInitialization(Object bean,String beanName) throws Throwable{
        return bean;
    }
    default Object postProcessAfterInitialization(Object bean,String beanName) throws Throwable{
        return bean;
    }
}
複製代碼

2.AdvisorRegistry.java

提供對於Advisor的註冊和獲取post

public interface AdvisorRegistry {
    public void registAdvisor(Advisor ad);
    public List<Advisor> getAdvisors();
}
複製代碼

3.BeanFactory.java

此時咱們建立好實例以後須要放入Bean工廠裏面,還須要在BeanFactory.java中加入註冊監聽的方法

public interface BeanFactory {
    Object getBean(String name) throws Throwable;
    //新加入的註冊方法
    void registerBeanPostProcessor(BeanPostProcessor beanPostProcessor);
}
複製代碼

4.DefaultBeanFactory.java

這裏是BeanFactory的默認實現,在此提供registerBeanPostProcessor的實現

(1)定義一個集合

private List<BeanPostProcessor> beanPostProcessors = Collections.synchronizedList(new ArrayList<>());
複製代碼

(2)註冊方法(僅僅爲添加進線程同步集合beanPostProcessors中)

@Override
public void registerBeanPostProcessor(BeanPostProcessor beanPostProcessor) {
    this.beanPostProcessors.add(beanPostProcessor);
    if (beanPostProcessor instanceof BeanFactoryAware){
        ((BeanFactoryAware) beanPostProcessor).setBeanFactory(this);
    }
}
複製代碼

5.Aware和BeanFactoryAware

如何去使用觀察者模式,還必須提供喚醒的功能

(1)此時在Bean初始化前和初始化後加入處理(DefaultBeanFactory中的doGetBean方法)

// 應用bean初始化前的處理
    instance = this.applyPostProcessBeforeInitialization(instance, beanName);

    // 執行初始化方法
    this.doInit(bd, instance);

    // 應用bean初始化後的處理
    instance = this.applyPostProcessAfterInitialization(instance, beanName);
複製代碼

(2)applyPostProcessBeforeInitialization()和applyPostProcessAfterInitialization()方法其實就是遍歷你的全部註冊進來的processor,而後一個一個調用它們的方法,按照這個規範來擴展多少功能均可以

// 應用bean初始化前的處理
private Object applyPostProcessBeforeInitialization(Object bean, String beanName) throws Throwable {
    for (BeanPostProcessor bpp : this.beanPostProcessors) {
        bean = bpp.postProcessBeforeInitialization(bean, beanName);
    }
    return bean;
}

// 應用bean初始化後的處理
private Object applyPostProcessAfterInitialization(Object bean, String beanName) throws Throwable {
    for (BeanPostProcessor bpp : this.beanPostProcessors) {
        bean = bpp.postProcessAfterInitialization(bean, beanName);
    }
    return bean;
}
複製代碼

6.AdvisorAutoProxyCreator的實現

AOP 織入功能 applyPostProcessAfterInitialization 確定在此實現

@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws Throwable {

}
複製代碼

③ 實現織入:判斷bean是否須要加強

回憶這個過程: 織入流程:初始化bean---判斷是否須要加強 ? 代理加強---返回實例 : 返回實例

1. 如何判斷bean實例是否要加強?

1.獲取bean的類及全部方法(經過反射getMethods()和getDeclareMethods())
2.遍歷Advisor,取advisor中的Pointcut來匹配類和方法
Pointcut中的兩個匹配方法:
    boolean matchClass(Class<?> targetClass);
    boolean matchMethod(Method method,Class<?> targetClass);
    
便是用前面經過反射得到的方法和matchMethod()來比較
複製代碼

2. 此時咱們須要肯定getMethods()和getDeclareMethods()能夠取到全部的方法嗎?

若是閱讀過源碼中的說明,就會發現getMethods()方法返回全部的公開方法,
沒法取得私有和保護類型的方法,getDeclareMethods()沒法取得父類
的繼承方法(第一小段最後一句but excluding inherited methods)

因此這兩個方法仍然不夠,咱們到時候會直接使用spring裏面內置的方法
複製代碼

3. 回到AdvisorAutoProxyCreator.java中的postProcessAfterInitialization()方法

// 第一步:在此判斷bean是否須要進行切面加強
    List<Advisor> matchAdvisors = getMatchedAdvisors(bean, beanName);

// 第二步:如須要就進行加強,建立代理對象,進行代理加強。再返回加強的對象。
    if (CollectionUtils.isNotEmpty(matchAdvisors)) {
        bean = this.createProxy(bean, beanName, matchAdvisors);
    }
複製代碼

4. 編寫(3)提到的getMatchedAdvisors()方法

private List<Advisor> getMatchedAdvisors(Object bean, String beanName) {
    if (CollectionUtils.isEmpty(advisors)) {
        return null;
    }

    // 獲得類、全部的方法
    Class<?> beanClass = bean.getClass();
    List<Method> allMethods = this.getAllMethodForClass(beanClass);

    // 存放匹配的Advisor的list
    List<Advisor> matchAdvisors = new ArrayList<>();
    // 遍歷Advisor來找匹配的
    for (Advisor ad : this.advisors) {
        if (ad instanceof PointcutAdvisor) {
            if (isPointcutMatchBean((PointcutAdvisor) ad, beanClass, allMethods)) {
                matchAdvisors.add(ad);
            }
        }
    }

    return matchAdvisors;
}
複製代碼

5. 在(4)中獲得全部方法的getAllMethodForClass()

此時咱們須要用到Spring自帶的ClassUtils和ReflectionUtils,ps:能夠直接在maven中添加SpringBoot的依賴以後無須再進行導包。

這裏的邏輯爲建立一個集合,使用ClassUtils自帶的遍歷方法getAllInterfacesForClassAsSet()遍歷全部接口,以後把自身beanClass也一塊兒丟進這個集合中,以後來一個循環把接口中的全部方法依次遍歷出來

private List<Method> getAllMethodForClass(Class<?> beanClass) {
    List<Method> allMethods = new LinkedList<>();
    Set<Class<?>> classes = new LinkedHashSet<>(ClassUtils.getAllInterfacesForClassAsSet(beanClass));
    classes.add(beanClass);
    for (Class<?> clazz : classes) {
        Method[] methods = ReflectionUtils.getAllDeclaredMethods(clazz);
        for (Method m : methods) {
            allMethods.add(m);
        }
    }

    return allMethods;
}
複製代碼

6. 在getMatchedAdvisors中匹配Advisor和bean的全部方法

private boolean isPointcutMatchBean(PointcutAdvisor pa, Class<?> beanClass, List<Method> methods) {
    Pointcut p = pa.getPointcut();

    // 首先判斷類是否匹配
    // 注意以前說過的AspectJ狀況下這個匹配是不可靠的,須要經過方法來匹配
    //這裏的判斷僅僅起到過濾做用,類不匹配的前提下直接跳過
    if (!p.matchClass(beanClass)) {
        return false;
    }

    // 再判斷是否有方法匹配
    for (Method method : methods) {
        if (p.matchMethod(method, beanClass)) {
            return true;
        }
    }
    return false;
}
複製代碼

7.postProcessAfterInitialization()方法的實現

此時咱們已經獲得了匹配上的方法,能夠對這些方法進行代理加強操做了

@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws Throwable {

    // 在此判斷bean是否須要進行切面加強
    List<Advisor> matchAdvisors = getMatchedAdvisors(bean, beanName);
    // 如須要就進行加強,再返回加強的對象。
    if (CollectionUtils.isNotEmpty(matchAdvisors)) {
        bean = this.createProxy(bean, beanName, matchAdvisors);
    }
    return bean;
}
複製代碼

8.建立代理對象

private Object createProxy(Object bean, String beanName, List<Advisor> matchAdvisors) throws Throwable {
    // 經過AopProxyFactory工廠去完成選擇、和建立代理對象的工做。
    return AopProxyFactory.getDefaultAopProxyFactory().createAopProxy(bean, beanName, matchAdvisors, beanFactory)
            .getProxy();
}
複製代碼

④ 代理加強的實現

這部分涉及的內容比較多,建議先理清思路而後看懂邏輯便可,由於咱們的主要出發點在於之後可以理解源碼而不是真的要本身完整實現一個spring

1. 代理加強的邏輯是怎樣的?

代理可分爲jdk動態代理(使用invoke方法加強)和cglib動態代理(使用intercept方法加強),判斷代理生成方式後返回相應代理對象便可
複製代碼

2. jdk動態代理和cglib動態代理的二者抽象

咱們知道,要實現jdk動態代理,就要實現InvocationHandler接口,要實現cglib動態代理,就要實現MethodInteceptor接口,咱們能夠對這二者進行抽象,抽成一個AopProxy接口,裏面提供獲取代理對象的方法便可

public interface AopProxy {
    Object getProxy();
    Object getProxy(ClassLoader classLoader);
}
複製代碼

3. jdk動態代理的invoke和cglib動態代理intercept邏輯同樣嗎?

同樣,都是使用Advice進行加強
---> 從Advisor中找出對當前方法進行加強的Advice
---> 是否有匹配的Advice?責任鏈式應用Advice加強:執行被代理對象的方法
---> 返回結果

jdk要生成代理對象,完成織入加強,須要如下數據
    要實現的接口,目標對象,匹配的Advisors,可能還須要BeanFactory
cglib須要的數據:
    要繼承的類,實現的接口,構造參數類型,構造參數,目標對象,
    匹配的Advisor和BeanFactory
    
    爲什麼須要構造參數和構造參數類型,由於擴展目標類若是沒提供無參構造方法,那它的子類建立時必須調用父類的有參構造方法
複製代碼

4. JdkDynamicAopProxy和CglibDynamicAopProxy的設計

AopProxyUtils

先不看JdkDynamicAopProxy和CglibDynamicAopProxy的具體實現,咱們先看它們的結構,由於前面咱們也提到了,它們倆作的事情,功能實際上是同樣的,因此能夠提取相同的功能實現部分爲一個通用方法applyAdvices()

咱們能夠直接定義一個工具類AopProxyUtils來儲存這樣的通用方法

public static Object applyAdvices(Object target, Method method, Object[] args, 
    List<Advisor> matchAdvisors,Object proxy, BeanFactory beanFactory) throws Throwable {
    // 這裏要作什麼?
    // 一、獲取要對當前方法進行加強的advice
    List<Object> advices = AopProxyUtils.getShouldApplyAdvices(target.getClass(), method, matchAdvisors,
            beanFactory);
    // 二、若有加強的advice,責任鏈式加強執行
    if (CollectionUtils.isEmpty(advices)) {
        return method.invoke(target, args);
    } else {
        // 責任鏈式執行加強
        AopAdviceChainInvocation chain = new AopAdviceChainInvocation(proxy, target, method, args, advices);
        return chain.invoke();
    }
}

public static List<Object> getShouldApplyAdvices(Class<?> beanClass, Method method, List<Advisor> matchAdvisors,
    BeanFactory beanFactory) throws Throwable {
        if (CollectionUtils.isEmpty(matchAdvisors)) {
            return null;
        }
        List<Object> advices = new ArrayList<>();
        for (Advisor ad : matchAdvisors) {
            if (ad instanceof PointcutAdvisor) {
                if (((PointcutAdvisor) ad).getPointcut().matchMethod(method, beanClass)) {
                    advices.add(beanFactory.getBean(ad.getAdviceBeanName()));
                }
            }
        }

        return advices;
}
複製代碼

如何來進行責任鏈的加強,注意咱們必須定義一個責任鏈記錄索引,好比下方代碼中咱們就使用i做爲索引,在invoke()方法中,先判斷i是否小於advices的size,若是小於,那就是還有加強未執行,以後的判斷邏輯很簡單,若是這個加強是前置加強,那就是直接執行便可,

若是是環繞加強return ((MethodSurroudAdvice) advice).invoke(invokeMethod, null, this),參考MethodSurroudAdvice中的invoke()的參數,此時咱們須要理解,這裏的invoke方法中的invokeMethod對象取得的就是public Object invoke() throws Throwable這個方法自己,null表示無參數,this表示自身

後置加強是返回結果後加強,因此先去調用invoke---Object returnValue = this.invoke();,而後再加強

若是i<advices.size()不成立,那就執行目標方法return method.invoke(target, args);

public class AopAdviceChainInvocation {

    private static Method invokeMethod;
    static {
        try {
            invokeMethod = AopAdviceChainInvocation.class.getMethod("invoke", null);
        } catch (NoSuchMethodException | SecurityException e) {
            e.printStackTrace();
        }
    }

    private Object proxy;
    private Object target;
    private Method method;
    private Object[] args;
    private List<Object> advices;

    public AopAdviceChainInvocation(Object proxy, Object target, Method method, Object[] args, List<Object> advices) {
        super();
        this.proxy = proxy;
        this.target = target;
        this.method = method;
        this.args = args;
        this.advices = advices;
    }

    // 責任鏈執行記錄索引號
    private int i = 0;

    public Object invoke() throws Throwable {
        if (i < this.advices.size()) {
            Object advice = this.advices.get(i++);
            if (advice instanceof MethodBeforeAdvice) {
                // 執行前置加強
                ((MethodBeforeAdvice) advice).before(method, args, target);
            } else if (advice instanceof MethodSurroudAdvice) {
                // 執行環繞加強和異常處理加強。注意這裏給入的method 和 對象 是invoke方法和鏈對象
                return ((MethodSurroudAdvice) advice).invoke(invokeMethod, null, this);
            } else if (advice instanceof AfterReturningAdvice) {
                // 當是後置加強時,先得獲得結果,再執行後置加強邏輯
                Object returnValue = this.invoke();
                ((AfterReturningAdvice) advice).afterReturning(returnValue, method, args, target);
                return returnValue;
            }
            return this.invoke();
        } else {
            return method.invoke(target, args);
        }
    }
}
複製代碼
JdkDynamicAopProxy和CglibDynamicAopProxy的代碼實現

jdk:

public class JdkDynamicAopProxy implements AopProxy, InvocationHandler {

    private static final Log logger = LogFactory.getLog(JdkDynamicAopProxy.class);

    private String beanName;
    private Object target;
    private List<Advisor> matchAdvisors;

    private BeanFactory beanFactory;

    public JdkDynamicAopProxy(String beanName, Object target, List<Advisor> matchAdvisors, BeanFactory beanFactory) {
        super();
        this.beanName = beanName;
        this.target = target;
        this.matchAdvisors = matchAdvisors;
        this.beanFactory = beanFactory;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        return AopProxyUtils.applyAdvices(target, method, args, matchAdvisors, proxy, beanFactory);
    }

    @Override
    public Object getProxy() {
        return this.getProxy(target.getClass().getClassLoader());
    }

    @Override
    public Object getProxy(ClassLoader classLoader) {
        if (logger.isDebugEnabled()) {
            logger.debug("爲" + target + "建立代理。");
        }
        return Proxy.newProxyInstance(classLoader, target.getClass().getInterfaces(), this);
    }

}
複製代碼

cglib:

public class CglibDynamicAopProxy implements AopProxy, MethodInterceptor {

    private static final Log logger = LogFactory.getLog(CglibDynamicAopProxy.class);
    private static Enhancer enhancer = new Enhancer();

    private String beanName;
    private Object target;

    private List<Advisor> matchAdvisors;

    private BeanFactory beanFactory;

    public CglibDynamicAopProxy(String beanName, Object target, List<Advisor> matchAdvisors, BeanFactory beanFactory) {
        super();
        this.beanName = beanName;
        this.target = target;
        this.matchAdvisors = matchAdvisors;
        this.beanFactory = beanFactory;
    }

    @Override
    public Object getProxy() {
        return this.getProxy(target.getClass().getClassLoader());
    }

    @Override
    public Object getProxy(ClassLoader classLoader) {
        if (logger.isDebugEnabled()) {
            logger.debug("爲" + target + "建立cglib代理。");
        }
        Class<?> superClass = this.target.getClass();
        enhancer.setSuperclass(superClass);
        enhancer.setInterfaces(this.getClass().getInterfaces());
        enhancer.setCallback(this);
        Constructor<?> constructor = null;
        try {
            constructor = superClass.getConstructor(new Class<?>[] {});
        } catch (NoSuchMethodException | SecurityException e) {

        }
        if (constructor != null) {
            return enhancer.create();
        } else {
            BeanDefinition bd = ((DefaultBeanFactory) beanFactory).getBeanDefinition(beanName);
            return enhancer.create(bd.getConstructor().getParameterTypes(), bd.getConstructorArgumentRealValues());
        }
    }

    @Override
    public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        return AopProxyUtils.applyAdvices(target, method, args, matchAdvisors, proxy, beanFactory);
    }

}
複製代碼

兩個動態代理也只是用了它們自己的一些api而已,咱們只須要看懂咱們的邏輯便可,注意的是cglib的動態代理中咱們必須作到傳遞構造參數類型和構造參數,此時咱們又有了新的問題

如何傳遞建立bean實例時得到的數據到初始化後的aop中

建立時,是在defaultBeanFactory來進行操做
加強時,通過了AdvisorAutoProxyCreator
使用時,又在CglibDynamicAopProxy

此時咱們考慮將參數放在bean定義中使用ThreadLocal持有參數值,
由於BeanDefinition是同一個類的多個實例。
特別是prototype的時候,是共享的,
咱們須要保證每個建立流程中都是使用不一樣線程的數據
複製代碼

咱們在BeanDefinition中加入了兩個方法:由於咱們自己在BeanDefiniion中就已經有了構造器的緩存(public Constructor getConstructor(); public void setConstructor(Constructor constructor);),參數類型咱們能夠經過它們來得到

List<?> getConstructorArgumentValues();
public Object[] getConstructorArgumentRealValues();
複製代碼

咱們能夠經過CglibDynamicAopProxy中的getProxy方法來更好理解

else {
    BeanDefinition bd = ((DefaultBeanFactory) beanFactory).getBeanDefinition(beanName);
    return enhancer.create(bd.getConstructor().getParameterTypes(), bd.getConstructorArgumentRealValues());
}
複製代碼

這裏就是使用了getConstructor()來取得構造器,而後調用 getParameterTypes()來取得了構造參數的類型

⑤ 如何在Creator中使用AopProxy

應用工廠模式把選擇的邏輯交給工廠

public interface AopProxyFactory {
    AopProxy createAopProxy(Object bean, String beanName, List<Advisor> matchAdvisors, BeanFactory beanFactory)
            throws Throwable;

    /**
     * 得到默認的AopProxyFactory實例
     *
     * @return AopProxyFactory
     */
    static AopProxyFactory getDefaultAopProxyFactory() {
        return (AopProxyFactory) new DefaultAopProxyFactory();
    }
}
複製代碼

以後咱們實現了一個默認的工廠,這裏作了判斷是使用jdk仍是cglib,咱們省略了判斷而直接使用了cglib,spring源碼裏面的判斷也是如此,可是源碼還另外存在某些機制來判斷,在這裏咱們也再也不過多闡述了,在之後源碼解讀的時候會再展開

public class DefaultAopProxyFactory implements AopProxyFactory{
    @Override
    public AopProxy createAopProxy(Object bean, String beanName, List<Advisor> matchAdvisors, BeanFactory beanFactory)
            throws Throwable {
        // 是該用jdk動態代理仍是cglib?
        if (shouldUseJDKDynamicProxy(bean, beanName)) {
            return new JdkDynamicAopProxy(beanName, bean, matchAdvisors, beanFactory);
        } else {
            return new CglibDynamicAopProxy(beanName, bean, matchAdvisors, beanFactory);
        }
    }

    private boolean shouldUseJDKDynamicProxy(Object bean, String beanName) {
        // 如何判斷?有實現接口就用JDK,沒有就用cglib?
        return false;
    }
}
複製代碼

4、下一篇依舊是Spring的配置,以後會是源碼

測試代碼相對仍是較多,就不粘貼出來了,AOP這一篇的代碼不少,並且比較凌亂(本身都這麼以爲),須要多花時間去整理總結,有必要的話在以後會再一次進行一次更加詳細點的總結篇,把一些過程再講述清楚點。有問題或者是建議可在留言區提出讓我改進。共勉,謝謝。

相關文章
相關標籤/搜索