zoukankan      html  css  js  c++  java
  • 夜话JAVA设计模式之代理模式(Proxy)

    代理模式定义:为另一个对象提供一个替身或者占位符以控制对这个对象的访问。---《Head First 设计模式》

    代理模式换句话说就是给某一个对象创建一个代理对象,由这个代理对象控制对原对象的引用,并且创建这个代理对象后可以在调用原对象时增加一些额外操作。

    下面就由一张代理原理图说明:

    1.Subject抽象主题,Proxy和RealSubject都要实现的接口。这允许任何客户都可以像处理RealSubject对象一样地处理Proxy.

    2.Proxy:代理类,必须持有所代理对象的引用。

    3.RealSubject被代理的类,真正做事的对象。Proxy会控制对RealSubject的访问。

    举一个栗子:我们有一个看电影的接口,此时有一个只有看电影的类、还有一个代理商,代理了这部电影,并在看电影开始和结束都加入了广告。

    看电影接口:

    /**
     * @author monkjavaer
     * @date 2018/09/05 22:18
     */
    public interface Subject {
    
        void seeMovie();
    }

    真正的电影:

    /**
     * @author monkjavaer
     * @date 2018/09/05 22:19
     */
    public class RealSubject implements Subject{
    
        @Override
        public void seeMovie() {
            System.out.println("RealSubject:看电影");
        }
    }
    

     代理商:

    /**
     * 代理类
     * @author monkjavaer
     * @date 2018/09/05 22:20
     */
    public class Proxy implements Subject {
    
        private Subject subject;
    
        public Proxy(Subject subject) {
            this.subject = subject;
        }
    
        @Override
        public void seeMovie() {
            System.out.println("Proxy:加广告");
            subject.seeMovie();
            System.out.println("Proxy:加广告");
        }
    }
    

    测试:

    /**
     * 测试
     * @author monkjavaer
     * @date 2018/09/05 22:22
     */
    public class ProxyTest {
        public static void main(String[] args) {
    
            Proxy proxy = new Proxy(new RealSubject());
            proxy.seeMovie();
    
        }
    }
    

      结果:

      Proxy:加广告
      RealSubject:看电影
      Proxy:加广告

    上面就是我们传说中的静态代理。

    好了,说了这么多,总结静态代理123。。。

    优点: RealSubject通过Proxy实现了功能的增强。 在没有Proxy的时候,RealSubject依然可以执行。

    缺点: Proxy要实现和RealSubject同样的接口Subject,Proxy只能为Subject进行代理;如果有很多接口的实现类需要代理,那么就需要很多的代理类。

    嗯,,,既然静态代理有这个缺点,那我们啷个办呢。。。。。。

    下面我们就引入动态代理。

    jdk动态代理:java动态代理位于java.lang.reflect这个包下。其实现原理就是通过反射获取构造函数对象并生成代理类实例。

    java Proxy 源码中有这样的一段描述:

    /*
             * Look up or generate the designated proxy class.
             */
            Class<?> cl = getProxyClass0(loader, intfs);
    
            /*
             * Invoke its constructor with the designated invocation handler.
             */
            try {
                if (sm != null) {
                    checkNewProxyPermission(Reflection.getCallerClass(), cl);
                }
    
                final Constructor<?> cons = cl.getConstructor(constructorParams);
                final InvocationHandler ih = h;
                if (!Modifier.isPublic(cl.getModifiers())) {
                    AccessController.doPrivileged(new PrivilegedAction<Void>() {
                        public Void run() {
                            cons.setAccessible(true);
                            return null;
                        }
                    });
                }
                return cons.newInstance(new Object[]{h});
            } catch (IllegalAccessException|InstantiationException e) {
                throw new InternalError(e.toString(), e);
            } 
    

     JDK动态代理的实现步骤:

    1.创建一个实现接口InvocationHandler的类,它必须实现invoke方法

    2.创建被代理的类以及接口

    3.通过Proxy的静态方法newProxyInstance(ClassLoaderloader, Class[] interfaces, InvocationHandler h)创建一个代理类

    4.通过代理类调用方法

    下面是具体的java代码实现:

    场景:小明吃饭前后都有人帮忙做一些事情。。。

    /**
     * 人
     */
    public interface People {
    
        void eat();
    }
    

     小明:

    public class XiaoMing implements People {
        @Override
        public void eat() {
            System.out.println("小明吃红烧肉");
        }
    }
    

     代理类:

    /**
     * 代理类
     * @author monkjavaer
     * @date 2018/8/31 15:39
     */
    public class InvocationHandlerImpl implements InvocationHandler {
    
        /**
         * 代理的对象
         */
        private Object object;
    
        public InvocationHandlerImpl(Object object) {
            this.object = object;
        }
    
        /**
         *
         * @param proxy JDK动态生成的最终代理对象
         * @param method 调用真实对象的某个方法的Method对象
         * @param args 调用真实对象某个方法时接受的参数
         * @return 代理对象
         * @throws Throwable
         */
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("before:把饭做好");
    
            //当代理对象调用真实对象的方法时,其会自动的跳转到代理对象关联的handler对象的invoke方法来进行调用
            Object invokeObject = method.invoke(object, args);
    
            System.out.println("after:洗碗");
            return invokeObject;
        }
    }
    

     测试:

    /**
     * 小明吃饭
     * @author monkjavaer
     * @date 2018/8/31 15:53
     */
    public class ProxyTest {
        public static void main(String[] args) {
            People people = new XiaoMing();
            InvocationHandlerImpl handler1 = new InvocationHandlerImpl(people);
            //类加载器
            ClassLoader classLoader1 = people.getClass().getClassLoader();
            //得到全部的接口
            Class[] interfaces1 = people.getClass().getInterfaces();
            //返回代理类的一个实例
            People peopleProxy = (People) Proxy.newProxyInstance(classLoader1, interfaces1, handler1);
    
            peopleProxy.eat();
        }
    }
    

     根据上面的jdk动态代理我们得知: 

       jdk动态代理有局限,必须针对接口,如果没有接口像上面的例子People、目标没有实现接口那么这个办法就GG了。

       这时我们就需要用到另一种代理Cglib动态代理。预知Cglib请听下回分解。。。

  • 相关阅读:
    There is an overlap in the region chain修复
    There is an overlap in the region chain
    region xx not deployed on any region server
    python 中的re模块,正则表达式
    TCP粘包问题解析与解决
    yield from
    Git push提交时报错Permission denied(publickey)...Please make sure you have the correct access rights and the repository exists.
    mysql 中Varchar 与char的区别
    Mysql 字符集及排序规则
    请实现一个装饰器,限制该函数被调用的频率,如10秒一次
  • 原文地址:https://www.cnblogs.com/monkjavaer/p/9595339.html
Copyright © 2011-2022 走看看