动态代理
JDK原生动态代理
jdk动态只能代理接口,所以我们只能代理实现了接口的类或者接口
public interface ProxyInterface throws SQLException { String proxyTest(String s);}public class ProxyInterfaceImpl implements ProxyInterface { @Override public String proxyTest(String s) throws SQLException { System.out.println(s); return s+" Hello world"; }}
定义一个实现InvocationHandler
接口的类
方法调用会被转发到该类的invoke()
方法中,你可以在该方法中增加处理逻辑
public class TestInvocationHandler implements InvocationHandler { private Object proxyObject; public TestInvocationHandler(Object proxyObject) { this.proxyObject = proxyObject; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("before method"); Object o= null; try { o = method.invoke(proxyObject,args); } catch (InvocationTargetException e) { throw e.getTargetException(); } System.out.println("after method"); return o; }}
利用Proxy
生成代理类
public class JdkProxyInstance { /** * * @param proxyObject 被代理类 * @param proxyInterface 代理接口 * @return */ public static Object instance(Object proxyObject,Class ... proxyInterface){ return Proxy.newProxyInstance(JdkProxyInstance.class.getClassLoader(),proxyInterface,new TestInvocationHandler(proxyObject)); }}public class ProxyTest { @Test public void jdkProxyTest(){ ProxyInterface proxyInterface=new ProxyInterfaceImpl(); proxyInterface= (ProxyInterface)JdkProxyInstance.instance(proxyInterface,ProxyInterface.class); String result=proxyInterface.proxyTest("chenbk"); System.out.println(result); }}
生成代理类的方法
public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces, InvocationHandler handler)
loader
指定代理类的类加载器
interfaces
代理对象需要实现的接口数组handler
方法调用的实际处理者,代理对象的方法调用都会转发到这里 我们可以在handler的invoke方法中加入代码逻辑,例如日志打印、安全检查等
对于从Object中继承的方法,JDK Proxy会把hashCode()、equals()、toString()这三个非接口方法转发给InvocationHandler,其余的Object方法则不会转发。
查看JDK代理生成的Java类
jdk代理在运行时生成字节码,我们可以在将生成的字节码保存到文件中,然后利用反编译工具查看Java代码
public class ProxyGeneratorUtil { public static void writeJdkProxyClassFile(String path,Class ... clazzs)throws IOException{ byte[] classFile=ProxyGenerator.generateProxyClass("ProxyTest",clazzs); Files.write(Paths.get(path),classFile); } @Test public void createJdkProxyClass(){ try { ProxyGeneratorUtil.writeJdkProxyClassFile("D:\\test\\ProxyTest.class",ProxyInterface.class); } catch (IOException e) { e.printStackTrace(); } }}
生成的类大概就是这个样子
import com.chenbk.utils.proxy.ProxyInterface;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.lang.reflect.Proxy;import java.lang.reflect.UndeclaredThrowableException;public final class ProxyTest extends Proxy implements ProxyInterface{ private static Method m1; private static Method m3; private static Method m2; private static Method m0; public ProxyTest(InvocationHandler paramInvocationHandler) throws { super(paramInvocationHandler); } public final String proxyTest(String paramString) throws { try { return (String)this.h.invoke(this, m3, new Object[] { paramString }); } catch (Error|SQLException|RuntimeException localError) { throw localError; } catch (Throwable localThrowable) { throw new UndeclaredThrowableException(localThrowable); } } // 省略了equal()、toString()、hashCode() 方法 static { try { m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") }); m3 = Class.forName("com.chenbk.utils.proxy.ProxyInterface").getMethod("proxyTest", new Class[] { Class.forName("java.lang.String") }); m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]); m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]); return; } catch (NoSuchMethodException localNoSuchMethodException) { throw new NoSuchMethodError(localNoSuchMethodException.getMessage()); } catch (ClassNotFoundException localClassNotFoundException) { throw new NoClassDefFoundError(localClassNotFoundException.getMessage()); } }}
public class Proxy implements java.io.Serializable { private static final long serialVersionUID = -2222568056686623797L; /** ***省略代码 */ /** * the invocation handler for this proxy instance. * @serial */ protected InvocationHandler h; /** * Constructs a new {@code Proxy} instance from a subclass * (typically, a dynamic proxy class) with the specified value * for its invocation handler. * * @param h the invocation handler for this proxy instance * * @throws NullPointerException if the given invocation handler, {@code h}, * is {@code null}. */ protected Proxy(InvocationHandler h) { Objects.requireNonNull(h); this.h = h; } /** ***省略代码 */}
我们可以看到生成的类继承了Proxy
,实现了我们我们需要代理的接口,实际上JDK代理只是生成了一个实现我们指定接口的类,该类的构造方法,需要传入一个InvocationHandler
,初始化父类的h
实例变量,在proxyTest
方法中会将调用转发到h
的invoke()
方法中,由于Java只支持单继承,所以Jdk代理只能代理接口,
异常抛出处理
通过上面的程序,我们已经知道,方法会被转发到InvocationHandler
的invoke()
方法中,如果在调用invoke()
方法中抛出了受检查的的异常,并且这个受检查的异常没有在方法中声明,就会被UndeclaredThrowableException
包装并抛出
try { return (String)this.h.invoke(this, m3, new Object[] { paramString }); } catch (Error|SQLException|RuntimeException localError) { throw localError; } catch (Throwable localThrowable) { throw new UndeclaredThrowableException(localThrowable); }
然后在invoke()
方法中,我们是利用反射去调用方法,当方法抛出异常时,会被反射包装成InvocationTargetException
在抛出,这样当代理类处理时,会发现异常不会是,方法声明的异常,然后在被UndeclaredThrowableException
包装
try { o = method.invoke(proxyObject,args); } catch (InvocationTargetException e) { throw e.getTargetException(); }
我们可以将InvocationTargetException
异常捕获,然后抛出真的异常
CGLIB动态代理
CGLIB是一个基于ASM的字节码生成库,它允许我们在运行时对字节码进行修改和动态生成。CGLIB通过继承方式实现代理。
定义一个实现了MethodInterceptor
接口的类,方法会被转发到该类的intercept()
方法
public class TestMethodInterceptor implements MethodInterceptor { @Override public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { System.out.println("beford method"); Object object=methodProxy.invokeSuper(o,objects); System.out.println("after method"); return object; }}
生成代理对象
public class CglibProxyInstance { public static Object instance(Class clazz){ Enhancer enhancer=new Enhancer(); enhancer.setCallback(new TestMethodInterceptor()); enhancer.setSuperclass(clazz); return enhancer.create(); }}public class ProxyTest { @Test public void cglibProxyTest(){ ProxyInterfaceImpl proxyInterface =(ProxyInterfaceImpl)CglibProxyInstance.instance(ProxyInterfaceImpl.class); String result=proxyInterface.proxyTest("chenbk"); System.out.println(result); }}
对代理对象进行方法调用都会转发到TestMethodInterceptor
的intercept()
方法,我们可以在intercept()
方法中写代码逻辑
对于从Object中继承的方法,CGLIB代理也会进行代理,如hashCode()、equals()、toString()等,但是getClass()、wait()等方法不会,因为它是final方法,CGLIB无法代理。final类型不能有子类,所以CGLIB也不能代理final类型。
查看生成的Java类代码
public static final String DEBUG_LOCATION_PROPERTY = "cglib.debugLocation";public class ProxyGeneratorUtil { public static void writeCglibProxyClassFile(String path,Class clazz){ System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, path); CglibProxyInstance.instance(clazz); } @Test public void createCglibProxyClass(){ ProxyGeneratorUtil.writeCglibProxyClassFile("D:\\test\\ProxyTest1",ProxyInterfaceImpl.class); }}
设置系统变量cglib.debugLocation
,指定生成的字节码要保存的文件目录,当生成代理对象时会自动将字节码保存到指定的目录
package com.chenbk.utils.proxy;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 ProxyInterfaceImpl$$EnhancerByCGLIB$$296efbd extends ProxyInterfaceImpl 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 static final Method CGLIB$proxyTest$0$Method; private static final MethodProxy CGLIB$proxyTest$0$Proxy; private static final Object[] CGLIB$emptyArgs; private static final Method CGLIB$finalize$1$Method; private static final MethodProxy CGLIB$finalize$1$Proxy; private static final Method CGLIB$equals$2$Method; private static final MethodProxy CGLIB$equals$2$Proxy; private static final Method CGLIB$toString$3$Method; private static final MethodProxy CGLIB$toString$3$Proxy; private static final Method CGLIB$hashCode$4$Method; private static final MethodProxy CGLIB$hashCode$4$Proxy; private static final Method CGLIB$clone$5$Method; private static final MethodProxy CGLIB$clone$5$Proxy; /** ***省略代码 */ final String CGLIB$proxyTest$0(String paramString) { return super.proxyTest(paramString); } public final String proxyTest(String paramString) { 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) return (String)tmp17_14.intercept(this, CGLIB$proxyTest$0$Method, new Object[] { paramString }, CGLIB$proxyTest$0$Proxy); return super.proxyTest(paramString); } /** ***省略代码 */ static { CGLIB$STATICHOOK1(); }}
可以看到,当代理对象的方法调用时,会将调用先转发到MethodInterceptor
的intercept()
方法中