文档章节

Spring AOP源码分析(五)Spring AOP的Cglib代理

乒乓狂魔
 乒乓狂魔
发布于 2015/02/07 10:22
字数 1160
阅读 294
收藏 0
点赞 0
评论 0
上一篇文章介绍了Spring AOP的JDK动态代理的过程,这一篇文章就要介绍下Spring AOP的Cglib代理过程,仍然是使用上一篇文章的工程案例。

JDK动态代理是由JdkDynamicAopProxy来生成代理对象的,Cglib则是由CglibAopProxy来生成代理对象的。JdkDynamicAopProxy、CglibAopProxy实现了AopProxy接口,如下:

public interface AopProxy {

	Object getProxy();

	Object getProxy(ClassLoader classLoader);

}

然后详细看下CglibProxy的代理对象的生成过程。CglibProxy、JdkDynamicAopProxy都拥有一个非常重要的属性AdvisedSupport advised这个属性包含了拦截的配置信息,这个属性在JdkDynamicAopProxy中已经说过了,不再详细说明。
public Object getProxy(ClassLoader classLoader) {
		if (logger.isDebugEnabled()) {
			logger.debug("Creating CGLIB proxy: target source is " + this.advised.getTargetSource());
		}
		try {
//此时的rootClass为BServiceImpl
			Class<?> rootClass = this.advised.getTargetClass();
			Assert.state(rootClass != null, "Target class must be available for creating a CGLIB proxy");

			Class<?> proxySuperClass = rootClass;
//这里判断rootClass是否是Cglib代理所产生的类(内部判断rootClass的className是否包含$$),对于本工程肯定不符合,跳过
			if (ClassUtils.isCglibProxyClass(rootClass)) {
				proxySuperClass = rootClass.getSuperclass();
				Class<?>[] additionalInterfaces = rootClass.getInterfaces();
				for (Class<?> additionalInterface : additionalInterfaces) {
					this.advised.addInterface(additionalInterface);
				}
			}
//验证proxySuperClass中的是否有final方法(仅仅是打印出来警告信息,不做任何处理)
			// Validate the class, writing log messages as necessary.
			validateClassIfNecessary(proxySuperClass);

			// 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 UndeclaredThrowableStrategy(UndeclaredThrowableException.class));

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

上述内容,就是使用Enhancer设置下要继承的父类、设置下要实现的接口、设置下回调然后就创建出代理对象。其中的一个重要的回调Callback为DynamicAdvisedInterceptor,在DynamicAdvisedInterceptor的intercept方法里面实现了和JDK动态代理同样类似的逻辑。
接下来我们看下这一拦截过程是如何实现的。在DynamicAdvisedInterceptor的intercept方法里:

@Override
		public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
			Object oldProxy = null;
			boolean setProxyContext = false;
			Class<?> targetClass = null;
			Object target = null;
			try {
				if (this.advised.exposeProxy) {
					// Make invocation available if necessary.
					oldProxy = AopContext.setCurrentProxy(proxy);
					setProxyContext = true;
				}
				// May be null. Get as late as possible to minimize the time we
				// "own" the target, in case it comes from a pool...
				target = getTarget();
				if (target != null) {
					targetClass = target.getClass();
				}
				List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
				Object retVal;
				// Check whether we only have one InvokerInterceptor: that is,
				// no real advice, but just reflective invocation of the target.
				if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) {
					// We can skip creating a MethodInvocation: just invoke the target directly.
					// Note that the final invoker must be an InvokerInterceptor, so we know
					// it does nothing but a reflective operation on the target, and no hot
					// swapping or fancy proxying.
					retVal = methodProxy.invoke(target, args);
				}
				else {
					// We need to create a method invocation...
					retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
				}
				retVal = processReturnType(proxy, target, method, retVal);
				return retVal;
			}
			finally {
				if (target != null) {
					releaseTarget(target);
				}
				if (setProxyContext) {
					// Restore old proxy.
					AopContext.setCurrentProxy(oldProxy);
				}
			}
		}

上面和JDK动态代理一样也是分两大步,第一步获取一个拦截器链,第二步创建一个MethodInvocation来执行这个拦截器链。
第一步:和JDK动态代理获取拦截器链的过程一样的。
第二步:它创建的MethodInvocation是CglibMethodInvocation,它是继承了JDK动态代理所创建的ReflectiveMethodInvocation,覆写了ReflectiveMethodInvocation的invokeJoinpoint方法。ReflectiveMethodInvocation的invokeJoinpoint方法内容如下:

protected Object invokeJoinpoint() throws Throwable {
		return AopUtils.invokeJoinpointUsingReflection(this.target, this.method, this.arguments);
	}

