最簡 Spring AOP 源碼分析 前言 基本概念 猜測實現原理 調試代碼 使用說明 源碼深入分析 總結 公衆號

前言

最近在研究 Spring 源碼,Spring 最核心的功能就是 IOC 容器AOP。本文定位是以最簡的方式,分析 Spring AOP 源碼。

基本概念

上面的思維導圖能夠概括了 Spring AOP,其最重要的是 Spring AOP 只能作用於 Bean,而 AspectJ 能夠在編譯期、類加載期對字節碼進行更改。

猜測實現原理

Spring AOP 的實現原理是動態代理,但是具體又是怎麼實現的呢?

在 Spring 容器中,我們使用的每個 bean 都是 BeanDefinition 的實例,容器會在合適的時機根據 BeanDefinition 的基本信息實例化 bean 對象。

所以比較簡單的做法是,Spring 會自動生成代理對象的代理類。我們在獲取 bean 時,Spring 容器返回代理類對象,而不是實際的 bean。

調試代碼

本文使用的代碼,安裝了 lombok,並基於 Spring Boot,是一個完全基於註解的最簡調試代碼。

註解配置類 AopConfig:

@Slf4j
@Component
@Aspect
public class AopConfig {

    @Pointcut("within(com.life.demo..*)")
    public void pointCut() {
    }

    @Before("com.life.demo.AopConfig.pointCut()")
    public void log() {
        log.info("this is point cut...");
    }
}

Spring 啓動類 AppApplication:

@SpringBootApplication
@EnableAspectJAutoProxy
public class AppApplication {

    public static void main(String[] args) {
        SpringApplication.run(AppApplication.class, args);
    }
}

Controller HelloWorldController:

package com.life.demo.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import lombok.extern.slf4j.Slf4j;

@RestController
@Slf4j
public class HelloWorldController {

    @GetMapping("/hello")
    public String greeting() {
        return "hello!";
    }
}

運行 Web 應用,在瀏覽器輸入網址 http://localhost:11111/hello,會看到 log:

INFO 96257 --- [io-11111-exec-1] com.life.demo.AopConfig                  : this is point cut...

驗證出成功配置了代理。

使用說明

  1. @EnableAspectJAutoProxy 開啓 AOP。
  2. 使用 @Aspect 註解的 bean 都會被 Spring 當做用來實現 AOP 的配置類。
  3. 配置 Advice,不做詳細介紹,具體參考 Spring AOP 官方文檔
  4. @Pointcut,用來匹配 Spring 容器中的所有 bean 的方法的。
@Pointcut("execution(* transfer(..))")// the pointcut expression
private void anyOldTransfer() {}// the pointcut signature

@Pointcut 中使用了 execution 來正則匹配方法簽名,這也是最常用的,除了 execution,我們再看看其他的幾個比較常用的匹配方式:

  • within:指定所在類或所在包下面的方法(Spring AOP 獨有)
    如 @Pointcut("within(com.javadoop.springaoplearning.service..*)")

  • @annotation:方法上具有特定的註解,如 @Subscribe 用於訂閱特定的事件。
    如 @Pointcut("execution(* .(..)) && @annotation(com.javadoop.annotation.Subscribe)")

  • bean(idOrNameOfBean):匹配 bean 的名字(Spring AOP 獨有)
    如 @Pointcut("bean(*Service)")

Tips:上面匹配中,通常 "." 代表一個包名,".." 代表包及其子包,方法參數任意匹配使用兩個點 ".."。

源碼深入分析

@EnableAspectJAutoProxy 開啓 AOP

@EnableAspectJAutoProxy 註解定義:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {
    boolean proxyTargetClass() default false;
    boolean exposeProxy() default false;
}
class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {

    @Override
    public void registerBeanDefinitions(
            AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {

        AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);

        AnnotationAttributes enableAspectJAutoProxy =
                AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class);
        if (enableAspectJAutoProxy != null) {
            if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) {
                AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
            }
            if (enableAspectJAutoProxy.getBoolean("exposeProxy")) {
                AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
            }
        }
    }

}

在 AppApplication 啓動類上要加入 @EnableAspectJAutoProxy 註解開啓 AOP,查看該註解源碼,其 proxyTargetClass() 是在 AspectJAutoProxyRegistrar 類中調用,而 AspectJAutoProxyRegistrar 是一個 ImportBeanDefinitionRegistrar。再往上追根溯源,可以看到是在接口 ConfigurableApplicationContext 中 void refresh() 調用。

