动态代理入门

1. 动态代理介绍

  • 什么是动态代理
    • 简单来说就是使用反射动态创建代理对象,使用代理对象来代替目标对象,因此达到增强目标对象的功能
  • 动态代理的特点
    • 字节码随用随创建,随用随加载。它与静态代理的区别也在于此,因为静态代理是字节码一上来就创建好,并完成加载。装饰者模式就是静态代理的一种体现。
  • 动态代理的常用两种方式
    • 基于接口的动态代理
    • 基于子类的动态代理

2. 基于接口的动态代理

  • 在这里我们使用厂家的例子:以前厂家不仅要对产品进行售后处理,还得负责销售的环节,把产品卖给客户。但是随着时间的推移,出现了一个新的角色:代理商,这时候厂家只需要负责对产品的售后处理,销售的事情交给代理商即可。如果客户需要售后维修,那么只需要将产品交给代理商,由代理商送往厂家进行维修。
  • 下面我们用代码来演示这种情况。
  • 基于接口的动态代理是使用 JDK 官方提供的 Proxy 类,要求被代理类至少实现一个接口
  • 定义接口,表示厂家需要具备的功能(销售以及售后)
/**
 * 生产厂家需要实现的接口
 *
 * @author yukaifan
 * @InterfaceName IProducer
 * @date 2020-02-19 15:35
 */
public interface IProducer {
    /**
     * 销售
     *
     * @param money
     * @return
     */
    String sellProduct(double money);

    /**
     * 售后
     *
     * @param money
     * @return
     */
    String afterService(double money);
}

  • 定义厂家类
/**
 * 生产厂家
 *
 * @author yukaifan
 * @ClassName Producer
 * @date 2020-02-19 15:38
 */
public class Producer implements IProducer {

    @Override
    public String sellProduct(double money) {
        return "厂家销售产品,拿到 " + money + "元...";
    }

    @Override
    public String afterService(double money) {
        return "厂家提供售后服务,拿到 " + money + "元...";
    }
}
  • 定义测试类,进行动态代理
public class Client {
    public static void main(String[] args) {
        // 厂家
        final IProducer producer = new Producer();
       
        // 该对象相当于代理商
        IProducer proxyProducer = (IProducer) Proxy.newProxyInstance(producer.getClass().getClassLoader(),
                producer.getClass().getInterfaces(),
                new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        // 获取方法参数
                        Double money = (Double) args[0];
                        // 返回值
                        String returnValue;
                        if ("sellProduct".equals(method.getName())) {
                            // 对方法体进行增强
                            System.out.println("消费者花了 " + money + "元购买商品...");

                            // 对参数进行增强,代理商需要抽取 2 成利润
                            money = money * 0.8;

                            // 对返回值进行增强
                            returnValue = (String) method.invoke(producer, money);
                            return returnValue + "代理商抽取 2 成利润...";
                        } else {
                            // 增强方法体
                            System.out.println("消费者花了 " + money + "元进行售后服务...");

                            // 如果是售后,代理商不赚差价,不增强参数,增强返回值
                            returnValue = (String) method.invoke(producer, args);
                            return returnValue + "代理商没有抽取利润...";
                        }
                    }
                });

        // 消费者花费 10000 块向代理商卖电脑
        System.out.println(proxyProducer.sellProduct(10000.0));
        System.out.println("-----------------------------");
        // 消费者花费 2000 块进行售后维修
        System.out.println(proxyProducer.afterService(2000.0));
    }
}
  • 使用 Proxy.newProxyInstance() 创建代理对象,参数如下:
    • ClassLoader : 类加载器,用于加载代理对象字节码,和被代理对象使用相同的类加载器。固定写法:被代理对象.getClass().getClassLoader()
    • Class[] : 字节码数组,用于让代理对象和被代理对象具有相同的接口方法。固定写法:被代理对象.getClass().getInterfaces()
    • InvocationHandler : 处理器,用于提供增强的代码,一般都是编写一个该接口的匿名内部类
  • invoke() 方法用于拦截被代理对象的方法,执行被代理对象的任何方法都会被该方法拦截到,参数如下:
    • Object proxy :代理对象的引用
    • Method method :当前执行的方法
    • Object[] args :当前执行方法所需的参数
    • 返回值类型与被代理对象方法一致
  • 匿名内部类访问外部方法的成员变量时都要求外部成员变量添加 final 修饰符,final 修饰变量代表该变量只能被初始化一次,以后不能被修改。参考链接:
  • 运行结果如图
    在这里插入图片描述

3. 基于子类的动态代理

  • 还是使用上面的例子
  • 基于子类的动态代理是使用第三方库 CGLIB 提供的 Enhancer 类,要求被代理类不能是被 final 修饰的类(最终类)
  • 导入第三方库依赖 CGLIB 或者手动导入jar 包 cglib-2.1.3.jarasm.jar
<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>2.1_3</version>
</dependency>
  • 厂家类无需实现接口
public class Producer {
    public String sellProduct(double money) {
        return "厂家销售产品,拿到 " + money + "元...";
    }

    public String afterService(double money) {
        return "厂家提供售后服务,拿到 " + money + "元...";
    }
}
  • 编写测试类
public class Client {
    public static void main(String[] args) {
        // 厂家
        final Producer producer = new Producer();
        
        // 该对象相当于代理商
        Producer cglibProducer = (Producer) Enhancer.create(producer.getClass(), new MethodInterceptor() {
            @Override
            public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
            	// 具体逻辑代码与上面的一致
            	// ...
            }
        });

        // 消费者花费 10000 块向代理商卖电脑
        System.out.println(cglibProducer.sellProduct(10000.0));
        System.out.println("-----------------------------");
        // 消费者花费 2000 块进行售后维修
        System.out.println(cglibProducer.afterService(2000.0));
    }
}
  • 使用 Enhancer.create() 创建代理对象,参数如下:
    • Class : 字节码,用于指定被代理对象的字节码。固定写法:被代理对象.getClass()
    • Callback : 回调接口,用于提供增强的代码,一般都是编写该接口的子接口实现类 MethodInterceptor
  • intercept() 方法用于拦截被代理对象的方法,执行被代理对象的任何方法都会被该方法拦截到,参数如下:
    • Object proxy :代理对象的引用
    • Method method :当前执行的方法
    • Object[] args :当前执行方法所需的参数
    • MethodProxy methodProxy :当前执行方法的代理对象
    • 返回值类型与被代理对象方法一致
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值