zoukankan      html  css  js  c++  java
  • Java 设计模式系列(十二)代理模式

    设计模式之美 - 代理模式

    设计模式之美目录:https://www.cnblogs.com/binarylei/p/8999236.html

    代理模式:给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用。GoF 的《设计模式》一书中把 RPC 称作远程代理。其它应用场景如缓存、监控、统计、鉴权、限流、事务、幂等、日志等。

    package com.github.binarylei.design.proxy;
    
    public interface UserService {
        public void say();
    }
    
    public class UserServiceImpl implements UserService {
        @Override
        public void say() {
            System.out.println("Hello World!");
        }
    }
    

    1. 静态代理

    public void test() {
        UserServiceImpl obj = new UserServiceImpl();
        UserService userService = new UserService() {
            @Override
            public void say() {
                System.out.println("这是静态代理");
                obj.say();
            }
        };
        userService.say();
    }
    

    很明显静态代理每个被代理的类都要手写一个代理类,当修改被代理的类时也要修改对应的代理类。解决这个问题则是由程序来生成对应的代理类,这就是动态代理。

    2. 动态代理

    2.1 JDK 动态代理

    public void test1() throws Exception {
        UserServiceImpl obj = new UserServiceImpl();
    
        UserService userService = (UserService) Proxy.newProxyInstance(
                UserServiceImpl.class.getClassLoader(), UserServiceImpl.class.getInterfaces(),
                new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        if (method.getDeclaringClass() == Object.class) {
                            return method.invoke(obj, args);
                        } else {
                            System.out.println(proxy.getClass().getName());
                            System.out.println(method);
                            Object ret = method.invoke(obj, args);
                            return ret;
                        }
                    }
                });
        userService.say();
        System.out.println(userService);
    }
    

    注意:JDK 中所要进行动态代理的类必须要实现一个接口 ,也就是说只能对该类所实现接口中定义的方法进行代理,这在实际编程中具有一定的局限性,而且使用反射的效率也并不是很高。

    2.2 CGLib 动态代理

    使用 CGLib 实现动态代理,完全不受代理类必须实现接口的限制,而且 CGLib 底层采用 ASM 字节码生成框架,使用字节码技术生成代理类,比使用 Java 反射效率要高。唯一需要注意的是,CGLib 不能对声明为 final 的方法进行代理,因为 CGLib 原理是动态生成被代理类的子类。

    <dependency>
        <groupId>cglib</groupId>
        <artifactId>cglib</artifactId>
        <version>3.2.8</version>
    </dependency>
    
    public void test2() {
        Enhancer enhancer = new Enhancer();
        //1. 设置父类
        enhancer.setSuperclass(UserServiceImpl.class);
    
        //2. 设置回调函数
        enhancer.setCallback(new MethodInterceptor() {
            @Override
            public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
    
                System.out.println(method + " proxy");
                Object ret = proxy.invokeSuper(obj, args);
                return null;
            }
        });
    
        //3. 获取代理对象
        UserService userService = (UserService) enhancer.create();
        userService.say();
    }
    

    参数:Object 为由 CGLib 动态生成的代理类实例,Method 为上文中实体类所调用的被代理的方法引用,Object[] 为参数值列表,MethodProxy 为生成的代理类对方法的代理引用。proxy.invokeSuper(obj, arg) 从代理实例的方法调用返回的值。

    3. 动态代理原理

    3.1 原理

    JDK 的动态代理实际上是在内存中生成了一个字节码类,并进行编译,加载。JVM 生成的类名称都是以 $ 开头,eg: $Proxy0

    public void test1() throws Exception {
        UserServiceImpl obj = new UserServiceImpl();
    
        UserService userService = (UserService) Proxy.newProxyInstance(
                UserServiceImpl.class.getClassLoader(), UserServiceImpl.class.getInterfaces(),
                new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        System.out.println(proxy.getClass().getName()); // $Proxy0
                        Object ret = method.invoke(obj, args);
                        return ret;
                    }
                });
        userService.say();
        System.out.println(userService);
    
        byte[] bytes = ProxyGenerator.generateProxyClass("$Proxy0", obj.getClass().getInterfaces());
        FileOutputStream out = new FileOutputStream(
            this.getClass().getResource("").getPath() + "$Proxy0.class");
        out.write(bytes);
    }
    

    查看生成的 $Proxy0.class 类如下:

    import com.github.binarylei.design.proxy.UserService;
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    import java.lang.reflect.UndeclaredThrowableException;
    
    public final class $Proxy0 extends Proxy implements UserService {
        private static Method m3;
    
        public $Proxy0(InvocationHandler var1) throws  {
            super(var1);
        }
        
        public final void say() throws  {
            try {
                super.h.invoke(this, m3, (Object[])null);
            } catch (RuntimeException | Error var2) {
                throw var2;
            } catch (Throwable var3) {
                throw new UndeclaredThrowableException(var3);
            }
        }
    
        static {
            try {
                m3 = Class.forName("com.github.binarylei.design.proxy.UserService").getMethod("say", new Class[0]);
            } catch (NoSuchMethodException var2) {
                throw new NoSuchMethodError(var2.getMessage());
            } catch (ClassNotFoundException var3) {
                throw new NoClassDefFoundError(var3.getMessage());
            }
        }
    
        // ...
    }
    

    3.2 手写动态代理

    (1) 定义 MyInvocationHandler 接口

    @FunctionalInterface
    public interface MyInvocationHandler {
    
        Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
    }
    

    (2) 实现自己的 MyProxy 类

    package com.github.binarylei.design.proxy.my;
    
    import javax.tools.JavaCompiler;
    import javax.tools.JavaFileObject;
    import javax.tools.StandardJavaFileManager;
    import javax.tools.ToolProvider;
    import java.io.File;
    import java.io.FileWriter;
    import java.lang.reflect.Constructor;
    import java.lang.reflect.Method;
    
    /**
     * @author: leigang
     * @version: 2018-10-02
     */
    public class MyProxy {
    
        public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, MyInvocationHandler h) {
            FileWriter out = null;
            try {
                // 1. 动态生成源代码 .java 文件
                String src = generateSrc(interfaces);
    
                // 2. .java 文件生成到磁盘
                File file = new File(MyProxy.class.getResource("").getPath() + "$Proxy1.java");
                out = new FileWriter(file);
                out.write(src);
                out.flush();
                out.close();
    
                // 3. 把 .java 文件编译成 .class 文件
                JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
                StandardJavaFileManager manager = compiler.getStandardFileManager(
                        null, null, null);
                Iterable<? extends JavaFileObject> iterable = manager.getJavaFileObjects(file);
    
                JavaCompiler.CompilationTask task = compiler.getTask(
                        null, manager, null, null, null, iterable);
                task.call();
                manager.close();
    
                // 4. 编译生成的 .class 类到 JVM 中
                Class<?> clazz = Class.forName("com.github.binarylei.design.proxy.my.$Proxy1");
    
                // 5. 返回字节码重组以后新代理对象
                Constructor<?> constructor = clazz.getConstructor(MyInvocationHandler.class);
                return constructor.newInstance(h);
            } catch (Exception e) {
                e.printStackTrace();
            }
            return null;
        }
    
        private static final String ln = "
    ";
    
        private static String generateSrc(Class<?>[] interfaces) {
            StringBuilder sb = new StringBuilder();
            sb.append("package com.github.binarylei.design.proxy.my;").append(ln);
            sb.append("import com.github.binarylei.design.proxy.UserService;").append(ln);
            sb.append("import java.lang.reflect.Method;").append(ln);
            sb.append("public final class $Proxy1 implements ").append(interfaces[0].getSimpleName()).append(" {").append(ln);
            sb.append("private static MyInvocationHandler h;").append(ln);
            sb.append("public $Proxy1(MyInvocationHandler h) throws Exception {").append(ln);
            sb.append("this.h = h;").append(ln);
            sb.append("}").append(ln);
    
            for (Class<?> clazz : interfaces) {
                Method[] methods = clazz.getMethods();
                for (Method m : methods) {
                    sb.append("public final void say() {").append(ln);
                    sb.append("try {").append(ln);
                    sb.append("Method m = Class.forName("").append(clazz.getName()).append("").getMethod("")
                            .append(m.getName()).append("", new Class[0]);").append(ln);
                    sb.append("h.invoke(this, m, (Object[]) null);").append(ln);
                    sb.append("} catch (Throwable e) {").append(ln);
                    sb.append("e.printStackTrace();").append(ln);
                    sb.append("}").append(ln);
                    sb.append("}").append(ln);
                }
            }
            sb.append("}").append(ln);
            return sb.toString();
        }
    
    }
    
    

    (3) 测试

    public void test3() {
        UserServiceImpl obj = new UserServiceImpl();
    
        UserService userService = (UserService) MyProxy.newProxyInstance(
                UserServiceImpl.class.getClassLoader(), UserServiceImpl.class.getInterfaces(),
                new MyInvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        System.out.println(proxy.getClass().getName());
                        System.out.println(method);
                        Object ret = method.invoke(obj, args);
                        return ret;
                    }
                });
        userService.say();
        System.out.println(userService);
    }
    
    

    参考:

    1. 实战CGLib系列文章 MethodInterceptor和Enhancer_CGLib:https://yq.aliyun.com/ziliao/296216

    每天用心记录一点点。内容也许不重要,但习惯很重要!

  • 相关阅读:
    SpringMVC中静态获取request对象 Spring中获取 HttpServletRequest对象【转载】
    springcloud 的loadbalancer 轮询算法切换方法 2021.4.3
    springboot项目启动增加图标
    rabbitmq 端口作用以及修改方法
    centos8 安装rabbitmq
    springcloud config client Value获取不到信息的问题的处理方法
    springcloud config配置git作为数据源然后启动报错 If you want an embedded database (H2, HSQL or Derby), please put it on the classpath.
    Sublime Text的列模式如何操作
    centos8 安装redis
    jQuery简单的Ajax调用
  • 原文地址:https://www.cnblogs.com/binarylei/p/9012175.html
Copyright © 2011-2022 走看看