1. 代理模式
代理模式是较常见的模式之一,在许多框架中经常见到,比如Spring的面向切面的编程,MyBatis中缓存机制对PooledConnection的管理等。代理模式使得客户端在使用目标对象的时候间接通过代理类进行,代理类在操作目标对象的前后可以添加额外操作。
2. 代理模式UML
代理模式的类图结构非常简单,如下所示
Subject:抽象主题角色,一般为一个接口
RealSubject:抽象主题角色的实现类,对其中的方法进行实现
Proxy:代理类,其继承自抽象主题角色,实现其中的方法,并且持有实现类对象。
3. 举例
说起来比较乱,举例如下:
建立抽象主题类:
/**
* @author hongyu.shao created on 15-1-26 下午6:16
* @version 1.0.0
*/
public interface HelloWorldService {
public void sayHello(String name);
}
/**
* @author hongyu.shao created on 15-1-26 下午6:17
* @version 1.0.0
*/
public class HelloWorldServiceImpl implements HelloWorldService {
@Override
public void sayHello(String name) {
System.out.println("hello,"+name);
}
}
/**
* @author hongyu.shao created on 15-1-26 下午8:23
* @version 1.0.0
*/
public class HelloWorldServiceProxy implements HelloWorldService {
private HelloWorldService helloWorldService;
public HelloWorldServiceProxy(HelloWorldService helloWorldService){
this.helloWorldService = helloWorldService;
}
@Override
public void sayHello(String name) {
System.out.println("shake hand!");
helloWorldService.sayHello("hello,"+name);
System.out.println("goodbye");
}
}
main方法如下:
public static void main(String[] args){
HelloWorldService proxy = new HelloWorldServiceProxy(new HelloWorldServiceImpl());
proxy.sayHello("shao");
}
4. Java动态代理
通过上面的例子可以看出,针对每一个接口,都需要实现自己的代理类,这样会导致工程中的类数目爆炸增长。
在java中,提供了统一的代理接口InvocationHandler和代理类Proxy。其使用方法举例如下。
出了上面的HelloWorldService及其实现类HelloWorldServiceImpl,再建立一个Service如下:
Service接口:
/**
* @author hongyu.shao created on 15-1-26 下午8:33
* @version 1.0.0
*/
public interface BuyFoodService {
public void buyFood();
}
/**
* @author hongyu.shao created on 15-1-26 下午8:33
* @version 1.0.0
*/
public class BuyFoodServiceImpl implements BuyFoodService{
@Override
public void buyFood() {
System.out.println("chose food and pay money!");
}
}
创建代理类:
/**
* @author hongyu.shao created on 15-1-26 下午6:19
* @version 1.0.0
*/
public class ServiceHandler implements InvocationHandler{
private Object target;
public Object bind(Object target){
this.target=target;
return Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = null;
System.out.println("service initinal!");
result = method.invoke(target,args);
System.out.println("service destroy!");
return result;
}
}
该代理类实现了InvocationHandler接口,并且调用Proxy.newProxyInstance生成具体代理类对象。但是,新建的代理类对象ServiceHandler和具体的Service完全没有关系,是完全解耦的。
Main方法:
public static void main(String[] args){
ServiceHandler handler = new ServiceHandler();
HelloWorldService helloWorldService = (HelloWorldService)handler.bind(new HelloWorldServiceImpl());
helloWorldService.sayHello("shao");
BuyFoodService buyFoodService = (BuyFoodService)handler.bind(new BuyFoodServiceImpl());
buyFoodService.buyFood();
}
可以看到,ServiceHandler既可以作为HelloWorldService的代理类,也可以作为BuyFoodService的代理类,具有很好的可扩展性。
5. MyBatis中对Java动态代理的简单使用
使用MyBatis连接池时,当关闭连接时,不是真正的关闭到数据库的连接,而是把连接归还到空闲连接池中。
MyBatis通过类PooledConnection对Connection进行二次封装,并实现InvocationHandler接口
class PooledConnection implements InvocationHandler {
private static final String CLOSE = "close";
private static final Class[] IFACES = new Class[]{Connection.class};
private int hashCode = 0;
private PooledDataSource dataSource;
private Connection realConnection;
private Connection proxyConnection;
private long checkoutTimestamp;
private long createdTimestamp;
private long lastUsedTimestamp;
private int connectionTypeCode;
private boolean valid;
/**
* Constructor for SimplePooledConnection that uses the Connection and PooledDataSource passed in
*
* @param connection - the connection that is to be presented as a pooled connection
* @param dataSource - the dataSource that the connection is from
*/
public PooledConnection(Connection connection, PooledDataSource dataSource) {
this.hashCode = connection.hashCode();
this.realConnection = connection;
this.dataSource = dataSource;
this.createdTimestamp = System.currentTimeMillis();
this.lastUsedTimestamp = System.currentTimeMillis();
this.valid = true;
proxyConnection = (Connection) Proxy.newProxyInstance(Connection.class.getClassLoader(), IFACES, this);
}
。。。。。
}
从构造函数中可以看出,类成员变量realConnection赋值为真正的数据库连接Connection对象,而对proxyConnection的赋值如下:
proxyConnection = (Connection) Proxy.newProxyInstance(Connection.class.getClassLoader(), IFACES, this);
这里,使用了Java的动态代理,采用ProxyConnection对象代理Connection
下面看下 PooledConnection实现的invoke方法
/**
* Required for InvocationHandler implementation.
*
* @param proxy - not used
* @param method - the method to be executed
* @param args - the parameters to be passed to the method
* @see java.lang.reflect.InvocationHandler#invoke(Object, java.lang.reflect.Method, Object[])
*/
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
String methodName = method.getName();
if (CLOSE.hashCode() == methodName.hashCode() && CLOSE.equals(methodName)) {
dataSource.pushConnection(this);
return null;
} else {
try {
return method.invoke(getValidConnection(), args);
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
}
}
从上面可以看出,如果调用的方法是close方法,通过
dataSource.pushConnection(this);直接把连接放回连接池中,其他方法这直接调用Connection对象的方法。
6. Java动态代理源码分析
Java动态代理主要涉及到InvocationHandler接口和Proxy类。
一般实现InvocationHandler接口的invoke方法,在该方法中执行相应的逻辑。通过Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)产生动态代理对象。下面重点解析该方法:
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);
/*
* Invoke its constructor with the designated invocation handler.
*/
try {
Constructor cons = cl.getConstructor(constructorParams);
return (Object) cons.newInstance(new Object[] { h });
} 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());
}
}
在该方法中,首先调用getProxyClass方法产生动态代理类的Class类,然后通过该Class类的构造方法进行实例化,其中构造函数的参数是实现了InvocationHandler接口的对象。进入getProxyClass方法中查看:
在该方法中,前半部分分别尝试从缓存中取Class对象,如果取不到,则根据传递的接口的访问权限确定动态生成的代理类的包路径,最后调用byte[] proxyClassFile = ProxyGenerator.generateProxyClass( proxyName, interfaces); 生成动态代理类的二字码文件,为了了解动态代理类的生成过程,直接调用该方法,并对其生成的文件进行反编译。
其中测试main方法如下:
public static void main(String[] args){
byte[] classByte = ProxyGenerator.generateProxyClass("NewProxy",new Class[]{HelloWorldService.class});
File file = new File("D:/classes/NewProxy.class");
FileOutputStream stream = null;
try {
stream = new FileOutputStream(file);
stream.write(classByte);
stream.flush();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
if(stream!=null){
try {
stream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
我们重新使用了上文中创建的接口HelloWorldService,该main函数产生了针对于接口HelloWorldService的动态代理类二字码文件NewProxy.class,对其进行反编译得到如下代码。
import com.qunar.service.HelloWorldService;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
public final class NewProxy extends Proxy
implements HelloWorldService
{
private static Method m1;
private static Method m3;
private static Method m0;
private static Method m2;
public NewProxy(InvocationHandler paramInvocationHandler)
throws
{
super(paramInvocationHandler);
}
public final boolean equals(Object paramObject)
throws
{
try
{
return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();
}
catch (RuntimeException localRuntimeException)
{
throw localRuntimeException;
}
catch (Throwable localThrowable)
{
}
throw new UndeclaredThrowableException(localThrowable);
}
public final void sayHello(String paramString)
throws
{
try
{
this.h.invoke(this, m3, new Object[] { paramString });
return;
}
catch (RuntimeException localRuntimeException)
{
throw localRuntimeException;
}
catch (Throwable localThrowable)
{
}
throw new UndeclaredThrowableException(localThrowable);
}
public final int hashCode()
throws
{
try
{
return ((Integer)this.h.invoke(this, m0, null)).intValue();
}
catch (RuntimeException localRuntimeException)
{
throw localRuntimeException;
}
catch (Throwable localThrowable)
{
}
throw new UndeclaredThrowableException(localThrowable);
}
public final String toString()
throws
{
try
{
return (String)this.h.invoke(this, m2, null);
}
catch (RuntimeException localRuntimeException)
{
throw localRuntimeException;
}
catch (Throwable localThrowable)
{
}
throw new UndeclaredThrowableException(localThrowable);
}
static
{
try
{
m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
m3 = Class.forName("com.qunar.service.HelloWorldService").getMethod("sayHello", new Class[] { Class.forName("java.lang.String") });
m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
return;
}
catch (NoSuchMethodException localNoSuchMethodException)
{
throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
}
catch (ClassNotFoundException localClassNotFoundException)
{
}
throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
}
}
该类实现了HelloWorldService接口,并实现了其sayHello的方法,在sayHello方法的内部则调用了实现了InvocationHandler接口的对象的invoke方法,这样在调用sayHello方法的时候就能透明的进行代理方法的调用了,是不是很奇妙。