• Java 基础【19】代理


       Java 代理(Proxy)模式与现实中的代理含义一致,如旅游代理、明星的经纪人。

       在目标对象实现基础上,增加额外的功能操作,由此来扩展目标对象的功能。

       JavaWeb 中最常见的过滤器、Struts 中的拦截器、Spring 中的 AOP...都有代理的应用。

       此篇博客将编写例子描述 Java 底层技术和开源类库Cglib实现代理的方法,并对比各方法的优缺性。

       例子源码:https://github.com/OrsonEx/proxy-demo.git

    1.JDK 静态代理

       抽象接口:

    /**
     * 用户服务抽象
     */
    public interface UserService {
    
        /**
         * 用户登录
         *
         * @param userName 用户名
         * @param pwd      密码
         * @return 登陆结果
         */
        String login(String userName, String pwd);
    }

       实现该接口:

    /**
     * 用户服务实现
     *
     * @author Rambo 2019-03-01
     **/
    public class UserServiceImpl implements UserService {
    
    
        @Override
        public String login(String userName, String pwd) {
            Console.log("进行登陆逻辑.........");
    
            return "登陆结果";
        }
    }

       编码代理类,实现该接口,代理目标作为私有对象:

    /**
     * 用户服务代理类
     *
     * @author Rambo 2019-03-01
     **/
    public class UserServiceProxy implements UserService {
        private UserService userService;
    
        UserServiceProxy(UserService userService) {
            this.userService = userService;
        }
    
        @Override
        public String login(String userName, String pwd) {
            Console.log("登陆前扩展.....");
            userService.login(userName, pwd);
            Console.log("登陆后扩展.....");
            return "登陆结果";
        }
    }

       编写测试类:

        @Test
        public void testLogin() throws Exception {
            UserServiceProxy userServiceProxy = new UserServiceProxy(new UserServiceImpl());
            userServiceProxy.login("rambo","111111");
        }

       最原始实现代理的样子,缺点也很明显,当目标类方法调整后,需要同步维护代理类。且需要单独编码代理类,势必导致冗余。

    2.JDK 动态代理(接口代理)

       代理核心方法 Proxy.newProxyInstance :

        /**
         * JDK 生成代理类
         * @param loader 当前目标对象使用类加载器
         * @param interfaces 目标对象实现的接口的类型
         * @param h 事件处理对象,通过反射执行目标对象的方法
         * @return 生成的代理类实例
         */
        @CallerSensitive
        public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces, InvocationHandler h)

       代理工厂类:

    /**
     * 代理工厂类
     *
     * @author Rambo 2019-03-01
     **/
    public class JdkProxyFactory {
    
        private Object target;
    
        public JdkProxyFactory(Object target) {
            this.target = target;
        }
    
        public Object getProxyInstance() {
            return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), new InvocationHandler() {
                @Override
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    Console.log("执行目标前的扩展......");
                    Object returnValue = method.invoke(target, args);
                    Console.log("执行目标后的扩展......");
                    return returnValue;
                }
            });
        }
    }

       编写测试类:

        @Test
        public void testGetProxyInstance() throws Exception {
            UserService proxyInstance = (UserService) new ProxyFactory(new UserServiceImpl()).getProxyInstance();
            proxyInstance.login("rambo", "111111");
        }

       目标对象需要实现接口,代理对象不用实现目标对象的接口。

       需要统一实现 InvocationHandler 接口中 invoke 方法,编码实现目标方法前后的扩展操作。

       和静态代理相比:代理对象通过反射动态生成,无需进行编码。目标对象方法进行调整后,代理对象无需做任何调整。

    3.Cglib 代理 (子类代理)

       当目标对象是个单独的类,没有实现任何接口,是无法使用上述两种代理方法,这时候怎么办?

       可以使用 Cglib 代理(需要单独引入 cglib 类库),底层通过一个小而快的字节码处理框架 Asm 来转换字节码并生成新的类。

       Cglib 通过自定义目标对象的子类进行目标对象的扩展,且这种扩展进行在 Jvm 运行期。

       不局限目标类建模方式(有无继承接口)、运行期增强目标类、底层精致的 asm 字节码框架,使 Cglib 成为许多 AOP 框架生成动态代理的首选。

       代理工厂类:

    /**
     * Cglib 动态代理工厂
     *
     * @author Rambo 2019-03-01
     **/
    public class CgbProxyFactory implements MethodInterceptor {
    
        private Object target;
    
        public CgbProxyFactory(Object target) {
            this.target = target;
        }
    
        public Object getProxyInstance() {
            /*
              Enhancer类为Cglib库中的字节码增强器,它可以方便对你想要处理的类进行扩展;
              将被代理类 target 设置成父类,然后设置当前 intercept 为代理拦截器;
              最后执行 enhancer.create() 动态生成一个代理类。
             */
            Enhancer en = new Enhancer();
            en.setSuperclass(target.getClass());
            en.setCallback(this);
            return en.create();
    
        }
    
        @Override
        public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
            Console.log("执行目标前的扩展......");
            Object returnValue = method.invoke(target, args);
            Console.log("执行目标后的扩展......");
            return returnValue;
        }
    }

       编写测试用例:

        @Test
        public void testGetProxyInstance() throws Exception {
            UserService proxyInstance = (UserService) new CgbProxyFactory(new UserServiceImpl()).getProxyInstance();
            proxyInstance.login("rambo","111111");
        }

       JDK InvocationHandler 、Cglib MethodInterceptor 具体实现的细节,如你有兴趣可翻翻源码,这里就不赘述了。

       至此代理的几种方式都已描述完毕,希望能帮助你对 java 代理有系统的了解。

  • 相关阅读:
    在 Asp.NET MVC 中使用 SignalR 实现推送功能
    开发高效的Tag标签系统数据库设计
    【译】ASP.NET MVC 5 官方教程
    介绍 ASP.NET Identity
    iOS使用UIWebView遇到Error Domain=WebKitErrorDomain Code=101 “The operation couldn’t be completed. (WebKitErrorDomain error 101
    浏览器是怎样工作的:渲染引擎,HTML解析
    堆 和 栈的 区别(经典)
    Javascript的堆和栈的简单理解
    页面呈现、重绘、回流。
    什么是 dynaTrace Ajax
  • 原文地址:https://www.cnblogs.com/java-class/p/10455094.html
Copyright © 2020-2023  润新知