一、动态代理模式的基本介绍
1、代理对象,不需要实现接口,但是目标对象要实现接口,否则不能动态代理;
2、代理对象的生成,是利用 JDK 的 API,动态的在内存中构建代理对象;
3、动态代理也叫做:JDK 代理、接口代理
4、实现步骤
(1)代理对象和真实对象实现相同的接口;
(2)代理对象 = Proxy.newProxyInstance();
(3)使用代理对象调用方法
(4)增强方法
增强方法方式:
① 增强参数列表
② 增强返回值类型
③ 增强方法体执行逻辑
二、JDK 中生成代理对象的 API
1、代理类所在包:java.lang.reflect.Proxy
2、JDK 实现代理只需要使用 newProxyInstance 方法,但是该方法需要接受三个参数,完整的写法是:
1 public static Object newProxyInstance(ClassLoader loader,
2 Class<?>[] interfaces,
3 InvocationHandler h)
三、动态代理应用实例
将上一节的静态代理改进成动态代理模(即:JDK代理模式)
1、UML 类图
2、代码实现:
接口:
1 /**
2 * 接口
3 */
4 public interface ITeacherDao {
5 void teach(); //授课
6
7 void sayHello(String name);
8 }
被代理对象(目标对象):
1 public class TeacherDao implements ITeacherDao{
2 @Override
3 public void sayHello(String name) {
4 System.out.println("Hello," + name);
5 }
6
7 @Override
8 public void teach() {
9 System.out.println("老师授课中。。。");
10 }
11 }
代理工厂:
1 public class ProxyFactory {
2
3 //维护一个目标对象,Object
4 private Object target;
5
6 /**
7 * 通过构造方法,对 target 进行初始化
8 */
9 public ProxyFactory(Object target) {
10 this.target = target;
11 }
12
13 /**
14 * 给目标对象 生成一个代理对象
15 * @return
16 */
17 public Object getProxyInstance() {
18 /**
19 * public static Object newProxyInstance(ClassLoader loader,
20 * Class<?>[] interfaces,
21 * InvocationHandler h)
22 * 参数说明:
23 * 1、ClassLoader loader:
24 * 指定当前目标对象使用的类加载器,获取加载器的方法固定的
25 * 2、Class<?>[] interfaces
26 * 目标对象(被代理对象)实现的接口类型,使用泛型方式确认类型
27 * 3、InvocationHandler h:
28 * 事件处理,执行目标对象的方法时,会触发事件处理器方法,
29 * 会把当前执行的目标对象方法作为一个参数传入
30 */
31 return Proxy.newProxyInstance(target.getClass().getClassLoader(),
32 target.getClass().getInterfaces(),
33 new InvocationHandler() {
34
35 @Override
36 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
37 System.out.println("JDK代理开始");
38 //通过反射机制调用目标对象的方法
39 Object invoke = method.invoke(target, args);
40 System.out.println("JDK代理提交");
41 return invoke;
42 }
43 });
44 }
45 }
客户端:
1 public class Client {
2 public static void main(String[] args) {
3 //创建一个目标对象
4 ITeacherDao target = new TeacherDao();
5
6 //给目标对象创建代理对象,可以转成 ITeacherDAO
7 ProxyFactory proxyFactory = new ProxyFactory(target);
8 ITeacherDao proxyInstance = (ITeacherDao)proxyFactory.getProxyInstance();
9
10 System.out.println("proxyInstance=" + proxyInstance);
11 //proxyInstance类型=class com.sun.proxy.$Proxy0 内存中动态生成了代理对象
12 System.out.println("proxyInstance类型=" + proxyInstance.getClass());
13
14 //通过代理对象,调用目标对象的方法
15 proxyInstance.teach();
16
17 proxyInstance.sayHello("Tom");
18 }
19 }
四、动态代理应用实例
我们用买电脑这件事情来叙述一下动态代理:
当我们想买一个联想电脑,而此时联想电脑公司在北京,我们在西安,如果买电脑还得去北京,比较浪费时间。
这是在西安有一个联想代理商,我们可以从代理商这里预订一台电脑,而代理商去北京给我们取货。这样就形成了一种动态代理。
在这里:
北京联想公司=真实对象
西安联想代理商=代理对象
这里西安代理商可以把联想电脑销售到西安各地,扩大了销售市场,相当于增强了功能。
代码演示:
卖电脑的接口类:
1 public interface SaleComputer {
2
3 public String sale(double money);
4
5 public void show();
6 }
卖电脑真实类:
1 /**
2 * 真实类
3 */
4 public class Lenovo implements SaleComputer {
5 @Override
6 public String sale(double money) {
7
8 System.out.println("花了"+money+"元买了一台联想电脑...");
9 return "联想电脑";
10 }
11
12 @Override
13 public void show() {
14 System.out.println("展示电脑....");
15 }
16 }
代理对象类
1 import java.lang.reflect.InvocationHandler;
2 import java.lang.reflect.Method;
3 import java.lang.reflect.Proxy;
4
5 public class ProxyTest {
6
7 public static void main(String[] args) {
8 //1.创建真实对象
9 Lenovo lenovo = new Lenovo();
10
11 //2.动态代理增强lenovo对象
12 /*
13 三个参数:
14 1. 类加载器:真实对象.getClass().getClassLoader()
15 2. 接口数组:真实对象.getClass().getInterfaces()
16 3. 处理器:new InvocationHandler()
17 */
18 SaleComputer proxy_lenovo = (SaleComputer) Proxy.newProxyInstance(lenovo.getClass().getClassLoader(), lenovo.getClass().getInterfaces(), new InvocationHandler() {
19
20 /*
21 代理逻辑编写的方法:代理对象调用的所有方法都会触发该方法执行
22 参数:
23 1. proxy:代理对象
24 2. method:代理对象调用的方法,被封装为的对象
25 3. args:代理对象调用的方法时,传递的实际参数
26 */
27 @Override
28 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
29 /*System.out.println("该方法执行了....");
30 System.out.println(method.getName());
31 System.out.println(args[0]);
32
33
34
35
36 */
37 //判断是否是sale方法
38 if(method.getName().equals("sale")){
39 //1.增强参数
40 double money = (double) args[0];
41 money = money * 0.85;
42 System.out.println("专车接你....");
43 //使用真实对象调用该方法
44 String obj = (String) method.invoke(lenovo, money);
45 System.out.println("免费送货...");
46 //2.增强返回值
47 return obj+"_鼠标垫";
48 }else{
49 Object obj = method.invoke(lenovo, args);
50 return obj;
51 }
52
53
54
55 }
56 });
57
58 //3.调用方法
59
60 String computer = proxy_lenovo.sale(8000);
61 System.out.println(computer);
62
63 proxy_lenovo.show();
64 }
65 }