设计模式-代理模式

参考博客[Java散记]静态代理和动态代理Java的三种代理模式Java 静态代理、Java动态代理、CGLIB动态代理

参考书籍:《大话数据结构》

代理模式简介

代理模式涉及到的角色:

  1. 抽象主题类(Subject):通过接口或者抽象类的方式来定义真实主题类或者代理类需要完成的业务方法;
  2. 真实主题类(RealSubject):实现或者继承抽象主题类;
  3. 代理类(Proxy):实现或者继承抽象主题类,并且含有一个真实主题类对象,可以实现真实主题类的功能,同时也可以对其进行功能增强。

image-20201026163315099

代理模式的优势

  1. 可以隐藏委托类,也就是真实主题类的实现;
  2. 完成客户类与委托类之间的解耦,在不修改委托类源代码的情况下可以做一些额外的处理。

代理类主要分为静态代理、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文件就已经存在了,所以运行速度要比动态代理更快。但是同样也存在着一些缺陷:

  1. 可维护性低。由于代理类和真实类都实现(继承)了同一个接口(抽象类),所以如果接口发生了更改,则所有的代理类和真实类都需要修改,增加了工作量。
  2. 可重复性低。对于每一个服务都需要创建一个代理类

动态代理

详细参考Java 静态代理、Java动态代理、CGLIB动态代理

动态代理与静态代理的区别

  1. 静态代理在编译的时候已经完成,编译完成后代理类是一个实际的class文件;
  2. 动态代理是在运行时动态生成的,即编译玩出城后没有实际的class文件,而是在运行时动态生成类字节码,并加载到JVM中。

JVM的类加载机制中加载阶段需要做的三件事

  1. 通过一个类的全名或者其他途径获取这个类的二进制字节流;
  2. 将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构;
  3. 在内存中生成一个代表这个类的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动态创建代理对象的模式

  1. 查找目标类中非final的public类型的方法(public的无法被重写);
  2. 将这些方法的定义转为字节码;
  3. 将组成的字节码转换成相应的代理的class对象,然后通过反射来获得代理类对象;
  4. 通过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动态代理的区别

  1. JDK动态代理基于Java的反射机制实现,必须要实现接口的业务类才可以用这种方法生成动态代理对象;
  2. CGLIB动态代理基于ASM框架生成业务类的子类来实现;
  3. JDK动态代理的优势是最小依赖关系,减少依赖意味着开发和维护的简化并且有JDK自身的支持。使用CGLIB动态代理第三方框架的有使是无需实现接口,达到代理类无入侵,我们只需操作我们关心的类,不必为其他相关类增加工作量,性能较高。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

YuanbaoQiang

你的鼓励将是我创作的最大动力~

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值