最簡 Spring AOP 源碼分析!
- 2020 年 12 月 10 日
- 筆記
前言
最近在研究 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 應用,在瀏覽器輸入網址 //localhost:11111/hello,會看到 log:
INFO 96257 --- [io-11111-exec-1] com.life.demo.AopConfig : this is point cut...
驗證出成功配置了代理。
使用說明
- @EnableAspectJAutoProxy 開啟 AOP。
- 使用 @Aspect 註解的 bean 都會被 Spring 當做用來實現 AOP 的配置類。
- 配置 Advice,不做詳細介紹,具體參考 Spring AOP 官方文檔。
- @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!