首页 > 代码库 > Java-马士兵动态代理模式

Java-马士兵动态代理模式

Java-马士兵动态代理模式

模拟jdk的动态代理的实现原理, 这些东西没有必要写出来,写项目的时候用不上,主要是面试和理解原理;

?

有些工具可以直接生成二进制码,没有必要生成文件。

代理模式-聚合与继承方式比较

参考地址:http://www.cnblogs.com/shamgod/p/4591782.html

?

一、概述

1.目标:要在Tankmove()方法做时间代理及日志代理(可以设想以后还要增加很多代理处理),且代理间的顺序可活更换

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

  1. public interface Movable {
  2. ??public void move();
  3. ?}

??

2.Tank.java

  1. import java.util.Random;
  2. ?
  3. public class Tank implements Movable {
  4. ?
  5. ????@Override
  6. ????public void move() {
  7. ????????System.out.println("Tank moving.......");
  8. ????????try {
  9. ????????????Thread.sleep(new Random().nextInt(5000));
  10. ????????} catch (InterruptedException e) {
  11. ????????????e.printStackTrace();
  12. ????????}
  13. ????}
  14. ?
  15. }

??

3.TankTimeProxy.java

  1. public class TankTimeProxy implements Movable {
  2. ?
  3. ????Movable m;
  4. ?
  5. ????public TankTimeProxy(Movable m) {
  6. ????????this.m = m;
  7. ????}
  8. ?
  9. ????@Override
  10. ????public void move() {
  11. ????????System.out.println("Time Proxy start...........");
  12. ????????long start = System.currentTimeMillis();
  13. ????????m.move();
  14. ????????long end = System.currentTimeMillis();
  15. ????????System.out.println("花费时间:"+(end - start));
  16. ????????System.out.println("Time Proxy end...........");
  17. ????}
  18. ?
  19. }

?

4.TankLogProxy.java

  1. public class TankLogProxy implements Movable {
  2. ????Movable m;
  3. ????public TankLogProxy(Movable m) {
  4. ????????this.m = m;
  5. ????}
  6. ????@Override
  7. ????public void move() {
  8. ????????System.out.println("Log Proxy start...........");
  9. ????????m.move();
  10. ????????System.out.println("Log Proxy end...........");
  11. ????}
  12. }

??

5.Tank2Time.java

  1. public class Tank2Time extends Tank {
  2. ?
  3. ????public void move(){
  4. ????????System.out.println("Tank2 time start...........");
  5. ????????long start = System.currentTimeMillis();
  6. ????????super.move();
  7. ????????long end = System.currentTimeMillis();
  8. ????????System.out.println("花费时间:"+(end - start));
  9. ????????System.out.println("Tank2 time end...........");
  10. ????}
  11. }

??

6.Tank3Log.java

  1. public class Tank3Log extends Tank2Time {
  2. ?
  3. ????public void move(){
  4. ????????System.out.println("Tank3Log start...........");
  5. ????????super.move();
  6. ????????System.out.println("Tank3Log end...........");
  7. ????}
  8. }

??

7.Client.java

  1. public class Client {
  2. ?
  3. ????@Test
  4. ????public void testProxy(){
  5. ????????Tank t = new Tank();
  6. ????????Movable m;
  7. ????????//一、聚合的方式(较灵活,因为实现了接口)
  8. ????????//1.1聚合方式的代理,先日志代理,后时间代理
  9. ????????TankTimeProxy ttp1 = new TankTimeProxy(t);
  10. ????????TankLogProxy tlp1 = new TankLogProxy(ttp1);
  11. ????????m = tlp1;
  12. ????????m.move();
  13. ????????System.out.println("\n==============================分隔线==========================\n");
  14. ????????//1.2聚合方式的代理,先时间代理,后日志代理(可以灵活切换顺序)
  15. ????????TankLogProxy tlp2 = new TankLogProxy(t);
  16. ????????TankTimeProxy ttp2 = new TankTimeProxy(tlp2);
  17. ????????m = ttp2;
  18. ????????m.move();
  19. ????????System.out.println("\n==============================分隔线==========================\n");
  20. ????????//二、继承的方式
  21. ????????//2.1代理时间
  22. ????????Tank2Time t2 = new Tank2Time();
  23. ????????t2.move();
  24. ????????System.out.println("\n==============================分隔线==========================\n");
  25. ????????//2.2先代理日志,后时间,不能灵活切换
  26. ????????Tank3Log t3 = new Tank3Log();
  27. ????????t3.move();
  28. ????}
  29. }

??

三、运行结果

技术分享

??

四、小结

凡是要求灵活多变的功能,多数用接口多态实现

?

?

组合方式实现代理的缺点:每实现一个需求都需要写一个代理类,比如:为了实现在方法前后加日志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

  1. package com.weiqinshian.proxy;
  2. public interface Moveable
  3. {
  4. ???public void move();
  5. }

?

