AOP动态代理解析5-cglib代理的实现
CGLIB是一个强大的高性能的代码生成包。它广泛地被许多AOP的框架使用,例如Spring AOP和dynaop,为他们提供方法的Interception(拦截)。EasyMock和jMock是通过使用模仿(moke)对象来测试Java代码的包。它们都通过使用CGLIB来为那些没有接口的类创建模仿(moke)对象。
CGLIB包的底层通过使用一个小而快的字节码处理框架ASM,来转换字节码并生成新的类。除了CGLIB包,脚本语言例如Groovy和BeanShell,也是使用ASM来生成Java的字节码。当然不鼓励直接使用ASM,因为它要求你必须对JVM内部结构包括class文件的格式和指令集都很熟悉。
CGLIB代理的类是委托给CglibAopProxy类去实现的。Cglib2AopProxy的入口是在getProxy,也就是说在CglibAopProxy类的getProxy方法中实现了Enhancer的创建及接口封装。
public Object getProxy() {
return getProxy(null);
} public Object getProxy(ClassLoader classLoader) {
if (logger.isDebugEnabled()) {
logger.debug("Creating CGLIB proxy: target source is " + 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);
}
}
//验证Class
validateClassIfNecessary(proxySuperClass);
//创建及配置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 MemorySafeUndeclaredThrowableStrategy(UndeclaredThrowableException.class));
enhancer.setInterceptDuringConstruction(false);
//设置拦截器
Callback[] callbacks = getCallbacks(rootClass);
Class<?>[] types = new Class<?>[callbacks.length];
for (int x = 0; x < types.length; x++) {
types[x] = callbacks[x].getClass();
}
enhancer.setCallbackFilter(new ProxyCallbackFilter(
this.advised.getConfigurationOnlyCopy(), this.fixedInterceptorMap, this.fixedInterceptorOffset));
enhancer.setCallbackTypes(types);
enhancer.setCallbacks(callbacks);
//生成代理类以及创建代理
Object proxy;
if (this.constructorArgs != null) {
proxy = enhancer.create(this.constructorArgTypes, this.constructorArgs);
}
else {
proxy = enhancer.create();
} return proxy;
}
catch (CodeGenerationException ex) {
throw new AopConfigException("Could not generate CGLIB subclass of class [" +
this.advised.getTargetClass() + "]: " +
"Common causes of this problem include using a final class or a non-visible class",
ex);
}
catch (IllegalArgumentException ex) {
throw new AopConfigException("Could not generate CGLIB subclass of class [" +
this.advised.getTargetClass() + "]: " +
"Common causes of this problem include using a final class or a non-visible class",
ex);
}
catch (Exception ex) {
throw new AopConfigException("Unexpected AOP exception", ex);
}
}
以上函数完整地阐述了一个创建Spring中的Enhancer的过程,参考Enhancer的文档查看每个步骤的含义,这里最重要的是通过getCallbacks方法设置拦截器链。
private Callback[] getCallbacks(Class<?> rootClass) throws Exception {
//对于expose-proxy属性的处理
boolean exposeProxy = this.advised.isExposeProxy();
boolean isFrozen = this.advised.isFrozen();
boolean isStatic = this.advised.getTargetSource().isStatic();
//将拦截器封装在DynamicAdvisedInterceptor中
Callback aopInterceptor = new DynamicAdvisedInterceptor(this.advised);
// Choose a "straight to target" interceptor. (used for calls that are
// unadvised but can return this). May be required to expose the proxy.
Callback targetInterceptor;
if (exposeProxy) {
targetInterceptor = isStatic ?
new StaticUnadvisedExposedInterceptor(this.advised.getTargetSource().getTarget()) :
new DynamicUnadvisedExposedInterceptor(this.advised.getTargetSource());
}
else {
targetInterceptor = isStatic ?
new StaticUnadvisedInterceptor(this.advised.getTargetSource().getTarget()) :
new DynamicUnadvisedInterceptor(this.advised.getTargetSource());
}
// Choose a "direct to target" dispatcher (used for
// unadvised calls to static targets that cannot return this).
Callback targetDispatcher = isStatic ?
new StaticDispatcher(this.advised.getTargetSource().getTarget()) : new SerializableNoOp();
//将拦截器链加入Callback中
Callback[] mainCallbacks = new Callback[]{
aopInterceptor, // for normal advice
targetInterceptor, // invoke target without considering advice, if optimized
new SerializableNoOp(), // no override for methods mapped to this
targetDispatcher, this.advisedDispatcher,
new EqualsInterceptor(this.advised),
new HashCodeInterceptor(this.advised)
};
Callback[] callbacks;
// If the target is a static one and the advice chain is frozen,
// then we can make some optimisations by sending the AOP calls
// direct to the target using the fixed chain for that method.
if (isStatic && isFrozen) {
Method[] methods = rootClass.getMethods();
Callback[] fixedCallbacks = new Callback[methods.length];
this.fixedInterceptorMap = new HashMap<String, Integer>(methods.length); // TODO: small memory optimisation here (can skip creation for methods with no advice)
for (int x = 0; x < methods.length; x++) {
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(methods[x], rootClass);
fixedCallbacks[x] = new FixedChainStaticTargetInterceptor(
chain, this.advised.getTargetSource().getTarget(), this.advised.getTargetClass());
this.fixedInterceptorMap.put(methods[x].toString(), x);
}
// Now copy both the callbacks from mainCallbacks
// and fixedCallbacks into the callbacks array.
callbacks = new Callback[mainCallbacks.length + fixedCallbacks.length];
System.arraycopy(mainCallbacks, 0, callbacks, 0, mainCallbacks.length);
System.arraycopy(fixedCallbacks, 0, callbacks, mainCallbacks.length, fixedCallbacks.length);
this.fixedInterceptorOffset = mainCallbacks.length;
}
else {
callbacks = mainCallbacks;
}
return callbacks;
}
在getCallback中Spring考虑了很多情况,对于我们来说,只需要理解最常用的就可以了,比如将advised属性封装在DynamicAdvisedInterceptor并加入在callbacks中,这么做的目的是什么呢,如何调用呢?CGLIB中对于方法的拦截是通过将自定义的拦截器(实现MethodInterceptor接口)加入Callback中并在调用代理时直接激活拦截器中的intercept方法来实现的,那么在getCallback中正是实现了这样一个目的,DynamicAdvisedInterceptor继承自MethodInterceptor,加入Callback中后,在再次调用代理时会直接调用DynamicAdvisedInterceptor中的intercept方法,由此推断,对于CGLIB方式实现的代理,其核心逻辑必然在DynamicAdvisedInterceptor中的intercept中。
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())) {
//如果拦截器链为空则直接激活原方法
retVal = methodProxy.invoke(target, args);
}
else {
//进入链
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方式实现代理中的invoke方法大同小异,都是首先构造链,然后封装此链进行串联调用,稍有些区别就是在JDK中直接构造ReflectiveMethodInvocation,而在cglib中使用CglibMethodInvocation。CglibMethodInvocation继承自ReflectiveMethodInvocation,但是proceed方法并没有重写。
AOP动态代理解析5-cglib代理的实现的更多相关文章
- Spring AOP demo 和获取被CGLIB代理的对象
本文分为两部分:1)给出Spring AOP的一个例子(会使用CGLIB代理):2)给出获取被CGLIB代理的原始对象. 1.Spring AOP Demo 这部分参考了博文(http://www.v ...
- Java代理模式之Cglib代理
Cglib代理,也叫做子类代理.在内存中构建一个子类对象从而实现对目标对象功能的扩展. CGLIB包的底层是通过使用一个小而快的字节码处理框架ASM,来转换字节码并生成新的类.不鼓励直接使用ASM,因 ...
- 代理模式之Cglib代理
Cglib代理,也叫做子类代理.在内存中构建一个子类对象从而实现对目标对象功能的扩展. l JDK的动态代理有一个限制,就是使用动态代理的对象必须实现一个或多个接口.如果想代理没有实现接口的类,就可 ...
- jdk动态代理与cglib代理、spring aop代理实现原理解析
原创声明:本博客来源为本人原创作品,绝非他处摘取,转摘请联系博主 代理(proxy)的定义:为某对象提供代理服务,拥有操作代理对象的功能,在某些情况下,当客户不想或者不能直接引用另一个对象,而代理对象 ...
- 基于Spring AOP的JDK动态代理和CGLIB代理
一.AOP的概念 在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术.AOP是OOP的 ...
- jdk动态代理与cglib代理、spring aop代理实现原理
原创声明:本博客来源与本人另一博客[http://blog.csdn.net/liaohaojian/article/details/63683317]原创作品,绝非他处摘取 代理(proxy)的定义 ...
- 何为代理?jdk动态代理与cglib代理、spring Aop代理原理浅析
原创声明:本博客来源为本人原创作品,绝非他处摘取,转摘请联系博主 代理(proxy)的定义:为某对象提供代理服务,拥有操作代理对象的功能,在某些情况下,当客户不想或者不能直接引用另一个对象,而代理对象 ...
- jdk动态代理与cglib代理、spring Aop代理原理-代理使用浅析
原创声明:本博客来源为本人原创作品,绝非他处摘取,转摘请联系博主 代理(proxy)的定义:为某对象提供代理服务,拥有操作代理对象的功能,在某些情况下,当客户不想或者不能直接引用另一个对象,而代理对象 ...
- Java代理模式精讲之静态代理,动态代理,CGLib代理
代理(Proxy)是一种设计模式,通俗的讲就是通过别人达到自己不可告人的目的(玩笑). 如图: 代理模式的关键点是:代理对象与目标对象.代理对象是对目标对象的扩展,并会调用目标对象 这三个代理模式,就 ...
- Spring框架系列(11) - Spring AOP实现原理详解之Cglib代理实现
我们在前文中已经介绍了SpringAOP的切面实现和创建动态代理的过程,那么动态代理是如何工作的呢?本文主要介绍Cglib动态代理的案例和SpringAOP实现的原理.@pdai Spring框架系列 ...
随机推荐
- UVALive 6577 Binary Tree 二叉树的LRU串
今天继续攒人品...真开心啊O(∩_∩)O~~各种身体不舒服~~ https://icpcarchive.ecs.baylor.edu/external/65/6577.pdf 题意是这样的,现在有一 ...
- IT笔试题
史上最发散的软件工程师笔试题 1.请编写代码实现如下逻辑:有一只猫和多只老鼠,猫叫了一声,老鼠都吓跑了. 2.请介绍一种数据结构和算法,代表你在这个领域的能力. 3.请从正反两面介绍一下你对代码质量的 ...
- 调用c++接口类
调用c++接口类 public class CarDeviceDll { /*对dll库进行一些初始化*/ [DllImport("IDI.dll")] public static ...
- LeetCode 242 Valid Anagram
Problem: Given two strings s and t, write a function to determine if t is an anagram of s. For examp ...
- NIS 报错No such map passwd.byname. Reason: Can't bind to server which serves this domain
在NIS—client端使用命令:ypcat passwd ,把错如上题, 原因:client端ypbind服务未启动解决方法:当然是启动ypbind了,命令:service ypbind start ...
- Hibernate简单分页
5.1 准备工作 建立项目,加入jar 建立hibernate.cfg.xml 建立pojo类和对应的映射文件 5.2 建立vo类PageEntity package org.guangsoft.vo ...
- Maven Java EE Configuration Problem 的完美解决办法
背景: 最近在修改项目的时候,发现修改了项目依赖以后会出现如下图:Maven Java EE Configuration Problem 的问题,对于有强迫症的我来说,看到项目上面有个很小的红色小叉号 ...
- ios安装cocoaPods
1. 安装 a. 查看源 i. gem sources -l b. 删除源 i. sudo gem sources -r https://rubygems.org/ c. 设置源 i. s ...
- strlen与sizeof
strlen计算不包括终止符null字节的字符串长度,而sizeof则计算包括终止null字节的长度.另一个差别,strlen需要一次函数调用,而sizeof在编译时计算缓冲区长度.
- NYOJ题目124中位数
aaarticlea/png;base64,iVBORw0KGgoAAAANSUhEUgAAAssAAAJUCAIAAABsWvwaAAAgAElEQVR4nO3dPXLjuraG4TsJ5xqIYw