Java Proxy和CGLIB动态代理原理
代理模式给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用。同时代理对象可以调用被代理对象的方法,并对其进行增强。可以总结为代理对象 = 增强代码 + 目标对象(原对象)
静态代理的代理关系在编译时就确定,而动态代理的代理关系是在运行期确定的。
用Java动态代理
需要用到Proxy类的两个静态方法
- getProxyClass
- 这个方法, 会从你传入的接口Class中,“拷贝”类结构信息到一个新的Class对象中,但新的Class对象带有构造器,是可以创建对象的
- newProxyInstance(一般直接用这个)
- 封装了得到代理类Class对象、构造函数等细节,直接返回了代理对象
public interface Delivery { /** * 进行打包 */ void pack(); /** * 发货 */ void deliver(); /** * 签收 */ void sign(); }
EMS进行发货
public class Ems implements Delivery { @Override public void pack() { System.out.println("邮政快递员打好包"); } @Override public void deliver() { System.out.println("邮政快递运输"); } @Override public void sign() { System.out.println("收件人签收"); } }
测试用例
public class DeliveryPackage { public static void main(String[] args) throws InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException { Ems ems = new Ems(); Delivery delivery = (Delivery) getProxy(ems); delivery.pack(); delivery.deliver(); delivery.sign(); } public static Object getProxy(Object target) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { //target为被代理对象,获取代理类的Class Class<?> proxyClass = Proxy.getProxyClass(target.getClass().getClassLoader(), target.getClass().getInterfaces()); //获取代理类的构造器 Constructor<?> constructor = proxyClass.getConstructor(InvocationHandler.class); //获取代理对象 Object invokeObj = constructor.newInstance((InvocationHandler) (proxy, method, args) -> { Object invoke = method.invoke(target, args); return invoke; }); return invokeObj; } }
使用Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler handler)方法,方法会根据指定的参数动态创建代理对象。三个参数的意义如下:
loader,指定代理对象的类加载器;interfaces,代理对象需要实现的接口,可以同时指定多个接口;handler,方法调用的实际处理者,代理对象的方法调用都会转发到这里。
public static Object getProxyByProxyMethod(Object target) { // 直接返回代理对象 return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object invoke = method.invoke(target, args); System.out.println("代理对象增强方法"); return invoke; } }); }
newProxyInstance()返回一个实现指定接口的代理对象,对对象的所有方法调用会转发给InvocationHandler.invoke()方法。动态代理神奇的地方就是:
- 代理对象是在程序运行时产生的,而不是编译期
- 对代理对象的所有接口方法调用都会转发到
InvocationHandler.invoke()方法,在invoke()方法里可以加入任何逻辑,比如修改方法参数,加入日志功能、安全检查功能等;
注意1:对于从Object中继承的方法,JDK Proxy会把hashCode()、equals()、toString()这三个非接口方法转发给InvocationHandler,其余的Object方法则不会转发
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException { Objects.requireNonNull(h); // 下面这一段其实就是getProxyClass方法的内部实现 // -------------------------------------------------------------- final Class<?>[] intfs = interfaces.clone(); final SecurityManager sm = System.getSecurityManager(); if (sm != null) { checkProxyAccess(Reflection.getCallerClass(), loader, intfs); } /* * Look up or generate the designated proxy class. */ // 获得接口加上构造函数后的Class对象 Class<?> cl = getProxyClass0(loader, intfs); // ---------------------------------------------------------------- /* * Invoke its constructor with the designated invocation handler. */ try { if (sm != null) { checkNewProxyPermission(Reflection.getCallerClass(), cl); } // 获得构造函数 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 (IllegalAccessException|InstantiationException e) { throw new InternalError(e.toString(), e); } catch (InvocationTargetException e) { Throwable t = e.getCause(); if (t instanceof RuntimeException) { throw (RuntimeException) t; } else { throw new InternalError(t.toString(), t); } } catch (NoSuchMethodException e) { throw new InternalError(e.toString(), e); } }
为什么必须实现接口?
System.out.println(delivery.getClass().getSuperclass()); //class java.lang.reflect.Proxy
无论是什么代理类,都会继承自Proxy,而Java又是单继承的,所以想要被代理对象与代理对象产生联系,就只能通过接口来实现了
CGLIB动态代理
Code Generation Library是一个基于ASM的字节码生成库,允许在运行时对字节码进行修改和动态生成。CGLIB通过继承方式实现代理。
CGLib采用非常底层的字节码技术,可以为一个类创建一个子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,并顺势植入横切逻辑
字节码生成技术实现AOP,其实就是继承被代理对象,然后Override需要被代理的方法,在覆盖该方法时,自然是可以插入自己的代码的。因为需要Override被代理对象的方法,所以自然用CGLIB技术实现AOP时,就必须要求需要被代理的方法不能是final方法,因为final方法不能被子类覆盖。
a.使用CGLIB动态代理不要求必须有接口,生成的代理对象是目标对象的子类对象,所以需要代理的方法不能是private或者final或者static的
b.使用CGLIB动态代理需要有对cglib的jar包依赖(导入asm.jar和cglib-nodep-2.1_3.jar)
来看示例,假设我们有一个没有实现任何接口的类HelloConcrete:
public class HelloConcrete { public String sayHello(String str) { return "HelloConcrete: " + str; } }
因为没有实现接口该类无法使用JDK代理,通过CGLIB代理实现如下:
- 首先实现一个MethodInterceptor,方法调用会被转发到该类的intercept()方法
- 然后在需要使用HelloConcrete的时候,通过CGLIB动态代理获取代理对象
// CGLIB动态代理 // 首先实现一个MethodInterceptor,方法调用会被转发到该类的intercept()方法。 class MyMethodInterceptor implements MethodInterceptor{ /**
拦截方法:在代理实例上拦截并处理目标方法的调用,返回结果
* obj:目标对象代理的实例;
* method:目标对象调用父类方法的method实例;
* args:调用父类方法传递参数;
* proxy:代理的方法去调用目标方法
*/
@Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { logger.info("You said: " + Arrays.toString(args)); return proxy.invokeSuper(obj, args); } } // 2. 然后在需要使用HelloConcrete的时候,通过CGLIB动态代理获取代理对象 Enhancer enhancer = new Enhancer(); //增强器,动态代码生成器 enhancer.setSuperclass(HelloConcrete.class); //设置父类,就是被代理的目标类 enhancer.setCallback(new MyMethodInterceptor()); //设置回调 HelloConcrete hello = (HelloConcrete)enhancer.create(); //通过字节码动态技术创建子类 System.out.println(hello.sayHello("I love you!"));
运行上述代码输出结果:
日志信息: You said: [I love you!]
HelloConcrete: I love you!
通过CGLIB的Enhancer来指定要代理的目标对象、实际处理代理逻辑的对象,最终通过调用create()方法得到代理对象,对这个对象所有非final方法的调用都会转发给MethodInterceptor.intercept()方法,在intercept()方法里可以加入任何逻辑,比如修改方法参数,加入日志功能、安全检查功能等;通过调用MethodProxy.invokeSuper()方法,将调用转发给原始对象
注意:对于从Object中继承的方法,CGLIB代理也会进行代理,如hashCode()、equals()、toString()等,但是getClass()、wait()等方法不会,因为它是final方法,CGLIB无法代理。
如果对CGLIB代理之后的对象类型进行深挖,可以看到如下信息:
# HelloConcrete代理对象的类型信息 class=class cglib.HelloConcrete$$EnhancerByCGLIB$$e3734e52 superClass=class lh.HelloConcrete interfaces: interface net.sf.cglib.proxy.Factory invocationHandler=not java proxy class
看到使用CGLIB代理之后的对象类型是cglib.HelloConcrete$$EnhancerByCGLIB$$e3734e52,这是CGLIB动态生成的类型;父类是HelloConcrete,印证CGLIB是通过继承实现代理;同时实现了net.sf.cglib.proxy.Factory接口,这个接口是CGLIB自己加入的,包含一些工具方法。
注意:
(1).CGLIB是通过实现目标类的子类来实现代理,不需要定义接口
(2).生成代理对象使用最多的是通过Enhancer和继承Callback接口的MethodInterceptor接口来生成代理对象,设置callback对象的作用是当调用代理对象方法的时候会交给callback对象的来处理。
(3).创建子类对象是通过使用Enhancer类的对象,通过设置enhancer.setSuperClass(Class class)和enhancer.setCallback(Callback callback)来创建代理对象。
参考:
https://www.cnblogs.com/CarpenterLee/p/8241042.html
http://rejoy.iteye.com/blog/1627405
http://www.cnblogs.com/shijiaqi1066/p/3429691.html(推荐)
浙公网安备 33010602011771号