鲁春利的工作笔记,好记性不如烂笔头



代理模式(Proxy Pattern)是GoF 23种Java常用设计模式之一。

代理模式的定义:Provide a surrogate or placeholder for another object to control access to it(为其他对象提供一种代理以控制对这个对象的访问)。

使用代理模式创建代理对象,让代理对象控制目标对象的访问(目标对象可以是远程的对象、创建开销大的对象或需要安全控制的对象),并且可以在不改变目标对象的情况下添加一些额外的功能。


代理模式一般涉及到的角色有:
    1、抽象角色:可以是抽象类,也可以是接口,真实对象和代理对象的共同接口;
    2、真实角色:代理角色所代表的真实对象,是我们最终要引用的目标对象。
    3、代理角色:也叫委托类、代理类,内部含有对真实对象(目标对象)的引用,从而可以操作真实对象,同时代理对象提供与真实对象相同的接口以便在任何时刻都能代替真实对象。同时,代理对象可以在执行真实对象操作时,附加其他的操作(如记录日志、进行完全检查等),相当于对真实对象进行封装。


静态代理

抽象角色:

package com.invicme.apps.aop.proxy;

/**
 * 
 * @author lucl
 * 
 * 抽象角色
 *
 */
public interface IDoSomeThing {
    public void doSomeThing ();
}

真实角色:

package com.invicme.apps.aop.proxy;

/**
 * 
 * @author lucl
 * 
 * 真实角色
 *
 */
public class DoSomeThing implements IDoSomeThing {

    @Override
    public void doSomeThing() {
        System.out.println("I'm doing some thing...");
    }

}

代理角色:

package com.invicme.apps.aop.proxy;

/**
 * 
 * @author lucl
 * 
 * 代理类
 *
 */
public class DoSomeThingProxy implements IDoSomeThing {
    
    private IDoSomeThing iDoSomeThing;
    private String userName;
    
    public DoSomeThingProxy (IDoSomeThing iDoSomeThing, String userName) {
        this.iDoSomeThing = iDoSomeThing;
        this.userName = userName;
    }

    @Override
    public void doSomeThing() {
        // 调用目标对象之前可以做相关操作(如记录日志、根据用户判断是否能够进行doSomeThing的操作)
        System.out.println("Current user " + userName + " want to do some thing.");
        this.iDoSomeThing.doSomeThing();
        // 调用目标对象之后可以做相关操作
        System.out.println("Current user " + userName + " do some thing end.");
    }
}

测试驱动类

package com.invicme.apps.aop.proxy;

/**
 * 
 * @author lucl
 * 
 * 驱动类,希望执行某些业务逻辑
 *
 */
public class MainDriver {
    public static void main(String[] args) {
        IDoSomeThing iDoSomeThing = new DoSomeThing();
        IDoSomeThing doSomeThingProxy = new DoSomeThingProxy(iDoSomeThing, "张三");
        doSomeThingProxy.doSomeThing();
        
        doSomeThingProxy = new DoSomeThingProxy(iDoSomeThing, "李四");
        doSomeThingProxy.doSomeThing();
    }
}

静态代理有一个问题就是需要我们手动创建代理类,如果业务方法不止一个,那么就要在每个业务方法中都加入额外的方法。如果业务类不止一个,我们就要分别创建代理类。代码量增大,也为之后的维护增加了难度。解决的办法就是动态代理模式。


动态代理

动态代理需要引用java中的Proxy类以及InvocationHandler接口。Proxy类主要用来获取动态代理对象,InvocationHandler接口用来约束调用者实现。


Proxy提供用于创建动态代理类和代理对象的静态方法,它是所有动态代理类的父类。
wKiom1iET8CRAmohAACsvbvrHIM915.jpg


InvocationHandler handler = new MyInvocationHandler(...);

创建某一接口 Foo 的代理:

Class proxyClass = Proxy.getProxyClass(Foo.class.getClassLoader(), new Class[] { Foo.class });
Foo f = (Foo) proxyClass.getConstructor(new Class[] { InvocationHandler.class })
                        .newInstance(new Object[] { handler });

或使用以下更简单的方法:

Foo f = (Foo) Proxy.newProxyInstance(Foo.class.getClassLoader(),
                     new Class[] { Foo.class }, 
                     handler);

wKioL1iEUFPgg026AADabu2EMto112.jpg

InvocationHandler 是代理实例的调用处理程序 实现的接口。

package com.invicme.tools.proxy.dynamic;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.Arrays;

