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请听下回分解。。。

  • 相关阅读:
    使用Entity Framework时要注意的一些性能问题
    在EntityFrameWork中定义值对象的注意事项
    关于EntityFrameWork中的贪婪加载和延时加载
    Centos上安装mplayer
    Centos上安装madplay
    编译可加载lcd驱动的linux uImage
    ok6410上移植madplay
    linux上安装Qt
    ok6410的mplayer移植
    交叉编译tslib
  • 原文地址:https://www.cnblogs.com/monkjavaer/p/9595339.html
Copyright © 2011-2022 走看看