首页 > 代码库 > Java-马士兵动态代理模式
Java-马士兵动态代理模式
Java-马士兵动态代理模式
模拟jdk的动态代理的实现原理, 这些东西没有必要写出来,写项目的时候用不上,主要是面试和理解原理;
?
有些工具可以直接生成二进制码,没有必要生成文件。
代理模式-聚合与继承方式比较
参考地址:http://www.cnblogs.com/shamgod/p/4591782.html
?
一、概述
1.目标:要在Tank的move()方法做时间代理及日志代理(可以设想以后还要增加很多代理处理),且代理间的顺序可活更换
2.思路:
(1)聚合:代理类聚合了被代理类,且代理类及被代理类都实现了movable接口,则可实现灵活多变,具体看代码
(2)继承:继承不够灵活,具体看代码
??
二、代码
1.Movable.java
2.Tank.java
3.TankTimeProxy.java
4.TankLogProxy.java
5.Tank2Time.java
6.Tank3Log.java
7.Client.java
?1.Movable.java
- public interface Movable {
- ??public void move();
- ?}
??
2.Tank.java
- import java.util.Random;
- ?
- public class Tank implements Movable {
- ?
- ????@Override
- ????public void move() {
- ????????System.out.println("Tank moving.......");
- ????????try {
- ????????????Thread.sleep(new Random().nextInt(5000));
- ????????} catch (InterruptedException e) {
- ????????????e.printStackTrace();
- ????????}
- ????}
- ?
- }
??
3.TankTimeProxy.java
- public class TankTimeProxy implements Movable {
- ?
- ????Movable m;
- ?
- ????public TankTimeProxy(Movable m) {
- ????????this.m = m;
- ????}
- ?
- ????@Override
- ????public void move() {
- ????????System.out.println("Time Proxy start...........");
- ????????long start = System.currentTimeMillis();
- ????????m.move();
- ????????long end = System.currentTimeMillis();
- ????????System.out.println("花费时间:"+(end - start));
- ????????System.out.println("Time Proxy end...........");
- ????}
- ?
- }
?
4.TankLogProxy.java
- public class TankLogProxy implements Movable {
- ????Movable m;
- ????public TankLogProxy(Movable m) {
- ????????this.m = m;
- ????}
- ????@Override
- ????public void move() {
- ????????System.out.println("Log Proxy start...........");
- ????????m.move();
- ????????System.out.println("Log Proxy end...........");
- ????}
- }
??
5.Tank2Time.java
- public class Tank2Time extends Tank {
- ?
- ????public void move(){
- ????????System.out.println("Tank2 time start...........");
- ????????long start = System.currentTimeMillis();
- ????????super.move();
- ????????long end = System.currentTimeMillis();
- ????????System.out.println("花费时间:"+(end - start));
- ????????System.out.println("Tank2 time end...........");
- ????}
- }
??
6.Tank3Log.java
- public class Tank3Log extends Tank2Time {
- ?
- ????public void move(){
- ????????System.out.println("Tank3Log start...........");
- ????????super.move();
- ????????System.out.println("Tank3Log end...........");
- ????}
- }
??
7.Client.java
- public class Client {
- ?
- ????@Test
- ????public void testProxy(){
- ????????Tank t = new Tank();
- ????????Movable m;
- ????????//一、聚合的方式(较灵活,因为实现了接口)
- ????????//1.1聚合方式的代理,先日志代理,后时间代理
- ????????TankTimeProxy ttp1 = new TankTimeProxy(t);
- ????????TankLogProxy tlp1 = new TankLogProxy(ttp1);
- ????????m = tlp1;
- ????????m.move();
- ????????System.out.println("\n==============================分隔线==========================\n");
- ????????//1.2聚合方式的代理,先时间代理,后日志代理(可以灵活切换顺序)
- ????????TankLogProxy tlp2 = new TankLogProxy(t);
- ????????TankTimeProxy ttp2 = new TankTimeProxy(tlp2);
- ????????m = ttp2;
- ????????m.move();
- ????????System.out.println("\n==============================分隔线==========================\n");
- ????????//二、继承的方式
- ????????//2.1代理时间
- ????????Tank2Time t2 = new Tank2Time();
- ????????t2.move();
- ????????System.out.println("\n==============================分隔线==========================\n");
- ????????//2.2先代理日志,后时间,不能灵活切换
- ????????Tank3Log t3 = new Tank3Log();
- ????????t3.move();
- ????}
- }
??
三、运行结果
??
四、小结
凡是要求灵活多变的功能,多数用接口多态实现
?
?
组合方式实现代理的缺点:每实现一个需求都需要写一个代理类,比如:为了实现在方法前后加日志TankLogProxy、为了实现记录方法运行时间TankTimeProxy,随着系统的复杂,如果还需要实现权限、事务管理,用这种设计方法,代理类会越来越多。有没有一种方式,能够让我们不写这些代理类? 动态代理,动态的去代理,代理类是动态生成的,不需要我们编写,这样就可以解决这个代理类很多的问题,这样会极大地减少了我们的工作。
?
?
?
?
?
代理模式-动态代理 调用Proxy.newProxyInstance()
http://www.cnblogs.com/shamgod/p/4592014.html
一、概述
1.目标:不自己写代理类,利用Proxy.newProxyInstance()动态生成
2.用到的知识点:
(1)//编译源码,生成class,注意编译环境要换成jdk1.6才有compiler,单纯的jre没有compiler,会空指针错误
JavaCompiler jc = ToolProvider.getSystemJavaCompiler();
(2)//文件管事器
StandardJavaFileManager fileMgr = jc.getStandardFileManager(null, null, null);
(3)//编译单元
Iterable units = fileMgr.getJavaFileObjects(file);
(4)//编译任务
CompilationTask t = jc.getTask(null, fileMgr, null, null, null, units);
(5)//编译
t.call();
(6)//把类load到内存里
URL[] urls = new URL[] {new URL("file:/"+System.getProperty("user.dir")+"/src/proxy/TankTimeProxy.class")};
URLClassLoader uc = new URLClassLoader(urls);
Class c = uc.loadClass("proxy.TankTimeProxy");
(7)//生成实例
//return c.newInstance(); //c.newInstance()会调用无参数的Construtor,若类没有无参的Constructor时会出错
Constructor ctr = c.getConstructor(Movable.class);
return ctr.newInstance(new Tank());
??
二、代码
1.Movable.java
2.Tank.java
3.Proxy.java
4.Client.java
?1.Moveable.java
- package com.weiqinshian.proxy;
- public interface Moveable
- {
- ???public void move();
- }
?
2.Tank.java
- package com.weiqinshian.proxy;
- import java.util.Random;
- public class Tank implements Moveable
- {
- ???@Override
- ???public void move()
- ???{
- ??????System.out.println("tank move........");
- ??????try
- ??????{
- ?????????Thread.sleep(new Random().nextInt(10000));
- ??????} catch (InterruptedException e)
- ??????{
- ?????????e.printStackTrace();
- ??????}
- ???}
- }
?
3.Proxy.java
- package com.weiqinshian.proxy;
- import java.io.File;
- import java.io.FileWriter;
- import java.lang.reflect.Constructor;
- import java.net.URL;
- import java.net.URLClassLoader;
- import javax.tools.JavaCompiler;
- import javax.tools.StandardJavaFileManager;
- import javax.tools.ToolProvider;
- import javax.tools.JavaCompiler.CompilationTask;
- public class Proxy
- {
- ???public static Object newProxyInstance() throws Exception
- ???{
- ??????String rt = "\n\r";
- ??????// 动态代理文件的源码
- ??????String str = "package com.weiqinshian.proxy;" + rt +
- ??????"public class TankTimeProxy implements Moveable {" + rt +
- ??????"private Moveable m;" + rt +
- ??????"public TankTimeProxy(Moveable m) {" + rt + "this.m = m;" + rt + "}" + rt +
- ??????"@Override" + rt + "public void move() {" + rt + "System.out.println(\"Time Proxy start...........\");" + rt + "long start = System.currentTimeMillis();" + rt + "m.move();" + rt
- ????????????+ "long end = System.currentTimeMillis();" + rt + "System.out.println(\"花费时间:\"+(end - start));" + rt + "System.out.println(\"Time Proxy end...........\");" + rt + "}" + rt +
- ????????????"}";
- ??????// 把源码写到java文件里
- ??????File file = new File("d:/src/com/weiqinshian/proxy/TankTimeProxy.java");
- ??????FileWriter fw = new FileWriter(file);
- ??????fw.write(str);
- ??????fw.flush();
- ??????fw.close();
- ??????// 编译源码,生成class,注意编译环境要换成jdk才有compiler,单纯的jre没有compiler,会空指针错误
- ??????JavaCompiler jc = ToolProvider.getSystemJavaCompiler();
- ??????// 文件管事器
- ??????StandardJavaFileManager fileMgr = jc.getStandardFileManager(null, null, null);
- ??????// 编译单元
- ??????Iterable units = fileMgr.getJavaFileObjects(file);
- ??????// 编译任务
- ??????CompilationTask t = jc.getTask(null, fileMgr, null, null, null, units);
- ??????// 编译
- ??????t.call();
- ??????fileMgr.close();
- ??????// 把类load到内存里src\com\weiqinshian\proxy
- ??????URL[] urls = new URL[]
- ??????{ new URL("file:/" + "d:/src/") };
- ??????System.out.println("file:/" + System.getProperty("user.dir") + "/src/com/weiqinshian/proxy/TankTimeProxy.class");
- ??????URLClassLoader uc = new URLClassLoader(urls);
- ??????Class c = uc.loadClass("com.weiqinshian.proxy.TankTimeProxy");
- ??????// 生成实例
- ??????// return c.newInstance();
- ??????// //c.newInstance()会调用无参数的Construtor,若类没有无参的Constructor时会出错
- ??????Constructor ctr = c.getConstructor(Moveable.class);
- ??????return ctr.newInstance(new Tank());
- ???}
- }
??
4.Client.java
- ?package com.weiqinshian.proxy;
- public class Client
- {
- ???public static void main(String[] args) throws Exception
- ???{
- ??????Moveable m = (Moveable) Proxy.newProxyInstance();
- ??????m.move();// 感觉没有生成任何代理类
- ???}
- }
三、运行结果
??
这种方式存在的问题:现在动态代理,动态生成的代理类是写死了的,是用字符串写死在类里面的,而且,只能动态生成实现了 Moveable接口的代理,如果要实现任意接口的代理应该怎么办? 那就不将动态生成代理类的字符串写死,动态拼接生成代理类。
代理模式--动态代理 修改成可以代理任意接口
?
一、概述
1.目标:把Proxy修改成可以代理任意接口及其任意方法,只要传接口名给newProxyInstance,就能动态生成实现了该接口的代理类。
2.思路:
(1)代理任意接口:把接口类型作为参数传给Proxy的newProxyInstance(Class interfze)
(2)代理任意方法:用interfze.getMethods()取出所有方法,拼接实现方法的字符串
??
二、代码
1.Movable.java
2.Tank.java
3.Proxy.java
4.Client.java
??
1.Movable.java
- package com.weiqinshian.proxy;
- public interface Moveable
- {
- ???public void move();
- ???public void stop();
- }
??
2.Tank.java
- package com.weiqinshian.proxy;
- import java.util.Random;
- public class Tank implements Moveable
- {
- ???@Override
- ???public void move()
- ???{
- ??????System.out.println("tank move........");
- ??????try
- ??????{
- ?????????Thread.sleep(new Random().nextInt(10000));
- ??????} catch (InterruptedException e)
- ??????{
- ?????????e.printStackTrace();
- ??????}
- ???}
- ???public void stop()
- ???{
- ??????System.out.println("Tank stopping.......");
- ???}
- }
?
3.Proxy.java
- package com.weiqinshian.proxy;
- import java.io.File;
- import java.io.FileWriter;
- import java.lang.reflect.Constructor;
- import java.lang.reflect.Method;
- import java.net.URL;
- import java.net.URLClassLoader;
- import javax.tools.JavaCompiler;
- import javax.tools.StandardJavaFileManager;
- import javax.tools.ToolProvider;
- import javax.tools.JavaCompiler.CompilationTask;
- ?
- public class Proxy
- {
- ???public static Object newProxyInstance(Class interfze) throws Exception
- ???{
- ??????String rt = "\n\r";
- ??????// 拼接"实现接口方法"的字符串
- ??????String methodStr = "";
- ??????for (Method m : interfze.getMethods())
- ??????{
- ?????????// 取出方法的修饰符和返回值类型
- ?????????String[] parts = m.toString().replace("abstract ", "").split("\\.");
- ?????????String[] parts2 = parts[0].split(" ");
- ?????????methodStr += "@Override" + rt + parts2[0] + " " + parts2[1] + " " + m.getName() + "() {" + rt + "System.out.println(\"Time Proxy start...........\");" + rt
- ???????????????+ "long start = System.currentTimeMillis();" + rt + "m." + m.getName() + "();" + rt + "long end = System.currentTimeMillis();" + rt
- ???????????????+ "System.out.println(\"花费时间:\"+(end - start));" + rt + "System.out.println(\"Time Proxy end...........\");" + rt + "}";
- ??????}
- ??????// 动态代理文件的源码
- ??????String str = "package com.weiqinshian.proxy; " + rt +
- ??????"public class TankTimeProxy implements " + interfze.getName() + " {" + rt +
- ??????"private " + interfze.getName() + " m;" + rt +
- ??????"public TankTimeProxy(" + interfze.getName() + " m) {" + rt + "this.m = m;" + rt + "}" + rt +
- ??????methodStr + rt +
- ??????"}";
- ??????// 把源码写到java文件里
- ??????File file = new File("d:/src/com/weiqinshian/proxy/TankTimeProxy.java");
- ??????FileWriter fw = new FileWriter(file);
- ??????fw.write(str);
- ??????fw.flush();
- ??????fw.close();
- ??????// 编译源码,生成class,注意编译环境要换成jdk才有compiler,单纯的jre没有compiler,会空指针错误
- ??????JavaCompiler jc = ToolProvider.getSystemJavaCompiler();
- ??????// 文件管事器
- ??????StandardJavaFileManager fileMgr = jc.getStandardFileManager(null, null, null);
- ??????// 编译单元
- ??????Iterable units = fileMgr.getJavaFileObjects(file);
- ??????// 编译任务
- ??????CompilationTask t = jc.getTask(null, fileMgr, null, null, null, units);
- ??????// 编译
- ??????t.call();
- ??????fileMgr.close();
- ??????// 把类load到内存里src\com\weiqinshian\proxy
- ??????URL[] urls = new URL[]
- ??????{ new URL("file:/" + "d:/src/") };
- ??????System.out.println("file:/" + System.getProperty("user.dir") + "/src/com/weiqinshian/proxy/TankTimeProxy.class");
- ??????URLClassLoader uc = new URLClassLoader(urls);
- ??????Class c = uc.loadClass("com.weiqinshian.proxy.TankTimeProxy");
- ??????// 生成实例
- ??????// return c.newInstance();
- ??????// //c.newInstance()会调用无参数的Construtor,若类没有无参的Constructor时会出错
- ??????Constructor ctr = c.getConstructor(interfze);
- ??????return ctr.newInstance(new Tank());
- ???}
- }
??
4.Client.java
- package com.weiqinshian.proxy;
- ?
- public class Client
- {
- ???public static void main(String[] args) throws Exception
- ???{
- ??????Moveable m = (Moveable) Proxy.newProxyInstance(Moveable.class);// 方法参数可以传任意接口类型
- ??????m.move();
- ??????m.stop();
- ???}
- }
?
三、运行结果
?
?
代理模式-动态代理 修改成可以任意修改代理逻辑
一、概述
1.目标:动态代理的代理逻辑可以任意修改
2.思路:
(1)要把代理逻辑抽离,站在jvm的角度思考,应独立出InvocationHandler接口,并接收被代理的对象及方法作为参数invoke(Object o, Method m),并本身作为参数传给newProxyInstance(Class interfze,InvocationHandler handler)?
(2)InvocationHandler本身聚合被代理类target,以便在target的方法前后增加代理逻辑
3.知识点:
(1)按名字找方法java.lang.reflect.Method md = proxy.Movable.class.getMethod("stop");
(2)按"."拆分字符串:String [] parts = m.toString().replace("abstract ", "").split("\\.");
??
二、代码
1.InvocationHandler.java
2.TimeHandler.java
3.Movable.java
4.Tank.java
5.Proxy.java
6.Client.java
??
1.InvocationHandler.java
1 package proxy;
2
3 import java.lang.reflect.Method;
4
5 public interface InvocationHandler {
6 public void invoke(Object o, Method m);
7 }
??
2.TimeHandler.java
1 package proxy;
2
3 import java.lang.reflect.InvocationTargetException;
4 import java.lang.reflect.Method;
5
6 public class TimeHandler implements InvocationHandler {
7
8 //保留被代理的对象
9 private Object target;
10
11 public TimeHandler(Object target) {
12 this.target = target;
13 }
14
15 @Override
16 public void invoke(Object o, Method m) {
17 System.out.println("Time Proxy start...........");
18 long start = System.currentTimeMillis();
19 try {
20 //除了静态方法,方法的调用都要先已知对象,所以要把对象o作为参数传进去
21 m.invoke(target);
22 } catch (Exception e) {
23 e.printStackTrace();
24 }
25 long end = System.currentTimeMillis();
26 System.out.println("花费时间:"+(end - start));
27 System.out.println("Time Proxy end...........");
28
29 }
30
31 }
??
3.Movable.java
1 package proxy;
2
3 public interface Movable {
4 public void move();
5 public void stop();
6 }
??
4.Tank.java
1 package proxy;
2
3 import java.util.Random;
4
5 public class Tank implements Movable {
6
7 @Override
8 public void move() {
9 System.out.println("Tank moving.......");
10 try {
11 Thread.sleep(new Random().nextInt(2000));
12 } catch (InterruptedException e) {
13 e.printStackTrace();
14 }
15 }
16
17 @Override
18 public void stop() {
19 System.out.println("Tank stopping.......");
20
21 }
22
23 }
??
5.Proxy.java
1 package proxy;
2
3 import java.io.File;
4 import java.io.FileWriter;
5 import java.lang.reflect.Constructor;
6 import java.lang.reflect.Method;
7 import java.net.URL;
8 import java.net.URLClassLoader;
9
10 import javax.tools.JavaCompiler;
11 import javax.tools.JavaCompiler.CompilationTask;
12 import javax.tools.StandardJavaFileManager;
13 import javax.tools.ToolProvider;
14
15 public class Proxy {
16//InvocationHandler ,代理的类型,调用的时候,你要告诉我,你想产生的是什么类型(日志、权限、时间)的代理
17 public static Object newProxyInstance(Class interfze,InvocationHandler handler) throws Exception {
18
19 String rt = "\n\r";
20
21 //拼接"实现接口方法"的字符串
22 String methodStr = "";
23 for(Method m: interfze.getMethods() ){
24
25 //取出方法的修饰符和返回值类型
26 String [] parts = m.toString().replace("abstract ", "").split("\\.");
27 String [] parts2 = parts[0].split(" ");
28
29 methodStr +=
30 "@Override" + rt +
31 parts2[0]+" "+parts2[1]+" "+m.getName()+"() {" + rt +
32 "try{"+ rt +
33 "java.lang.reflect.Method md = " + interfze.getName() + ".class.getMethod(\""+m.getName()+"\");" + rt +
34 //传this进去其实没什么用,invoke实际是调用target的方法m.invoke(target)
35 "handler.invoke(this, md);" + rt +
36 "}catch(Exception e){"+ rt +
37 " e.printStackTrace();" + rt +
38 "}" + rt +
39
40
41 "}"+ rt ;
42 }
43
44
45 //动态代理文件的源码
46 String str =
47 "package proxy;" + rt +
48
49 "public class TankTimeProxy implements " + interfze.getName() + " {"+rt+
50
51
52 //聚合Handler
53 "private InvocationHandler handler;" + rt +
54
55 "public TankTimeProxy(InvocationHandler handler) {" + rt +
56 "this.handler = handler;" + rt +
57 "}" + rt +
58
59 methodStr + rt +
60
61 "}" ;
62
63 //把源码写到java文件里
64 File file = new File(System.getProperty("user.dir")+"/src/proxy/TankTimeProxy.java");
65 FileWriter fw = new FileWriter(file);
66 fw.write(str);
67 fw.flush();
68 fw.close();
69
70 //编译源码,生成class,注意编译环境要换成jdk才有compiler,单纯的jre没有compiler,会空指针错误
71 JavaCompiler jc = ToolProvider.getSystemJavaCompiler();
72
73 //文件管事器
74 StandardJavaFileManager fileMgr = jc.getStandardFileManager(null, null, null);
75
76 //编译单元
77 Iterable units = fileMgr.getJavaFileObjects(file);
78
79 //编译任务
80 CompilationTask t = jc.getTask(null, fileMgr, null, null, null, units);
81
82 //编译
83 t.call();
84 fileMgr.close();
85
86 //把类load到内存里
87 URL[] urls = new URL[] {new URL("file:/"+System.getProperty("user.dir")+"/src/proxy/TankTimeProxy.class")};
88 URLClassLoader uc = new URLClassLoader(urls);
89 Class c = uc.loadClass("proxy.TankTimeProxy");
90
91 //生成实例
92 //return c.newInstance(); //c.newInstance()会调用无参数的Construtor,若类没有无参的Constructor时会出错
93 //Constructor ctr = c.getConstructor(interfze);
94 Constructor ctr = c.getConstructor(InvocationHandler.class);
95 return ctr.newInstance(handler);
96 }
97 }
??
6.Client.java
1 package proxy;
2
3 import java.io.IOException;
4
5 import org.junit.Test;
6
7 public class Client {
8
9 @Test
10 public void testProxy() throws Exception{
11
12 Movable m = (Movable)Proxy.newProxyInstance(Movable.class, new TimeHandler(new Tank()));
13 m.move();
14 m.stop();
15
16 }
17 }
??
三、运行结果
Java-马士兵动态代理模式