zoukankan      html  css  js  c++  java
  • 设计模式之代理模式

    什么是代理模式

    代理模式就是为一个对象提供一个代理对象,由这个代理对象控制对该对象的访问。

    理解代理模式,可以对照生活中的一些具体例子,比如房产中介、二手车交易市场、经纪人等。

    为什么要用代理模式

    通过使用代理模式,我们避免了直接访问目标对象时可能带来的一些问题,比如:远程调用,需要使用远程代理来帮我们处理一些网络传输相关的细节逻辑;可能需要基于某种权限控制对目标资源的访问,可以使用保护代理等。

    总的来说,通过是用代理模式,我们可以控制对目标对象的访问,可以在真实方法被调用前或调用后,通过代理对象加入额外的处理逻辑。

    代理模式分类

    代理模式分为静态代理和动态代理。动态代理根据实现不同又可细分为JDK动态代理和cglib动态代理。

    静态代理是由程序员创建或工具生成代理类的源码,再编译代理类。所谓静态也就是在程序运行前就已经存在代理类的字节码文件,代理类和委托类的关系在运行前就确定了。

    动态代理是在实现阶段不用关心代理类,而在运行时动态生成代理类的。

    静态代理

    以房哥买房子为例,用代码实现静态代理。

    1、首先建立一个Seller接口

    public interface Seller {
        void sell();
    }
    

    2、创建实现类,房哥,有一个方法,就是买房子

    public class FangGe implements Seller{
        @Override
        public void sell() {
            System.out.println("房哥要出手一套四合院");
        }
    }
    

    3、买房子需要找到买家,达成交易后还要办理过户等其他手续,房哥只想卖房收钱就完了。因此,需要找一个代理来帮房哥处理这些杂事。

    我们创建一个代理类FangGeProxy,代理类也需要实现Seller接口,行为上要保持和FangGe一样,都是要卖房子。同时该代理类还需要持有房哥的引用。

    public class FangGeProxy implements Seller{
        private FangGe fangGe;
    
        public FangGeProxy(FangGe fangGe){
            this.fangGe = fangGe;
        }
        @Override
        public void sell() {
            findBuyer();
            fangGe.sell();
            afterSell();
        }
        
        public void findBuyer(){
            System.out.println("代理帮助寻找买主");
        }
        
        public void afterSell(){
            System.out.println("达成交易后,办理相关手续");
        }
    }
    

    可以看到,房哥的代理类通过findBuyer()和afterSell()两个方法帮助房哥完成了其他一些杂事。

    4、测试类

    public class StaticProxyTest {
        public static void main(String[] args) {
            Seller seller = new FangGeProxy(new FangGe());
            seller.sell();
        }
    }
    

    输出:

    代理帮助寻找买主
    房哥要出手一套四合院
    达成交易后,办理相关手续
    

    最后,看下类图
    在这里插入图片描述

    静态代理的问题:

    1、由于静态代理类在编译前已经确定了代理的对象,因此静态代理只能代理一种类型的类,如果要给大量的类做代理,就需要编写大量的代理类;

    2、如果我们要给Seller,也就是目标对象要增加一些方法,则需要同步修改代理类,不符合开闭原则。

    JDK动态代理

    JDK的动态代理依赖于jdk给我们提供的类库实现,是一种基于接口实现的动态代理,在编译时并不知道要代理哪个类,而是在运行时动态生成代理类。同时也解决了静态代理中存在的问题。

    我们接上上面静态代理的例子,继续实现JDK的动态代理。

    1、我们建一个方法转发的处理器类,该类需要实现InvocationHandler接口。

    public class SellerInvocationHandler implements InvocationHandler {
    
        // 要代理的真实对象
        private Object target;
    
        /**
         * 使用Proxy类静态方法获取代理类实例
         */
        public Object getProxyInstance(Object target){
            this.target = target;
            Class<?> clazz = target.getClass();
            return Proxy.newProxyInstance(clazz.getClassLoader(),clazz.getInterfaces(),this);
        }
        
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            before();
            Object obj = method.invoke(this.target, args);
            after();
            return obj;
        }
    
        private void before() {
            System.out.println("执行方法前");
        }
        
        private void after() {
            System.out.println("执行方法后");
        }
    }
    

    2、新建JDK动态代理测试类,首先代理房哥卖房子

    public class JDKDynamicProxyTest {
        public static void main(String[] args) {
    
            // new一个房哥,下面帮房哥找个代理
            FangGe fangGe = new FangGe();
            SellerInvocationHandler sellerInvocationHandler = new SellerInvocationHandler();
            
            // 房哥的代理对象
            Seller seller = (Seller) sellerInvocationHandler.getProxyInstance(fangGe);
            seller.sell();
    
        }
    }
    

    输出:

    执行方法前
    房哥要出手一套四合院
    执行方法后
    

    可以看到,完成了代理。

    3、接下来我们新建另外一个类,User类,并使用JDK动态代理完成代理User类

    public interface IUser {
        void sayHello();
    
        void work();
    }
    
    public class UserImpl implements IUser{
        @Override
        public void sayHello() {
            System.out.println("hello,我是小明");
        }
    
        @Override
        public void work() {
            System.out.println("我正在写代码");
        }
    }
    

    修改测试类,

    public class JDKDynamicProxyTest {
        public static void main(String[] args) {
    
    /*        // new一个房哥,下面帮房哥找个代理
            FangGe fangGe = new FangGe();
            SellerInvocationHandler sellerInvocationHandler = new SellerInvocationHandler();
    
            // 房哥的代理对象
            Seller seller = (Seller) sellerInvocationHandler.getProxyInstance(fangGe);
            seller.sell();*/
    
            // 代理user类
            IUser user = new UserImpl();
            SellerInvocationHandler sellerInvocationHandler = new SellerInvocationHandler();
            IUser userProxy = (IUser) sellerInvocationHandler.getProxyInstance(user);
            userProxy.sayHello();
            userProxy.work();
    
        }
    }
    

    输出:

    执行方法前
    hello,我是小明
    执行方法后
    执行方法前
    我正在写代码
    执行方法后
    
    

    可以看到,我们SellerInvocationHandler 并未做任何改动,它便能为UserImpl类生成代理,并在执行方法的前后增加额外的执行逻辑。

    cglib动态代理

    JDK动态代理有一个局限就是,被代理的类必须要实现接口。如果被代理的类没有实现接口,则JDK动态代理就无能为力了。这个时候该cglib动态代理上场了。

    CGLIB是一个功能强大,高性能的代码生成包。它为没有实现接口的类提供代理,为JDK的动态代理提供了很好的补充。通常可以使用Java的动态代理创建代理,但当要代理的类没有实现接口或者为了更好的性能,CGLIB是一个好的选择。

    1、新建一个MyCglibInterceptor,实现MethodInterceptor接口。该类类似于JDK动态代理中的InvocationHandler实例,是实现cglib动态代理的主要类。

    public class MyCglibInterceptor implements MethodInterceptor {
    
        public Object getCglibProxyInstance(Object object){
            // 相当于Proxy,创建代理的工具类
            Enhancer enhancer = new Enhancer();
            enhancer.setSuperclass(object.getClass());
            enhancer.setCallback(this);
            return enhancer.create();
        }
    
        public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
            before();
            Object obj = methodProxy.invokeSuper(o, objects);
            after();
            return obj;
        }
    
        private void before() {
            System.out.println("执行方法之前");
        }
    
        private void after() {
            System.out.println("执行方法之后");
        }
    }
    

    2、新建cglib动态代理的测试类,先代理上面例子中的User类。

    public class CglibDynamicProxyTest {
        public static void main(String[] args) {
            MyCglibInterceptor myCglibInterceptor = new MyCglibInterceptor();
            IUser userCglibProxy = (IUser) myCglibInterceptor.getCglibProxyInstance(new UserImpl());
            userCglibProxy.sayHello();
            userCglibProxy.work();
        }
    }
    

    输出:

    执行方法之前
    hello,我是小明
    执行方法之后
    执行方法之前
    我正在写代码
    执行方法之后
    

    3、新建一个类HelloWorld,不实现任何接口,为该类实现动态代理。

    public class HelloWorld {
        public void hello(){
            System.out.println("世界这么大,我想去看看");
        }
    }
    

    测试代理类

    public class CglibDynamicProxyTest {
        public static void main(String[] args) {
    /*        MyCglibInterceptor myCglibInterceptor = new MyCglibInterceptor();
            IUser userCglibProxy = (IUser) myCglibInterceptor.getCglibProxyInstance(new UserImpl());
            userCglibProxy.sayHello();
            userCglibProxy.work();*/
    
            // 代理未实现任何接口的类
            MyCglibInterceptor myCglibInterceptor = new MyCglibInterceptor();
            HelloWorld helloWorldProxy = (HelloWorld) myCglibInterceptor.getCglibProxyInstance(new HelloWorld());
            helloWorldProxy.hello();
        }
    }
    

    输出:

    执行方法之前
    世界这么大,我想去看看
    执行方法之后
    

    使用cglib动态代理,我们实现了对普通类的代理。

    (完)

    设计模式系列文章

    单例模式
    工厂模式
    原型模式

  • 相关阅读:
    在VMware上安装CentOS-6.5 minimal
    [Android] Gradle 安装
    [WPF] 动画Completed事件里获取执行该动画的UI对象
    Realm数据库的使用(二)数据库的添加、删除、修改、查询
    Realm数据库的使用(一)数据库的简单介绍和模型的创建
    进入JVM的世界:《深入理解JVM虚拟机》-- 思维导图
    图解Disruptor框架(一):初识Ringbuffer
    图解Disruptor框架(二):核心概念
    常用排序算法的总结以及编码(Java实现)
    《Java并发编程实战》读书笔记一 -- 简介
  • 原文地址:https://www.cnblogs.com/happyone/p/11932874.html
Copyright © 2011-2022 走看看