- 浏览: 1601193 次
- 性别:
- 来自: 杭州
-
文章分类
最新评论
-
jsrgzhangzhiyong:
关于null值的转换还是感觉不太友好,就像 mapstruct ...
我也造了个轮子:BeanMapping(属性拷贝) -
he037:
a417930422 写道引用使用EPHEMERAL会引出一个 ...
基于zookeeper的分布式lock实现 -
seancheer:
qianshangding 写道首先节点启动后,尝试读取本地的 ...
zookeeper学习记录三(session,watcher,persit机制) -
雪夜归人:
您好,我想咨询一下,开源的canal都能支持mysql的哪些版 ...
Canal BinlogChange(mysql5.6) -
zhoudengyun:
copy 一份做记录,后续学习,请知悉
阿里巴巴开源项目: 基于mysql数据库binlog的增量订阅&消费
相关性能测试和使用例子,可以参见另一篇文章: cglib相关性能测试对比
背景
前段时间在工作中,包括一些代码阅读过程中,spring aop经常性的会看到cglib中的相关内容,包括BeanCopier,BulkBean,Enancher等内容,以前虽大致知道一些内容,原理是通过bytecode,但没具体深入代码研究,只知其所用不知其所以然,所以就特地花了半天多的工作时间研究了CGLIB的相关源码,同时结合看了下 spring Aop中对CGLIB的使用。
本文主要通过对cglib有原理的分析,反编译查看源码,例子等方式做一个介绍。
cglib基本信息
- cglib的官方网站: http://cglib.sourceforge.net/
- cglib目前的最新版本应该是2.2,公司普遍使用的版本也是这个
- 官网的samples : http://cglib.sourceforge.net/xref/samples/
cglib代码包结构
- core (核心代码)
- EmitUtils
- ReflectUtils
- KeyFactory
- ClassEmitter/CodeEmitter
- NamingPolicy/DefaultNamingPolicy
- GeneratorStrategy/DefaultGeneratorStrategy
- DebuggingClassWriter
- ClassGenerator/AbstractClassGenerator
- beans (bean操作类)
- BeanCopier
- BulkBean
- BeanMap
- ImmutableBean
- BeanGenerator
- reflect
- FastClass
- proxy
- Enhancer
- CallbackGenerator
- Callback
- MethodInterceptor , Dispatcher, LazyLoader , ProxyRefDispatcher , NoOp , FixedValue , InvocationHandler(提供和jdk proxy的功能)
- CallbackFilter
- util
- StringSwitcher
- ParallelSorter
- transform
core核心代码部分
EmitUtils
重要的工具类,主要封装了一些操作bytecode的基本函数,比如生成一个null_constructor,添加类属性add_property等
ReflectUtils
处理jdk reflect的工具类,比如获取一个类所有的Method,获取构造函数信息等。
ClassEmitter/CodeEmitter
对asm的classAdapter和MethodAdapter的实现,贯穿于cglib代码的处理
KeyFactory
类库中重要的唯一标识生成器,用于cglib做cache时做map key,比较底层的基础类。
例子:
interface BulkBeanKey { public Object newInstance(String target, String[] getters, String[] setters, String[] types); } (BulkBeanKey)KeyFactory.create(BulkBeanKey.class).newInstance(targetClassName, getters, setters, typeClassNames);
说明:
- 每个Key接口,都必须提供newInstance方法,但具体的参数可以随意定义,通过newInstance返回的为一个唯一标示,只有当传入的所有参数的equals都返回true时,生成的key才是相同的,这就相当于多key的概念。
NamingPolicy
默认的实现类:DefaultNamingPolicy, 具体cglib动态生成类的命名控制。
一般的命名规则:
- 被代理class name + "$$" + 使用cglib处理的class name + "ByCGLIB" + "$$" + key的hashcode
- 示例:FastSource$$FastClassByCGLIB$$e1a36bab.class
GeneratorStrategy
默认的实现类: DefaultGeneratorStrategy
控制ClassGenerator生成class的byte数据,中间可插入自己的处理。注意这里依赖了:DebuggingClassWriter进行class generator的处理
DebuggingClassWriter
cglib封装asm的处理类,用于生成class的byte流,通过GeneratorStrategy回调ClassGenerator.generateClass(DebuggingClassWriter),将自定义的class byte处理回调给具体的cglib上层操作类,比如由具体的BeanCopier去控制bytecode的生成。
ClassGenerator
其中一个抽象实现:AbstractClassGenerator。cglib代码中核心的Class bytecode操作主体,包含了一些cache,调用NamingPolicy,GeneratorStrategy进行处理,可以说是一个最核心的调度者。
对应的类图:

