代理模式

作者: 我可能是个假开发 | 来源:发表于2019-05-02 19:24 被阅读28次

    代理模式

    一、概念

    代理模式是指,为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户类和目标对象之间起到中介的作用。

    使用代理对象,是为了在不修改目标对象的基础上,增强主业务逻辑。
    客户类真正的想要访问的对象是目标对象,但客户类真正可以访问的对象是代理对象。
    客户类对目标对象的访问是通过访问代理对象来实现的。
    代理类与目标类要实现同一个接口。

    对于代理模式,需要注意以下几点:
    (1)代理类和目标类要实现同一个接口,即业务接口。
    (2)客户类对目标类的调用均是通过代理类完成的。
    (3)代理类的执行既执行了对目标类的增强业务逻辑,又调用了目标类的主业务逻辑。

    代理模式.png

    根据代理关系建立的时间不同,可以将代理分为两类:

    • 静态代理
    • 动态代理

    二、静态代理

    静态代理是指,代理类在程序运行前就已经定义好,其与目标类的关系在程序运行前就已经确立。

    demo:
    需求:目标类中小写的abc转为大写大ABC(不改变目标类的代码)

    TargetService:

    package com.hcx.design.pattern.proxy.staticproxy;
    
    /**
     * 主业务接口
     * Created by hongcaixia on 2019/5/2.
     */
    public interface TargetService {
        //目标方法
        String doFirst();
        void doSecond();
    }
    

    TargetServiceImpl:

    package com.hcx.design.pattern.proxy.staticproxy;
    
    /**
     * 目标类
     * Created by hongcaixia on 2019/5/2.
     */
    public class TargetServiceImpl implements TargetService{
    
        public String doFirst() {
            System.out.println("执行doFirst()");
            return "abc";
        }
    
        public void doSecond() {
            System.out.println("执行doSecond()方法");
        }
    }
    
    

    ProxyServiceImpl:

    package com.hcx.design.pattern.proxy.staticproxy;
    
    /**
     * 代理类
     * Created by hongcaixia on 2019/5/2.
     */
    public class ProxyServiceImpl implements TargetService{
    
        private TargetService targetService;
    
        public ProxyServiceImpl(TargetService targetService) {
            this.targetService = targetService;
        }
    
        public String doFirst() {
            String result = targetService.doFirst();
            return result.toUpperCase();
        }
    
        public void doSecond() {
            targetService.doSecond();
        }
    }
    
    

    MyTest:

    package com.hcx.design.pattern.proxy.staticproxy;
    
    /**
     * Created by hongcaixia on 2019/5/2.
     */
    public class MyTest {
    
        public static void main(String[] args) {
    //        TargetService targetService = new TargetServiceImpl();
            TargetService targetService = new ProxyServiceImpl(new TargetServiceImpl());
            //写完上面这句话,我突然一愣:这特喵不是装饰模式吗???!!!
            String result = targetService.doFirst();
            System.out.println("result是:"+result);
            targetService.doSecond();
            /**
             *  执行doFirst()
                result是:abc
                执行doSecond()方法
             */
            /**
             * 增强之后:
             * 执行doFirst()
               result是:ABC
               执行doSecond()方法
             */
        }
    }
    
    

    三、动态代理

    动态代理是指,程序在整个运行过程中根本就不存在目标类的代理类,目标对象的代理对象只是由代理生成工具(如代理工厂类)在程序运行时由 JVM 根据反射等机制动态生成的。代理对象与目标对象的代理关系在程序运行时才确立。
    对比静态代理,静态代理是指在程序运行前就已经定义好了目标类的代理类。代理类与目标类的代理关系在程序运行之前就确立了。

    1.JDK动态代理

    通过JDK的java.lang.reflect.Proxy 类实现动态代理,会使用其静态方法newProxyInstance(),依据目标对象、业务接口及业务增强逻辑三者,自动生成一个动态代理对象。

    public static newProxyInstance ( ClassLoader loader, Class<?>[] interfaces,
    InvocationHandler handler)
    loader:目标类的类加载器,通过目标对象的反射可获取
    interfaces:目标类实现的接口数组,通过目标对象的反射可获取
    handler:业务增强逻辑,需要再定义。
    

    InvocationHandler 接口:
    实现了 InvocationHandler 接口的类用于加强目标类的主业务逻辑。这个接口中有一个方法 invoke(),具体加强的代码逻辑就是定义在该方法中的。程序调用主业务逻辑时,会自动调用 invoke()方法。

    invoke()方法:

    public Object invoke ( Object proxy, Method method, Object[] args)
    proxy:代表生成的代理对象
    method:代表目标方法
    args:代表目标方法的参数
    

    由于该方法是由代理对象自动调用的,所以这三个参数的值不用程序员给出。第二个参数为 Method 类对象,该类有一个方法也叫invoke(),可以调用目标类的目标方法。
    这两个 invoke()方法,虽然同名,但无关:

    public Object invoke ( Object obj, Object... args)
    obj:表示目标对象
    args:表示目标方法参数,就是其上一层 invoke 方法的第三个参数
    

    该方法的作用是:调用执行 obj 对象所属类的方法,这个方法由其调用者 Method 对象确定。
    在代码中,一般的写法为method.invoke(target, args);
    其中,method 为上一层 invoke 方法的第二个参数。
    这样,即可调用了目标类的目标方法。

    TargetService:

    package com.hcx.design.pattern.proxy.jdkdynamicproxy;
    
    /**
     * 主业务接口
     * Created by hongcaixia on 2019/5/2.
     */
    public interface TargetService {
    
        //目标方法
        String doFirst();
        void doSecond();
    
    }
    

    TargetServiceImpl:

    package com.hcx.design.pattern.proxy.jdkdynamicproxy;
    
    /**
     * 目标类
     * Created by hongcaixia on 2019/5/2.
     */
    public class TargetServiceImpl implements TargetService {
    
        public String doFirst() {
            System.out.println("执行doFirst()");
            return "abc";
        }
    
        public void doSecond() {
            System.out.println("执行doSecond()方法");
        }
    }
    

    MyTest:

    package com.hcx.design.pattern.proxy.jdkdynamicproxy;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    
    /**
     * Created by hongcaixia on 2019/5/2.
     */
    public class MyTest {
        public static void main(String[] args) {
            final TargetService targetService = new TargetServiceImpl();
    
            /**
             * newProxyInstance:获取对应目标类的代理对象.
             * ClassLoader loader:目标类的类加载器
             * Class<?>[] interfaces:目标类所实现的所有接口
             * InvocationHandle h:InvocationHandler的实例
             */
            TargetService proxyService = (TargetService) Proxy.newProxyInstance(
                    targetService.getClass().getClassLoader(),
                    targetService.getClass().getInterfaces(),
                    new InvocationHandler() {
                        /**
                         * invoke:调用对应的目标类的方法.
                         * @param proxy 代理对象
                         * @param method 目标方法
                         * @param args 目标方法参数列表
                         * @return
                         * @throws Throwable
                         */
                        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                            //调用目标方法
                            Object result = method.invoke(targetService, args);
                            if(result!=null){
                                result = ((String)result).toUpperCase();
                            }
                            return result;
                        }
                    }
            );
    
            String result = proxyService.doFirst();
            System.out.println("result="+result);
            proxyService.doSecond();
            /**
             *  执行doFirst()
                result=ABC
                执行doSecond()方法
             */
        }
    }
    

    注意:使用jdk的动态代理要求目标类必须实现接口,其底层的执行原理与静态代理相同。

    2.CGLIB动态代理

    CGLIB(Code Generation Library)是一个开源项目,是一个强大的、高性能的、高质量的代码生成类库。它可以在运行期扩展和增强 Java 类。Hibernate 用它来实现持久对象的字节码的动态生成,Spring 用它来实现 AOP 编程。

    使用 JDK 的 Proxy 实现代理,要求目标类与代理类实现相同的接口。
    若目标类不存在接口,则无法使用该方式实现。
    但对于无接口的类,要为其创建动态代理,就要使用 CGLIB 来实现。CGLIB 代理的生成原理是生成目标类的子类,而子类是增强过的,这个子类对象就是代理对象。
    所以,使用CGLIB 生成动态代理,要求目标类必须能够被继承,即不能是 final 的类。

    CGLIB 包的底层是通过使用一个小而快的字节码处理框架 ASM(Java 字节码操控框架),来转换字节码并生成新的类。
    CGLIB 是通过对字节码进行增强来生成代理的。

    TargetService:

    package com.hcx.design.pattern.proxy.cglibdynamicproxy;
    
    /**
     * 目标类
     * Created by hongcaixia on 2019/5/2.
     */
    public class TargetService {
    
        public String doFirst(){
            System.out.println("执行doFist()方法");
            return "abc";
        }
    
        public void doSecond(){
            System.out.println("执行doSecond()方法");
        }
    }
    
    

    MyCglibFactory:

    package com.hcx.design.pattern.proxy.cglibdynamicproxy;
    
    import net.sf.cglib.proxy.Enhancer;
    import net.sf.cglib.proxy.MethodInterceptor;
    import net.sf.cglib.proxy.MethodProxy;
    
    import java.lang.reflect.Method;
    
    /**
     * Created by hongcaixia on 2019/5/2.
     */
    public class MyCglibFactory implements MethodInterceptor {
    
        private TargetService targetService;
    
        public MyCglibFactory(TargetService targetService) {
            this.targetService = targetService;
        }
    
        public TargetService myCglibFactory(){
    
            //创建增强器对象
            Enhancer enhancer = new Enhancer();
            //指定目标类,即父类
            enhancer.setSuperclass(TargetService.class);
            //设置回调接口对象(一个类实现了Callback接口,那么该类的对象就是回调对象)
            /**
             * 当前类实现了MethodInterceptor接口,而MethodInterceptor又继承了CallBack,
             * 所以当前类实现了Callback接口,所以当前类就是一个回调对象
             */
            enhancer.setCallback(this);
            //返回代理对象
            return (TargetService) enhancer.create();
        }
    
        /**
         * 回调方法
         * @param o 代理对象
         * @param method 代理对象的方法
         * @param objects 方法参数
         * @param methodProxy 代理对象方法的代理对象
         * @return
         * @throws Throwable
         */
        public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
            //调用目标方法
            Object result = method.invoke(targetService, objects);
            if(result!=null){
                result = ((String) result).toUpperCase();
            }
            return result;
        }
    }
    
    

    MyTest:

    package com.hcx.design.pattern.proxy.cglibdynamicproxy;
    
    /**
     * Created by hongcaixia on 2019/5/2.
     */
    public class MyTest {
        public static void main(String[] args) {
            TargetService proxyService = new MyCglibFactory(new TargetService()).myCglibFactory();
            String result = proxyService.doFirst();
            System.out.println("result="+result);
            proxyService.doSecond();
            /**
             * 执行doFist()方法
               result=ABC
               执行doSecond()方法
             */
        }
    }
    
    

    注意,使用MethodInterceptor需要手动添加两个jar:
    在idea中的步骤:
    File-->Project Structure-->Modules:

    手动引入第三方依赖.png

    cglib动态代理也可以使用接口的方式,在此不再赘述,代码详见文末的链接。

    方法回调设计:
    在 Java 中,就是类 A 调用类 B 中的某个方法 b,然后类 B 又在某个时候反过来调用类 A中的某个方法 a,对于 A 来说,这个 a 方法便叫做回调方法。
    Java 的接口提供了一种很好的方式来实现方法回调。
    这个方式就是定义一个简单的接口,在接口之中定义一个我们希望回调的方法。这个接口称为回调接口。

    在上面的例子中,
    MyCglibFactory类就相当于前面所说的 A类,
    而 Enhancer 类则是 B 类。
    A 类中调用了 Enhancer 类的setCallback(this)方法,并将回调
    对象 this 作为实参传递给了 Enhancer 类。Enhancer 类在后续执行过程中,会调用 A 类中的intercept()方法,这个 intercept()方法就是回调方法。

    代码链接:https://github.com/GitHongcx/design_pattern/tree/master/src/main/java/com/hcx/design/pattern/proxy

    相关文章

      网友评论

        本文标题:代理模式

        本文链接:https://www.haomeiwen.com/subject/aolwnqtx.html