import org.apache.ibatis.executor.BatchExecutor;
import org.apache.ibatis.executor.CachingExecutor;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.executor.ReuseExecutor;
import org.apache.ibatis.executor.SimpleExecutor;
import org.apache.ibatis.session.ExecutorType;
import org.apache.ibatis.transaction.Transaction;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 
 * @author lucl
 * @version 2017-01-22
 * 
 * target对象一般都通过拦截器拦截到的,具体参阅{@link org.apache.ibatis.session.Configuration}
 * <pre>
        public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
            executorType = executorType == null ? defaultExecutorType : executorType;
            executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
            Executor executor;
            if (ExecutorType.BATCH == executorType) {
              executor = new BatchExecutor(this, transaction);
            } else if (ExecutorType.REUSE == executorType) {
              executor = new ReuseExecutor(this, transaction);
            } else {
              executor = new SimpleExecutor(this, transaction);
            }
            if (cacheEnabled) {
              executor = new CachingExecutor(executor);
            }
            // 主要是interceptorChain的pulgin方法,实际为Plugin.wrap方法
            executor = (Executor) interceptorChain.pluginAll(executor);
            return executor;
      }
 * </pre>
 *
 */
public class ProxyHandler implements InvocationHandler {
    //
    private Logger logger = LoggerFactory.getLogger(getClass());
    
    private Object target;
    
    public ProxyHandler () {
        // ......
    }
    
    public ProxyHandler (Object target) {
        this.target = target;
    }
    
    /**
     * @param    proxy
     *     在其上调用方法的代理实例,注意这里是调用目标方法的代理类Proxy的对象
     * @param    method
     *     在代理实例上调用的接口方法的 Method 实例
     * @param    args
     *     通过代理对象(proxy)调用的目标对象的方法参数
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {
        logger.info("proxy is {}", proxy.getClass().getName());
        logger.info("method is {}", method.getName());
        if (null != args) {
            logger.info("args is {}", Arrays.asList(args));
        }
        
        // 
        shake();
        
        Object obj = method.invoke(target, args);
        
        // 
        shake();
        
        return obj;
    }

    /**
     * 在谈话前后加入自己的逻辑
     */
    private void shake() {
        logger.info("shaking.............");
    }
}

Proxy.getProxyClass实现方式:

package com.invicme.tools.proxy.dynamic.test01;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.invicme.tools.proxy.dynamic.ProxyHandler;
import com.invicme.tools.proxy.subject.Subject;

/**
 * 
 * @author lucl
 * @version 2017-01-22
 * 
 * 客户端
 *
 */
class Client {
    // 
    private Logger logger = LoggerFactory.getLogger(getClass());
    
