最简 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...
验证出成功配置了代理。
使用说明
- @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!

最简 Spring AOP 源码分析!的更多相关文章
- spring AOP源码分析(三)
		在上一篇文章 spring AOP源码分析(二)中,我们已经知道如何生成一个代理对象了,那么当代理对象调用代理方法时,增强行为也就是拦截器是如何发挥作用的呢?接下来我们将介绍JDK动态代理和cglib ... 
- Spring AOP 源码分析 - 拦截器链的执行过程
		1.简介 本篇文章是 AOP 源码分析系列文章的最后一篇文章,在前面的两篇文章中,我分别介绍了 Spring AOP 是如何为目标 bean 筛选合适的通知器,以及如何创建代理对象的过程.现在我们的得 ... 
- Spring AOP 源码分析 - 创建代理对象
		1.简介 在上一篇文章中,我分析了 Spring 是如何为目标 bean 筛选合适的通知器的.现在通知器选好了,接下来就要通过代理的方式将通知器(Advisor)所持有的通知(Advice)织入到 b ... 
- Spring AOP 源码分析 - 筛选合适的通知器
		1.简介 从本篇文章开始,我将会对 Spring AOP 部分的源码进行分析.本文是 Spring AOP 源码分析系列文章的第二篇,本文主要分析 Spring AOP 是如何为目标 bean 筛选出 ... 
- Spring AOP 源码分析系列文章导读
		1. 简介 前一段时间,我学习了 Spring IOC 容器方面的源码,并写了数篇文章对此进行讲解.在写完 Spring IOC 容器源码分析系列文章中的最后一篇后,没敢懈怠,趁热打铁,花了3天时间阅 ... 
- Spring AOP源码分析(三):基于JDK动态代理和CGLIB创建代理对象的实现原理
		AOP代理对象的创建 AOP相关的代理对象的创建主要在applyBeanPostProcessorsBeforeInstantiation方法实现: protected Object applyBea ... 
- 5.2 Spring5源码--Spring AOP源码分析二
		目标: 1. 什么是AOP, 什么是AspectJ 2. 什么是Spring AOP 3. Spring AOP注解版实现原理 4. Spring AOP切面原理解析 一. 认识AOP及其使用 详见博 ... 
- 5.2 spring5源码--spring AOP源码分析二--切面的配置方式
		目标: 1. 什么是AOP, 什么是AspectJ 2. 什么是Spring AOP 3. Spring AOP注解版实现原理 4. Spring AOP切面原理解析 一. 认识AOP及其使用 详见博 ... 
- spring aop 源码分析(三) @Scope注解创建代理对象
		一.源码环境的搭建: @Component @Scope(scopeName = ConfigurableBeanFactory.SCOPE_SINGLETON,proxyMode = ScopedP ... 
随机推荐
- binary hacks读数笔记(ld 链接讲解  二)
			这块将介绍一下ld链接命令的具体使用.ld的作用:ld是GNU binutils工具集中的一个,是众多Linkers(链接器)的一种.完成的功能自然也就是链接器的基本功能:把各种目标文件和库文件链接起 ... 
- 五:key关键字 string字符串 list列表 set集合 Zset有序集合
			key 1.keys 命令用于查找所有符合给定模式 pattern 的 key . keys * 查找所有的key返回一个列表 2.EXISTS xxx 检查是否有指定名字为xxx的key 有就返回 ... 
- Python_用PyQt5 建 notepad 界面
			用PyQt5建notepad界面 1 # -*-coding:utf-8 -*- 2 """ 3 简介:用PyQt5做一个对话框,有菜单(2个.有独立图标.快捷键).提示 ... 
- maven项目导出为jar包
			1:ctrl + R 输入cmd 2:切换路径到自己的项目路径下 3:执行--> mvn assembly:assembly ( 若显示编码问题: 查看编码方式:chcp 修改编码:chcp ... 
- .net core quartz job作业调度管理组件
			定时作业对于多数系统来说,都会用到,定时作业的实现方式可以有多种方式,简单的方式用Timer就可以实现,但是达不到通用的效果,本文采用Quartz基础组件来搭建一套企业通用的作业调度管理服务,希望对于 ... 
- 线程池中状态与线程数的设计分析(ThreadPoolExecutor中ctl变量)
			目录 预备知识 源码分析 submit()源码分析 shutdownNow()源码分析 代码输出 设计目的与优点 预备知识 可以先看下我的另一篇文章对于Java中的位掩码BitMask的解释. 1.一 ... 
- jquery删除文件
			1 <div class="panel panel-default"> 2 <div class="panel-body"> 3 < ... 
- 获取qq头像
			1 <?php 2 header('content-type: image/jpeg'); 3 $QQ = $_GET['qq']; 4 $time2 = date('Y-m-d H:i:s') ... 
- redis启动报错Could not connect to Redis at 127.0.0.1:6379: 由于目标计算机积极拒绝,无法连接。
			报错内容 解决办法 启动redis-server服务 测试 连接成功 
- 记一次腾讯TBS浏览服务集成实践
			这次的分享源于最近的实际开发工作. 项目需求是 在原生Android应用中嵌入WebView,放置用于支撑音视频直播业务的Web页: 另外还需提供Word.Excel.PowerPoint.PDF等常 ... 
