2019-08-21-24_Java动态代理的一些理解
前言
Github:https://github.com/HealerJean
1、Java
动态代理作用
主要用来做方法的增强,让你可以在不修改源码的情况下,增强一些方法,在方法执行前后做任何你想做的事情(甚至根本不去执行这个方法)
因为在 InvocationHandler
的 invoke
方法中,通过反射,可以直接获取正在调用方法对应的Method
对象,具体应用的话,比如可以添加调用日志,做事务控制等。
2、动态代理
2.1、JDK动态代理
2.1.1、代码实现
2.1.1.1、目标接口
public interface BuyHouse {
void buyHosue();
}
2.1.1.2、目标接口实现类
public class BuyHouseImpl implements BuyHouse {
@Override
public void buyHosue() {
System.out.println("我要买房");
}
}
2.1.1.3、动态处理器 (实现的是 InvocationHandler
)
public class DynamicProxyHandler implements InvocationHandler {
private Object object;
public DynamicProxyHandler(final Object object) {
this.object = object;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("买房前准备");
Object result = method.invoke(object, args);
System.out.println("买房后装修");
return result;
}
}
2.1.2、测试
2.1.1.4、测试
2.1.1.4.1、第一个参数 指定当前目标对象使用的类加载器,获取加载器的
2.1.1.4.2、第二个参数 指定目标对象实现的接口的类型,使用泛型方式确认类型, 这里的参数就是接口列表
2.1.1.4.3、第三个参数 指定动态处理器
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
BuyHouse proxyBuyHouse = (BuyHouse)
Proxy.newProxyInstance(BuyHouse.class.getClassLoader(),
buyHouse.getClass().getInterfaces(),
handler);
public class DynamicProxyTest {
public static void main(String[] args) throws IOException {
BuyHouse buyHouse = new BuyHouseImpl();
System.out.println("buyHouse " + buyHouse.getClass().getName());
DynamicProxyHandler handler = new DynamicProxyHandler(buyHouse);
// 1、第一个参数 指定当前目标对象使用的类加载器
// 2、第二个参数 指定目标对象实现的接口的类型 接口列表
// 3、第三个参数 指定动态处理器,
BuyHouse proxyBuyHouse = (BuyHouse) Proxy.newProxyInstance(
BuyHouse.class.getClassLoader(),
buyHouse.getClass().getInterfaces(),
handler);
System.out.println("proxyBuyHouse :" + proxyBuyHouse.getClass().getName());
proxyBuyHouse.buyHosue();
byte[] bytes = ProxyGenerator.generateProxyClass("$Proxy0", buyHouse.getClass().getInterfaces());
String code = IOUtils.toString(bytes, "utf-8");
FileOutputStream fileOutputStream = new FileOutputStream("D:/study/HealerJean.github.io/_posts/1_设计模式/hlj-design-pattern/porxy.class");
IOUtils.write(bytes, fileOutputStream);
}
}
buyHouse com.hlj.moudle.design.D09避免浪费.D21Proxy代理模式.BuyHouseImpl
proxyBuyHouse com.sun.proxy.$Proxy0
买房前准备
我要买房
买房后装修
2.1.3、生成的代理类
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
import com.hlj.moudle.design.D09避免浪费.D21Proxy代理模式.BuyHouse;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
public final class $Proxy0 extends Proxy implements BuyHouse {
private static Method m1;
private static Method m2;
private static Method m4;
private static Method m0;
private static Method m3;
public $Proxy0(InvocationHandler var1) throws {
super(var1);
}
public final boolean equals(Object var1) throws {
try {
return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
public final String toString() throws {
try {
return (String)super.h.invoke(this, m2, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final int countAdd(int var1) throws {
try {
return (Integer)super.h.invoke(this, m4, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
public final int hashCode() throws {
try {
return (Integer)super.h.invoke(this, m0, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final void buyHosue() throws {
try {
super.h.invoke(this, m3, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
m2 = Class.forName("java.lang.Object").getMethod("toString");
m4 = Class.forName("com.hlj.moudle.design.D09避免浪费.D21Proxy代理模式.BuyHouse").getMethod("countAdd", Integer.TYPE);
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
m3 = Class.forName("com.hlj.moudle.design.D09避免浪费.D21Proxy代理模式.BuyHouse").getMethod("buyHosue");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}
2.1.4、总结
jdk
动态代理是由java内部的反射机制来实现的 ,使用动态代理的对象必须实现一个或多个接口,jdk动态代理只能对接口进行代理,那么为什么呢
Java的继承机制注定了这些动态代理类们无法实现对class
的动态代理。动态代理实际上是程序在运行中,根据被代理的接口来动态生成代理类的class文件,并加载class
文件运行的过程,通过反编译被生成的$Proxy0.class文件发现:
class类定义为:
public final class $Proxy0 extends Proxy implements Interface {
public $Proxy0(InvocationHandler paramInvocationHandler) {
super(paramInvocationHandler);
}
$Proxy0
继承了需要被代理的类,由于java的单继承,动态生成的代理类已经继承了Proxy类的,就不能再继承其他的类,所以只能靠实现被代理类的接口的形式,故JDK的动态代理必须有接口。
2.2、gclib动态代理
2.2.1、代码实现
2.2.1.1、目标接口
public interface BuyHouse {
void buyHosue();
}
2.2.1.2、目标实现类
public class BuyHouseImpl implements BuyHouse {
@Override
public void buyHosue() {
System.out.println("我要买房");
}
}
2.2.1.3、CglibProxy
public class CglibProxy implements MethodInterceptor {
@SuppressWarnings("unchecked")
public <T> T getInstance(Object target, Class<T> clazz) {
//字节码加强器:用来创建动态代理类
Enhancer enhancer = new Enhancer();
//代理的目标对象
enhancer.setSuperclass(target.getClass());
//回调类,在代理类方法调用时会回调Callback类的intercept方法
enhancer.setCallback(this);
//创建代理类
Object result = enhancer.create();
System.out.println(result.getClass().getName());
return (T)result;
}
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
//调用目标类(父类)的方法
Object result = proxy.invokeSuper(obj, args);
System.out.println("proxy"+proxy);
return result;
}
// CGLIB创建的动态代理对象比JDK创建的动态代理对象的性能更高,
// 但是CGLIB创建代理对象时所花费的时间却比JDK多得多。所以对于单例的对象, 因为无需频繁创建对象,用CGLIB合适,
// 反之使用JDK方式要更为合适一些。
// 同时由于CGLib由于是采用动态创建子类的方法,对于final修饰的方法无法进行代理。
}
2.2.2、测试
public class CglibProxyTest {
public static void main(String[] args){
BuyHouse buyHouse = new BuyHouseImpl();
CglibProxy cglibProxy = new CglibProxy();
BuyHouseImpl buyHouseCglibProxy = cglibProxy.getInstance(buyHouse,BuyHouseImpl.class);
buyHouseCglibProxy.buyHosue();
}
}
2.2.2、总结
CGLib
是一个强大、高性能的Code生产类库,可以实现运行期动态扩展java类,Spring
在运行期间通过 CGlib
继承要被动态代理的类,重写父类(被代理的类)的方法,实现AOP面向切面编程呢。
JDK
的动态代理有一个限制,就是使用动态代理的对象必须实现一个或多个接口,如果想代理没有实现接口的类,就可以使用Cglib
实现。 Cglib
不能对 final
类以及 final
方法进行代理。因为他是继承被代理类的子类,如果被代理类使用了final就不能被继承
对接口进行代理的cglib,最后生成的源码是实现了该接口和Factory接口
对实现类进行代理的cglib,最后生成的源码是继承了实现类并实现了Factory接口
3、动态代理总结
3.1、jdk
代理和CGLIB代理的区别
1、JDK 和 CGLib动态代理性能对比-教科书上的描述,记得有但是
1、CGLib所创建的动态代理对象在实际运行时候的性能要比JDK动态代理高不少,有研究表明,大概要高10倍;
2、但是CGLib在创建对象的时候所花费的时间却比JDK动态代理要多很多,有研究表明,大概有8倍的差距;
3、因此,对于singleton的代理对象或者具有实例池(单例)的代理,因为无需频繁的创建代理对象,所以比较适合采用CGLib动态代理,反正,则比较适用JDK动态代理。
在1.6和1.7的时候,JDK动态代理的速度要比CGLib动态代理的速度要慢,但是并没有教科书上的10倍差距,在JDK1.8的时候,JDK动态代理的速度已经比CGLib动态代理的速度快很多了,但是JDK动态代理和CGLib动态代理的适用场景还是不一样的哈!
JDK动态代理只能对实现了接口的类生成代理,而不能针对类。
CGLIB是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法(最后生成的源码是实现了该接口和Factory接口) ,也可以对接口进行代理(最后生成的源码是继承了实现类并实现了Factory接口)
3.2、Spring
在选择用JDK
还是CGLiB
的依据
如果要被代理的对象是个实现类,那么Spring会使用JDK动态代理来完成操作(Spirng默认采用JDK动态代理实现机制);
如果要被代理的对象不是个实现类那么,Spring会强制使用CGLib来实现动态代理。