2.Tank.java

  1. package com.weiqinshian.proxy;
  2. import java.util.Random;
  3. public class Tank implements Moveable
  4. {
  5. ???@Override
  6. ???public void move()
  7. ???{
  8. ??????System.out.println("tank move........");
  9. ??????try
  10. ??????{
  11. ?????????Thread.sleep(new Random().nextInt(10000));
  12. ??????} catch (InterruptedException e)
  13. ??????{
  14. ?????????e.printStackTrace();
  15. ??????}
  16. ???}
  17. }

?

3.Proxy.java

  1. package com.weiqinshian.proxy;
  2. import java.io.File;
  3. import java.io.FileWriter;
  4. import java.lang.reflect.Constructor;
  5. import java.net.URL;
  6. import java.net.URLClassLoader;
  7. import javax.tools.JavaCompiler;
  8. import javax.tools.StandardJavaFileManager;
  9. import javax.tools.ToolProvider;
  10. import javax.tools.JavaCompiler.CompilationTask;
  11. public class Proxy
  12. {
  13. ???public static Object newProxyInstance() throws Exception
  14. ???{
  15. ??????String rt = "\n\r";
  16. ??????// 动态代理文件的源码
  17. ??????String str = "package com.weiqinshian.proxy;" + rt +
  18. ??????"public class TankTimeProxy implements Moveable {" + rt +
  19. ??????"private Moveable m;" + rt +
  20. ??????"public TankTimeProxy(Moveable m) {" + rt + "this.m = m;" + rt + "}" + rt +
  21. ??????"@Override" + rt + "public void move() {" + rt + "System.out.println(\"Time Proxy start...........\");" + rt + "long start = System.currentTimeMillis();" + rt + "m.move();" + rt
  22. ????????????+ "long end = System.currentTimeMillis();" + rt + "System.out.println(\"花费时间:\"+(end - start));" + rt + "System.out.println(\"Time Proxy end...........\");" + rt + "}" + rt +
  23. ????????????"}";
  24. ??????// 把源码写到java文件里
  25. ??????File file = new File("d:/src/com/weiqinshian/proxy/TankTimeProxy.java");
  26. ??????FileWriter fw = new FileWriter(file);
  27. ??????fw.write(str);
  28. ??????fw.flush();
  29. ??????fw.close();
  30. ??????// 编译源码,生成class,注意编译环境要换成jdk才有compiler,单纯的jre没有compiler,会空指针错误
  31. ??????JavaCompiler jc = ToolProvider.getSystemJavaCompiler();
  32. ??????// 文件管事器
  33. ??????StandardJavaFileManager fileMgr = jc.getStandardFileManager(null, null, null);
  34. ??????// 编译单元
  35. ??????Iterable units = fileMgr.getJavaFileObjects(file);
  36. ??????// 编译任务
  37. ??????CompilationTask t = jc.getTask(null, fileMgr, null, null, null, units);
  38. ??????// 编译
  39. ??????t.call();
  40. ??????fileMgr.close();
  41. ??????// 把类load到内存里src\com\weiqinshian\proxy
  42. ??????URL[] urls = new URL[]
  43. ??????{ new URL("file:/" + "d:/src/") };
  44. ??????System.out.println("file:/" + System.getProperty("user.dir") + "/src/com/weiqinshian/proxy/TankTimeProxy.class");
  45. ??????URLClassLoader uc = new URLClassLoader(urls);
  46. ??????Class c = uc.loadClass("com.weiqinshian.proxy.TankTimeProxy");
  47. ??????// 生成实例
  48. ??????// return c.newInstance();
  49. ??????// //c.newInstance()会调用无参数的Construtor,若类没有无参的Constructor时会出错
  50. ??????Constructor ctr = c.getConstructor(Moveable.class);
  51. ??????return ctr.newInstance(new Tank());
  52. ???}
  53. }

??

4.Client.java

  1. ?package com.weiqinshian.proxy;
  2. public class Client
  3. {
  4. ???public static void main(String[] args) throws Exception
  5. ???{
  6. ??????Moveable m = (Moveable) Proxy.newProxyInstance();
  7. ??????m.move();// 感觉没有生成任何代理类
  8. ???}
  9. }

三、运行结果

技术分享

??

这种方式存在的问题:现在动态代理,动态生成的代理类是写死了的,是用字符串写死在类里面的,而且,只能动态生成实现了 Moveable接口的代理,如果要实现任意接口的代理应该怎么办? 那就不将动态生成代理类的字符串写死,动态拼接生成代理类。

代理模式--动态代理 修改成可以代理任意接口

?

一、概述

1.目标:把Proxy修改成可以代理任意接口及其任意方法,只要传接口名给newProxyInstance,就能动态生成实现了该接口的代理类。

2.思路:

(1)代理任意接口:把接口类型作为参数传给ProxynewProxyInstance(Class interfze)

(2)代理任意方法:用interfze.getMethods()取出所有方法,拼接实现方法的字符串

??

二、代码

1.Movable.java

2.Tank.java

3.Proxy.java

4.Client.java

??

1.Movable.java

  1. package com.weiqinshian.proxy;
  2. public interface Moveable
  3. {
  4. ???public void move();
  5. ???public void stop();
  6. }

??