就是利用反射进行目标方法的调用执行。
再看下CglibMethodInvocation的invokeJoinpoint方法:

protected Object invokeJoinpoint() throws Throwable {
			if (this.publicMethod) {
				return this.methodProxy.invoke(this.target, this.arguments);
			}
			else {
				return super.invokeJoinpoint();
			}
		}

this.publicMethod就是说明所调用的方法是否是public类型的。我们来看下它的来历:
public CglibMethodInvocation(Object proxy, Object target, Method method, Object[] arguments,
				Class<?> targetClass, List<Object> interceptorsAndDynamicMethodMatchers, MethodProxy methodProxy) {
			super(proxy, target, method, arguments, targetClass, interceptorsAndDynamicMethodMatchers);
			this.methodProxy = methodProxy;
			this.publicMethod = Modifier.isPublic(method.getModifiers());
		}

在构建CglibMethodInvocation这个MethodInvocation时进行赋值的。Modifier.isPublic(method.getModifiers());就是判断该方法是否是public类型的。
CglibMethodInvocation与ReflectiveMethodInvocation仅仅在执行目标方法的时候有所不同,当目标方法是public方法时,ReflectiveMethodInvocation一直采用反射的策略执行目标方法。而CglibMethodInvocation却使用this.methodProxy.invoke(this.target, this.arguments)代理方法来执行。看下它的好处的描述(CglibMethodInvocation的invokeJoinpoint()方法的注释):

/**
		 * Gives a marginal performance improvement versus using reflection to
		 * invoke the target when invoking public methods.
		 */
		@Override
		protected Object invokeJoinpoint() throws Throwable {
//略
}

当执行public方法时,会比反射有一个更好的性能。然而当我们在使用cglib的callback的时候却还是使用反射,没有去使用MethodProxy。因此我们还是按照源码的使用方式来使用,来提升性能。
本文章中许多步骤省略了,是因为在上一篇SpringAOP JDK的动态代理文章中都进行了详细介绍,同时许多的接口也在上上一篇文章SpringAOP的接口说明中给出了详细的说明。

© 著作权归作者所有

共有 人打赏支持
乒乓狂魔
粉丝 971
博文 105
码字总数 271356
作品 0
长宁
程序员
Spring AOP就是这么简单啦

前言 只有光头才能变强 上一篇已经讲解了Spring IOC知识点一网打尽!,这篇主要是讲解Spring的AOP模块~ 之前我已经写过一篇关于AOP的文章了,那篇把比较重要的知识点都讲解过了一篇啦:Sprin...

Java3y ⋅ 05/24 ⋅ 0

Spring AOP 源码分析——创建代理对象

1.简介 与筛选合适的通知器相比,创建代理对象的过程则要简单不少,本文所分析的源码不过100行,相对比较简单。在接下里的章节中,我将会首先向大家介绍一些背景知识,然后再去分析源码。那下...

代码屠夫18 ⋅ 昨天 ⋅ 0

Spring之使用注解配置Spring AOP

Spring框架通过注解配置AOP是基于AspectJ实现的。 Spring框架只是直接使用了AspectJ的注解,但并没有使用AspectJ的编译器或织入器,仍然是在运行时动态生成AOP代理。 aspectj使用的是静态代理...

Java攻城玩家 ⋅ 05/31 ⋅ 0

透过现象看原理:详解 Spring 中 Bean 的 this 调用导致 AOP 失效的原因

原文出处:光闪 前言 在我们使用Spring时,可能有前辈教导过我们,在bean中不要使用this来调用被@Async、@Transactional、@Cacheable等注解标注的方法,this下注解是不生效的。 那么大家可曾...

光闪 ⋅ 05/16 ⋅ 0

java利用AOP 实现操作日志记录(一)

除springMvc外需要引入@Aspect注解依赖: 定义切面类: 切面类可根据业务要求自行添加逻辑 在spring的容器xml中添加配置: 此处有个注意点:网上说利用AOP无法拦截controller层,经测试,需要...

晓泊 ⋅ 05/08 ⋅ 0

说说在 Spring 中如何创建增强类(AOP)

Spring 使用增强类来定义横切逻辑,同时由于 Spring 只支持方法连接点,而增强还包括在方法的哪一点上加入横切代码,所以增强类既包括横切逻辑,又包括部分连接点的信息。 1 增强类型 AOP 联...

deniro ⋅ 06/07 ⋅ 0

Spring AOP类中方法调用自身其他方法的解决方案

由来 例如这个类: 方法需要调用类中的方法,问题是进行了AOP拦截,在类中直接调用方法,将不会经过AOP拦截 工具类 写了一个工具类来解决自己调用自己的问题: 上面的代码这样改: 获得代理对...

