设计模式之代理模式
应用场景:为其他对象提供一种代理以控制对这个对象的访问。从结构上来看和 Decorator 模式类似,
但 Proxy 是控制,更像是一种对功能的限制,而 Decorator 是增加职责。
Spring 的 Proxy 模式在 AOP 中有体现,比如 JdkDynamicAopProxy 和 Cglib2AopProxy。
代理实战演示
静态代理
1 2 3 4 5 6 7 8 9 10 11 12 13
| 1// 定义需要代理的功能接口
2public interface Person {
3 public void findLove();// 相亲
4 public void zufangzi();
5
6 public void buy();
7
8 public void findJob();
9
10 //......
11}
12
13 |
被代理者需要是代理接口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| 1// 儿子
2public class Son implements Person {
3 public void findLove() {
4 //我没有时间
5 //工作忙
6 System.out.println("找对象,肤白貌美大长腿");
7 }
8
9 public void zufangzi() {
10
11 }
12
13 public void buy() {
14
15 }
16
17 public void findJob() {
18
19 }
20}
21
22
23 |
代理类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| 1// 静态代理
2public class Father {
3 // 静态代理对象
4 private Son son;
5 //没办法扩展
6 public Father(Son son){// 代理对象使用构造方法传入
7 this.son=son;
8 }
9 //目标对象的引用给拿到
10 public void findLove(){
11 System.out.println("根据你的要求物色");
12 this.son.findLove();
13 System.out.println("双方父母是不是同意");
14 }
15
16}
17
18 |
测试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| 1 @Test
2 public void findLove() {
3 //只能帮儿子找对象
4 //不能帮表妹、不能帮陌生人
5 Father father = new Father(new Son());
6
7 father.findLove();
8
9 /**
10 * 输出:
11 * 根据你的要求物色
12 * 找对象,肤白貌美大长腿
13 * 双方父母是不是同意
14 */
15 }
16
17 |
jdk动态代理
jdk 动态代理需要实现代理接口【Person】
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| 1//jdk 动态代理需要实现代理接口【Person】
2public class XieMu implements Person {
3 public void findLove() {
4 System.out.println("高富帅");
5 System.out.println("身高180cm");
6 System.out.println("胸大,6块腹肌");
7 }
8
9 public void zufangzi() {
10 System.out.println("租房子");
11 }
12
13 public void buy() {
14 System.out.println("买东西");
15 }
16
17 public void findJob() {
18 System.out.println("月薪20K-50k");
19 System.out.println("找工作");
20 }
21}
22
23
24 |
代理接口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| 1/**
2 * 动态代理,需要实现{@link InvocationHandler}
3 * @since 动态代理
4 */
5
6public class JDKMeipo implements InvocationHandler {
7
8 //被代理的对象,把引用给保存下来
9 private Person target;
10
11 public Object getInstance(Person target){
12 this.target=target;
13 Class<? extends Person> targetClass = target.getClass();
14 //解字节码是如何重组的
15 //用来生成一个新的对象(字节码重组来实现)
16 return Proxy.newProxyInstance(targetClass.getClassLoader(),targetClass.getInterfaces(),this);
17 }
18
19
20
21
22 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
23
24 System.out.println("我是媒婆:我要给你找对象,现在已经拿到你的需求");
25 System.out.println("开始物色");
26
27 method.invoke(this.target,args);
28
29 System.out.println("如果合适的话,就准备办事");
30
31 return null;
32 }
33}
34
35
36 |
测试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| 1 @Test
2 public void getInstance() {
3 try {
4 Person obj = (Person)new JDKMeipo().getInstance(new XieMu());
5 System.out.println(obj.getClass());
6 obj.findJob();
7 //原理:
8 //1、拿到被代理对象的引用,并且获取到它的所有的接口,反射获取
9 //2、JDK Proxy类重新生成一个新的类、同时新的类要实现被代理类所有实现的所有的接口
10 //3、动态生成Java代码,把新加的业务逻辑方法由一定的逻辑代码去调用(在代码中体现)
11 //4、编译新生成的Java代码.class
12 //5、再重新加载到JVM中运行
13 //以上这个过程就叫字节码重组
14 //JDK中有个规范,只要要是$开头的一般都是自动生成的
15 //通过反编译工具可以查看源代码
16 byte [] bytes = ProxyGenerator.generateProxyClass("$Proxy0",new Class[]{Person.class});
17 FileOutputStream os = new FileOutputStream("E://$Proxy0.class");
18 os.write(bytes);
19 os.close();
20 } catch (Exception e) {
21 e.printStackTrace();
22 }
23 }
24
25 |
cglib代理
1 2 3 4 5 6 7 8 9
| 1public class ZhangSan {
2
3 public void findLove(){
4 System.out.println("肤白貌美大象腿");
5 }
6
7}
8
9 |
代理类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| 1/**
2 * 使用Cglib实现动态代理{@link MethodInterceptor}
3 */
4public class CglibMeipo implements MethodInterceptor {
5
6 public Object getInstance(Class<?> clazz){
7 Enhancer enhancer = new Enhancer();
8 //要把哪个设置为即将生成的新类父类
9 enhancer.setSuperclass(clazz);
10 // 回调执行intercept方法对目标对象功能的增强
11 enhancer.setCallback(this);
12 Object o = enhancer.create();
13 return o;
14 }
15
16
17 public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
18 //业务的增强
19
20 System.out.println("我是媒婆:我要给你找对象,现在已经拿到你的需求");
21 System.out.println("开始物色");
22
23 methodProxy.invokeSuper(o,objects);
24
25 System.out.println("如果合适的话,就准备办事");
26 return null;
27 }
28}
29
30
31 |
测试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| 1 @Test
2 public void getInstance() {
3
4 try {
5 ZhangSan obj = (ZhangSan)new CglibMeipo().getInstance(ZhangSan.class);
6 obj.findLove();
7 System.out.println("--------------------------------");
8 // System.out.println(obj.getClass());
9 /**
10 * 输出:
11 * 我是媒婆:我要给你找对象,现在已经拿到你的需求
12 * 开始物色
13 * 肤白貌美大象腿
14 * 如果合适的话,就准备办事
15 * --------------------------------
16 */
17 } catch (Exception e) {
18 e.printStackTrace();
19 }
20
21 }
22
23 |
自定义动态代理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83
| 1/**
2 * 自定义动态代理
3 */
4public class GPProxy {
5 public static final String ln = "\r\n";
6
7 public static Object newProxyInstance(GPClassLoader classLoader,Class<?> [] interfaces,GPInvocationHandler h){
8
9 try {
10 //1、动态生成源代码.java文件
11
12 String src = generateSrc(interfaces);
13
14 //2、Java文件输出磁盘
15 String filePath = GPProxy.class.getResource("").getPath();
16 System.out.println(filePath);
17 File f = new File(filePath + "$Proxy0.java");
18 FileWriter fw = new FileWriter(f);
19 fw.write(src);
20 fw.flush();
21 fw.close();
22
23 //3、把生成的.java文件编译成.class文件
24 JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
25 StandardJavaFileManager manage = compiler.getStandardFileManager(null,null,null);
26 Iterable iterable = manage.getJavaFileObjects(f);
27
28 JavaCompiler.CompilationTask task = compiler.getTask(null,manage,null,null,null,iterable);
29 task.call();
30 manage.close();
31
32 //4、编译生成的.class文件加载到JVM中来
33 Class proxyClass = classLoader.findClass("$Proxy0");
34 Constructor c = proxyClass.getConstructor(GPInvocationHandler.class);
35 f.delete();
36
37 //5、返回字节码重组以后的新的代理对象
38 return c.newInstance(h);
39 }catch (Exception e){
40 e.printStackTrace();
41 }
42 return null;
43 }
44
45 private static String generateSrc(Class<?>[] interfaces){
46
47 StringBuffer sb = new StringBuffer();
48 sb.append("package com.gupaoedu.vip.pattern.proxy.custom;" + ln);
49 sb.append("import com.gupaoedu.vip.pattern.proxy.staticed.Person;" + ln);
50 sb.append("import java.lang.reflect.Method;" + ln);
51 sb.append("public class $Proxy0 implements " + interfaces[0].getName() + "{" + ln);
52
53 sb.append("GPInvocationHandler h;" + ln);
54
55 sb.append("public $Proxy0(GPInvocationHandler h) { " + ln);
56
57 sb.append("this.h = h;");
58
59 sb.append("}" + ln);
60
61
62 for (Method m : interfaces[0].getMethods()){
63 sb.append("public " + m.getReturnType().getName() + " " + m.getName() + "() {" + ln);
64 sb.append("try{" + ln);
65 sb.append("Method m = " + interfaces[0].getName() + ".class.getMethod(\"" + m.getName() + "\",new Class[]{});" + ln);
66 sb.append("this.h.invoke(this,m,null);" + ln);
67 sb.append("}catch(Throwable e){" + ln);
68 sb.append("e.printStackTrace();" + ln);
69 sb.append("}");
70 sb.append("}");
71 }
72
73 sb.append("}" + ln);
74
75 return sb.toString();
76 }
77
78
79
80}
81
82
83 |
GPInvocationHandler
1 2 3 4 5
| 1public interface GPInvocationHandler {
2 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
3}
4
5 |
GPClassLoader
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
| 1public class GPClassLoader extends ClassLoader{
2
3 private File classPathFile;
4
5 public GPClassLoader(){
6 String classPath = GPClassLoader.class.getResource("").getPath();
7 this.classPathFile = new File(classPath);
8 }
9
10 @Override
11 protected Class<?> findClass(String name) throws ClassNotFoundException {
12
13 String className = GPClassLoader.class.getPackage().getName() + "." + name;
14
15 if(classPathFile != null){
16 File classFile = new File(classPathFile,name.replaceAll("\\.","/") + ".class");
17 if(classFile.exists()){
18 FileInputStream in = null;
19 ByteArrayOutputStream out = null;
20
21 try{
22 in = new FileInputStream(classFile);
23 out = new ByteArrayOutputStream();
24 byte [] buff = new byte[1024];
25 int len;
26 while ((len = in.read(buff)) != -1){
27 out.write(buff,0,len);
28 }
29 return defineClass(className,out.toByteArray(),0,out.size());
30 }catch (Exception e){
31 e.printStackTrace();
32 }finally {
33 if(null != in){
34 try {
35 in.close();
36 } catch (IOException e) {
37 e.printStackTrace();
38 }
39 }
40
41 if(out != null){
42 try {
43 out.close();
44 } catch (IOException e) {
45 e.printStackTrace();
46 }
47 }
48 }
49 }
50
51 }
52
53 return null;
54
55 }
56}
57
58 |
CustomMeipo
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| 1public class CustomMeipo implements GPInvocationHandler {
2
3 //被代理的对象,把引用给保存下来
4 private Person target;
5
6 public Object getInstance(Person target) throws Exception{
7 this.target = target;
8
9 Class<?> clazz = target.getClass();
10
11 //下半截,老师深入底层来给大家讲解字节码是如何重组的
12 //用来生成一个新的对象(字节码重组来实现)
13 return GPProxy.newProxyInstance(new GPClassLoader(),clazz.getInterfaces(),this);
14 }
15
16 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
17 System.out.println("我是媒婆:我要给你找对象,现在已经拿到你的需求");
18 System.out.println("开始物色");
19
20 method.invoke(this.target,args);
21
22 System.out.println("如果合适的话,就准备办事");
23
24 return null;
25 }
26}
27
28
29 |
测试
1 2 3 4 5 6 7 8 9 10 11 12 13
| 1 public static void main(String[] args) {
2
3 try {
4 Person obj = (Person)new CustomMeipo().getInstance(new XieMu());
5 System.out.println(obj.getClass());
6 obj.findLove();
7
8 } catch (Exception e) {
9 e.printStackTrace();
10 }
11 }
12
13 |