    /**
     * 
     * @param classQName
     */
    private void execProxy (String classQName) {
        logger.info("class name is {}", classQName);
        try {
            Class<? extends Object> clazz = Class.forName(classQName);
            ProxyHandler h = new ProxyHandler(clazz.newInstance());
            
            ClassLoader classLoader = clazz.getClassLoader();
            Class<?> proxyClass = Proxy.getProxyClass(classLoader, clazz.getInterfaces());
            
            // 写法固定,不可以更改,参阅JDK API的Proxy介绍
            Constructor<?> constructor = proxyClass.getConstructor(new Class[]{InvocationHandler.class});
            Subject subject = (Subject)constructor.newInstance(new Object[] { h });
            logger.info("the talk return msg is {}", subject.talk("张三"));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
    public static void main(String[] args) {
        if (null == args || args.length != 1) {
            return;
        }
        Client client = new Client();
        client.execProxy(args[0]);
    }

}


Proxy.newProxyInstance实现方式:

package com.invicme.tools.proxy.dynamic.test02;

import java.lang.reflect.Proxy;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.invicme.tools.proxy.dynamic.ProxyHandler;
import com.invicme.tools.proxy.subject.Subject;

/**
 * 
 * @author lucl
 * @version 2017-01-22
 * 
 * 客户端
 *
 */
class Client {
    //
    private Logger logger = LoggerFactory.getLogger(getClass());
    
    private void execProxy (String classQName) {
        logger.info("class name is {}", classQName);
        try {
            Class<? extends Object> clazz = Class.forName(classQName);
            ClassLoader classLoader = clazz.getClassLoader();
            Class<?>[] interfaces = clazz.getInterfaces();
            // 这里通过Class的newInstance生成目标对象的实例
            ProxyHandler h = new ProxyHandler(clazz.newInstance());
            Subject subject = (Subject)Proxy.newProxyInstance(classLoader, interfaces, h);
            logger.info("the talk return msg is {}", subject.talk("张三"));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
    /**
     * 
     * @param args
     */
    public static void main(String[] args) {
        if (null == args || args.length != 1) {
            return;
        }
        Client client = new Client();
        client.execProxy(args[0]);
    }

}


总结:通过Java动态代理的方式可以实现AOP编程,即在不影响原有业务逻辑的情况下加入新的功能(如日志记录、安全检查等);比如之前的所有日志记录都可以在业务代码之外完成,保证业务代码是干净的。


public class java.lang.reflect.Proxy implements java.io.Serializable

Proxy的主要静态变量

    private static final long serialVersionUID = -2222568056686623797L;

    /** 
     * parameter types of a proxy class constructor 
     * 代理类构造函数的参数类型
    */
    private static final Class<?>[] constructorParams =
        { InvocationHandler.class };

    /**
     * a cache of proxy classes
     * 代理类的缓存
     */
    private static final WeakCache<ClassLoader, Class<?>[], Class<?>>
        proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());

    /**
     * the invocation handler for this proxy instance.
     * // 关联的调用处理器引用
     * @serial
     */
    protected InvocationHandler h;

Proxy的构造方法

    /**
     * // 由于 Proxy 内部从不直接调用构造函数,所以 private 类型意味着禁止任何调用
     * Prohibits instantiation.
     */
    private Proxy() {
        // ......
    }
    
    /**
     * // 由于 Proxy 内部从不直接调用构造函数,所以 protected 意味着只有子类可以调用
     */
    protected Proxy(InvocationHandler h) {
        doNewInstanceCheck();
        this.h = h;
    }

Proxy静态方法newProxyInstance

    /**
     * This method is equivalent to:
     * <pre>
     *     Proxy.getProxyClass(loader, interfaces).
     *         getConstructor(new Class[] { InvocationHandler.class }).
     *         newInstance(new Object[] { handler });
     * </pre>
     *
     */
    @CallerSensitive
    public static Object newProxyInstance(ClassLoader loader,
                        Class<?>[] interfaces,
                        InvocationHandler h) throws IllegalArgumentException {
        // 检查 h 不为空,否则抛异常
        if (h == null) {
            throw new NullPointerException();
        }

        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 {
            // 调用代理对象的构造方法(也就是$Proxy0(InvocationHandler h))
            final Constructor<?> cons = cl.getConstructor(constructorParams);
            final InvocationHandler ih = h;
            if (sm != null && ProxyAccessHelper.needsNewInstanceCheck(cl)) {
                // create proxy instance with doPrivilege as the proxy class may
                // implement non-public interfaces that requires a special permission
                return AccessController.doPrivileged(new PrivilegedAction<Object>() {
                    public Object run() {
                        return newInstance(cons, ih);
                    }
                });
            } else {
                return newInstance(cons, ih);
            }
        } catch (NoSuchMethodException e) {
            throw new InternalError(e.toString());
        }
    }


Proxy静态方法getProxyClass方法

    @CallerSensitive
    public static Class<?> getProxyClass(ClassLoader loader,
                                         Class<?>... interfaces)
        throws IllegalArgumentException
    {
        final Class<?>[] intfs = interfaces.clone();
        final SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
        }

        return getProxyClass0(loader, intfs);
    }

ProxygetProxyClass方法调用ProxyGenerator的 generateProxyClass方法产生ProxySubject.class的二进制数据:

byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces);
try {
    return defineClass0(loader, proxyName,proxyClassFile, 0, proxyClassFile.length);
} catch (ClassFormatError e) {
    // ......
}

总结:

一个典型的动态代理创建对象过程可分为以下四个步骤:

1、通过实现InvocationHandler接口创建自己的调用处理器

IvocationHandler handler = new InvocationHandlerImpl(...);

2、通过为Proxy类指定ClassLoader对象和一组interface创建动态代理类

Class clazz = Proxy.getProxyClass(classLoader,new Class[]{...});

3、通过反射机制获取动态代理类的构造函数,其参数类型是调用处理器接口类型

Constructor constructor = clazz.getConstructor(new Class[]{InvocationHandler.class});

4、通过构造函数创建代理类实例,此时需将调用处理器对象作为参数被传入

Interface Proxy = (Interface)constructor.newInstance(new Object[] (handler));

为了简化对象创建过程,Proxy类中的newInstance方法封装了2~4,只需两步即可完成代理对象的创建。

生成的ProxySubject继承Proxy类实现Subject接口,实现的Subject的方法实际调用处理器的invoke方法,而invoke方法利用反射调用的是被代理对象的的方法:

Object result=method.invoke(proxied,args);

美中不足:

诚然,Proxy已经设计得非常优美,但是还是有一点点小小的遗憾之处,那就是它始终无法摆脱仅支持interface代理的桎梏,因为它的设计注定了这个遗憾。