Spring AOP 创建 AopProxy 代理对象原理分析

Spring AOP 创建 AopProxy 代理对象原理分析

前言

本章开始我们将介绍一些 Spring AOP 具体的设计和实现。

项目环境

相关文章

1.设计原理

在 Spring AOP 模块中,一个主要的部分是代理对象的生成,而对于 Spring 应用,可以看到是通过配置和调用 Spring 的 ProxyFactoryBean 来完成这个任务的。在 ProxyFactoryBean 中封装了主要代理对象的生产过程。在这个过程中,可以使用 JDK 和 CGLIB 两种方式生成。以 ProxyFactoryBean 的设计为中心,相关的类继承关系图如下:
在这里插入图片描述
在这个类的继承关系中,可以看到完成 AOP 应用的类,比如 ProxyFactory、AspectJProxyFactory、ProxyFactoryBean,他们都在同一个类的基础体系下,都是 ProxyConfig、AdvisedSupport、ProxyCreatorSupport 的子类。

  • 作为共同基类,可以将 ProxyConfig 看成是一个数据基类,这个数据基类为 ProxyFactoryBean 这样的子类提供了配置属性。

  • 在另一个基类 AdvisedSupport 的实现中,封装了 AOP 对通知器的相关操作,这些操作对于不同的 AOP 代理对象的生产都是一样的,单对象语句的 AOP 代理对象的创建,AdvisedSupport 把它们交给子类去完成。

  • 对于 ProxyCreatorSupport,可以将它看到是其子类创建 AOP 代理对象的一个辅助类。

通过继承以上提到的基类功能,具体的 AOP 代理对象的生产,根据不同的需求,分别由 ProxyFactory、AspectJProxyFactory、ProxyFactoryBean 来完成。

对于需要使用 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 基础概述》)。

有了上述配置之后,就可以使用 ProxyFactoryBean 完成 AOP 的基本功能了。

配置示例:

    <bean id="testAOP" class="org.springframework.aop.framework.ProxyFactoryBean">

        <property name="proxyInterfaces">
            <value>com.csdn.spring.aop.proxy.factorybean.TestInterface</value>
        </property>

        <property name="target">
            <bean class="com.csdn.spring.aop.proxy.factorybean.TestTarget"></bean>
        </property>

        <property name="interceptorNames">
            <list>
                <value>testAdvisor</value>
            </list>
        </property>

    </bean>

    <bean id="testAdvisor" class="com.csdn.spring.aop.proxy.factorybean.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 的定义来判断作用域,生成不同的对象实例。
	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 类来具体生成代理对象。

org.springframework.aop.framework.ProxyFactoryBean#getSingletonInstance 源码如下:

	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);
		}
	}

下面我们会详细讨论 JDK 和 CGLIB 如何生成 AopProxy 的具体过程。

5.JDK 生成 AopProxy 代理对象

在 JdkDynamicAopProxy 中,使用了 JDK 的 Proxy 类来生成代理对象,首先需要从 advised 对象中取得代理对象的代理接口配置,然后调用 Proxy 的 newProxyInstance 方法生成代理对象。

在生产代理对象时,需要指明三个参数

  • 类加载器
  • 代理接口
  • Proxy 回调方法所在的对象,这个对象需要实现 InvocationHandler 接口

而 JdkDynamicAopProxy 这个类实现了 InvocationHandler 接口和 invoke 方法,所以 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运行辅助

这里的 AopProxy 代理对象的生成,可以看成是一个静态的 AOP 基础设施建立过程。通过这个准备过程,将代理对象、拦截器这些待调用的部分都准备好,等待着 AOP 运行过程中对这些基础设施的使用。对于应用触发的 AOP 应用,会涉及 AOP 框架的运行和对 AOP 基础设计的使用。这些动态运行部分,是从前面提到的拦截器回调入口开始的,这些拦截器调用的实现原理和 AopProxy 代理对象生产一样,也是 AOP 实现的重要组成部分,下一章我们来详细讨论。

8.参考

  • 《Spring技术内幕:深入解析Spring架构与设计原理(第2版)》- 计文柯
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值