2.Tank.java

  1. package com.weiqinshian.proxy;
  2. import java.util.Random;
  3. public class Tank implements Moveable
  4. {
  5. ???@Override
  6. ???public void move()
  7. ???{
  8. ??????System.out.println("tank move........");
  9. ??????try
  10. ??????{
  11. ?????????Thread.sleep(new Random().nextInt(10000));
  12. ??????} catch (InterruptedException e)
  13. ??????{
  14. ?????????e.printStackTrace();
  15. ??????}
  16. ???}
  17. ???public void stop()
  18. ???{
  19. ??????System.out.println("Tank stopping.......");
  20. ???}
  21. }

?

3.Proxy.java

  1. package com.weiqinshian.proxy;
  2. import java.io.File;
  3. import java.io.FileWriter;
  4. import java.lang.reflect.Constructor;
  5. import java.lang.reflect.Method;
  6. import java.net.URL;
  7. import java.net.URLClassLoader;
  8. import javax.tools.JavaCompiler;
  9. import javax.tools.StandardJavaFileManager;
  10. import javax.tools.ToolProvider;
  11. import javax.tools.JavaCompiler.CompilationTask;
  12. ?
  13. public class Proxy
  14. {
  15. ???public static Object newProxyInstance(Class interfze) throws Exception
  16. ???{
  17. ??????String rt = "\n\r";
  18. ??????// 拼接"实现接口方法"的字符串
  19. ??????String methodStr = "";
  20. ??????for (Method m : interfze.getMethods())
  21. ??????{
  22. ?????????// 取出方法的修饰符和返回值类型
  23. ?????????String[] parts = m.toString().replace("abstract ", "").split("\\.");
  24. ?????????String[] parts2 = parts[0].split(" ");
  25. ?????????methodStr += "@Override" + rt + parts2[0] + " " + parts2[1] + " " + m.getName() + "() {" + rt + "System.out.println(\"Time Proxy start...........\");" + rt
  26. ???????????????+ "long start = System.currentTimeMillis();" + rt + "m." + m.getName() + "();" + rt + "long end = System.currentTimeMillis();" + rt
  27. ???????????????+ "System.out.println(\"花费时间:\"+(end - start));" + rt + "System.out.println(\"Time Proxy end...........\");" + rt + "}";
  28. ??????}
  29. ??????// 动态代理文件的源码
  30. ??????String str = "package com.weiqinshian.proxy; " + rt +
  31. ??????"public class TankTimeProxy implements " + interfze.getName() + " {" + rt +
  32. ??????"private " + interfze.getName() + " m;" + rt +
  33. ??????"public TankTimeProxy(" + interfze.getName() + " m) {" + rt + "this.m = m;" + rt + "}" + rt +
  34. ??????methodStr + rt +
  35. ??????"}";
  36. ??????// 把源码写到java文件里
  37. ??????File file = new File("d:/src/com/weiqinshian/proxy/TankTimeProxy.java");
  38. ??????FileWriter fw = new FileWriter(file);
  39. ??????fw.write(str);
  40. ??????fw.flush();
  41. ??????fw.close();
  42. ??????// 编译源码,生成class,注意编译环境要换成jdk才有compiler,单纯的jre没有compiler,会空指针错误
  43. ??????JavaCompiler jc = ToolProvider.getSystemJavaCompiler();
  44. ??????// 文件管事器
  45. ??????StandardJavaFileManager fileMgr = jc.getStandardFileManager(null, null, null);
  46. ??????// 编译单元
  47. ??????Iterable units = fileMgr.getJavaFileObjects(file);
  48. ??????// 编译任务
  49. ??????CompilationTask t = jc.getTask(null, fileMgr, null, null, null, units);
  50. ??????// 编译
  51. ??????t.call();
  52. ??????fileMgr.close();
  53. ??????// 把类load到内存里src\com\weiqinshian\proxy
  54. ??????URL[] urls = new URL[]
  55. ??????{ new URL("file:/" + "d:/src/") };
  56. ??????System.out.println("file:/" + System.getProperty("user.dir") + "/src/com/weiqinshian/proxy/TankTimeProxy.class");
  57. ??????URLClassLoader uc = new URLClassLoader(urls);
  58. ??????Class c = uc.loadClass("com.weiqinshian.proxy.TankTimeProxy");
  59. ??????// 生成实例
  60. ??????// return c.newInstance();
  61. ??????// //c.newInstance()会调用无参数的Construtor,若类没有无参的Constructor时会出错
  62. ??????Constructor ctr = c.getConstructor(interfze);
  63. ??????return ctr.newInstance(new Tank());
  64. ???}
  65. }

??

4.Client.java

  1. package com.weiqinshian.proxy;
  2. ?
  3. public class Client
  4. {
  5. ???public static void main(String[] args) throws Exception
  6. ???{
  7. ??????Moveable m = (Moveable) Proxy.newProxyInstance(Moveable.class);// 方法参数可以传任意接口类型
  8. ??????m.move();
  9. ??????m.stop();
  10. ???}
  11. }

?

三、运行结果

技术分享

?

?

代理模式-动态代理 修改成可以任意修改代理逻辑

一、概述

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-马士兵动态代理模式