参考博客:[Java散记]静态代理和动态代理,Java的三种代理模式,Java 静态代理、Java动态代理、CGLIB动态代理
参考书籍:《大话数据结构》
代理模式简介
代理模式涉及到的角色:
- 抽象主题类(Subject):通过接口或者抽象类的方式来定义真实主题类或者代理类需要完成的业务方法;
- 真实主题类(RealSubject):实现或者继承抽象主题类;
- 代理类(Proxy):实现或者继承抽象主题类,并且含有一个真实主题类对象,可以实现真实主题类的功能,同时也可以对其进行功能增强。
代理模式的优势:
- 可以隐藏委托类,也就是真实主题类的实现;
- 完成客户类与委托类之间的解耦,在不修改委托类源代码的情况下可以做一些额外的处理。
代理类主要分为静态代理、JDK 动态代理和 CGLIB 动态代理,它们各有优缺点,没有最好的, 存在就是有意义的,在不同的场景下它们会有不同的用武之地。
静态代理
公共接口:IUserDao
public interface IUserDao {
// 保存数据
void save();
}
真实对象:UserDao
public class UserDao implements IUserDao{
@Override
public void save() {
System.out.println("真实对象保存了数据!");
}
}
代理对象:UserDaoProxy
public class UserDaoProxy implements IUserDao{
// 定义一个目标对象
private UserDao userDao;
public UserDaoProxy(UserDao userDao) {
this.userDao = userDao;
}
@Override
public void save() {
System.out.println("代理类开启事务!");
userDao.save();
System.out.println("代理类结束事务!");
}
}
测试:
public class Client {
public static void main(String[] args) {
// 创建目标对象
UserDao userDao = new UserDao();
// 创建代理类对象
UserDaoProxy userDaoProxy = new UserDaoProxy(userDao);
// 执行代理类的方法
userDaoProxy.save();
}
}
输出结果:
代理类开启事务!
真实对象保存了数据!
代理类结束事务!
自然静态代理拥有代理模式的基本优点,除此之外,对于静态代理,委托类和代理类的.class文件就已经存在了,所以运行速度要比动态代理更快。但是同样也存在着一些缺陷:
- 可维护性低。由于代理类和真实类都实现(继承)了同一个接口(抽象类),所以如果接口发生了更改,则所有的代理类和真实类都需要修改,增加了工作量。
- 可重复性低。对于每一个服务都需要创建一个代理类
动态代理
详细参考:Java 静态代理、Java动态代理、CGLIB动态代理
动态代理与静态代理的区别:
- 静态代理在编译的时候已经完成,编译完成后代理类是一个实际的class文件;
- 动态代理是在运行时动态生成的,即编译玩出城后没有实际的class文件,而是在运行时动态生成类字节码,并加载到JVM中。
JVM的类加载机制中加载阶段需要做的三件事:
- 通过一个类的全名或者其他途径获取这个类的二进制字节流;
- 将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构;
- 在内存中生成一个代表这个类的Class对象,作为方法区中对这个类访问的入口。
JDK动态代理
动态代理对象不需要实现接口,但是要求目标对象需要实现接口。
JDK生成代理对象主要涉及到的类有:
java.lang.reflect.Proxy
public static Object newProxyInstance(
// 指定当前目标对象的类加载器
ClassLoader loader,
// 目标对象实现的接口的类型
Class<?>[] interfaces,
// 时间处理器
InvocationHandler h)
// 返回指定接口的代理实例,该代理实例将方法调用调度到指定的调用处理程序。
java.lang.reflect.InvocationHandler
// 在代理实例上处理方法调用并返回结果。 在与其关联的代理实例上调用方法时,将在调用处理程序上调用该方法。
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;
接口:IUserDao
public interface IUserDao {
// 方法1
void save();
// 方法2
void save2();
}
真实对象:UserDao
public class UserDao implements IUserDao{
@Override
public void save() {
System.out.println("UserDao ==> save()");
}
@Override
public void save2() {
System.out.println("UserDao ==> save2()");
}
}
代理工厂类(中间类):ProxyFactory
/**
* @description: 代理工厂类(中间类),动态生成代理类
* @author: YuanbaoQiang
* @time: 2020/10/28 15:17
*/
public class UserProxyFactory implements InvocationHandler {
// 中间类持有委托类的引用,构成一种静态代理关系
private Object target;
/*
* @description: 传入委托类的对象
* @param target 委托类的对象
* @return:
* @author: YuanbaoQiang
* @time: 2020/10/28 15:26
*/
public UserProxyFactory(Object target) {
this.target = target;
}
/*
* @description: 动态生成代理类对象
* @param
* @return: java.lang.Object
* @author: YuanbaoQiang
* @time: 2020/10/28 15:27
*/
public Object getProxyInstance(){
return Proxy.newProxyInstance(
// 指定代理类的类加载器
target.getClass().getClassLoader(),
// 代理类需要实现的接口
target.getClass().getInterfaces(),
// 方法调用的实际处理者,代理对象的方法调用都会转发到这里
this
);
}
/*
* @description:
* @param proxy 代理对象
* @param method 代理方法
* @param args 方法形参
* @return: java.lang.Object
* @author: YuanbaoQiang
* @time: 2020/10/28 15:31
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("代理类开启事务!");
// 执行目标对象的方法
Object returnValue = method.invoke(target, args);
System.out.println("代理类结束事务!");
return returnValue;
}
}
测试:
public class Client {
public static void main(String[] args) {
// 接口的实现类(真实对象,委托类)
IUserDao target = new UserDao();
// 委托类的类型
System.out.println(target.getClass());
// 将目标对象传入到中间类中
UserProxyFactory userProxyFactory = new UserProxyFactory(target);
// 中间类给目标对象创建代理对象
IUserDao targetProxy = (IUserDao) userProxyFactory.getProxyInstance();
// 执行save方法
targetProxy.save();
// 执行save2()方法
targetProxy.save2();
// 输出代理类对象的类型
System.out.println(targetProxy.getClass());
}
}
输出结果:
class com.yuanbaoqiang.dynamicProxy.UserDao
代理类开启事务!
UserDao ==> save()
代理类结束事务!
--------------------------------
代理类开启事务!
UserDao ==> save2()
代理类结束事务!
class com.sun.proxy.$Proxy0
CGlib动态代理
上面的静态代理和动态JDK代理都需要有一个接口(静态代理中,代理类和委托类都需要实现接口,JDK代理中只有委托类时是需要实现接口的,代理类动态生成),但是有的时候目标对象是一个单独的对象,并没有实现任何的接口,此时就可以将该委托类当作父类,使用目标子类作为代理对象,这种方式叫做:cglib
代理。
首先cglib动态代理是由第三方框架实现的,所以测试前需要导入jar包,如果是maven项目添加依赖即可,如果不是maven
项目,也可以参考IDEA如何在项目中快速添加Maven依赖(因为有个包的确很难找),导入maven外部lib时,搜索ciglib
即可。
ciglib动态创建代理对象的模式:
- 查找目标类中非final的public类型的方法(public的无法被重写);
- 将这些方法的定义转为字节码;
- 将组成的字节码转换成相应的代理的class对象,然后通过反射来获得代理类对象;
- 通过MethodInterceptor来处理代理类中的方法。
目标类:UserDao
public class UserDao {
public void save(){
System.out.println("真实对象保存了数据!");
}
}
代理工厂类(中间类):UserProxyFactory
public class ProxyFactory implements MethodInterceptor {
// 维护一个目标对象
private Object target;
public ProxyFactory(Object target) {
this.target = target;
}
public Object getProxyInstance(){
// 工具类
Enhancer enhancer = new Enhancer();
// 设置父类为目标类型
enhancer.setSuperclass(target.getClass());
// 设置回调函数
enhancer.setCallback(this);
// 创建子类(代理对象)
return enhancer.create();
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("代理类开启事务!");
// 执行目标对象的方法
Object returnValue = method.invoke(target, objects);
System.out.println("代理类结束事务!");
return returnValue;
}
}
测试:
public class Client {
public static void main(String[] args) {
// 创建目标对象
UserDao target = new UserDao();
System.out.println(target.getClass());
// 获取代理工厂类,将目标对象传入
ProxyFactory proxyFactory = new ProxyFactory(target);
// 创建目标对象的代理对象
UserDao targetProxy = (UserDao) proxyFactory.getProxyInstance();
// 执行save()方法
targetProxy.save();
}
}
输出结果:
class com.yuanbaoqiang.dynamicProxy.cglib.UserDao
代理类开启事务!
真实对象保存了数据!
代理类结束事务!
JDK动态代理和CGLIB动态代理的区别
- JDK动态代理基于Java的反射机制实现,必须要实现接口的业务类才可以用这种方法生成动态代理对象;
- CGLIB动态代理基于ASM框架生成业务类的子类来实现;
- JDK动态代理的优势是最小依赖关系,减少依赖意味着开发和维护的简化并且有JDK自身的支持。使用CGLIB动态代理第三方框架的有使是无需实现接口,达到代理类无入侵,我们只需操作我们关心的类,不必为其他相关类增加工作量,性能较高。