动态代理是指在运行时,动态生成代理类。代理类的字节码将在运行时生成并载入当前的ClassLoader.
生成动态代理类的方法很多,如JDK自带的动态代理、CGLIB、Javassist或者ASM库。
JDK动态代理使用简单,它内置在JDK中,因此不需要引入第三方Jar包,但相对功能比较弱。CGLIB和Javassist都是高级的字节码生成库,总体性能比JDK自带的动态代理好,而且功能十分强大。ASM是低级的字节码生成工具,使用ASM已经近乎在使用Javabytecode编程,对开发人员要求较高,也是性能最好的一种动态代理生成工具。但ASM的使用过于繁琐,而且性能也没有数量级的提升,与CGLIB等高级字节码生成工具相比,ASM程序的可维护性也较差。
JDK实现
1、步骤
1)通过实现InvocationHandler接口创建自己的调用处理器
2)通过为Proxy类指定ClassLoader对象和一组interface来创建动态代理类
3)通过反射机制获得动态代理类的构造函数,其唯一参数类型是调用处理器接口类型
4)通过构造函数创建动态代理类实例,构造时调用处理器对象作为参数被传入
2、创建代理
3、代码
执行过程:
如图,目标对象的方法调用被Proxy拦截,在InvocationHandler中的回调方法中通过反射调用,这种动态代理的方法实现了对类的方法的运行时修改。
JDK动态代理有个缺点,那就是不能对类进行代理,只能对接口进行代理,如果我们的类没有实现任何接口,那么就不能使用这种方式进行动态代理(因为$Proxy()这个类继承了Proxy,Java的集成不允许出现多个父类)。准确的说这不应该是缺点,一个良好的系统,每个类都应该有一个接口与之对应。针对接口编程。
CGLIB实现
Javassist实现
一种是使用代理工厂创建,另一种通过使用动态代码创建。使用代理工厂创建时,方法与CGLIB类似,也需要实现一个用于代理逻辑处理的Handler:例如createJavassistDynProxy();使用动态代码创建,生成字节码,这种方式可以非常灵活,甚至可以在运行时生成业务逻辑,如createJavassistBytecodeDynamicProxy()方法。
三种动态代理:JDK动态代理创建速度优于CGLIB动态代理,但是在函数的调用性能上远不如CGLIB和Javassist。故CGLIB和Javassist整体性能上比JDK动态代理好。
性能比较:
Javassist字节码最快,CGLIB次之,是JDK的两倍。
它们都是通过字节码生成来实现动态代理的。只不过是它们生成的字节码不一样,像JDK,CGLIB都考虑了很多因素,以及继承或包装了自己的一些类,所以生成的字节码非常大,而我们很多时候用不上这些,手工生成的字节码非常小(Javassist是手工生成的字节码),所以速度快。
另外,ASM也是手工生成的字节码,速度也很快,但是它没有一个数量级,通常情况下选用Javassist生成字节码的方式。
另外,从严格意义上将Aspectj不能算动态代理,应该是静态代理,因为它采用的是编译器植入。用aspectj,需要写相应的xml,定义切面,织入点等,然后由aspectj的编译器来编译出新的字节码文件,这明显是静态代理。
下面附上jdk proxy内幕
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException { //InvocationHandler不能为空,因为对代理对象的所有方法调用实际上都会委托到InvocationHandler的invoke方法, //这个我们后面通过查看产生的代理类的源代码便会一目了然 if (h == null) { throw new NullPointerException(); } //这个是核心的地方,通过提供的ClassLoader和interface列表来产生代理类,具体的实现可以参考getProxyClass这个方法的实现, //真正的工作是由sun.misc.ProxyGenerator这个类来完成的,可以google查看具体的逻辑.在我们的程序中通过设置 //System.setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles", "true")可以查看产生的类文件 Class cl = getProxyClass(loader, interfaces); //因为代理类继承了Proxy类.而Proxy中定义了构造函数protected Proxy(InvocationHandler h),所以可以反射得到Constructer实例 //创建代理对象 try { Constructor cons = cl.getConstructor(constructorParams); return (Object) cons.newInstance(new Object[] { h }); } catch (NoSuchMethodException e) { throw new InternalError(e.toString()); } catch (IllegalAccessException e) { throw new InternalError(e.toString()); } catch (InstantiationException e) { throw new InternalError(e.toString()); } catch (InvocationTargetException e) { throw new InternalError(e.toString()); } }
public interface Subject { void pub(String key, String content); String sub(String key); } public class SimpleSubject implements Subject { private Map<String, String> msg = new ConcurrentHashMap<String, String>(); public void pub(String key, String content) { System.out.println("pub msg: key is " + key + ", content is " + content); msg.put(key, content); } public String sub(String key) { if (msg.containsKey(key)) { String ret = msg.get(key); System.out.println("sub msg: key is " + key + ", result is " + ret); return ret; } return null; } } public class SubjectProxyFactory { //TODO: cache public static Subject getSubject(final Subject realSubject) { return (Subject) Proxy.newProxyInstance(realSubject.getClass().getClassLoader(), new Class[] { Subject.class }, new InvocationHandler() { public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("\naction before method invocation...."); Object retVal = method.invoke(realSubject, args); System.out.println("action after method invocation....\n"); return retVal; } }); } } public class Demo { public static void main(String[] args) { //设置此系统属性,以查看代理类文件 System.setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles", "true"); //创建真实对象 Subject subj = new SimpleSubject(); subj.pub("name", "kevin.fan"); subj.sub("name"); //创建代理对象 Subject proxy = SubjectProxyFactory.getSubject(subj); proxy.pub("hobby", "r&b music"); proxy.sub("name"); } }
pub msg: key is name, content is kevin.fan sub msg: key is name, result is kevin.fan action before method invocation.... pub msg: key is hobby, content is r&b music action after method invocation.... action before method invocation.... sub msg: key is name, result is kevin.fan action after method invocation….
//这里很清楚了,代理类继承了Proxy类,并且实现了Proxy.newProxyInstance这个方法中传入的接口 public final class $Proxy0 extends Proxy implements Subject { //这些方法在下面的static init block中进行初始化 private static Method m4; private static Method m1; private static Method m3; private static Method m0; private static Method m2; static { try { m4 = Class.forName("com.aliyun.demo.kevin.coder.lang.proxy.Subject").getMethod("sub", new Class[] { Class.forName("java.lang.String") }); m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") }); m3 = Class.forName("com.aliyun.demo.kevin.coder.lang.proxy.Subject").getMethod("pub", new Class[] { Class.forName("java.lang.String"), Class.forName("java.lang.String") }); m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]); m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]); return; } catch (NoSuchMethodException localNoSuchMethodException) { throw new NoSuchMethodError(localNoSuchMethodException.getMessage()); } catch (ClassNotFoundException localClassNotFoundException) { throw new NoClassDefFoundError(localClassNotFoundException.getMessage()); } } //构造函数,接收一个 InvocationHandler作为参数,这就是为什么Proxy.newProxyInstance方法里可以 //通过InvocationHandler实例作为参数来反射获取Constructer实例 public $Proxy0(paramInvocationHandler) throws { super(paramInvocationHandler); } //下面通过这个来看下代理对象中方法是怎样调用的 public final String sub(String paramString) throws { try { //全部是通过调用InvocationHandler的invoke方法,传入对应的方法和参数 return (String)this.h.invoke(this, m4, new Object[] { paramString }); } catch (Error|RuntimeException localError) { throw localError; } catch (Throwable localThrowable) { throw new UndeclaredThrowableException(localThrowable); } } public final boolean equals(Object paramObject) throws { try { return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue(); } catch (Error|RuntimeException localError) { throw localError; } catch (Throwable localThrowable) { throw new UndeclaredThrowableException(localThrowable); } } public final void pub(String paramString1, String paramString2) throws { try { this.h.invoke(this, m3, new Object[] { paramString1, paramString2 }); return; } catch (Error|RuntimeException localError) { throw localError; } catch (Throwable localThrowable) { throw new UndeclaredThrowableException(localThrowable); } } public final int hashCode() throws { try { return ((Integer)this.h.invoke(this, m0, null)).intValue(); } catch (Error|RuntimeException localError) { throw localError; } catch (Throwable localThrowable) { throw new UndeclaredThrowableException(localThrowable); } } public final String toString() throws { try { return (String)this.h.invoke(this, m2, null); } catch (Error|RuntimeException localError) { throw localError; } catch (Throwable localThrowable) { throw new UndeclaredThrowableException(localThrowable); } } }
Spring AOP里最重要的两个概念是增强(Advice)和切面(Advisor),增强是织入到目标类连接点上的一段程序代码,切面决定要给什么类的什么方法实施增强。是怎么做到对特定方法实施增强的呢?对invoke方法中的method#name进行判断。
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("\naction before method invocation...."); Object retVal = method.invoke(realSubject, args); System.out.println("action after method invocation....\n"); return retVal; }
Spring提供了ProxyFactoryBean创建代理,及利用BeanPostProcessor实现自动创建代理。
Spring是在运行期利用JDK或CGLib创建代理,我们还可以在类加载期间通过字节码编辑的技术,将切面织入到目标类中,这种织入方式称为LTW(Load Time Weaving)。Spring为LTW的过程提供了细粒度的控制,它支持在单个ClassLoader范围内实施类文件转换,且配置更为简单。
附小知识:
Ref ref = new RefImpl();
System.out.println(ref.getClass().getSimpleName()); //RefImpl类型
ref.f(); //调用接口方法
// ref.g(); //向上转型后实现类添加的方法不能调用
if(ref instanceof RefImpl){
RefImpl ref1 = (RefImpl)ref; //类型识别后转型
ref1.g("zhouyang");
// ref1.w(); //私有方法不能访问
相关推荐
在Java中,我们可以使用JDK自带的动态代理或者第三方库如CGLIB、Javassist、ASM来实现。 **JDK动态代理**: JDK的动态代理主要依赖于`java.lang.reflect.Proxy`和`java.lang.reflect.InvocationHandler`两个类。...
本压缩包包含关于三种主要的动态代理实现方式:JDK动态代理、CGLIB以及javassist的相关资料。 首先,JDK动态代理是Java标准库提供的一种动态代理机制,它依赖于java.lang.reflect包中的Proxy和InvocationHandler...
- **ORM(对象关系映射)**:Hibernate等ORM框架在无法使用JDK动态代理(即目标类没有实现接口)时,也会使用CGLib来创建代理对象,以便于在保存或加载对象时进行额外操作。 - **性能优化**:在某些情况下,通过...
java6string源码Java 动态代理 动态代理是 Java 开发人员生成代理对象的有用...ASM、Javassist、ByteBuddy、CGLIB 和传统 JDK 动态代理的特定代码生成创建者。 ObjectInvoker是定义方法行为的地方。 createInvokerProx
接下来,我们将分别介绍 JDK 动态代理、CGLIB 动态代理、Javassist 代理以及 ASM 代理。 ### JDK 动态代理 JDK 动态代理主要依赖于 `java.lang.reflect.Proxy` 类和 `java.lang.reflect.InvocationHandler` 接口。...
asm-3.3.1.jar, 43581 , 2019-03-06 aspectjweaver-1.6.11.jar, 1690239 , 2019-03-06 (jdk1.8和spring4)\cglib-2.2.2.jar, 287192 , 2019-03-06 (jdk1.8和spring4)\commons-dbcp-1.2.2.jar, 121757 , 2019-...
asm-3.1.jar aspectjrt-1.6.0.jar aspectjtools-1.6.0.jar aspectjweaver-1.6.0.jar bcprov-jdk16-1.46.jar c3p0-0.9.1.2.jar cas-client-core-3.3.3.jar cglib-2.2.2.jar commons-beanutils-1.8.0.jar ...
在Java中,有两种主要的动态代理实现方式:一是使用`java.lang.reflect.Proxy`类,二是使用`javassist`或`ASM`这样的字节码库。`Proxy`类是Java标准库提供的原生支持,而字节码库则允许更底层的代码操作。 1. **...
2. **字节码操作库动态代理**:如ASM、CGLIB和Javassist等,它们通过直接操作字节码生成代理类,通常比JDK动态代理效率更高,但可能会有更复杂的API。例如,CGLIB是通过继承目标类来创建代理,而ASM和Javassist则...
asm-3.1.jar aspectjrt.jar aspectjweaver-1.6.12.jar aspectjweaver.jar c3p0-0.9.1.2.jar cas-client-core-3.2.1.jar cglib-2.2.jar cglib-nodep-2.1_3.jar classmate-0.5.4.jar commons-beanutils-1.8.0.jar ...
asm-analysis-3.2.jar asm-commons-3.2.jar asm-tree-3.2.jar asm-util-3.2.jar aspectjweaver-1.7.4.jar avalon-framework-4.1.3.jar bsf-api-3.1.jar cache-api-0.4.jar cglib-nodep-2.2.jar citrus-webx-all-...
activation-1.1.jar ...asm-3.3.1.jar asm-3.3.jar asm-commons-3.3.jar asm-tree-3.3.jar axis2-java2wsdl-1.0.jar builder-0.6.2.jar c3p0-0.9.1.2.jar cglib-2.2.2.jar cglib-2.2.jar classworlds-1.1.jar ...
4,asm-all-3.1.jar 5,bcprov-jdk15on-147.jar 6,c3p0-0.9.2.1.jar 7,cglib-3.0.jar 8,common-annotations.jar 9,commons-beanutils-1.8.3.jar 10,commons-codec-1.6.jar 11,commons-collections-3.2.1.jar ...
- **asm.jar, asm-attrs.jar**:ASM字节码库,与CGLIB配合使用,处理字节码操作。 - **ehcache.jar**:EHCache缓存库,提供二级缓存功能,若无其他缓存机制,它是必需的。 - **antlr.jar**:ANTLR是一个语言识别...
4. **asm-attrs.jar** - 这是ASM的一个附属库,提供了对类属性的支持。 - **用途**:与ASM结合使用,支持更复杂的类生成需求。 5. **ehcache.jar** - EHCache是一个纯Java的二级缓存实现,用于提高数据访问速度...
Javassist和ASM则是更底层的字节码库,可以直接编辑和生成类的字节码,提供更灵活的代理实现。 序列化(Serialization)和反序列化(Deserialization)是Java中用于持久化对象和跨进程通信的关键技术。对象实现...
8. **asm-3.3.1.jar**:ASM是一个Java字节码操控和分析框架,用于动态生成类或者增强已有类的功能,CGLIB的底层实现就依赖于ASM。 9. **slf4j-api-1.7.5.jar**:Simple Logging Facade for Java,SLF4J提供一个简单...
Hibernate在没有JDK代理支持的情况下使用CGLIB来创建实体类的代理对象,以便于实现懒加载和其他高级特性。 4. **dom4j-1.6.1.jar**:DOM4J是一个非常优秀的Java XML API,它提供了全面而强大的XML处理功能,包括...
10. **cglib-nodep.jar** 或 **asm.jar**:这两者之一用于代码的动态代理和字节码操作,特别是在没有JDK代理支持的环境中。 11. **ejb3-persistence.jar**:EJB 3.0持久化API,对于兼容EJB 3的环境可能是必要的。 ...
- **工具**:常见的字节码生成工具包括JDK自带工具、CGLib、ASM、Javassist等。 - **性能比较**:根据不同的场景和需求,这些工具在字节码生成效率上的表现有所不同,一般认为ASM的性能最高,其次是Javassist,...