美文网首页设计模式
设计模式--代理模式

设计模式--代理模式

作者: IT雪山 | 来源:发表于2018-09-20 11:29 被阅读0次

    一, 什么是代理

    简单来讲,代理就是帮助【目标对象】去完成应该做,但是不想活不擅长做的事情,比如媒婆帮你找对象,黄牛帮你抢票

    二, 代理模式分类

    本文以Java为例,代理分为静态代理和动态代理

    • 静态代理: 就是编写一个代理类,去代理【目标对象】,比较简单。
    • 动态代理: 就是运行期间,通过反射,对【目标对象】产生一个【代理对象】(动态代理很重要,大多数框架都是通过反射进行的)动态代理起到增强的作用,这种增强符合开闭原则,不会对目标对象进行修改,而只需要扩展。
      动态代理的实现方式也分为2种,下面2中方式生成的代理都是继承Proxy(spring都用到有接口用jdk没有用cglib)
      • jdk 实现:基于接口,也就是说【目标对象】必须实现一个接口,【目标对象】和【代理对象】都实现该接口。
      • cglib实现: 可以被继承的普通类(不能是final 类)他是通过继承测方式,产生一个子类,这个子类就是代理类。

    三, 静态代理实现

    静态代理产生于代码编译阶段,即一旦代码运行就不可变了。下面我们来看一个例子:

    public interface IPerson {
        public void doSomething();
    }
    
    public class Person implements IPerson {
        public void doSomething(){
            System.out.println("I want wo sell this house");
        }
    }
    
    public class PersonProxy {
        private IPerson iPerson;
        private final static Logger logger = LoggerFactory.getLogger(PersonProxy.class);
     
        public PersonProxy(IPerson iPerson) {
            this.iPerson = iPerson;
        }
        public void doSomething() {
            logger.info("Before Proxy");
            iPerson.doSomething();
            logger.info("After Proxy");
        }
         
        public static void main(String[] args) {
            PersonProxy personProxy = new PersonProxy(new Person());
            personProxy.doSomething();
        }
    }
    

    静态代理有很大的局限性,需要固定的类编写接口,需要实现接口的每一个函数,造成代码大量重复。

    四, 动态代理模式实现之JDK

    3.1用法举例:

    首先定义一个接口:

    public interface Subject {
        public void doSomething();
    }
    

    实现类

    public class RealSubject implements Subject{
    
        @Override
        public void doSomething() {
            // TODO Auto-generated method stub
             System.out.println( "call doSomething()" );   
        }
    }
    

    自定义一个InvocationHandler

    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    
    public class ProxyHandler implements InvocationHandler{
        
        private Object realSubject;  //目标对象
        
        public ProxyHandler(Object realSubject) {
            // TODO Auto-generated constructor stub
            this.realSubject = realSubject;
        }
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            // TODO Auto-generated method stub
                   // 在目标对象的方法执行之前简单的打印一下  
                   System.out.println("------------------before------------------");  
                   //利用反射调用【目标对象】的方法
                   Object result = method.invoke( realSubject, args); 
                   // 在目标对象的方法执行之后简单的打印一下  
                   System.out.println("-------------------after------------------");  
              
              return result;  
    
        }
    
    }
    

    动态代理测试类

    public class Main {
        public static void main( String args[] )   
          {  
            RealSubject real = new RealSubject();  
            Subject proxySubject = (Subject)Proxy.newProxyInstance(Subject.class.getClassLoader(), new Class[]{Subject.class},  new ProxyHandler(real));
            
            proxySubject.doSomething();
          }
    }
    

    以上就是动态代理jdk的实现方式,用起来非常简单,其实就是AOP的一个简单实现了,在对目标对象的方法执行之前和执行之后进行增强,Spring的AOP实现也是用了Proxy,InvocationHandler这2个东西。接下来介绍一下实现原理

    3.2 实现原理

    首先看一下JDK是怎样生成代理对象的。既然生成代理对象用的是Proxy 的newProxyInstance,那么我们看下源码是如何实现的

     public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
            throws IllegalArgumentException
        {
            Objects.requireNonNull(h); //判空
    
            final Class<?>[] intfs = interfaces.clone(); 
            final SecurityManager sm = System.getSecurityManager();
            if (sm != null) {
                checkProxyAccess(Reflection.getCallerClass(), loader, intfs); //验证权限
            }
    
            /*
             * 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);
            } catch (InvocationTargetException e) {
                Throwable t = e.getCause();
                if (t instanceof RuntimeException) {
                    throw (RuntimeException) t;
                } else {
                    throw new InternalError(t.toString(), t);
                }
            } catch (NoSuchMethodException e) {
                throw new InternalError(e.toString(), e);
            }
        }
    
    

    其中newInstance只是调用Constructor.newInstance来构造相应的代理类实例,这里重点是看getProxyClass0这个方法的实现:

    private static Class<?> getProxyClass0(ClassLoader loader,  
                                           Class<?>... interfaces) {  
        // 代理的接口数量不能超过65535(没有这种变态吧)  
        if (interfaces.length > 65535) {  
            throw new IllegalArgumentException("interface limit exceeded");  
        }  
        // JDK对代理进行了缓存,如果已经存在相应的代理类,则直接返回,否则才会通过ProxyClassFactory来创建代理  
        return proxyClassCache.get(loader, interfaces);  
    }  
    

    代理缓存是通过WeakCache实现的

    private static final WeakCache<ClassLoader, Class<?>[], Class<?>>  
        proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());  
    

    ProxyClassFactory是Proxy的一个静态内部类,实现了WeakCache的内部接口BiFunction的apply方法:

    private static final class ProxyClassFactory
            implements BiFunction<ClassLoader, Class<?>[], Class<?>>
        {
            // 所有代理类名称的前缀
            private static final String proxyClassNamePrefix = "$Proxy";
    
            // 用于生成代理类名字的计数器 
            private static final AtomicLong nextUniqueNumber = new AtomicLong();
    
            @Override
            public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
    
                Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
                for (Class<?> intf : interfaces) {
                    /*
                     * Verify that the class loader resolves the name of this
                     * interface to the same Class object.
                     */
                    Class<?> interfaceClass = null;
                    try {
                        interfaceClass = Class.forName(intf.getName(), false, loader);
                    } catch (ClassNotFoundException e) {
                    }
                    if (interfaceClass != intf) {
                        throw new IllegalArgumentException(
                            intf + " 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.put(interfaceClass, Boolean.TRUE) != null) {
                        throw new IllegalArgumentException(
                            "repeated interface: " + interfaceClass.getName());
                    }
                }
                // 生成的代理类的包名 
                String proxyPkg = null;     // package to define proxy class in
                int accessFlags = Modifier.PUBLIC | Modifier.FINAL;
    
                /*
                 * Record the package of a non-public proxy interface so that the
                 * proxy class will be defined in the same package.  Verify that
                 * all non-public proxy interfaces are in the same package.
                 */
                // 对于非公共接口,代理类的包名与接口的相同 
                for (Class<?> intf : interfaces) {
                    int flags = intf.getModifiers();
                    if (!Modifier.isPublic(flags)) {
                        accessFlags = Modifier.FINAL;
                        String name = intf.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, use com.sun.proxy package
                    proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
                }
    
                /*
                 * Choose a name for the proxy class to generate.
                 */
                long num = nextUniqueNumber.getAndIncrement();
                String proxyName = proxyPkg + proxyClassNamePrefix + num;
    
                /*
                 * Generate the specified proxy class.
                 */
               // 这里才是真正的生成代理类的字节码的地方  
                byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
                    proxyName, interfaces, accessFlags);
                try {
                  // 根据二进制字节码返回相应的Class实例  
                    return 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());
                }
            }
        }
    

    生成的动态代理如下:

    class $Proxy0 extends Proxy{
      
       $Proxy0(InvocationHandler h){
          super(h)
       }
        void  doSomething(){
           this.h.invoke()
       }
    }
    

    它的好处理时可以为我们生成任何一个接口的代理类,并将需要增强的方法织入到任意目标函数。但它仍然具有一个局限性,就是只有实现了接口的类,才能为其实现代理。

    五 动态代理模式实现之cglib

    相关文章

      网友评论

        本文标题:设计模式--代理模式

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