动态代理
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()
方法中