最簡 Spring AOP 源碼分析!

前言

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

基本概念

上面的思惟導圖可以歸納了 Spring AOP,其最重要的是 Spring AOP 只能做用於 Bean,而 AspectJ 可以在編譯期、類加載期對字節碼進行更改。java

猜想實現原理

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

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

因此比較簡單的作法是,Spring 會自動生成代理對象的代理類。咱們在獲取 bean 時,Spring 容器返回代理類對象,而不是實際的 bean。web

調試代碼

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

註解配置類 AopConfig:express

@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:api

@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:app

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)中,但願你們關注^_^

代碼和思惟導圖在 GitHub 項目中,歡迎你們 star!

相關文章
相關標籤/搜索