IOC 容器管理 AOP 實例

在創建 bean 時,會調用 AbstractAutowireCapableBeanFactory#doCreateBean(...)。

protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args)
            throws BeanCreationException {

    // 初始化 bean
    BeanWrapper instanceWrapper = null;
    if (mbd.isSingleton()) {
        instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
    }
    if (instanceWrapper == null) {
        // 1. 創建實例
        instanceWrapper = createBeanInstance(beanName, mbd, args);
    }
    ...

    // Initialize the bean instance.
    Object exposedObject = bean;
    try {
        // 2. 裝載屬性
        populateBean(beanName, mbd, instanceWrapper);
        if (exposedObject != null) {
            // 3. 初始化
            exposedObject = initializeBean(beanName, exposedObject, mbd);
        }
    }
    ...
}

着重看第3步 initializeBean(...) 方法:

protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) {
    if (System.getSecurityManager() != null) {
        AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
            invokeAwareMethods(beanName, bean);
            return null;
        }, getAccessControlContext());
    }
    else {
        invokeAwareMethods(beanName, bean);
    }

    Object wrappedBean = bean;
    if (mbd == null || !mbd.isSynthetic()) {
        wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
    }

    try {
        invokeInitMethods(beanName, wrappedBean, mbd);
    }
    catch (Throwable ex) {
        throw new BeanCreationException(
                (mbd != null ? mbd.getResourceDescription() : null),
                beanName, "Invocation of init method failed", ex);
    }
    if (mbd == null || !mbd.isSynthetic()) {
        // 執行每個 BeanPostProcessor 的 postProcessAfterInitialization 方法!
        wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
    }

    return wrappedBean;
}

Spring IOC 容器創建 bean 實例時,最後都會對 bean 進行處理,來實現增強。對於 Spring AOP 來說,就是創建代理類。

上面代碼中函數 applyBeanPostProcessorsAfterInitialization(...) 最終調用了 AbstractAutoProxyCreator 實現的 postProcessAfterInitialization() 方法。

/**
 * Create a proxy with the configured interceptors if the bean is
 * identified as one to proxy by the subclass.
 * @see #getAdvicesAndAdvisorsForBean
 */
@Override
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
    if (bean != null) {
        Object cacheKey = getCacheKey(bean.getClass(), beanName);
        if (this.earlyProxyReferences.remove(cacheKey) != bean) {
            return wrapIfNecessary(bean, beanName, cacheKey);
        }
    }
    return bean;
}

wrapIfNecessary(...)方法在需要時返回了代理類。

protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
    if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
        return bean;
    }
    if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
        return bean;
    }
    if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
        this.advisedBeans.put(cacheKey, Boolean.FALSE);
        return bean;
    }

    // 1. Create proxy if we have advice.
    Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
    if (specificInterceptors != DO_NOT_PROXY) {
        this.advisedBeans.put(cacheKey, Boolean.TRUE);
        // 2. 核心!重點!重要!
        Object proxy = createProxy(
                bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
        this.proxyTypes.put(cacheKey, proxy.getClass());
        return proxy;
    }

    this.advisedBeans.put(cacheKey, Boolean.FALSE);
    return bean;
}

上述代碼第 1 步 getAdvicesAndAdvisorsForBean(...) 方法是返回某個 beanName 下的 Advice 和 Advisor,如果返回結果不爲空的話,纔會創建代理。其核心方法就是 createProxy(...)。

protected Object createProxy(Class<?> beanClass, @Nullable String beanName,
        @Nullable Object[] specificInterceptors, TargetSource targetSource) {

    if (this.beanFactory instanceof ConfigurableListableBeanFactory) {
        AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass);
    }

    // 1. 獲取合適的 ProxyFactory
    ProxyFactory proxyFactory = new ProxyFactory();
    proxyFactory.copyFrom(this);

    if (!proxyFactory.isProxyTargetClass()) {
        if (shouldProxyTargetClass(beanClass, beanName)) {
            proxyFactory.setProxyTargetClass(true);
        }
        else {
            evaluateProxyInterfaces(beanClass, proxyFactory);
        }
    }

    Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
    proxyFactory.addAdvisors(advisors);
    proxyFactory.setTargetSource(targetSource);
    customizeProxyFactory(proxyFactory);

    proxyFactory.setFrozen(this.freezeProxy);
    if (advisorsPreFiltered()) {
        proxyFactory.setPreFiltered(true);
    }

    // 2. 創建並返回合適的 AOP 對象
    return proxyFactory.getProxy(getProxyClassLoader());
}

