美文网首页
Java中的代理模式

Java中的代理模式

作者: luweicheng24 | 来源:发表于2017-09-30 15:34 被阅读0次

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

    代理分为静态代理和动态代理:

    • 静态代理

    1. 首先创建一个业务接口(公司员工申请奖金的接口):
    /**
     * 
     * 作者: luweicheng24
     * 时间: 2017年9月30日
     * 功能描述: 公司员工申请奖金的业务接口
     *
     */
    public interface ApplyBonus {
        void result();// 申请结果
    }
    
    
    1. 创建一个员工类,实现申请奖金的业务接口:
    /**
     *  
     * 作者: luweicheng24
     * 时间: 2017年9月30日
     * 功能描述: 员工类
     *
     */
    public class Employee implements ApplyBonus {
        public Employee(String position) {
            this.position = position;
        }
        private int salary; //奖金薪水
        private String position; // 职位
        public String getPosition() {
            return position;
        }
        public void setPosition(String position) {
            this.position = position;
        }
        public int getSalary() {
            return salary;
        }
    
        public void setSalary(int salary) {
            this.salary = salary;
        }
        @Override
        public void result() {
                System.out.println(position+"申请奖金的最后金额="+salary);
        }
        @Override
        public String toString() {
            return "Person [salary=" + salary + ", position=" + position + "]";
        }
        
    }
    
    
    1. 创建一个员工的代理类实现根据员工职位决定奖金:
    /**
    * 
    * 作者: luweicheng24
    * 时间: 2017年9月30日
    * 功能描述: 员工代理类
    *
    */
    public class EmployeeProxy implements ApplyBonus{
       private int salary; // 代理奖金
       private String position; // 代理对象的职位
       private ApplyBonus   employee; // 代理对象
       /**
        *  代理对象的构造函数
        * @param employee 员工对象
        */
       public  EmployeeProxy(Employee employee) {
           this.employee = employee;
           this.position = employee.getPosition();
       }
       @Override
       public void result() {
           switch (position) {
           case "员工":
               salary = 1000;          
               break;
           case "主管":  
               salary  = 2000;
               break;
           }
       }
       @Override
       public String toString() {
           return "代理信息 [奖金 =" + salary + ", 职位=" + position + "]";
       }   
    }
    
    

    员工代理类通过传入代理对象,对代理对象的信息进行核实和确认之后合理分发奖金,静态代理通常在开发中为了补充被代理对象的逻辑,以及在前期开发中未做的一下工作任务。

    动态代理

    在Java中存在一个类 Proxy,该类是专门用来生成代理对象,然后执行代理方法等功能,该类通过一个静态方法生成代理,下面笔者贴部分源码加上自己的注释:

    /**
    *  loader : 需要代理对象的类加载器
    *  interfaces 表示该代理对象实现的接口的集合
    * InvocationHandler :一个包含invoke方法的接口
     * invoke方法是在代理对象每次调用被代理对象的方法的时候调用
    */
      public static Object newProxyInstance(ClassLoader loader,
                                              Class<?>[] interfaces,
                                              InvocationHandler h)
    
    

    InvocationHandler

    该类的原文注释:

    /**
         * Processes a method invocation on a proxy instance and returns
         * the result.  This method will be invoked on an invocation handler
         * when a method is invoked on a proxy instance that it is
         * associated with.
         */
    翻译的意思:处理代理实例上的方法调用并返回 结果。 
    这个方法将在调用处理程序中被调用当一个方法在代理实例上被调用时关联。
    

    该类只包含一个方法:

    public interface InvocationHandler {
    /**
    * proxy :表示代理对象
    * method: 表示代理的方法
    * args : 表示方法的参数
    * 该方法在被代理对象中的任何方法执行时执行
    */
     public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable;
    }
    

    所以需要在调用被代理对象方法执行时添加逻辑,就需要实现该类的接口,在invoke方法中,添加逻辑。

    了解InvocationHandler之后,继续看ProxynewProxyInstance方法,在该方法中主要包含三个方法:

       public static Object newProxyInstance(ClassLoader loader,
                                              Class<?>[] interfaces,
                                              InvocationHandler h)
            throws IllegalArgumentException
        {
            if (h == null) {
                throw new NullPointerException();
            }
    
            /*
             * Look up or generate the designated proxy class.
             */
            Class<?> cl = getProxyClass(loader, interfaces); // 1.通过类加载器和接口数组获取代理类
    
            /*
             * Invoke its constructor with the designated invocation handler.
             */
            try {
               /**
               * constructorParams 表示InvocationHandler实现类的数组
              *    / ** parameter types of a proxy class constructor */
              *    private final static Class[] constructorParams =
              *    { InvocationHandler.class };
              */
                Constructor cons = cl.getConstructor(constructorParams); // 2. 获取代理类的构造方法
                return cons.newInstance(new Object[] { h }); // 3. 创建代理对象
            } catch (NoSuchMethodException e) {
                throw new InternalError(e.toString());
            } catch (IllegalAccessException e) {
                throw new InternalError(e.toString());
            } catch (InstantiationException e) {
                throw new InternalError(e.toString());
            } catch (InvocationTargetException e) {
                throw new InternalError(e.toString());
            }
        }
    
    

    对如上三个步骤解释:

    1. Class<?> cl = getProxyClass(loader, interfaces); 获取代理类,贴出源码(省略了部分不重要的源码),部分代码添加注释
    public static Class<?> getProxyClass(ClassLoader loader,
                                             Class<?>... interfaces)
            throws IllegalArgumentException
        {
            if (interfaces.length > 65535) {  // 接口数组长度不能超过65535
                throw new IllegalArgumentException("interface limit exceeded");
            }
    
            Class<?> proxyClass = null; // 代理类的引用
    
            /* collect interface names to use as key for proxy class cache */
            String[] interfaceNames = new String[interfaces.length]; // 创建一个接口名组成的字符数组,在之后的代理类的缓存键值对中作为Key。
    
            // for detecting duplicates
            Set<Class<?>> interfaceSet = new HashSet<>(); // 创建一个set集合,存储接口类
           // 便利接口数组,存储接口类到set中
            for (int i = 0; i < interfaces.length; i++) {
                /*
                 * Verify that the class loader resolves the name of this
                 * interface to the same Class object.
                 */
                String interfaceName = interfaces[i].getName();
                Class<?> interfaceClass = null;
                try {
                    interfaceClass = Class.forName(interfaceName, false, loader);
                } catch (ClassNotFoundException e) {
                }
                if (interfaceClass != interfaces[i]) {
                    throw new IllegalArgumentException(
                        interfaces[i] + " is not visible from class loader");
                }
    
                /*
                 * Verify that the Class object actually represents an
                 * interface.
                 */
                if (!interfaceClass.isInterface()) {
                    throw new IllegalArgumentException(
                        interfaceClass.getName() + " is not an interface");
                }
    
                /*
                 * Verify that this interface is not a duplicate.
                 */
                if (interfaceSet.contains(interfaceClass)) {
                    throw new IllegalArgumentException(
                        "repeated interface: " + interfaceClass.getName());
                }
                interfaceSet.add(interfaceClass);
    
                interfaceNames[i] = interfaceName;
            }
    
            // 将接口名数组转换成一个L
            List<String> key = Arrays.asList(interfaceNames);
    
            /*
             * Find or create the proxy class cache for the class loader.
             */
            // 从loaderToCache一个弱引用的Map key是一个类加载器 value 是如下的cache集合,
          // cache 是值为接口名字的list 值为 类加载器 如果缓存中没有,添加到缓存
            Map<List<String>, Object> cache;
            synchronized (loaderToCache) {
                cache = loaderToCache.get(loader);
                if (cache == null) {
                    cache = new HashMap<>();
                    loaderToCache.put(loader, cache);
                }
     
            }
         // 加锁从缓存给代理类赋值
            synchronized (cache) {
    
                do {
                    Object value = cache.get(key);
                    if (value instanceof Reference) {
                        proxyClass = (Class<?>) ((Reference) value).get();
                    }
                    if (proxyClass != null) {
                        // proxy class already generated: return it
                        return proxyClass;
                    } else if (value == pendingGenerationMarker) {
                        // proxy class being generated: wait for it
                        try {
                            cache.wait();
                        } catch (InterruptedException e) {
                        }
                        continue;
                    } else {
                        cache.put(key, pendingGenerationMarker);
                        break;
                    }
                } while (true);
            }
        // 查找代理的包名
            try {
                String proxyPkg = null;     // package to define proxy class in
                for (int i = 0; i < interfaces.length; i++) {
                    int flags = interfaces[i].getModifiers();
                    if (!Modifier.isPublic(flags)) {
                        String name = interfaces[i].getName();
                        int n = name.lastIndexOf('.');
                        String pkg = ((n == -1) ? "" : name.substring(0, n + 1));
                        if (proxyPkg == null) {
                            proxyPkg = pkg;
                        } else if (!pkg.equals(proxyPkg)) {
                            throw new IllegalArgumentException(
                                "non-public interfaces from different packages");
                        }
                    }
                }
    
                if (proxyPkg == null) {     // if no non-public proxy interfaces,
                    proxyPkg = "";          // use the unnamed package
                }
    
                {
                    /*
                     * Choose a name for the proxy class to generate.
                     */
                    long num;
                    synchronized (nextUniqueNumberLock) {
                        num = nextUniqueNumber++;
                    }
                    String proxyName = proxyPkg + proxyClassNamePrefix + num;
                    /*
                     * Verify that the class loader hasn't already
                     * defined a class with the chosen name.
                     */
    
                    /*
                     * Generate the specified proxy class.
                     */
                    byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
                        proxyName, interfaces);
                    try {
                        proxyClass = defineClass0(loader, proxyName,
                            proxyClassFile, 0, proxyClassFile.length);
                    } catch (ClassFormatError e) {
                        /*
                         * A ClassFormatError here means that (barring bugs in the
                         * proxy class generation code) there was some other
                         * invalid aspect of the arguments supplied to the proxy
                         * class creation (such as virtual machine limitations
                         * exceeded).
                         */
                        throw new IllegalArgumentException(e.toString());
                    }
                }
                // add to set of all generated proxy classes, for isProxyClass
                proxyClasses.put(proxyClass, null);
         
            } finally {
                synchronized (cache) {  
                    if (proxyClass != null) {
                        cache.put(key, new WeakReference<Class<?>>(proxyClass));
                    } else {
                        cache.remove(key);
                    }
                    cache.notifyAll();// 唤醒等待的线程
                }
            }
            return proxyClass;
        }
    
    1. Constructor cons = cl.getConstructor(constructorParams);获取代理类的构造方法
    2. cons.newInstance(new Object[] { h });通过构造方法创建一个实例。

    好了,源码分析完了,回到上面的问题,如何动态代理员工进行奖金的分发,创建一个代理类实现InvocationHandler接口。

    /*
     * 代理处理类
     */
    public class DynamicProxy implements InvocationHandler{
        
        private Employee target;
        public DynamicProxy(Employee target) {
            this.target = target;
        }
            
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            /*
             * 代理类方法执行时调用
             */
            if(method.getName().equals("result")){
                
            if(target.getPosition().equals("经理")){
                target.setSalary(target.getSalary()+1000);
            }else {
                target.setSalary(target.getSalary()+500);
              }
            }
            
            Object obj = method.invoke(target, args);
            
            /*
             * 委托类方法执行后操作
             */
            return obj;
        }
    }
    
    

    耗费了好几个小时了,下面来测试一下:

    
    public class Proxytest {
        public static void main(String[] args) {
        ApplyBonus buyCar = (ApplyBonus)Proxy.newProxyInstance(Employee.class.getClassLoader(), new Class[]{ApplyBonus.class},new DynamicProxy(new Employee("经理")));
        buyCar.result();    
        }
    
    }
    

    控制台输出结果:

    经理申请奖金的最后金额=1000
    
    

    以上均是个人的理解,如有疑问留言讨论。

    相关文章

      网友评论

          本文标题:Java中的代理模式

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