Spring AOP 创建 AopProxy 代理对象原理分析
文章目录
- Spring AOP 创建 AopProxy 代理对象原理分析
-
- 前言
- 项目环境
- 相关文章
- 1.设计原理
- 2.配置 ProxyFactoryBean
- 3.AopProxy 的生成过程
- 4.ProxyFactoryBean 生成 AopProxy 代理对象
-
- 4.1 getObject
- 4.2 initializeAdvisorChain
- 4.3 getSingletonInstance
- 4.4 createAopProxy
- 5.JDK 生成 AopProxy 代理对象
- 6.CGLIB 生成 AopProxy 代理对象
- 7.总结
- 8.参考
前言 本章开始我们将介绍一些 Spring AOP 具体的设计和实现。
项目环境
- Java 8
- Spring framework 5.2.2.RELEASE
- github 地址:https://github.com/huajiexiewenfeng/spring-aop-demos
- 本章模块:proxy
1.设计原理 在 Spring AOP 模块中,一个主要的部分是代理对象的生成,而对于 Spring 应用,可以看到是通过配置和调用 Spring 的 ProxyFactoryBean 来完成这个任务的。在 ProxyFactoryBean 中封装了主要代理对象的生产过程。在这个过程中,可以使用 JDK 和 CGLIB 两种方式生成。以 ProxyFactoryBean 的设计为中心,相关的类继承关系图如下:
- Spring AOP 基础概述
- Spring AOP 的设计与实现
- Spring AOP 创建 AopProxy 代理对象原理分析
- Spring AOP 拦截器调用的实现
- 彩蛋:Spring AOP 和 IoC 以及容器启动之间有何关联?
文章图片
在这个类的继承关系中,可以看到完成 AOP 应用的类,比如 ProxyFactory、AspectJProxyFactory、ProxyFactoryBean,他们都在同一个类的基础体系下,都是 ProxyConfig、AdvisedSupport、ProxyCreatorSupport 的子类。
- 作为共同基类,可以将 ProxyConfig 看成是一个数据基类,这个数据基类为 ProxyFactoryBean 这样的子类提供了配置属性。
- 在另一个基类 AdvisedSupport 的实现中,封装了 AOP 对通知器的相关操作,这些操作对于不同的 AOP 代理对象的生产都是一样的,单对象语句的 AOP 代理对象的创建,AdvisedSupport 把它们交给子类去完成。
- 对于 ProxyCreatorSupport,可以将它看到是其子类创建 AOP 代理对象的一个辅助类。
对于需要使用 AspectJ 的 AOP 应用,AspectJProxyFactory 起到集成 Spring 和 AspectJ 的作用;对于使用 Spring AOP 的应用,ProxyFactoryBean 和 ProxyFactory 都提供了 AOP 功能的封装,只是使用 ProxyFactoryBean ,可以在 IoC 容器中完成声明式配置,而使用 ProxyFactory ,则需要编程式的使用 Spring AOP 功能。
2.配置 ProxyFactoryBean 从这部分开始,进入到 Spring AOP 的实现部分,在分析 Spring AOP 的实现原理中,主要以 ProxyFactoryBean 的实现作为例子来进行分析。因为 ProxyFactoryBean 是 Spring IoC 环境中创建的 AOP 应用的底层方法,也是最灵活的方法,Spring 通过他完成了对 AOP 使用的封装。我们以 ProxyFactoryBean 的实现为入口,来逐层深入。
首先我们需要了解 ProxyFactoryBean 的配置和使用,通过 XML 的方式来配置 ProxyFactoryBean 和 AOP。
- 定义使用的通知器 Advisor,这个通知器应该作为一个 Bean 来定义。这个通知器的实现定义了需要对目标对象进行增强的切面行为,也就是 Advice 通知。
- 定义 ProxyFactoryBean ,把它做诶了一个 Bean 来定义,它是封装 AOP 功能的主要类。在配置 ProxyFactoryBean 时,需要设定与 AOP 实现相关的重要属性,比如 proxyInterface、interceptorNames、target 等等。
- 定义 target 属性,作为 target 属性注入的 Bean,是需要用 AOP 通知器中的切面应用来增强的对象,也就是前面提到的 base 对象(《Spring AOP 基础概述》)。
配置示例:
com.csdn.spring.aop.proxy.factorybean.TestInterface
testAdvisor
掌握这些配置后,就可以具体看一看这些 AOP 是如何实现的,切面应用怎样通过 ProxyFactoryBean 对 target 对象起作用,下面我们继续详细的进行分析。
3.AopProxy 的生成过程 在 Spring AOP 的使用中,我们了解到,可以通过 ProxyFactoryBean 来配置目标对象和切面行为。在 ProxyFactoryBean 中,通过 interceptorNames 属性来配置以及定义好的通知器 Advisor。虽然名称是 interceptorNames,但是实际上是提供 AOP 应用配置通知器的地方。
在 ProxyFactoryBean 中需要为 target 目标对象生成 Proxy 代理对象,从而为 AOP 切面的组织做好准备工作。这些具体的代理对象生成工作是通过 JDK 的 Proxy 或者 CGLIB 来完成的。
生成过程如图所示:
文章图片
4.ProxyFactoryBean 生成 AopProxy 代理对象 ProxyFactoryBean 的 AOP 实现需要依赖 JDK 或者 CGLIB 提供的 Proxy 特性。从 FactoryBean 中获取对象,是以 getObject() 方法作为入口完成的;ProxyFactoryBean 实现中的 getObject 方法,是 FactoryBean 需要实现的接口。对 ProxyFactoryBean 来说,把需要对 target 目标对象增加增强处理,来通过 getObject 方法进行封装。这些增强处理是为 AOP 功能的实现提供服务的。
4.1 getObject
- org.springframework.aop.framework.ProxyFactoryBean#getObject
- 首先对通知器链进行初始化,通知器链封装了一系列的拦截器,这些拦截器都要从配置中读取,然后为代理对象的生成做好准备。
- 在生成代理对象时,需要根据 Bean 的定义来判断作用域,生成不同的对象实例。
- 不了解作用域( Scope),可以参考相关文章 - 《Spring Bean 作用域》
public Object getObject() throws BeansException {
// 初始化通知器链
initializeAdvisorChain();
// 判断 Bean 的作用域,生成对应的 proxy 对象
if (isSingleton()) {
return getSingletonInstance();
}
else {
if (this.targetName == null) {
logger.info("Using non-singleton proxies with singleton targets is often undesirable. " +
"Enable prototype proxies by setting the 'targetName' property.");
}
return newPrototypeInstance();
}
}
4.2 initializeAdvisorChain
为 Proxy 代理对象配置 Advisor 链是在 initializeAdvisorChain 方法中完成的,代码如下:
- advisorChainInitialized 是一个标志位,表示通知器链是否已经初始化,如果已经初始化,就不会再初始化,直接返回。
- 初始化工作发生在第一次通过 ProxyFactoryBean 去获取代理对象的时候。
- 在完成这个初始化之后,接着读取配置中出现的所有通知器,这个过程是通过 getBean 依赖查找完成。
- 然后将取得的通知器加入到连接器链中,这个动作是由 addAdvisorOnChainCreation 来完成。
private synchronized void initializeAdvisorChain() throws AopConfigException, BeansException {
if (this.advisorChainInitialized) {
return;
// 通知器链已经初始化,直接返回
}// 异常检查,比如通知器链为空
...
// Materialize interceptor chain from bean names.
// 循环遍历拦截器
for (String name : this.interceptorNames) {
if (logger.isTraceEnabled()) {
logger.trace("Configuring advisor or advice '" + name + "'");
}if (name.endsWith(GLOBAL_SUFFIX)) {// 以 * 结尾
if (!(this.beanFactory instanceof ListableBeanFactory)) {
throw new AopConfigException(
"Can only use global advisors or interceptors with a ListableBeanFactory");
}
// 添加 Advisor 链的调用
addGlobalAdvisor((ListableBeanFactory) this.beanFactory,
name.substring(0, name.length() - GLOBAL_SUFFIX.length()));
}else {
// If we get here, we need to add a named interceptor.
// We must check if it's a singleton or prototype.
// 如果程序在这里被调用,那么需要加入命名的拦截器 advice,并且需要检查这个 Bean 的作用域类型
Object advice;
if (this.singleton || this.beanFactory.isSingleton(name)) {
// Add the real Advisor/Advice to the chain.
// 通过依赖查找更加名称获取 advice
advice = this.beanFactory.getBean(name);
}
else {
// It's a prototype Advice or Advisor: replace with a prototype.
// Avoid unnecessary creation of prototype bean just for advisor chain initialization.
// 对原型类型Bean 的处理
advice = new PrototypePlaceholderAdvisor(name);
}
addAdvisorOnChainCreation(advice, name);
}
}
}this.advisorChainInitialized = true;
}
4.3 getSingletonInstance
生成 singleton 的代理对象在 getSingletonInstance 方法中完成,这个方法是 ProxyFactoryBean 生成 AopProxy 代理对象的调用入口。代理对象会封装对 target 目标对象的调用,也就是说针对 target 对象的方法调用行为会被这里生成的代理对所拦截。生成步骤如下:
- 首先读取 ProxyFactoryBean 中的配置,为生成代理对象做好必要的准备,比如设置代理的方法调用接口等。
- Spring 通过 AopProxy 类来具体生成代理对象。
private synchronized Object getSingletonInstance() {
if (this.singletonInstance == null) {
this.targetSource = freshTargetSource();
if (this.autodetectInterfaces && getProxiedInterfaces().length == 0 && !isProxyTargetClass()) {
// Rely on AOP infrastructure to tell us what interfaces to proxy.
// 根据 AOP 框架来判断需要代理的接口
Class> targetClass = getTargetClass();
if (targetClass == null) {
throw new FactoryBeanNotInitializedException("Cannot determine target class for proxy");
}
// 这里设置代理对象的接口
setInterfaces(ClassUtils.getAllInterfacesForClass(targetClass, this.proxyClassLoader));
}
// Initialize the shared singleton instance.
super.setFrozen(this.freezeProxy);
// 注意这里的方法会使用 ProxyFactory 来生成需要的 Proxy
this.singletonInstance = getProxy(createAopProxy());
}
return this.singletonInstance;
}
这里出现了 AopProxy 类型的对象,Spring 利用这个 AopProxy 接口类把 AOP 嗲你对象的实现与框架的其他部分有效的分离开来,AopProxy 是一个接口,它有两个子类实现分别是 CglibAopProxy 和 JdkDynamicAopProxy,从名称就可以看出一个是 CGLIB 的实现,一个是 JDK 动态代理的实现。
4.4 createAopProxy
最终这个 createAopProxy 的核心实现方法如下:
org.springframework.aop.framework.DefaultAopProxyFactory#createAopProxy
- 如果目标对象是接口类或者本身就是一个 Proxy 生成的代理类,那么使用 JDK 来生成代理对象
- 否则使用 CGLIB 来生成代理对象
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.");
}
// 如果目标类是接口或者是一个Proxy生成的代理类
if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
return new JdkDynamicAopProxy(config);
}
// 使用 CGLIB 生成
return new ObjenesisCglibAopProxy(config);
}
else {
return new JdkDynamicAopProxy(config);
}
}
【Spring系列|Spring AOP 创建 AopProxy 代理对象原理分析】下面我们会详细讨论 JDK 和 CGLIB 如何生成 AopProxy 的具体过程。
5.JDK 生成 AopProxy 代理对象 在 JdkDynamicAopProxy 中,使用了 JDK 的 Proxy 类来生成代理对象,首先需要从 advised 对象中取得代理对象的代理接口配置,然后调用 Proxy 的 newProxyInstance 方法生成代理对象。
在生产代理对象时,需要指明三个参数
- 类加载器
- 代理接口
- Proxy 回调方法所在的对象,这个对象需要实现 InvocationHandler 接口
Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
中的这个 this
可以将 JdkDynamicAopProxy 指派给 Proxy 对象,换言之,JdkDynamicAopProxy 对象本身在 Proxy代理的接口方法被调用时,会触发 invoke 方法的回调,这个回调方法完成了 AOP 实现的封装。org.springframework.aop.framework.JdkDynamicAopProxy#getProxy(java.lang.ClassLoader)
public Object getProxy(@Nullable ClassLoader classLoader) {
if (logger.isTraceEnabled()) {
logger.trace("Creating JDK dynamic proxy: " + this.advised.getTargetSource());
}
Class>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);
findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
}
6.CGLIB 生成 AopProxy 代理对象 我们继续来看 CGLIB 来生成 Proxy 代理对象的实现,代码如下:
public Object getProxy(@Nullable ClassLoader classLoader) {
if (logger.isTraceEnabled()) {
logger.trace("Creating CGLIB proxy: " + this.advised.getTargetSource());
}try {
// 从 advised 中取得 IoC 容器配置的 Target 对象
Class> rootClass = this.advised.getTargetClass();
Assert.state(rootClass != null, "Target class must be available for creating a CGLIB proxy");
Class> proxySuperClass = rootClass;
if (rootClass.getName().contains(ClassUtils.CGLIB_CLASS_SEPARATOR)) {
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...
// 创建配置配置 CGLIB 的 Enhancer,这个 Enhancer 是 CGLIB 的主要操作类
Enhancer enhancer = createEnhancer();
if (classLoader != null) {
enhancer.setClassLoader(classLoader);
if (classLoader instanceof SmartClassLoader &&
((SmartClassLoader) classLoader).isClassReloadable(proxySuperClass)) {
enhancer.setUseCache(false);
}
}
// 设置 enhancer 对象,包括设置代理接口、回调用法
// 来自 advised 的 IoC 配置,比如使用 AOP 的 DynamicAdvisedInterceptor 拦截器
enhancer.setSuperclass(proxySuperClass);
enhancer.setInterfaces(AopProxyUtils.completeProxiedInterfaces(this.advised));
enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
enhancer.setStrategy(new ClassLoaderAwareGeneratorStrategy(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.
// 通过 enhancer 生成代理对象
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 生产代理对象。在这个生产代理对象的过程中,Enhancer 对象 callback 回调的设置,正是这些回调封装了 Spring AOP 的实现,就想前面介绍的 JDK 的 Proxy 对象的 invoke 回调方法一样。在 Enhancer 设置 callback 回调过程中,实际上是通过 DynamicAdvisedInterceptor 拦截器来完成 AOP 功能的,这个会在下一篇文章说到。
这样,通过使用 AopProxy 对象封装 target 目标对象之后,ProxyFactoryBean 的 getObject 方法得到的对象就不是一个普通的 Java 对象了,而是一个 AopProxy 代理对象。
在 ProxyFactoryBean 中配置的 target 目标对象,这时已经不会被直接调用其方法实现,而是作为 AOP 实现的一部分。对 target 目标对象的方法调用,首先被 AopProxy 拦截。对不同的 AopProxy 代理对象生成方式,会使用不同的拦截回调入库。例如,对于 JDK 的 AopProxy 代理对象,使用的是 InvocationHandler 的 invoke 回调入口;而对于 CGLIB 的 AopProxy 代理对象,使用的是设置好的 callback回调。
7.总结 可以将 AOP 的实现部分看成两个部分
- 基础设施准备
- AOP运行辅助
8.参考
- 《Spring技术内幕:深入解析Spring架构与设计原理(第2版)》- 计文柯
推荐阅读
- JAVA|(七)SpringCloud系列——流量控制组件Sentinel实战
- spring|使用这个算法我可以实现英雄联盟里英雄的走位|Java 开发实战
- 带你彻底吃透Spring
- spring系列|Spring系列之primary可以解决什么问题()
- spring boot d多层级mapper
- Spring中的 BeanWrapper
- spring|《java版Spring Cloud分布式微服务电商》- 项目简介
- spring|SpringBoot 整合Security 登陆页面,用户认证,成功失处理
- java|Java面试题