设计模式之动态代理

动态代理

1. 动态代理概述

1. 代理:代理对象代理真实对象,达到增强真实对象功能的目的。
	* 具体表现在对实际对象的方法的三个方面进行增强
		1. 方法参数
		2. 方法返回值
		3. 方法体
2. 静态代理与动态代理区别:
	1. 静态代理:有一个类文件描述代理模式(用的不多)
 	2. 动态代理:在内存中形成代理类(实现机理是反射技术)
3. 动态代理:
	1. 特点:字节码随用随创建,随用随加载
	2. 作用:不修改源码的基础啥关对方法增强。(类似于python中的装饰器)
	3. 分类:
		1. 基于接口的动态代理
		2. 基于子类的动态代理

下面一张图帮助理解:
在这里插入图片描述

2. 基于接口的动态代理

2.1 实现步骤

1. 要求:被代理对象至少继承一个接口
2. 涉及的类:Proxy
3. 提供者:JDK官方
4. 实现步骤:
	1. 定义接口		
	2. 定义实际处理类
	3. 动态代理设置:
			1. 代理对象和真实对象实现相同的接口
					* 类型的强转,使得他们继承了相同的接口
			2. 代理对象 = Proxy.newProxyInstance();
					* 处理器的三个参数说明:
						1. 类加载器:真实对象.getClass().getClassLoader()
						2. 接口数组:真实对象.getClass().getInterfaces()
						3. 处理器:new InvocationHandler()
					* 注意:Proxy导包是:java.lang.reflect.Proxy
					* 举例:
					  SaleComputer proxy_lenovo = (SaleComputer)Proxy.newProxyInstance(lenovo.getClass().getClassLoader(), lenovo.getClass().getInterfaces(), new InvocationHandler({
						/**
						* proxy:代理对象
						* method:调用的方法,被封装成一个方法对象
						* args:调用方法时的实际参数
						*/
						@Override
						public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
							return null;
						}
					  }));
			3. 使用代理对象调用方法。
			4. 增强方法

2.2 动态代理的实现案例

在这里插入图片描述

用上图的卖电脑的例子举例,采用动态代理模拟这个过程:
要求:用户拿8000块去代理商处卖联想电脑,代理商赚两层,也就是用用户所给钱的80%去找厂家拿货。代理商给予用户优惠政策,派车接用户过来,并免费送货,最后还送一个鼠标。

文件目录:
在这里插入图片描述

第一步:定义卖电脑的接口:SaleComputer.java

package cn.wanghao.demo;

public interface SaleComputer {
    /**
     * 给钱,进行卖电脑
     * @param money 钱
     * @return 卖了一台什么电脑
     */
    public String sale(double money);
}

第二步:定义实际处理类:Lonovo.java

package cn.wanghao.demo;

public class Lonovo implements SaleComputer {
    @Override
    public String sale(double money) {
        System.out.println("到厂家处花费了"+money+"钱,卖了一台联想电脑!");
        return "得到联想电脑";
    }
}

第三步:动态代理设置:Demo.java

(1)基础代码(模板),没有增强实际对象的代码如下:

package cn.wanghao.demo;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class Demo {
    public static void main(String[] args) {
        //1. 创建实际对象
        Lonovo lonovo = new Lonovo();

        //2. 创建代理对象
        /*
            1. 类加载器:真实对象.getClass().getClassLoader()
            2. 接口数组:真实对象.getClass().getInterfaces()
            3. 处理器:new InvocationHandler()
            * 类型强转是为了让他们继承相同的接口-SaleComputer
         */
        SaleComputer proxy = (SaleComputer) Proxy.newProxyInstance(lonovo.getClass().getClassLoader(), lonovo.getClass().getInterfaces(), new InvocationHandler() {
            /**
			* proxy:代理对象
			* method:调用的方法,被封装成一个方法对象
			* args:调用方法时的实际参数
			*/
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            	if ("sale".equals(method.getName())) {
	                String result = (String) method.invoke(lonovo, args);
	                return result;
	            } else {
					String result = (String) method.invoke(lonovo, args);
                    return result;
				}
            }
        });

        //3. 调用方法
        System.out.println("用户花8000元去代理商处卖联想电脑...");
        String result = proxy.sale(8000);
        System.out.println(result);
    }
}

(2)对实际对象的参数增强
在这里插入图片描述
(2)增强返回值
在这里插入图片描述
(3)增强方法体:
在这里插入图片描述

最终代码:

package cn.wanghao.demo;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class Demo {
    public static void main(String[] args) {
        //1. 创建实际对象
        Lonovo lonovo = new Lonovo();

        //2. 创建代理对象
        /*
            1. 类加载器:真实对象.getClass().getClassLoader()
            2. 接口数组:真实对象.getClass().getInterfaces()
            3. 处理器:new InvocationHandler()
            * 类型强转是为了让他们继承相同的接口-SaleComputer
         */
        SaleComputer proxy = (SaleComputer) Proxy.newProxyInstance(lonovo.getClass().getClassLoader(), lonovo.getClass().getInterfaces(), new InvocationHandler() {
            /**
             * proxy:代理对象
             * method:调用的方法,被封装成一个方法对象
             * args:调用方法时的实际参数
             */
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                if ("sale".equals(method.getName())) {
                    //增强参数
                    double price = (double) args[0];
                    price *= 0.8;
                    //增强方法体
                    System.out.println("专车接用户...");
                    String result = (String) method.invoke(lonovo, price); //使用真实对象调用该方法
                    System.out.println("免费送货...");
                    //增强返回值
                    return result + "_外加一个鼠标。";
                } else {
                    String result = (String) method.invoke(lonovo, args);
                    return result;
                }
            }
        });

        //3. 调用方法
        System.out.println("用户花8000元去代理商处卖联想电脑...");
        String result = proxy.sale(8000);
        System.out.println(result);
    }
}

运行结果:
在这里插入图片描述

3. 基于子类的动态代理

3.1 实现步骤

1. 要求:被代理对象不能是最终类,也就是不能被final修饰。基于接口的动态代理只能代理接口或者集成接口的类,并且代理对象的类型是
		其接口类型,而不是被代理对象的类型。而基于子类的动态代理可以直接代理没有继承接口的类,并且代理对象的类型就是被代理对象的类型。
3. 涉及的类:Enhancer
4. 提供者:第三方cglib库
5. 实现步骤:
	1. 导入cglib的jar包或坐标
	2. 定义实际处理类
	3. 动态代理设置:
			1. 代理对象 = Enhancer.create();
					* 处理器的两个参数说明:
						1. 字节码:真实对象.getClass()
						2. 处理器:new MethodIntercepter()
					* 举例:
					  SaleComputer proxy_lenovo = (SaleComputer)Enhancer.create(lenovo.getClass(), new MethodIntercepter({
						/**
						* proxy:代理对象
						* method:调用的方法,被封装成一个方法对象
						* args:调用方法时的实际参数
						* methodProxy: 当前执行方法的代理对象
						*/
						@Override
						public Object intercept(Object proxy, Method method, Object[] args,  MethodProxy methodProxy) throws Throwable {
							return null;
						}
					  }));
			2. 使用代理对象调用方法。
			3. 增强方法

基于接口和基于出子类的动态代理,在处理器中编写的代码是一样的,也是proxy()和intercept()方法内的代码是一样的。所以,我就不重复举例子了。

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

ElegantCodingWH

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

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

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

打赏作者

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

抵扣说明:

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

余额充值