SpringAOP原理之---Java动态代理
2016-11-17 18:51
519 查看
前言:
前两天在学习Spring的AOP时,看到Spring默认使用JDK动态代理来实现AOP,于是对Java的动态代理原理产生了疑惑和兴趣,便查找了一些资料来解惑。现将理解记录如下。
参考资料:
1. Java 动态代理机制分析及扩展,第 1 部分
2. 使用ProxyGenerator类生成字节码
3. 说说 cglib 动态代理
4. ProxyGenerator.java源码
- 静态代理:编译前代码就已创建或生成,编译后代理类的.class文件就生成了。
- 动态代理:在程序运行时,代理类的.class文件由JVM利用反射机制生成或由ASM(字节码生成框架)动态创建。在Java中,动态代理的实现方式有两种:JDK动态代理,使用反射机制生成代理类的字节码文件,限于实现了接口的委托类;CGLIB动态代理,利用ASM框架生成代理类的字节码文件,限于没有使用final修饰的类。限制的具体原因看完后面的内容后即可明白。
输出:
before count…
CountImpl do something…
after count…
在JDK动态代理中,使用到了两个类:java.lang.reflect.Proxy和java.lang.reflect.InvocationHandler。
InvocationHandler是个十分简单的接口,只有一个方法:
Proxy是个比较复杂的类,方法众多,不过我们在利用其实现动态代理时往往只需要关注一个方法即可:
最后我们来看一下JDK利用反射生成的代理类的代码:
看到这,是否对JDK的动态代理瞬间理清楚了呢? 其实cglib的动态代理和这个类似,只不过cglib是用asm框架生成的字节码文件,而且生成的动态代理类是委托类的子类,如果委托类是final的,cglib就无能为力了。
前两天在学习Spring的AOP时,看到Spring默认使用JDK动态代理来实现AOP,于是对Java的动态代理原理产生了疑惑和兴趣,便查找了一些资料来解惑。现将理解记录如下。
参考资料:
1. Java 动态代理机制分析及扩展,第 1 部分
2. 使用ProxyGenerator类生成字节码
3. 说说 cglib 动态代理
4. ProxyGenerator.java源码
一、代理的概念
1. 什么是代理模式
代理模式是常用的设计模式之一,按照代理的创建时期可以分为两种:- 静态代理:编译前代码就已创建或生成,编译后代理类的.class文件就生成了。
- 动态代理:在程序运行时,代理类的.class文件由JVM利用反射机制生成或由ASM(字节码生成框架)动态创建。在Java中,动态代理的实现方式有两种:JDK动态代理,使用反射机制生成代理类的字节码文件,限于实现了接口的委托类;CGLIB动态代理,利用ASM框架生成代理类的字节码文件,限于没有使用final修饰的类。限制的具体原因看完后面的内容后即可明白。
2. 为什么要用代理
解决在直接访问对象时带来的问题,如:要访问的对象不在本机器上;直接访问造成系统开销过大等二、静态代理
静态代理,直接编码实现代理类。假如有委托类B,其实现了接口A的doSomething()方法,那么代理类C同样要实现接口A,不过要持有委托类B的实例引用,以便在doSomething()方法中做分派转发,在B.doSomething()前后可以织入一些其他的动作。1. 实现
/** *委托类和代理类的接口 */ public interface Count { public void count(); } /** *委托类,这里做实际的业务逻辑 */ public class CountImpl implements Count { @Override public void count() { System.out.println("CountImpl do something..."); } } public static void main(String[] args) throws Exception { CountImpl countImpl = new CountImpl(); Count count = new CountProxy(countImpl); count.count(); }
输出:
before count…
CountImpl do something…
after count…
2. 优点
编码简洁明了3. 缺点和解决方案
对于多个代理来说,代码重用率低,灵活性不足。使用动态代理来解决。三、JDK动态代理
1. 分析
我想在实现之前,先浅析一下JDK动态代理的基本原理,了解原理之后再去看实现,会清晰明白许多。在JDK动态代理中,使用到了两个类:java.lang.reflect.Proxy和java.lang.reflect.InvocationHandler。
InvocationHandler是个十分简单的接口,只有一个方法:
/** *该方法会在JDK利用反射生成代理类字节码文件时,在接口的实现方法(如接口A的doSomething()方法)中调用,因为生成的动态代理类中有一个InvocationHandler的引用。 *该接口的用法是,我们实现该接口,并在实现类内部持有一个委托类的引用,在invoke方法中围绕着委托类的方法做一些前置、后置操作。 *Object proxy 生成的动态代理类的实例 *Method method 委托类的方法对象 *Object[] args 委托类的方法所需要用到的参数列表 */ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
Proxy是个比较复杂的类,方法众多,不过我们在利用其实现动态代理时往往只需要关注一个方法即可:
/** *该静态方法用来动态生成代理类对象,该对象继承于Proxy类并实现了委托类的所有接口, *内部持有我们自定义的InvocationHandler对象的引用。 *ClassLoader loader 类加载器,一般使用委托类的类加载器,不过基本上用户类都是AppClassLoader加载的 *Class<?>[] interfaces 委托类所实现的所有接口,这样代理类的实例就可以由委托类的某个接口来持有了 *InvocationHandler h InvocationHandler实现类对象,代理类在调用方法时实际上都分派转发给了h.invoke */ public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)throws IllegalArgumentException{ //InvocationHandler不能为空 Objects.requireNonNull(h); final Class<?>[] intfs = interfaces.clone(); final SecurityManager sm = System.getSecurityManager(); if (sm != null) { //检测类加载器和要实现的接口的合法性 checkProxyAccess(Reflection.getCallerClass(), loader, intfs); } // 生成动态代理类的Class对象 Class<?> cl = getProxyClass0(loader, intfs); try { if (sm != null) { checkNewProxyPermission(Reflection.getCallerClass(), cl); } //获取动态代理类的构造函数,构造函数的的唯一参数即InvocationHandler final Constructor<?> cons = cl.getConstructor(constructorParams); final InvocationHandler ih = h; if (!Modifier.isPublic(cl.getModifiers())) { AccessController.doPrivileged(new PrivilegedAction<Void>() { public Void run() { cons.setAccessible(true); return null; } }); } //使用构造器获取动态代理类对象 return cons.newInstance(new Object[]{h}); }//catch块省略 }
最后我们来看一下JDK利用反射生成的代理类的代码:
/** *异常处理的代码都省略了 */ public final class $Proxy extends Proxy implements Count{ private static Method m3; private static Method m1; private static Method m2; public $Proxy(InvocationHandler paramInvocationHandler) { super(paramInvocationHandler); } public final int count() { return ((Integer)this.h.invoke(this, m3, new Object[] { paramUser })).intValue(); } public final boolean equals(Object paramObject){ return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue(); } public final String toString(){ return (String)this.h.invoke(this, m2, null); } static{ m3 = Class.forName("com.rambo.proxy.Count").getMethod("count"); m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") }); m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]); return; } }
看到这,是否对JDK的动态代理瞬间理清楚了呢? 其实cglib的动态代理和这个类似,只不过cglib是用asm框架生成的字节码文件,而且生成的动态代理类是委托类的子类,如果委托类是final的,cglib就无能为力了。
2. 实现
public class CountIH implements InvocationHandler { private Count count; /**1 * 内部持有实现类的引用 */ public CountIH(Count countImpl) { this.count = countImpl; } //将调用分派转发到委托类的方法上,可在方法执行前后做一些前置、后置操作。如记录日志、安全控制、事务控制等等 @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("before count..."); Object reslut = method.invoke(count, args); System.out.println("after count..."); return reslut; } } public static void main(String[] args) throws Exception { CountImpl countImpl = new CountImpl(); InvocationHandler handler = new CountIH(countImpl); //对于Spring中如何强转类型,还有疑问,继续看SpringAOP时再去寻找答案吧 Count count = (Count) Proxy.newProxyInstance(Count.class.getClassLoader(), countImpl.getClass().getInterfaces(), handler); count.count(); }
3. 优点
解决静态代理的问题4. 缺点和解决方案
如果委托类没有实现接口,则无法使用JDK动态代理,这时候就要用到cglib了;而cglib对于final类无能为力,所以这两个动态代理方法是相辅相成的。四、cglib动态代理
/** *利用增强器直接生成动态代理类对象,我推测this作为参数实际上和JDK动态代理中的newProxyInstance *方法中的InvocationHandler h参数作用类似,都是为了代理类进行分派转发。 *JDK动态代理中是为了调用h.invoke方法,cglib中是为了调用this.intercept方法。 */ public class CountProxyCglib implements MethodInterceptor { public Object getProxyInstance(Object target){ return Enhancer.create(target.getClass(), this); } @Override public Object intercept(Object target, Method method, Object[] params, MethodProxy proxy) throws Throwable { System.out.println("before count..."); //调用父类的方法 Object result = proxy.invokeSuper(target, params); System.out.println("after count..."); return result; } } /** *net.sf.cglib.proxy.Enhancer类的静态方法 */ public static Object create(Class type, Callback callback) { Enhancer e = new Enhancer(); e.setSuperclass(type); e.setCallback(callback); return e.create(); }
五、SpringAOP和动态代理
Spring使用JDK动态代理或CGLIB代理来实现,Spring缺省使用JDK动态代理来实现,从而任何接口都可别代理,如果被代理的对象实现不是接口将默认使用CGLIB代理,不过CGLIB代理当然也可应用到接口。相关文章推荐
- Spring AOP原理——Java中的动态代理机制
- Spring 4 学习笔记4:Java动态代理(Spring AOP原理)
- 反射实现 AOP 动态代理模式(Spring AOP 的实现 原理)
- 最简单的动态代理实例(spring基于接口代理的AOP原理)
- 反射实现 AOP 动态代理模式(Spring AOP 的实现 原理)
- 反射实现 AOP 动态代理模式(Spring AOP实现原理)
- 反射实现AOP 动态代理模式(Spring AOP 的实现原理)
- [转贴] 反射实现 AOP 动态代理模式(Spring AOP 的实现原理)
- 反射实现 AOP 动态代理模式(Spring AOP 的实现 原理)
- 反射实现 AOP 动态代理模式(Spring AOP 的实现 原理)
- 反射实现AOP动态代理模式(Spring AOP实现原理)
- 反射实现 AOP 动态代理模式(Spring AOP 的实现 原理)
- 反射实现 AOP 动态代理模式(Spring AOP 的实现 原理)
- 反射实现 AOP 动态代理模式(Spring AOP 的实现 原理)
- 反射实现 AOP 动态代理模式(Spring AOP 的实现 原理)
- 反射实现 AOP 动态代理模式(Spring AOP 的实现 原理)
- Spring中AOP的两种代理方式(Java动态代理和CGLIB代理)
- 转:Spring中AOP的两种代理方式(Java动态代理和CGLIB代理)
- 反射实现 AOP 动态代理模式(Spring AOP 的实现 原理)
- 反射实现 AOP 动态代理模式(Spring AOP 的实现 原理)