大大枣 ⋅ 01/22 ⋅ 0

说说 Spring AOP 的底层实现技术(JDK 与 CGLib 动态代理)

Spring AOP 使用了两种代理机制: 基于 JDK 的动态代理(接口代理)。 基于 CGLib 的动态代理(类代理)。 1 订单服务实例 假设有这样的一个订单服务,它提供新增订单的方法,我们需要对这个...

deniro ⋅ 06/01 ⋅ 0

Aspectj与Spring AOP比较

1、简介 今天有多个可用的 AOP 库, 它们需要能够回答许多问题: 是否与用户现有的或新的应用程序兼容? 在哪里可以实现 AOP? 与自己的应用程序集成多快? 性能开销是多少? 在本文中, 我们将...

沈渊 ⋅ 04/18 ⋅ 0

spring事务和jdbc事务

Spring事务的基本原理 Spring事务的本质其实就是数据库对事务的支持,没有数据库的事务支持,spring是无法提供事务功能的。对于纯JDBC操作数据库,想要用到事务,可以按照以下步骤进行: 获取...

TonyStarkSir ⋅ 04/16 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

Springboot2 之 Spring Data Redis 实现消息队列——发布/订阅模式

一般来说,消息队列有两种场景,一种是发布者订阅者模式,一种是生产者消费者模式,这里利用redis消息“发布/订阅”来简单实现订阅者模式。 实现之前先过过 redis 发布订阅的一些基础概念和操...

Simonton ⋅ 33分钟前 ⋅ 0

error:Could not find gradle

一.更新Android Studio后打开Project,报如下错误: Error: Could not find com.android.tools.build:gradle:2.2.1. Searched in the following locations: file:/D:/software/android/andro......

Yao--靠自己 ⋅ 昨天 ⋅ 0

Spring boot 项目打包及引入本地jar包

Spring Boot 项目打包以及引入本地Jar包 [TOC] 上篇文章提到 Maven 项目添加本地jar包的三种方式 ,本篇文章记录下在实际项目中的应用。 spring boot 打包方式 我们知道,传统应用可以将程序...

Os_yxguang ⋅ 昨天 ⋅ 0

常见数据结构(二)-树(二叉树,红黑树,B树)

本文介绍数据结构中几种常见的树:二分查找树,2-3树,红黑树,B树 写在前面 本文所有图片均截图自coursera上普林斯顿的课程《Algorithms, Part I》中的Slides 相关命题的证明可参考《算法(第...

浮躁的码农 ⋅ 昨天 ⋅ 0

android -------- 混淆打包报错 (warning - InnerClass ...)

最近做Android混淆打包遇到一些问题,Android Sdutio 3.1 版本打包的 错误如下: Android studio warning - InnerClass annotations are missing corresponding EnclosingMember annotation......

切切歆语 ⋅ 昨天 ⋅ 0

eclipse酷炫大法之设置主题、皮肤

eclipse酷炫大法 目前两款不错的eclipse 1.系统设置 Window->Preferences->General->Appearance 2.Eclipse Marketplace下载【推荐】 Help->Eclipse Marketplace->搜索‘theme’进行安装 比如......

anlve ⋅ 昨天 ⋅ 0

vim编辑模式、vim命令模式、vim实践

vim编辑模式 编辑模式用来输入或修改文本内容,编辑模式除了Esc外其他键几乎都是输入 如何进入编辑模式 一般模式输入以下按键,均可进入编辑模式,左下角提示 insert(中文为插入) 字样 i ...

蛋黄Yolks ⋅ 昨天 ⋅ 0

大数据入门基础:SSH介绍

什么是ssh 简单说,SSH是一种网络协议,用于计算机之间的加密登录。 如果一个用户从本地计算机,使用SSH协议登录另一台远程计算机,我们就可以认为,这种登录是安全的,即使被中途截获,密码...

董黎明 ⋅ 昨天 ⋅ 0

web3j教程

web3j是一个轻量级、高度模块化、响应式、类型安全的Java和Android类库提供丰富API,用于处理以太坊智能合约及与以太坊网络上的客户端(节点)进行集成。 汇智网最新发布的web3j教程,详细讲解...

汇智网教程 ⋅ 昨天 ⋅ 0

谷歌:安全问题机制并不如你想象中安全

腾讯科技讯 5月25日,如今的你或许已经对许多网站所使用的“安全问题机制”习以为常了,但你真的认为包括“你第一个宠物的名字是什么?”这些问题能够保障你的帐户安全吗? 根据谷歌(微博)安...

问题终结者 ⋅ 昨天 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部