Java动态代理-Proxy

末蓝、 2022-02-28 08:44 377阅读 0赞

目录

  • 一、概念
  • 二、构成
  • 三、作用与特点
  • 四、分类

    • 4.1 基于接口的动态代理
    • 4.2 基于子类的动态代理

一、概念

动态代理提供一种抽象,能够将对象中不同方法的调用重定向到一个统一的处理函数,做自定义的逻辑处理。但是对于调用者,对此毫无察觉,就好像调用的方法是用传统方式实现的一般。Java动态代理是利用Java的反射技术,在运行时创建动态代理类及其对象。
动态代理类:一个实现某些给定接口的新类

二、构成

基于接口的动态代理的组成:

① 接口:定义真实对象和代理对象需要实现的接口
② 真实对象:被代理的对象,必须实现接口。
③ 代理对象:增强代码部分

基于子类的动态代理

① 真实对象:被代理的对象
② 代理对象:增强代码部分

三、作用与特点

作用:不修改源码的基础上,对方法增强。
动态代理的特点:字节码随用随创建,随用随加载
它与静态代理的区别也在于此。因为静态代理是字节码一上来就创建好,并完成加载。
装饰者模式就是静态代理的一种体现。

基于接口的动态代理,代理的是接口(Interface),而不是类(Class)。

四、分类

4.1 基于接口的动态代理

需求:
定义打折接口(接口)
定义超市真实类实现打折接口(真实对象)
在不改动超市真实类内部代码的前提现,增强超市类实现的接口的功能。(使用动态代理)

定义接口:

  1. public interface Discount {
  2. String sale(double price);
  3. void today();
  4. }

定义真实类,超市:

  1. public class Supermarket implements Discount {
  2. @Override
  3. public void today() {
  4. Date date = new Date();
  5. SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
  6. System.out.println("今天日期:" + sdf.format(date));
  7. }
  8. @Override
  9. public String sale(double price) {
  10. System.out.println("总共消费了"+price+"钱");
  11. return "购买的商品";
  12. }
  13. }

句柄:

  1. public class DiscountInvocationHandler implements InvocationHandler {
  2. private Object target;
  3. public Object bind(Object target) {
  4. this.target = target;
  5. System.out.println("target=" + target);
  6. // 绑定该类实现的所有接口,取得代理类
  7. return Proxy.newProxyInstance(target.getClass().getClassLoader(),
  8. target.getClass().getInterfaces(),
  9. this);
  10. }
  11. @Override
  12. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  13. // 判断method是否是我们需要增强的方法sale
  14. if ("sale".equals(method.getName())) {
  15. // 获取需要增强的参数
  16. double price = (double)args[0];
  17. price *= 0.8;
  18. // 增强method的方法体
  19. System.out.println("增强方法体。。。");
  20. // 增强method的返回值
  21. String value = (String) method.invoke(this.target, price);
  22. value += "+赠送礼品一件";
  23. return value;
  24. }
  25. return method.invoke(this.target, args);
  26. }
  27. }

其中:
Proxy的静态方法newProxyInstance参数讲解:

ClassLoader loader:类加载器。它是用于加载代理对象字节码。和被代理对象使用相同的类加载器。固定写法。
Class<?>[] interfaces:接口数组,保证了代理接口和真实接口实现了相同的接口。真实对象实现的接口。也是固定写法。
InvocationHandler h 匿名内部类。核心代码在此处处理。
Object 返回值。称为代理对象。强转为接口。

接口invoke方法,代理对象调用的所有方法都会触发该方法执行。参数:

Object proxy:代理对象。一般不用该对象!!
Method method:方法对象。代理对象(proxyLenovo)调用的接口方法封装而成的对象。
Object[] args:代理对象调用方法时,传递的实际参数,封装到args数组了
Object:返回值。return null 表示没有返回值。

4.2 基于子类的动态代理

需要使用第三方jar包:cglibasm
注意:创建代理对象的要求:被代理类不能是最终类!!!!

一个生产者Producer.java,如何增强他的方法?

  1. /** * 一个生产者 **/
  2. public class Producer {
  3. /** * 销售 * @param money */
  4. public void saleProduct(float money) {
  5. System.out.println("销售商品,价格:" + money);
  6. }
  7. /** * 售后 * @param money */
  8. public void afterService(float money) {
  9. System.out.println("提供售后服务,服务费:" + money);
  10. }
  11. }
  12. /** * @description: 模拟消费者 **/
  13. public class Client {
  14. public static void main(String[] args) {
  15. final Producer producer = new Producer();
  16. Producer cglibProducer = (Producer) Enhancer.create(producer.getClass(), new MethodInterceptor() {
  17. /** * 执行被代理对象的任何方法都会经过该方法。 * @param proxy * @param method * @param args * 以上三个参数和基于接口的动态代理中invoke方法的参数是一样的 * @param methodProxy 当前执行方法的代理对象 * @return 和被代理对象有相同的返回值 * @throws Throwable */
  18. @Override
  19. public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
  20. Object returnValue = null;
  21. // 1.获取方法执行的参数
  22. Float money = (Float)args[0];
  23. // 2.判断当前方法是否是销售
  24. if ("saleProduct".equals(method.getName())) {
  25. returnValue = method.invoke(producer, money * 1.2f);
  26. }
  27. return returnValue;
  28. }
  29. });
  30. cglibProducer.saleProduct(10000f);
  31. }
  32. }

推荐博文:
传送门

发表评论

表情:
评论列表 (有 0 条评论,377人围观)

还没有评论,来说两句吧...

相关阅读

    相关 java动态代理Proxy

    动态代理,实现了方法开闭原则,把实现类交给代理实现,把接口交给代理实现,把对象交给代理操作 代理代理的三种形态:接口,类,对象 三种代理实现方式: 接口: //