背景
前段时间在写hank hsf 的工具(http://www.atatech.org/articles/23763) 时,发现cndcp 中,一个单例的bean,被jmonitor 做了代理, 动态生成了3个class, 其中 CndcpConsolidationTmsOrderServiceImpl
jmonitor 配置文件。
<bean id="jmonitor-interceptor" class="com.alibaba.alimonitor.jmonitor.plugin.spring.JMonitorMethodInterceptor" />
<!-- 配置你想要拦截的namespace -->
<bean id="jmonitor-pointcut" class="org.springframework.aop.support.JdkRegexpMethodPointcut" scope="prototype">
<property name="patterns">
<list>
<value>com.alibaba.cainiao.cndcp.hsfservice.event.*</value>
<value>com.alibaba.cainiao.cndcp.hsfservice.order.*</value>
<value>com.alibaba.cainiao.cndcp.hsfservice.schedule.*</value>
</list>
</property>
</bean>
<aop:config proxy-target-class="true">
<aop:advisor advice-ref="jmonitor-interceptor" pointcut-ref="jmonitor-pointcut" />
</aop:config>
cglib proxy 示例
cglib 源码地址: https://github.com/cglib/cglib.git, 在包net.sf.cglib.samples 中有一个proxy 的示例,但是代码存在一些问题。
这里先从一个简单的示例入手,包含3个类:
1、MyBean 一个普通的javabean ,有两个方法。
2、MyMethodInterceptor 一个拦截器, 在方法执行前,向控制台输出一些信息。
3、ProxyMain
public class MyBean implements java.io.Serializable {
String sampleProperty = "original";
public MyBean(){
}
public MyBean(String sampleProperty){
super();
this.sampleProperty = sampleProperty;
}
public void setSampleProperty(String value) {
System.out.println(this + "========in bean setSampleProperty:::: " + sampleProperty);
this.sampleProperty = value;
}
public int doPlug(int num) {
return num + 1;
}
}
public class MyMethodInterceptor implements MethodInterceptor {
private String interceptorNo;
public MyMethodInterceptor(String interceptorNo){
super();
this.interceptorNo = interceptorNo;
}
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
Object retValFromSuper = null;
System.out.println("#######interceptor:" + interceptorNo + "####execut method:" + method.getName());
retValFromSuper = proxy.invokeSuper(obj, args);
return retValFromSuper;
}
}
public class ProxyMain {
public static void main(String args[]) {
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "/tmp/2");
// 第一步,创建一个Enhancer对象
Enhancer e = new Enhancer();
// 第二步,设置需要拦截的class
e.setSuperclass(MyBean.class);
// 第三部,添加两个拦截器,
MethodInterceptor[] interceptors = new MethodInterceptor[2];
interceptors[0] = new MyMethodInterceptor("0");//第一个拦截器
interceptors[1] = new MyMethodInterceptor("1");//第二个拦截器
e.setCallbacks(interceptors);
//第四步,设置拦截器的过滤规则
e.setCallbackFilter(new CallbackFilter() {
public int accept(Method method) {
if (method.getDeclaringClass().getName().equals("java.lang.Object")) {
// 当执行的是 从父类 object 上继承下来的方法如 equals, toString 等将调用拦截器材数组中的第二个拦截器。
return 1;
}
return 0;//默认调用第一个拦截器
}
});
//cglib 生成代理类
MyBean mybean = (MyBean) e.create();
mybean.toString();
mybean.setSampleProperty("TEST222222");
mybean.setSampleProperty("TEST333333");
}
}
这里需要注意的是,拦截器虽然可以定义多个, 但是经过filter 后生效的只能有一个。 当然你可以不定义filter,但那样当你传入多个拦截器时,就会 enhance.create 的时候就会抛出异常。*【 但是spring 中定义可以定义多个拦截器,那是由于spring 在拦截器上又做了一层封装, 下一篇文章会介绍到 】*
这里看到动态生成的3个类:
MyBean
MyBean
MyBean
其中 MyBean
代码执行后,jmap 查看内存对象, 【这里同spring aop相比缺少原始类的一个对象,这会在spring aop 文章中介绍到】
cglib proxy 核心代码分析
【引用:http://agapple.iteye.com/blog/799827 】
asm
cglib 是通过调用asm 生成字节码的:
asm 生成字节码的流程很简单: 定义一个 ClassWriter , 它有一系列的visit* 方法:
ClassWriter.visit 定义class实现的接口,版本,类名等。
ClassWriter.visitMethod 定义class中的方法
ClassWriter.visitField定义class中的属性
最后 ClassWriter.toByteArray 负责生成字节码。
asm 加载class : 通过调用ClassLoader.defineClass(String name, byte[] b, int off, int len)
// byte[] b 为刚才ClassWriter生成的字节码。
asm 中 ClassRead 用于从byte[],中获取class 上相关信息。
cglib 中代理类的生成
Cglib中是通过ClassGenerator的子类实现不同场景的字节码的生成的。[ FastClass(G
enerate ) 表示 Generate 是 FastClass的内部内。]
其中: spring aop 涉及到两个核心的class Generate: Enhance 和 FastClass(Generate)
类生成器: Enhance 用于生成我们的代理类。
类生成器: FastClass(Generate) ,为代理的每个方法生成一个快速的定位类,用于取代反射。
代理类MyBean
package net.sf.cglib.samples;
import java.lang.reflect.Method;
import net.sf.cglib.core.ReflectUtils;
import net.sf.cglib.core.Signature;
import net.sf.cglib.proxy.Callback;
import net.sf.cglib.proxy.Factory;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
public class MyBean$$EnhancerByCGLIB$$69c57fb0
extends MyBean
implements Factory
{
private boolean CGLIB$BOUND;
private static final ThreadLocal CGLIB$THREAD_CALLBACKS;
private static final Callback[] CGLIB$STATIC_CALLBACKS;
private MethodInterceptor CGLIB$CALLBACK_0;
private MethodInterceptor CGLIB$CALLBACK_1;
private static final Method CGLIB$setSampleProperty$0$Method;
private static final MethodProxy CGLIB$setSampleProperty$0$Proxy;
private static final Object[] CGLIB$emptyArgs;
private static final Method CGLIB$doPlug$1$Method;
private static final MethodProxy CGLIB$doPlug$1$Proxy;
private static final Method CGLIB$finalize$2$Method;
private static final MethodProxy CGLIB$finalize$2$Proxy;
private static final Method CGLIB$equals$3$Method;
private static final MethodProxy CGLIB$equals$3$Proxy;
private static final Method CGLIB$toString$4$Method;
private static final MethodProxy CGLIB$toString$4$Proxy;
private static final Method CGLIB$hashCode$5$Method;
private static final MethodProxy CGLIB$hashCode$5$Proxy;
private static final Method CGLIB$clone$6$Method;
private static final MethodProxy CGLIB$clone$6$Proxy;
static void CGLIB$STATICHOOK1()
{
CGLIB$THREAD_CALLBACKS = new ThreadLocal();
CGLIB$emptyArgs = new Object[0];
Class localClass1 = Class.forName("net.sf.cglib.samples.MyBean$$EnhancerByCGLIB$$69c57fb0");
Class localClass2;
Method[] tmp95_92 = ReflectUtils.findMethods(new String[] { "finalize", "()V", "equals", "(Ljava/lang/Object;)Z", "toString", "()Ljava/lang/String;", "hashCode", "()I", "clone", "()Ljava/lang/Object;" }, (localClass2 = Class.forName("java.lang.Object")).getDeclaredMethods());
CGLIB$finalize$2$Method = tmp95_92[0];
CGLIB$finalize$2$Proxy = MethodProxy.create(localClass2, localClass1, "()V", "finalize", "CGLIB$finalize$2");
Method[] tmp115_95 = tmp95_92;
CGLIB$equals$3$Method = tmp115_95[1];
CGLIB$equals$3$Proxy = MethodProxy.create(localClass2, localClass1, "(Ljava/lang/Object;)Z", "equals", "CGLIB$equals$3");
Method[] tmp135_115 = tmp115_95;
CGLIB$toString$4$Method = tmp135_115[2];
CGLIB$toString$4$Proxy = MethodProxy.create(localClass2, localClass1, "()Ljava/lang/String;", "toString", "CGLIB$toString$4");
Method[] tmp155_135 = tmp135_115;
CGLIB$hashCode$5$Method = tmp155_135[3];
CGLIB$hashCode$5$Proxy = MethodProxy.create(localClass2, localClass1, "()I", "hashCode", "CGLIB$hashCode$5");
Method[] tmp175_155 = tmp155_135;
CGLIB$clone$6$Method = tmp175_155[4];
CGLIB$clone$6$Proxy = MethodProxy.create(localClass2, localClass1, "()Ljava/lang/Object;", "clone", "CGLIB$clone$6");
tmp175_155;
Method[] tmp233_230 = ReflectUtils.findMethods(new String[] { "setSampleProperty", "(Ljava/lang/String;)V", "doPlug", "(I)I" }, (localClass2 = Class.forName("net.sf.cglib.samples.MyBean")).getDeclaredMethods());
CGLIB$setSampleProperty$0$Method = tmp233_230[0];
CGLIB$setSampleProperty$0$Proxy = MethodProxy.create(localClass2, localClass1, "(Ljava/lang/String;)V", "setSampleProperty", "CGLIB$setSampleProperty$0");
Method[] tmp253_233 = tmp233_230;
CGLIB$doPlug$1$Method = tmp253_233[1];
CGLIB$doPlug$1$Proxy = MethodProxy.create(localClass2, localClass1, "(I)I", "doPlug", "CGLIB$doPlug$1");
tmp253_233;
return;
}
final void CGLIB$setSampleProperty$0(String paramString)
{
super.setSampleProperty(paramString);
}
public final void setSampleProperty(String paramString)
{
MethodInterceptor tmp4_1 = this.CGLIB$CALLBACK_0;
if (tmp4_1 == null)
{
tmp4_1;
CGLIB$BIND_CALLBACKS(this);
}
if (this.CGLIB$CALLBACK_0 != null) {
return;
}
super.setSampleProperty(paramString);
}
final int CGLIB$doPlug$1(int paramInt)
{
return super.doPlug(paramInt);
}
public final int doPlug(int paramInt)
{
MethodInterceptor tmp4_1 = this.CGLIB$CALLBACK_0;
if (tmp4_1 == null)
{
tmp4_1;
CGLIB$BIND_CALLBACKS(this);
}
MethodInterceptor tmp17_14 = this.CGLIB$CALLBACK_0;
if (tmp17_14 != null)
{
Object tmp49_44 = tmp17_14.intercept(this, CGLIB$doPlug$1$Method, new Object[] { new Integer(paramInt) }, CGLIB$doPlug$1$Proxy);
tmp49_44;
return tmp49_44 == null ? 0 : ((Number)tmp49_44).intValue();
}
return super.doPlug(paramInt);
}
final void CGLIB$finalize$2()
throws Throwable
{
super.finalize();
}
protected final void finalize()
throws Throwable
{
MethodInterceptor tmp4_1 = this.CGLIB$CALLBACK_1;
if (tmp4_1 == null)
{
tmp4_1;
CGLIB$BIND_CALLBACKS(this);
}
if (this.CGLIB$CALLBACK_1 != null) {
return;
}
super.finalize();
}
final boolean CGLIB$equals$3(Object paramObject)
{
return super.equals(paramObject);
}
public final boolean equals(Object paramObject)
{
MethodInterceptor tmp4_1 = this.CGLIB$CALLBACK_1;
if (tmp4_1 == null)
{
tmp4_1;
CGLIB$BIND_CALLBACKS(this);
}
MethodInterceptor tmp17_14 = this.CGLIB$CALLBACK_1;
if (tmp17_14 != null)
{
Object tmp41_36 = tmp17_14.intercept(this, CGLIB$equals$3$Method, new Object[] { paramObject }, CGLIB$equals$3$Proxy);
tmp41_36;
return tmp41_36 == null ? false : ((Boolean)tmp41_36).booleanValue();
}
return super.equals(paramObject);
}
final String CGLIB$toString$4()
{
return super.toString();
}
public final String toString()
{
MethodInterceptor tmp4_1 = this.CGLIB$CALLBACK_1;
if (tmp4_1 == null)
{
tmp4_1;
CGLIB$BIND_CALLBACKS(this);
}
MethodInterceptor tmp17_14 = this.CGLIB$CALLBACK_1;
if (tmp17_14 != null) {
return (String)tmp17_14.intercept(this, CGLIB$toString$4$Method, CGLIB$emptyArgs, CGLIB$toString$4$Proxy);
}
return super.toString();
}
final int CGLIB$hashCode$5()
{
return super.hashCode();
}
public final int hashCode()
{
MethodInterceptor tmp4_1 = this.CGLIB$CALLBACK_1;
if (tmp4_1 == null)
{
tmp4_1;
CGLIB$BIND_CALLBACKS(this);
}
MethodInterceptor tmp17_14 = this.CGLIB$CALLBACK_1;
if (tmp17_14 != null)
{
Object tmp36_31 = tmp17_14.intercept(this, CGLIB$hashCode$5$Method, CGLIB$emptyArgs, CGLIB$hashCode$5$Proxy);
tmp36_31;
return tmp36_31 == null ? 0 : ((Number)tmp36_31).intValue();
}
return super.hashCode();
}
final Object CGLIB$clone$6()
throws CloneNotSupportedException
{
return super.clone();
}
protected final Object clone()
throws CloneNotSupportedException
{
MethodInterceptor tmp4_1 = this.CGLIB$CALLBACK_1;
if (tmp4_1 == null)
{
tmp4_1;
CGLIB$BIND_CALLBACKS(this);
}
MethodInterceptor tmp17_14 = this.CGLIB$CALLBACK_1;
if (tmp17_14 != null) {
return tmp17_14.intercept(this, CGLIB$clone$6$Method, CGLIB$emptyArgs, CGLIB$clone$6$Proxy);
}
return super.clone();
}
public static MethodProxy CGLIB$findMethodProxy(Signature paramSignature)
{
String tmp4_1 = paramSignature.toString();
switch (tmp4_1.hashCode())
{
case -1574182249:
if (tmp4_1.equals("finalize()V")) {
return CGLIB$finalize$2$Proxy;
}
break;
}
}
public MyBean$$EnhancerByCGLIB$$69c57fb0()
{
CGLIB$BIND_CALLBACKS(this);
}
public MyBean$$EnhancerByCGLIB$$69c57fb0(String paramString)
{
super(paramString);
CGLIB$BIND_CALLBACKS(this);
}
public static void CGLIB$SET_THREAD_CALLBACKS(Callback[] paramArrayOfCallback)
{
CGLIB$THREAD_CALLBACKS.set(paramArrayOfCallback);
}
public static void CGLIB$SET_STATIC_CALLBACKS(Callback[] paramArrayOfCallback)
{
CGLIB$STATIC_CALLBACKS = paramArrayOfCallback;
}
private static final void CGLIB$BIND_CALLBACKS(Object paramObject)
{
69c57fb0 local69c57fb0 = (69c57fb0)paramObject;
if (!local69c57fb0.CGLIB$BOUND)
{
local69c57fb0.CGLIB$BOUND = true;
Object tmp23_20 = CGLIB$THREAD_CALLBACKS.get();
if (tmp23_20 == null)
{
tmp23_20;
CGLIB$STATIC_CALLBACKS;
}
Callback[] tmp44_39 = ((Callback[])tmp23_20);
69c57fb0 tmp44_42 = local69c57fb0;
tmp44_42.CGLIB$CALLBACK_1 = ((MethodInterceptor)tmp44_39[1]);
tmp44_42.CGLIB$CALLBACK_0 = (tmp31_28 == null ? tmp31_28 : (MethodInterceptor)tmp44_39[0]);
}
}
public Object newInstance(Callback[] paramArrayOfCallback)
{
CGLIB$SET_THREAD_CALLBACKS(paramArrayOfCallback);
CGLIB$SET_THREAD_CALLBACKS(null);
return new 69c57fb0();
}
public Object newInstance(Callback paramCallback)
{
throw new IllegalStateException("More than one callback object required");
CGLIB$SET_THREAD_CALLBACKS(null);
return new 69c57fb0();
}
public Object newInstance(Class[] paramArrayOfClass, Object[] paramArrayOfObject, Callback[] paramArrayOfCallback)
{
CGLIB$SET_THREAD_CALLBACKS(paramArrayOfCallback);
Class[] tmp9_8 = paramArrayOfClass;
switch (tmp9_8.length)
{
case 0:
tmp9_8;
break;
case 1:
void tmp39_33 = new 69c57fb0();
if (tmp39_33[0].getName().equals("java.lang.String")) {
tmp39_33;
}
break;
}
}
public Callback getCallback(int paramInt)
{
CGLIB$BIND_CALLBACKS(this);
switch (paramInt)
{
case 0:
break;
case 1:
break;
}
return null;
}
public void setCallback(int paramInt, Callback paramCallback)
{
switch (paramInt)
{
case 0:
this.CGLIB$CALLBACK_0 = ((MethodInterceptor)paramCallback);
break;
case 1:
this.CGLIB$CALLBACK_1 = ((MethodInterceptor)paramCallback);
break;
}
}
public Callback[] getCallbacks()
{
CGLIB$BIND_CALLBACKS(this);
return new Callback[] { this.CGLIB$CALLBACK_0, this.CGLIB$CALLBACK_1 };
}
public void setCallbacks(Callback[] paramArrayOfCallback)
{
Callback[] tmp2_1 = paramArrayOfCallback;
this.CGLIB$CALLBACK_0 = ((MethodInterceptor)tmp2_1[0]);
this.CGLIB$CALLBACK_1 = ((MethodInterceptor)tmp2_1[1]);
}
static {}
}
代理类方法的执行流程
以方法,MyBean.doPlus 为例,MyBean
a、 final int CGLIB$doPlug$1 //直接调用其父类的方法。
b、 public final int doPlug(int paramInt) 判断是否有拦截器,有拦截器执行拦截器的intercept 方法。
c、 MethodProxy 对象 . MethodProxy 的两个方法 invokeSuper (对应CGLIB$doPlug$1方法) 和invoke(对应doPlug方法), 如果当前传递的对象是Enhance后的(直接使用cglib 传递的都是enhance 后的对象) ,这时就需要调用invokeSuper,否则就会出现 循环调用,导致对象溢出。
流程: 调用doplug的步骤
- 1 MyBean
EnhancerByCGLIB EnhancerByCGLIB7522cdf0.doPlus
- 2 MyMethodInterceptor.intercept
- 3 MethodProxy.invokeSuper
-
4 MethodProxy 用有个 FastClassInfo 属性, 在 MethodProxy.invokeSuper 或 MethodProxy.invoke 第一次被执行时,会创建该对象。
private static class FastClassInfo { FastClass f1; FastClass f2; int i1; int i2; }
FastClassInfo.f1 对应的就是: MyBean
FastClassByCGLIB FastClassByCGLIB15105a78, 而 FastClassInfo.f2 对应的就是:MyBeanEnhancerByCGLIB EnhancerByCGLIB7522cdf0FastClassByCGLIB FastClassByCGLIB69ea8397, 他们的作用取代反射,直接使用使用坐标查询,f1用于定位 原始bean的doplus 方法, f2 用于定位 enhance 的bean (CGLIB$doPlug$1)方法。 cglib 这里是为了FastClass的简介, 对代理类的每个Method 生成一个FastClass。 这也就是为什么我们在cndcp 中看到 CndcpConsolidationTmsOrderServiceImplEnhancerByCGLIB EnhancerByCGLIB35f433a5FastClassByCGLIB FastClassByCGLIB1e3b1902 有7个对象
那为什么 CndcpConsolidationTmsOrderServiceImplFastClassByCGLIB FastClassByCGLIB6aa1ec88 对象只有3个,而不是7个。
那是因为 CndcpConsolidationTmsOrderServiceImpl本身只定义了3个public 方法, 而其父类Object 还有4个非final 的protected 或public 方法(toString,finalize,hashCode,equal), 因此还会为原始bean生成4个 ObjectFastClassByCGLIB FastClassByCGLIBxxx 对象。
相关文档
cglib 已经停止几年没有维护了,相关文档比较少。
http://agapple.iteye.com/blog/799827 这也是我厂同学写的。
源码地址: https://github.com/cglib/cglib.git