- 外部的BeanCopier都包含了一Generator,继承自AbstractClassGenerator,实现了generateClass(ClassVisitor v),Object firstInstance(Class type)方法。
- AbstractClassGenerator自身会根据Source进行cache,所以针对已经生成过的class,这里KeyFactory对应的值要相等,则会直接返回cache中的结果。所以BeanCopier每次create慢只是每次都需要new两个对象,一个是KeyFactory.newInstance,另一个是firstInstance方法调用生成一个对象。
反编译tips
大家都知道cglib是进行bytecode操作,会动态生成class,最快最直接的学习就是结合他生成的class,对照代码进行学习,效果会好很多。
system.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "指定输出目录");
可参见 cores/DebuggingClassWriter代码。说明:这样cglib会将动态生成的每个class都输出到文件中,然后我们可以通过decomp进行反编译查看源码。
beans (相关操作类)
BeanCopier
简单的示例代码就不做介绍,相信大家都指导怎么用,这里主要介绍下Convert的使用。
- 许多网友都做过BeanCopier,BeanUtils的测试,基本BeanCopier的性能是BeanUtils的10倍以上。,出了反射这一性能差异外,BeanUtils默认是开启Converter功能,允许同名,不同类型的属性进行拷贝,比如Date对象到String属性。
- 有兴趣的同学可以去比较下PropertyUtils,默认不开启Converter功能,发现性能是BeanUtils的2倍多。
初始化例子:BeanCopier copier = BeanCopier.create(Source.class, Target.class, true);
第三个参数useConverter,是否开启Convert,默认BeanCopier只会做同名,同类型属性的copier,否则就会报错。
public class BeanCopierTest { public static void main(String args[]) { System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "/tmp/1"); BeanCopier copier = BeanCopier.create(Source.class, Target.class, true); Source from = new Source(); from.setValue(1); Target to = new Target(); Converter converter = new BigIntConverter(); copier.copy(from, to, converter); //使用converter类 System.out.println(to.getValue()); } } class BigIntConverter implements net.sf.cglib.core.Converter { @Override public Object convert(Object value, Class target, Object context) { System.out.println(value.getClass() + " " + value); // from类中的value对象 System.out.println(target); // to类中的定义的参数对象 System.out.println(context.getClass() + " " + context); // String对象,具体的方法名 if (target.isAssignableFrom(BigInteger.class)) { return new BigInteger(value.toString()); } else { return value; } } } ---- 反编译后看的代码: public class Target$$BeanCopierByCGLIB$$e1c34377 extends BeanCopier { public void copy(Object obj, Object obj1, Converter converter) { Target target = (Target)obj1; Source source = (Source)obj; // 注意是直接调用,没有通过reflect target.setValue((BigInteger)converter.convert(new Integer(source.getValue()), CGLIB$load_class$java$2Emath$2EBigInteger, "setValue")); } }
使用注意
- 避免每次进行BeanCopier.create创建对象,一般建议是通过static BeanCopier copier = BeanCopier.create()
- 合理使用converter。
- 应用场景:两个对象之间同名同属性的数据拷贝, 不能单独针对其中的几个属性单独拷贝
BulkBean
相比于BeanCopier,BulkBean将整个Copy的动作拆分为getPropertyValues,setPropertyValues的两个方法,允许自定义处理的属性。
public class BulkBeanTest { public static void main(String args[]) { System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "/home/ljh/cglib"); String[] getter = new String[] { "getValue" }; String[] setter = new String[] { "setValue" }; Class[] clazzs = new Class[] { int.class }; BulkBean bean = BulkBean.create(BulkSource.class, getter, setter, clazzs); BulkSource obj = new BulkSource(); obj.setValue(1); Object[] objs = bean.getPropertyValues(obj); for (Object tmp : objs) { System.out.println(tmp); } } } class BulkSource { private int value; ..... } // 反编译后的代码: public void getPropertyValues(Object obj, Object aobj[]) { BulkSource bulksource = (BulkSource)obj; aobj[0] = new Integer(bulksource.getValue()); }
使用注意
- 避免每次进行BulkBean.create创建对象,一般建议是通过static BulkBean.create copier = BulkBean.create
- 应用场景:针对特定属性的get,set操作,一般适用通过xml配置注入和注出的属性,运行时才确定处理的Source,Target类,只需关注属性名即可。
BeanMap
相比于BeanCopier,BulkBean,都是针对两个Pojo Bean进行处理,那如果对象一个是Pojo Bean和Map对象之间,那就得看看BeanMap,将一个java bean允许通过map的api进行调用。
几个支持的操作接口:
- Object get(Object key)
- Object put(Object key, Object value)
- void putAll(Map t)
- Set entrySet()
- Collection values()
- boolean containsKey(Object key)
- ....
public class BeanMapTest { public static void main(String args[]) { // 初始化 BeanMap map = BeanMap.create(new Pojo()); // 构造 Pojo pojo = new Pojo(); pojo.setIntValue(1); pojo.setBigInteger(new BigInteger("2")); // 赋值 map.setBean(pojo); // 验证 System.out.println(map.get("intValue")); System.out.println(map.keySet()); System.out.println(map.values()); } } class Pojo { private int intValue; private BigInteger bigInteger; .... } //反编译代码查看: //首先保存了所有的属性到一个set中 private static FixedKeySet keys = new FixedKeySet(new String[] { "bigInteger", "intValue" }); public Object get(Object obj, Object obj1) { (Pojo)obj; String s = (String)obj1; s; s.hashCode(); JVM INSTR lookupswitch 2: default 72 // -139068386: 40 // 556050114: 52; goto _L1 _L2 _L3 _L2: "bigInteger"; //属性判断是否相等 equals(); JVM INSTR ifeq 73; goto _L4 _L5 _L5: break MISSING_BLOCK_LABEL_73; _L4: getBigInteger(); return; _L3: .... }
使用注意
- 避免每次进行BeanMap map = BeanMap.create();创建对象,不同于BeanCopier对象,BeanMap主要针对对象实例进行处理,所以一般建议是map.setBean(pojo);进行动态替换持有的对象实例。
- 应用场景:针对put,putAll操作会直接修改pojo对象里的属性,所以可以通过beanMap.putAll(map)进行map<->pojo属性的拷贝。
BeanGenerator
暂时没有想到合适的使用场景,不过BeanGenerator使用概念是很简单的,就是将一个Map<String,Class>properties的属性定义,动态生成一个pojo bean类。
BeanGenerator generator = new BeanGenerator(); generator.addProperty("intValue", int.class); generator.addProperty("integer", Integer.class); generator.addProperty("properties", Properties.class); Class clazz = (Class) generator.createClass(); Object obj = generator.create(); PropertyDescriptor[] getters = ReflectUtils.getBeanGetters(obj.getClass()); for (PropertyDescriptor getter : getters) { Method write = getter.getWriteMethod(); System.out.println(write.getName()); }
ImmutableBean
bean Immutable模式的一种动态class实现,Immutable模式主要应用于服务设计上,返回的pojo bean对象,不运行进行write方法调用。
说明 个人是不太建议使用cglib动态class的方式来实现bean Immutable的模式,Immutable模式应该是一种服务接口上的显示声明,而不是如此隐晦,而且pojo bean尽量做到是轻量级,简答的set/get方法,如果要做充血的领域模型那就另当别论了。 |
reflect (class,method处理)
FastClass
顾明思义,FastClass就是对Class对象进行特定的处理,比如通过数组保存method引用,因此FastClass引出了一个index下标的新概念,比如getIndex(String name, Class[] parameterTypes)就是以前的获取method的方法。
通过数组存储method,constructor等class信息,从而将原先的反射调用,转化为class.index的直接调用,从而体现所谓的FastClass。
public class FastClassTest { public static void main(String args[]) throws Exception { System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "/home/ljh/cglib"); FastClass clazz = FastClass.create(FastSource.class); // fast class反射调用 FastSource obj = (FastSource) clazz.newInstance(); clazz.invoke("setValue", new Class[] { int.class }, obj, new Object[] { 1 }); clazz.invoke("setOther", new Class[] { int.class }, obj, new Object[] { 2 }); int value = (Integer) clazz.invoke("getValue", new Class[] {}, obj, new Object[] {}); int other = (Integer) clazz.invoke("getOther", new Class[] {}, obj, new Object[] {}); System.out.println(value + " " + other); // fastMethod使用 FastMethod setValue = clazz.getMethod("setValue", new Class[] { int.class }); System.out.println("setValue index is : " + setValue.getIndex()); FastMethod getValue = clazz.getMethod("getValue", new Class[] {}); System.out.println("getValue index is : " + getValue.getIndex()); FastMethod setOther = clazz.getMethod("setOther", new Class[] { int.class }); System.out.println("setOther index is : " + setOther.getIndex()); FastMethod getOther = clazz.getMethod("getOther", new Class[] {}); System.out.println("getOther index is : " + getOther.getIndex()); // 其他 System.out.println("getDeclaredMethods : " + clazz.getJavaClass().getDeclaredMethods().length); System.out.println("getConstructors : " + clazz.getJavaClass().getConstructors().length); System.out.println("getFields : " + clazz.getJavaClass().getFields().length); System.out.println("getMaxIndex : " + clazz.getMaxIndex()); } } class FastSource { private int value; private int other; }
proxy (spring aop相关)
总体类结构图:
Callback & CallbackGenerator
- MethodInterceptor
- 类似于spring aop的around Advise的功能,大家都知道,不多做介绍。唯一需要注意的就是proxy.invokeSuper和proxy.invoke的区别。invokeSuper是退出当前interceptor的处理,进入下一个callback处理,invoke则会继续回调该方法,如果传递给invoke的obj参数出错容易造成递归调用
- Dispatcher, ProxyRefDispatcher
- 类似于delegate的模式,直接将请求分发给具体的Dispatcher调用,是否有着接口+实现分离的味道,将接口的方法调用通过Dispatcher转到实现target上。ProxyRefDispatcher与Dispatcher想比,loadObject()多了个当前代理对象的引用。
-
//反编译的部分代码 public final int cal(int i, int j) { CGLIB$CALLBACK_1; if(CGLIB$CALLBACK_1 != null) goto _L2; else goto _L1 _L1: JVM INSTR pop ; CGLIB$BIND_CALLBACKS(this); CGLIB$CALLBACK_1; _L2: loadObject(); //每次都进行调用 (DefaultCalcService); i; j; cal(); //调用实现类的方法 return; }
- LazyLoader
- 相比于Dispatcher,lazyLoader在第一次获取了loadObject后,会进行缓存,后续的请求调用都会直接调用该缓存的属性.
-
//反编译部分代码 public final int cal(int i, int j) { this; return ((DefaultCalcService)CGLIB$LOAD_PRIVATE_3()).cal(i, j); } private final synchronized Object CGLIB$LOAD_PRIVATE_3() { CGLIB$LAZY_LOADER_3; //保存的属性 if(CGLIB$LAZY_LOADER_3 != null) goto _L2; else goto _L1 _L1: JVM INSTR pop ; this; CGLIB$CALLBACK_3; if(CGLIB$CALLBACK_3 != null) goto _L4; else goto _L3 _L3: JVM INSTR pop ; CGLIB$BIND_CALLBACKS(this); CGLIB$CALLBACK_3; _L4: loadObject(); JVM INSTR dup_x1 ; CGLIB$LAZY_LOADER_3; _L2: return; }
- NoOp
- 不做任何处理,结合Filter针对不需要做代理方法直接返回,调用其原始方法
- FixedValue
- 强制方法返回固定值,可结合Filter进行控制
- InvocationHandler(提供和jdk proxy的功能),不常用
CallbackFilter
主要的作用就是callback调度,主要的一个方法:int accept(Method method);
返回的int在int值,代表对应method需要插入的callback,会静态生成到class的代码中,这样是cglib proxy区别于jdk proxy的方式,一个是静态的代码调用,一个是动态的reflect。
可以查看: Enhancer类中的emitMethods方法,line:883。在构造class method字节吗之前就已经确定需要运行的callback。
Enhancer
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "/home/ljh/cglib"); LogInteceptor logInteceptor = new LogInteceptor(); CalDispatcher calDispatcher = new CalDispatcher(); CalcProxyRefDispatcher calcProxyRefDispatcher = new CalcProxyRefDispatcher(); LazyLoaderCallback lazyLoaderCallback = new LazyLoaderCallback(); Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(CalcService.class); //接口类 enhancer.setCallbacks(new Callback[] { logInteceptor, calDispatcher, calcProxyRefDispatcher,lazyLoaderCallback, NoOp.INSTANCE }); // callback数组 enhancer.setCallbackFilter(new CalcCallbackFilter()); // filter CalcService service = (CalcService) enhancer.create(); int result = service.cal(1, 1);
Util (工具类,感觉有点鸡肋)
- StringSwitcher 提供string和int的map映射查询,给定一个string字符串,返回同个下标数组的int值,感觉很鸡肋,用Map不是可以很快速的实现功能
- ParallelSorter 看了具体的代码,没啥意思,就是提供了一个二分的快速排序和多路归并排序。没有所谓的并行排序,原本以为会涉及多线程处理,可惜没有
transform
暂时没仔细研究,更多的是对asm的封装,等下次看了asm代码后再回来研究下。
-----
相关性能测试和使用说明,可以参见另一篇文章: cglib相关性能测试对比
评论
这是我的
public class com.agapple.cglib.beans.Target$$BeanCopierByCGLIB$$e1c34377 extends net.sf.cglib.beans.BeanCopier SourceFile: "<generated>" minor version: 0 major version: 46 Constant pool: const #1 = Asciz com/agapple/cglib/beans/Target$$BeanCopierByCGLIB$$e1c34377;
而你给的类名是:
public class BeanCopier$BeanCopierKey$$KeyFactoryByCGLIB$$f32401fd extends KeyFactory
你是不是反编译错了另外一个文件?
你好,可以打包你的测试代码我看一下吗?谢谢@
这是我的
public class com.agapple.cglib.beans.Target$$BeanCopierByCGLIB$$e1c34377 extends net.sf.cglib.beans.BeanCopier SourceFile: "<generated>" minor version: 0 major version: 46 Constant pool: const #1 = Asciz com/agapple/cglib/beans/Target$$BeanCopierByCGLIB$$e1c34377;
而你给的类名是:
public class BeanCopier$BeanCopierKey$$KeyFactoryByCGLIB$$f32401fd extends KeyFactory
你是不是反编译错了另外一个文件?
package org.lyx.beancopier; import net.sf.cglib.beans.BeanCopier; import net.sf.cglib.core.Converter; import net.sf.cglib.core.DebuggingClassWriter; public class BeanCopierTest { public static void main(String[] args) { System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "d:\\temp"); BeanCopier copier = BeanCopier.create(Source.class, Target.class, true); Source from = new Source(); from.setValue(1); Target to = new Target(); Converter converter = new BigIntConverter(); copier.copy(from, to, converter); //使用converter类 System.out.println(to.getValue()); } } package org.lyx.beancopier; public class Source { private int value; public int getValue() { return value; } public void setValue(int value) { this.value = value; } } package org.lyx.beancopier; public class Target { private int value; public int getValue() { return value; } public void setValue(int value) { this.value = value; } } package org.lyx.beancopier; import java.math.BigInteger; import net.sf.cglib.core.Converter; public class BigIntConverter implements Converter{ public Object convert(Object value, Class target, Object context) { System.out.println(value.getClass() + " " + value); // from类中的value对象 System.out.println(target); // to类中的定义的参数对象 System.out.println(context.getClass() + " " + context); // String对象,具体的方法名 if (target.isAssignableFrom(BigInteger.class)) { return new BigInteger(value.toString()); } else { return value; } } }
这样输出的结果是:
CGLIB debugging enabled, writing to 'd:\temp'
class java.lang.Integer 1
int
class java.lang.String setValue
1
结果是对的。但是反编译出来的那个class文件好像,不是你上面的那样。没有copy方法呢。如下:
package net.sf.cglib.beans; import net.sf.cglib.core.KeyFactory; public class BeanCopier$BeanCopierKey$$KeyFactoryByCGLIB$$f32401fd extends KeyFactory implements BeanCopier.BeanCopierKey { private final String FIELD_0; private final String FIELD_1; private final boolean FIELD_2; public BeanCopier$BeanCopierKey$$KeyFactoryByCGLIB$$f32401fd() { } public Object newInstance(String paramString1, String paramString2, boolean paramBoolean) { return new f32401fd(paramString1, paramString2, paramBoolean); } public BeanCopier$BeanCopierKey$$KeyFactoryByCGLIB$$f32401fd(String paramString1, String paramString2, boolean paramBoolean) { this.FIELD_0 = paramString1; this.FIELD_1 = paramString2; this.FIELD_2 = paramBoolean; } public int hashCode() { (95401 * 54189869); } public boolean equals(Object paramObject) { if ((paramObject instanceof f32401fd)) { String tmp18_15 = ((f32401fd)paramObject).FIELD_0; String tmp18_8 = this.FIELD_0; if (tmp18_15 == null) { if (tmp18_8 != null) break label35; tmp18_15; tmpTernaryOp = tmp18_8; break label45; } } } public String toString() { String tmp11_8 = this.FIELD_0; if (tmp11_8 != null) tmpTernaryOp = new StringBuffer().append(tmp11_8.toString()); } }
这样就没有得到想要的那个class文件呢。麻烦您看一下,谢谢!@
可以看下 cglib相关性能测试对比http://www.iteye.com/topic/801577
那里有一些具体的使用例子,对照例子看可能更容易理解

用的是jude免费版http://jude.change-vision.com/jude-web/index.html,纯java的写的,所以具有跨平台性, linux下使用比较合适。
如果windows可以用用startuml,也是免费的。
推荐一个UML工具Visual Paradigm ,社区版是免费的,也是Java写的,跨平台。
下载地址:http://www.visual-paradigm.com/download/vpuml.jsp?edition=ce
多谢支持。
对类似jvm字节码的处理学习上的确有点门槛,类似的还有javassist,BCEL,asm
毕竟文档相对少,最好是有一定的业务需求,再去理解一下它的适用场景,再结合一下反编译工具,掌握起来会相对更快
用的是jude免费版http://jude.change-vision.com/jude-web/index.html,纯java的写的,所以具有跨平台性, linux下使用比较合适。
如果windows可以用用startuml,也是免费的。
<div class="quote_div">我也看不懂,我只求怎么去用它,至于它内部是什么样子,我就不管它了,当然大牛除外了.</div>
<p><br><br>一般常用的也就是 BeanCopier, BulkBean, BeanMap, FastClass, Enhancer(MethodInterceptor)。<br>例子上面应该都有一部分代码,主要是针对其适用场景和不适用的场景。<br><br>简单的概括几个常用的类: <br><br>BeanCopier:适用两个Pojo Bean之间,<span style="color: red;">所有属性</span>的全复制,两边的source和target的属性可以不一致,以setter方法为准,调用getter方法获取数据。<br><br>BulkBean : 相比于BeanCopier,它可以指定<span style="color: red;">特定的一组属性</span>进行处理,然后可以调用getter,setter方法进行属性拷贝。一般会应用在动态注入属性是通过xml配置时,特别有用,拷贝特定的属性到指定的目标对象上。<br><br>BeanMap : 相比于BeanCopier和BulkBean,它可以用于<span style="color: #ff0000;">解决pojo bean和map之间的转化</span>,比如将一个map的属性赋值给一个bean上,很方便把。<br><br>FastClass : 相比于java class对象,它利用了所谓的Index[]数组进行存储method,constructor。实现method.invoke实际上是先找到FastClass存储的index下标,然后进行调用。使用上和Class没什么特殊的区别,就是<span style="color: #ff0000;">多了这么一个index下标</span>概念。<br><br>Enhancer: 一般知道个MethodInterceptor就差不多了,其他的callback可以不关注。目前直接写Enhancer应该不太多,用spring aop会比较多。</p>
<p> </p>
还是看看javassist吧:D
<p><span style="font-size: small;"><br></span></p>
<p><span style="font-size: small;">看几个ProxyFactoryBean中跟cglib相关的参数: </span><span style="color: #ff0000;"><span style="font-size: small;">来自于spring 1.x版本的文档描述</span></span></p>
<p> </p>
<ol>
<li><span style="font-family: Verdana, Arial, sans-serif;"><code class="literal"><span style="font-size: small;"> proxyTargetClass</span></code><span style="font-size: small;">:这个属性为</span><code class="literal"><span style="font-size: small;">true</span></code><span style="font-size: small;">时,目标类本身被代理而不是目标类的接口。如果这个属性值被设为</span><code class="literal"><span style="font-size: small;">true</span></code><span style="font-size: small;">,CGLIB代理将被创建</span></span></li>
<li><span style="font-family: Verdana, Arial, sans-serif;"><code class="literal"><span style="font-size: small;"> optimize</span></code><span style="font-size: small;">:用来控制</span><span class="emphasis" style="color: #000000;"><em><span style="font-size: small;">通过CGLIB创建</span></em></span><span style="font-size: small;">的代理是否使用激进的优化策略。除非完全了解AOP代理如何处理优化,否则不推荐用户使用这个设置。目前这个属性仅用于CGLIB代理;对于JDK动态代理(缺省代理)无效。</span></span></li>
<li><span style="font-family: Verdana, Arial, sans-serif;"><code class="literal"><span style="font-size: small;"> frozen</span></code><span style="font-size: small;">:用来控制代理工厂被配置之后,是否还允许修改通知。缺省值为</span><code class="literal"><span style="font-size: small;">false</span></code><span style="font-size: small;">(即在代理被配置之后,不允许修改代理的配置)。</span></span></li>
</ol>
<p> </p>
<p><span style="font-size: small;"><br></span></p>
<p><span style="font-size: small;">以前一直不明白</span><span style="font-family: monospace;"><span style="font-size: small;">optimize和frozen的使用</span></span><span style="font-family: monospace;"><span style="font-size: small;">,正好借这次</span><span style="font-family: Verdana, Arial, Helvetica, sans-serif;"><span style="font-size: small;">Cglib2AopProxy的学习。</span></span></span></p>
<p><span style="font-size: small;"><br></span></p>
<p><span style="font-size: small;">ProxyCallbackFilter : 核心的callback的调度逻辑</span></p>
<p> <span style="background-color: #fafafa; white-space: pre;"><dt>For exposed proxies</dt></span></p>
<pre name="code" class="官方javaodc描述"><dd>Exposing the proxy requires code to execute before and after the
method/chain invocation. This means we must use
DynamicAdvisedInterceptor, since all other interceptors can avoid the
need for a try/catch block</dd>
<dt>For Object.finalize():</dt>
<dd>No override for this method is used.</dd>
<dt>For equals():</dt>
<dd>The EqualsInterceptor is used to redirect equals() calls to a
special handler to this proxy.</dd>
<dt>For methods on the Advised class:</dt>
<dd>the AdvisedDispatcher is used to dispatch the call directly to
the target</dd>
<dt>For advised methods:</dt>
<dd>If the target is static and the advice chain is frozen then a
FixedChainStaticTargetInterceptor specific to the method is used to
invoke the advice chain. Otherwise a DyanmicAdvisedInterceptor is
used.</dd>
<dt>For non-advised methods:</dt>
<dd>Where it can be determined that the method will not return <code>this</code>
or when <code>ProxyFactory.getExposeProxy()</code> returns <code>false</code>,
then a Dispatcher is used. For static targets, the StaticDispatcher is used;
and for dynamic targets, a DynamicUnadvisedInterceptor is used.
If it possible for the method to return <code>this</code> then a
StaticUnadvisedInterceptor is used for static targets - the
DynamicUnadvisedInterceptor already considers this.</dd>
</dl></pre>
<p> </p>
<p>里面针对haveAdvice和isFrozen做了些优化</p>
<p>1. 如果是finalize方法,直接返回NoOp,不做任何代理</p>
<p>2. 如果proxy对象是spring advised的子类,则通过cglib的Dispatcher,直接委托给advisor对象进行方法调用</p>
<p>3. 如果是equals或者hashcode方法,委托给对应的EqualsInterceptor,HashCodeInterceptor,不会走入spring的inteceptor chian。</p>
<p>4. 如果spring没有配置advise,则走了cglib的MethodInterceptor进行处理,直接delegate到相关proxy.method上(例如:StaticUnadvisedExposedInterceptor,DynamicUnadvisedExposedInterceptor会区分targetSource是否是单例,exposeProxy属性做相应的优化),也<span style="background-color: #ffffff;">不会走入spring的inteceptor chian。</span></p>
<p>5. <span style="background-color: #ffffff;">如果spring配置了advise , 而且是<span style="background-color: #ffffff;">isFrozen,默认值为false,这样会走到基于cglib MethodInterceptor的DynamicAdvisedInterceptor进行处理。</span></span></p>
<p>6. 如果spring配置了advise,还会区分是否是isFrozen=true(inteceptor chian不会动态变化), 这样会根据事先分析好的method,比如有些method不是Advisor的pointcut匹配目标,则会直接走Dispatcher进行处理,不做代理。</p>
<p> </p>
<p> </p>
<p><span style="background-color: #ffffff;">spring的advise的一系列inteceptor,都是每次请求动态调用advised.getInterceptorsAndDynamicInterceptionAdvice获取,然后涉及global inteceptor(.*匹配) , pointcut filter(方法匹配), inteceptor等处理。</span></p>
<p> </p>
<p><span style="background-color: #ffffff;">isFrozen的功能相信大家应该能明白了,但很奇怪我没找到<span style="font-family: monospace; font-size: small;">optimize在aop相关代码中的使用,搜了下,只看到了这么一处使用而已</span></span></p>
<p>
</p>
<pre name="code" class="java">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.");
}
if (targetClass.isInterface()) {
return new JdkDynamicAopProxy(config);
}
if (!cglibAvailable) {
throw new AopConfigException(
"Cannot proxy target class because CGLIB2 is not available. " +
"Add CGLIB to the class path or specify proxy interfaces.");
}
return CglibProxyFactory.createCglibProxy(config);
}
else {
return new JdkDynamicAopProxy(config);
}
}</pre>
我用的spring版本为2.5.5,估计这份中文文档是spring 1.x的,难不成spring team已经做了代码优化和重构? 知道的人可以解答下
<p> </p>
发表评论
-
yugong QuickStart
2016-03-05 01:52 0几点说明 a. 数据迁移的方案可参见设计文档,oracl ... -
阿里巴巴开源项目: 阿里巴巴去Oracle数据迁移同步工具
2016-03-05 18:29 6587背景 08年左右,阿里巴巴开始尝试MySQL的相关 ... -
愚公performance
2016-03-02 17:29 0性能测试 全量测试 场景1 (单主键, ... -
yugong AdminGuide
2016-03-02 16:40 0环境要求 操作系统 数据库 迁移方案 部署 ... -
Tddl_hint
2014-01-27 13:52 0背景 工作原理 Hint格式 direct模 ... -
tddl5分库规则
2014-01-26 14:41 0背景 工作原理 构建语法树 元数据 基于 ... -
tddl5优化器
2014-01-22 15:12 0背景 工作原理 构建语法树 元数据 抽象语 ... -
Canal BinlogChange(mariadb5/10)
2014-01-20 17:25 4665背景 先前开源了一个 ... -
asynload quickstart
2013-10-08 22:49 0几点说明: 1. asyncload是做为一个j ... -
网友文档贡献
2013-09-18 15:50 01. Otter源代码解析系列 链接:http://e ... -
Manager配置介绍
2013-09-16 13:00 0通道配置说明 多种同步方式配置 a. 单向同步 ... -
canal&otter FAQ
2013-09-05 17:30 0常见问题 1. canal和 ... -
阿里巴巴开源项目:分布式数据库同步系统otter(解决中美异地机房)
2013-08-22 16:48 40528项目背景 阿里巴巴B2B公司,因为业务的特性 ... -
Otter AdminGuide
2013-08-19 11:06 0几点说明 otter系统自带了manager,所以简化了一 ... -
Otter高可用性
2013-08-17 23:41 0基本需求 网络不可靠,异地机房尤为明显. man ... -
Otter数据一致性
2013-08-17 23:39 0技术选型分析 需要处理一致性的业务场景: 多地修改 ( ... -
Otter扩展性
2013-08-17 22:20 0扩展性定义 按照实现不同,可分为两类: 数据处理自定 ... -
Otter双向回环控制
2013-08-17 21:37 0基本需求 支持mysql/oracle的异构数据库的双 ... -
Otter调度模型
2013-08-17 20:13 0背景 在介绍调度模型之前,首先了解一下otter系统要解 ... -
Otter Manager介绍
2013-08-16 11:16 0背景 otter4.0发布至 ...
相关推荐
全国大学生智能汽车竞赛自2006年起,由教育部高等教育司委托高等学校自动化类教学指导委员会举办,旨在加强学生实践、创新能力和培养团队精神的一项创意性科技竞赛。该竞赛至今已成功举办多届,吸引了众多高校学生的积极参与,此文件为智能车竞赛介绍
字卡v4.3.4 原版 三种UI+关键字卡控制+支持获取用户信息+支持强制关注 集卡模块从一开始的版本到助力版本再到现在的新规则版本。 集卡模块难度主要在于 如何控制各种不同的字卡组合 被粉丝集齐的数量。 如果不控制那么一定会出现超过数量的粉丝集到指定的字卡组合,造成奖品不够的混乱,如果大奖价值高的话,超过数量的粉丝集到大奖后,就造成商家的活动费用超支了。我们冥思苦想如何才能限制集到指定字卡组合的粉丝数,后我们想到了和支付宝一样的选一张关键字卡来进行规则设置的方式来进行限制,根据奖品所需的关键字卡数,设定规则就可以控制每种奖品所需字卡组合被粉丝集到的数量,规则可以在活动进行中根据需要进行修改,活动规则灵活度高。新版的集卡规则,在此次政府发布号的活动中经受了考验,集到指定字卡组合的粉丝没有超出规则限制。有了这个规则限制后,您无需盯着活动,建好活动后就无人值守让活动进行就行了,您只需要时不时来看下蹭蹭上涨的活动数据即可。 被封? 无需担心,模块内置有防封功能,支持隐藏主域名,显示炮灰域名,保护活动安全进行。 活动准备? 只需要您有一个认证服务号即可,支持订阅号借用认证服务号来做活动。如果您
出口设备线体程序详解:PLC通讯下的V90控制与开源FB284工艺对象实战指南,出口设备线体程序详解:PLC通讯与V90控制集成,工艺对象与FB284协同工作,开源学习V90控制技能,出口设备1200线体程序,多个plc走通讯,内部有多个v90,采用工艺对象与fb284 共同控制,功能快全部开源,能快速学会v90的控制 ,出口设备; 1200线体程序; PLC通讯; 多个V90; 工艺对象; FB284; 功能开源; V90控制。,V90工艺控制:开源功能快,快速掌握1200线体程序与PLC通讯
基于Arduino与DAC8031的心电信号模拟器资料:心电信号与正弦波的双重输出应用方案,Arduino与DAC8031心电信号模拟器:生成心电信号与正弦波输出功能详解,基于arduino +DAC8031的心电信号模拟器资料,可输出心电信号,和正弦波 ,基于Arduino;DAC8031;心电信号模拟器;输出心电信号;正弦波输出;模拟器资料,基于Arduino与DAC8031的心电信号模拟器:输出心电与正弦波
MATLAB口罩检测的基本流程 图像采集:通过摄像头或其他图像采集设备获取包含面部的图像。 图像预处理:对采集到的图像进行灰度化、去噪、直方图均衡化等预处理操作,以提高图像质量,便于后续的人脸检测和口罩检测。 人脸检测:利用Haar特征、LBP特征等经典方法或深度学习模型(如MTCNN、FaceBoxes等)在预处理后的图像中定位人脸区域。 口罩检测:在检测到的人脸区域内,进一步分析是否佩戴口罩。这可以通过检测口罩的边缘、纹理等特征,或使用已经训练好的口罩检测模型来实现。 结果输出:将检测结果以可视化方式展示,如在图像上标注人脸和口罩区域,或输出文字提示是否佩戴口罩。
1、文件内容:kernel-debug-devel-3.10.0-1160.119.1.el7.rpm以及相关依赖 2、文件形式:tar.gz压缩包 3、安装指令: #Step1、解压 tar -zxvf /mnt/data/output/kernel-debug-devel-3.10.0-1160.119.1.el7.tar.gz #Step2、进入解压后的目录,执行安装 sudo rpm -ivh *.rpm 4、更多资源/技术支持:公众号禅静编程坊
该文档提供了一个关于供应链管理系统开发的详细指南,重点介绍了项目安排、技术实现和框架搭建的相关内容。 文档分为以下几个关键部分: 项目安排:主要步骤包括搭建框架(1天),基础数据模块和权限管理(4天),以及应收应付和销售管理(5天)。 供应链概念:供应链系统的核心流程是通过采购商品放入仓库,并在销售时从仓库提取商品,涉及三个主要订单:采购订单、销售订单和调拨订单。 大数据的应用:介绍了数据挖掘、ETL(数据抽取)和BI(商业智能)在供应链管理中的应用。 技术实现:讲述了DAO(数据访问对象)的重用、服务层的重用、以及前端JS的继承机制、jQuery插件开发等技术细节。 系统框架搭建:包括Maven环境的配置、Web工程的创建、持久化类和映射文件的编写,以及Spring配置文件的实现。 DAO的需求和功能:供应链管理系统的各个模块都涉及分页查询、条件查询、删除、增加、修改操作等需求。 泛型的应用:通过示例说明了在Java语言中如何使用泛型来实现模块化和可扩展性。 文档非常技术导向,适合开发人员参考,用于构建供应链管理系统的架构和功能模块。
1.版本:matlab2014/2019a/2024a 2.附赠案例数据可直接运行matlab程序。 3.代码特点:参数化编程、参数可方便更改、代码编程思路清晰、注释明细。 4.适用对象:计算机,电子信息工程、数学等专业的大学生课程设计、期末大作业和毕业设计。
C#与VB实现欧姆龙PLC的Fins TCP通信案例源码:调用动态链接库进行数据读写,定时器与计数器数据区的简洁读写操作示例,C#与VB实现欧姆龙PLC的Fins TCP通信案例源码:调用动态链接库进行读写操作,涵盖定时器计数器数据区学习案例,C#欧姆龙plc Fins Tcp通信案例上位机源码,有c#和VB的Demo,c#上位机和欧姆龙plc通讯案例源码,调用动态链接库,可以实现上位机的数据连接,可以简单实现D区W区定时器计数器等数据区的读写,是一个非常好的学习案例 ,C#; 欧姆龙PLC; Fins Tcp通信; 上位机源码; 动态链接库; 数据连接; D区W区读写; 定时器计数器; 学习案例,C#实现欧姆龙PLC Fins Tcp通信上位机源码,读写数据区高效学习案例
可调谐石墨烯超材料吸收体的FDTD仿真模拟研究报告:吸收光谱的化学势调节策略与仿真源文件解析,可调谐石墨烯超材料吸收体:化学势调节光谱的FDTD仿真模拟研究,可调谐石墨烯超材料吸收体FDTD仿真模拟 【案例内容】该案例提供了一种可调谐石墨烯超材料吸收体,其吸收光谱可以通过改变施加于石墨烯的化学势来进行调节。 【案例文件】仿真源文件 ,可调谐石墨烯超材料吸收体; FDTD仿真模拟; 化学势调节; 仿真源文件,石墨烯超材料吸收体:FDTD仿真调节吸收光谱案例解析
RBF神经网络控制仿真-第二版
松下PLC与威纶通触摸屏转盘设备控制:FPWINPRO7与EBPRO智能编程与宏指令应用,松下PLC与威纶通触摸屏转盘设备控制解决方案:FPWINPRO7与EBPRO协同工作,实现多工位转盘加工与IEC编程模式控制,松下PLC+威纶通触摸屏的转盘设备 松下PLC工程使用程序版本为FPWINPRO7 7.6.0.0版本 威纶通HMI工程使用程序版本为EBPRO 6.07.02.410S 1.多工位转盘加工控制。 2.国际标准IEC编程模式。 3.触摸屏宏指令应用控制。 ,松下PLC; 威纶通触摸屏; 转盘设备控制; 多工位加工控制; IEC编程模式; 触摸屏宏指令应用,松下PLC与威纶通HMI联控的转盘设备控制程序解析
基于循环神经网络(RNN)的多输入单输出预测模型(适用于时间序列预测与回归分析,需Matlab 2021及以上版本),基于循环神经网络(RNN)的多输入单输出预测模型(matlab版本2021+),真实值与预测值对比,多种评价指标与线性拟合展示。,RNN预测模型做多输入单输出预测模型,直接替数据就可以用。 程序语言是matlab,需求最低版本为2021及以上。 程序可以出真实值和预测值对比图,线性拟合图,可打印多种评价指标。 PS:以下效果图为测试数据的效果图,主要目的是为了显示程序运行可以出的结果图,具体预测效果以个人的具体数据为准。 2.由于每个人的数据都是独一无二的,因此无法做到可以任何人的数据直接替就可以得到自己满意的效果。 这段程序主要是一个基于循环神经网络(RNN)的预测模型。它的应用领域可以是时间序列预测、回归分析等。下面我将对程序的运行过程进行详细解释和分析。 首先,程序开始时清空环境变量、关闭图窗、清空变量和命令行。然后,通过xlsread函数导入数据,其中'数据的输入'和'数据的输出'是两个Excel文件的文件名。 接下来,程序对数据进行归一化处理。首先使用ma
1.版本:matlab2014/2019a/2024a 2.附赠案例数据可直接运行matlab程序。 3.代码特点:参数化编程、参数可方便更改、代码编程思路清晰、注释明细。 4.适用对象:计算机,电子信息工程、数学等专业的大学生课程设计、期末大作业和毕业设计。
旅游管理系统中的功能模块主要是实现管理员;首页、个人中心、用户管理、旅游方案管理、旅游购买管理、系统管理,用户;首页、个人中心、旅游方案管理、旅游购买管理、我的收藏管理。前台首页;首页、旅游方案、旅游资讯、个人中心、后台管理等功能。经过认真细致的研究,精心准备和规划,最后测试成功,系统可以正常使用。分析功能调整与旅游管理系统实现的实际需求相结合,讨论了Java开发旅游管理系统的使用。 从上面的描述中可以基本可以实现软件的功能: 1、开发实现旅游管理系统的整个系统程序; 2、管理员;首页、个人中心、用户管理、旅游方案管理、旅游购买管理、系统管理等。 3、用户:首页、个人中心、旅游方案管理、旅游购买管理、我的收藏管理。 4、前台首页:首页、旅游方案、旅游资讯、个人中心、后台管理等相应操作; 5、基础数据管理:实现系统基本信息的添加、修改及删除等操作,并且根据需求进行交流查看及回复相应操作。
Boost二级升压光伏并网结构的Simulink建模与MPPT最大功率点追踪:基于功率反馈的扰动观察法调整电压方向研究,Boost二级升压光伏并网结构的Simulink建模与MPPT最大功率点追踪:基于功率反馈的扰动观察法调整电压方向研究,Boost二级升压光伏并网结构,Simulink建模,MPPT最大功率点追踪,扰动观察法采用功率反馈方式,若ΔP>0,说明电压调整的方向正确,可以继续按原方向进行“干扰”;若ΔP<0,说明电压调整的方向错误,需要对“干扰”的方向进行改变。 ,Boost升压;光伏并网结构;Simulink建模;MPPT最大功率点追踪;扰动观察法;功率反馈;电压调整方向。,光伏并网结构中Boost升压MPPT控制策略的Simulink建模与功率反馈扰动观察法
运行GUI版本,可二开
Deepseek相关主题资源及行业影响
WP Smush Pro 是一款专为 WordPress 网站设计的图像优化插件。 一、主要作用 图像压缩 它能够在不影响图像质量的前提下,大幅度减小图像文件的大小。例如,对于一些高分辨率的产品图片或者风景照片,它可以通过先进的压缩算法,去除图像中多余的数据。通常 JPEG 格式的图像经过压缩后,文件大小可以减少 40% – 70% 左右。这对于网站性能优化非常关键,因为较小的图像文件可以加快网站的加载速度。 该插件支持多种图像格式的压缩,包括 JPEG、PNG 和 GIF。对于 PNG 图像,它可以在保留透明度等关键特性的同时,有效地减小文件尺寸。对于 GIF 图像,也能在一定程度上优化文件大小,减少动画 GIF 的加载时间。 懒加载 WP Smush Pro 实现了图像懒加载功能。懒加载是一种延迟加载图像的技术,当用户滚动页面到包含图像的位置时,图像才会加载。这样可以避免一次性加载大量图像,尤其是在页面内容较多且包含许多图像的情况下。例如,在一个新闻网站的长文章页面,带有大量配图,懒加载可以让用户在浏览文章开头部分时,不需要等待所有图片加载,从而提高页面的初始加载速度,同时也能
Could not create share link. Missing file: C:\Users\xx\.conda\envs\omni\Lib\site-packages\gradio\frpc_windows_amd64_v0.3 1. Download this file: https://cdn-media.huggingface.co/frpc-gradio-0.3/frpc_windows_amd64.exe 2. Rename the downloaded file to: frpc_windows_amd64_v0.3 3. Move the file to this location: C:\Users\xx\.conda\envs\omni\Lib\site-packages\gradio