设计模式之代理模式

释放双眼,带上耳机,听听看~!

设计模式之代理模式

应用场景:为其他对象提供一种代理以控制对这个对象的访问。从结构上来看和 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

给TA打赏
共{{data.count}}人
人已打赏
安全运维

MySQL到MongoDB的数据同步方法!

2021-12-11 11:36:11

安全运维

Ubuntu上NFS的安装配置

2021-12-19 17:36:11

个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索