静态代理
为每个Service对象写一个ServiceProxy。在ServiceProxy中写一些原本在Service中的重复性的冗余代码,使得在Service对象中能够更加专注于真正的业务代码。
但是在实际的项目中,会出现很多的Service对象,从而导致静态代理不仅没有减少我们的工作量,反而增加了更多的冗余代码。
什么是动态代理
java动态代理是利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。
具体使用场景可以在日志管理、权限认证、安全检查和事务控制等类似情景中使用。
动态代理底层实现
jdk
概述:通过Proxy.newProxyInstance(ClassLoader,Class<?>[] args,InvocationHandler)方法,先生成新的class文件,然后加载到jvm中,然后使用反射,先用class取得他的构造方法,然后使用构造方法得到他的一个实例。
mybatis获取dao时用的底层原理也是动态代理。
//使用动态代理对象:指的是在程序运行的过程中,动态地通过代码的方式为指定的类生成Proxy代理对象。
/*Proxy用于生成动态代理
参数1:ClassLoader 类加载器,底层代码通过反射机制,通过类加载器获取字节码信息创建代理对象
参数2:Class[] 目标对象(被代理的对象)的接口类型的数组
这里之所以用数组是因为一个类可以继承多个接口
注:这里的代理类代理的是接口,而不是实现类;在invoke方法是传入目标对象,才确定是具体为哪个类代理
参数3:invocationHandler invoke方法 在执行真正的目标对象的函数时,会先执行代理对象中的功能,因此
可以在此书写额外功能
返回值:创建好的动态代理对象
*/
// 被代理对象|目标对象
final UserService userService = new UserServiceImpl();
// 参数1:
ClassLoader classLoader = userService.getClass().getClassLoader();
// ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
// 参数2:
Class[] classes = {UserService.class};
// 参数3:
UserService proxy = (UserService) Proxy.newProxyInstance(classLoader, classes, new InvocationHandler() {
// 通过动态代理对象调用被代理对象的函数方法时,会优先指定invoke方法
// 参数1:当前代理对象
// 参数2:当前代理对象调用的方法
// 参数3:当前代理对象调用的方法的参数
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object invoke=null;
try{
System.out.println("开启事务");
invoke = method.invoke(userService, args);
System.out.println("提交事务");
}catch (Exception e){
System.out.println("回滚事务");
}
return invoke;
}
});
proxy.save("test");
这里生成代码的过程中,都使用了缓存,jdk自带的使用了weakReference引用,而cglib使用的直接是 WeakHashMap,基本也类似。
两者区别
名称 | 备注 |
---|---|
静态代理 | 简单,代理模式,是动态代理的理论基础,常见使用在代理模式 |
jdk动态代理 | 需要有顶层接口才能使用,使用反射完成,使用了动态生成字节码技术。但是在只有顶层接口的时候也可以使用,例如mybatis的mapper文件是代理。 |
cglib动态代理 | 可以直接代理类,使用了ASM字节码操纵框架。不能对final类进行继承。 |