ProxyFactory

@Override
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
    if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
        Class<?> targetClass = config.getTargetClass();
        if (targetClass == null) {
            throw new AopConfigException("TargetSource cannot determine target class: " +
                    "Either an interface or a target is required for proxy creation.");
        }
        if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
            return new JdkDynamicAopProxy(config);
        }
        return new ObjenesisCglibAopProxy(config);
    }
    else {
        return new JdkDynamicAopProxy(config);
    }
}

查看代碼最終發現是在 DefaultAopProxyFactory#createAopProxy(...) 方法中實現。

AopProxy 接口的 2 個實現類:CglibAopProxy 和 JdkDynamicAopProxy。這裏就不分析 JdkDynamicAopProxy 類,僅分析 CglibAopProxy 類。CglibAopProxy 類實現的 getProxy(...) 方法如下:

@Override
public Object getProxy(@Nullable ClassLoader classLoader) {
    if (logger.isTraceEnabled()) {
        logger.trace("Creating CGLIB proxy: " + this.advised.getTargetSource());
    }

    try {
        Class<?> rootClass = this.advised.getTargetClass();
        Assert.state(rootClass != null, "Target class must be available for creating a CGLIB proxy");

        Class<?> proxySuperClass = rootClass;
        if (ClassUtils.isCglibProxyClass(rootClass)) {
            proxySuperClass = rootClass.getSuperclass();
            Class<?>[] additionalInterfaces = rootClass.getInterfaces();
            for (Class<?> additionalInterface : additionalInterfaces) {
                this.advised.addInterface(additionalInterface);
            }
        }

        // Validate the class, writing log messages as necessary.
        validateClassIfNecessary(proxySuperClass, classLoader);

        // Configure CGLIB Enhancer...
        Enhancer enhancer = createEnhancer();
        if (classLoader != null) {
            enhancer.setClassLoader(classLoader);
            if (classLoader instanceof SmartClassLoader &&
                    ((SmartClassLoader) classLoader).isClassReloadable(proxySuperClass)) {
                enhancer.setUseCache(false);
            }
        }
        enhancer.setSuperclass(proxySuperClass);
        enhancer.setInterfaces(AopProxyUtils.completeProxiedInterfaces(this.advised));
        enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
        enhancer.setStrategy(new ClassLoaderAwareUndeclaredThrowableStrategy(classLoader));

        Callback[] callbacks = getCallbacks(rootClass);
        Class<?>[] types = new Class<?>[callbacks.length];
        for (int x = 0; x < types.length; x++) {
            types[x] = callbacks[x].getClass();
        }
        // fixedInterceptorMap only populated at this point, after getCallbacks call above
        enhancer.setCallbackFilter(new ProxyCallbackFilter(
                this.advised.getConfigurationOnlyCopy(), this.fixedInterceptorMap, this.fixedInterceptorOffset));
        enhancer.setCallbackTypes(types);

        // Generate the proxy class and create a proxy instance.
        return createProxyClassAndInstance(enhancer, callbacks);
    }
    catch (CodeGenerationException | IllegalArgumentException ex) {
        throw new AopConfigException("Could not generate CGLIB subclass of " + this.advised.getTargetClass() +
                ": Common causes of this problem include using a final class or a non-visible class",
                ex);
    }
    catch (Throwable ex) {
        // TargetSource.getTarget() failed
        throw new AopConfigException("Unexpected AOP exception", ex);
    }
}

CGLIB 生成代理的核心是 Enhancer,詳情見Enhancer API 文檔cglib 官網

總結

Spring AOP 使用了動態代理,作用於 IOC 容器管理的 bean。在獲取 bean 時會根據需要創建代理類,並返回代理類。在 Spring Boot 中使用 Spring AOP 時應該先用 @EnableAspectJAutoProxy 註解開啓代理,定義代理類和代理規則,不需要 XML 或其他配置。

Spring 的源碼太龐雜,調用鏈太深,在研究源碼的時候應該明確目標,掌握核心原理。就像學漢語字典,並不需要掌握其中的每一個漢字(況且 Spring 源碼更新頻率很快)。

公衆號

coding 筆記、點滴記錄,以後的文章也會同步到公衆號(Coding Insight)中,希望大家關注_

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