设计模式之动态代理

之前写了一篇代理模式的文章,同时介绍了使用代理模式的好处,但是那种代理模式也存在一定的弊端:代理类和被代理类紧紧的耦合在一起了,一个代理类只能为一个代理类服务。这种显然是不愿意看到的,下面用一个例子介绍一下Java的动态代理和深入分析一下Java的动态代理。
被代理对象接口
package com.yf.designpattern.proxy.dynamicproxy;

public interface Dog {
	public void info();

	public void run();
}


被代理对象
package com.yf.designpattern.proxy.dynamicproxy;

public class GunDog implements Dog {

	@Override
	public void info() {
		System.out.println("This is gun dog!!!");

	}

	@Override
	public void run() {
		System.out.println("The gun dog is running!!!");

	}

}
 

下面定义一个类似拦截器的类,该类的作用是对被代理类的功能增强,比如记录日志等其他功能
package com.yf.designpattern.proxy.dynamicproxy;

public class TxUtil {
	public void beginTx() {
		System.out.println("=======The transaction is begining========");
	}
	public void endTx(){
		System.out.println("=======The transaction is ending========");
	}
}
 

下面定义了一个织入类,该类实现了InvocationHandler接口(后面可以看到,该类就是代理类)
package com.yf.designpattern.proxy.dynamicproxy;

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

public class MyInvokationHander implements InvocationHandler {
	private Object target;

	public void setTarget(Object target) {
		this.target = target;
	}

	@Override
	public Object invoke(Object proxy, Method method, Object[] args)
			throws Throwable {
		TxUtil tu = new TxUtil();
		tu.beginTx();
		Object result = method.invoke(target, args);
		tu.endTx();
		return result;
	}

}
 

最后一个就是代理类的工厂类,该类主要产生各种代理类
package com.yf.designpattern.proxy.dynamicproxy;

import java.lang.reflect.Proxy;

public class MyProxyFactory {
	public static Object getProxy(Object target) throws Exception {
		MyInvokationHander myHander = new MyInvokationHander();
		myHander.setTarget(target);
		return Proxy.newProxyInstance(target.getClass().getClassLoader(),
				target.getClass().getInterfaces(), myHander);
	}
}
 

最后一个是测试类,用于演示动态代理
package com.yf.designpattern.proxy.dynamicproxy;

public class Test {

	/**
	 * @param args
	 * @throws Exception 
	 */
	public static void main(String[] args) throws Exception {
		System.out.println("Before Proxy.......");
		Dog target=new GunDog();
		target.info();
		target.run();
		System.out.println("After Proxy.......");
		//get an instance of MyInvokationHander
		Dog dog=(Dog) MyProxyFactory.getProxy(target);
		//MyInvokationHander.invoke
		dog.info();
		//MyInvokationHander.invoke
		dog.run();
		System.out.println(dog.getClass().getName());
	}

}
 

通过执行,输出结果如下:
Before Proxy.......
This is gun dog!!!
The gun dog is running!!!
After Proxy.......
=======The transaction is begining========
This is gun dog!!!
=======The transaction is ending========
=======The transaction is begining========
The gun dog is running!!!
=======The transaction is ending========
$Proxy0
这里我们发现,在GunDog通过代理工厂代理之后,会在每个方法执行之前和执行之后都打印日志,而且更为神奇的是,产生的这个代理类既不是Dog,也不是GunDog,而是一个$Proxy0的东西,这是神马??下面我们跟随代码一步一步来解答,首先我们的GunDog调用了代理工厂的getProxy 方法(MyProxyFactory.getProxy(target);),这个方法中关键的就是Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(), myHander); 通过查看JDK的API可知,Proxy是Java自带的动态代理类,而newProxyInstance方法主要作用是产生一个myHander实例,但是这个实例必须继承Proxy,并且实现代理对象的所有接口(target.getClass().getInterfaces()),这里可以看一下JDK源码,
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());
}
}
从这段源代码可知,这个方法确实通过构造方法返回了一个myHander这个实例,通过DEBUG一下Test,发现调用代理工厂之后,那个dog已经不是狗了,而已经成为MyInvokationHander(myHander的对象)了,但是为何它的名字叫“$Proxy0”呢,通过继续查看JDK的Proxy类,发现了如下逻辑:
try {
    String proxyPkg = null; // package to define proxy class in

    /*
     * 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 (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;
这段逻辑就是产生代理类名称的逻辑,proxyClassNamePrefix是Proxy的一个静待常量
  private final static String proxyClassNamePrefix = "$Proxy";由于此处,我们所有类都在一个package中,所以proxyPkg为空,这又是第一个代理类,所以num为0,所以我们产生这个代理类就就叫做“$Proxy0”。
下面又有一个疑问,既然代理工厂产生的是一个叫做$Proxy0的MyInvokationHander实例,那它为什么又回有GunDog的info()和run()方法呢??
上面已经说了,Proxy 的newProxyInstance方法会要求产生一个实现继承Proxy,实现被代理类实现的所有接口的实例,比如下面:
Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(), myHander)
它产生的就是一个myHander实例,该实例继承Proxy,实现了target实现的所有接口(target.getClass().getInterfaces()),而通过DEBUG代码发现,每次执行代dog.info();时,代码都会走到MyInvokationHander.invoke(Object proxy, Method method, Object[] args)方法中。所以我们可以猜测,这个方法Proxy.newProxyInstance其实在内存中产生了这样一个代理类:该代理类有被代理类的所有方法(被代理类肯定已经实现了被代理类实现的接口中的方法),这些方法内部再通过invoke方法调用被代理类的方法(有点绕口)。例如下面就是猜想的,在内存中的代理类:
package com.yf.designpattern.proxy.dynamicproxy;

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

public class MyInvokationHander implements InvocationHandler {
	private Object target;

	public void setTarget(Object target) {
		this.target = target;
	}

	public void info() throws SecurityException, NoSuchMethodException,
			Throwable {
		this.invoke(target, this.target.getClass().getDeclaredMethod("info",
				null), null);
	}

	public void run() throws SecurityException, NoSuchMethodException,
			Throwable {
		this.invoke(target, this.target.getClass().getDeclaredMethod("run",
				null), null);
	}

	@Override
	public Object invoke(Object proxy, Method method, Object[] args)
			throws Throwable {
		TxUtil tu = new TxUtil();
		tu.beginTx();
		Object result = method.invoke(target, args);
		tu.endTx();
		return result;
	}

} 

当然这是本人猜测的一个类,实际在内存中并不一定就是上面这个样子。
从上面可以看出,采用动态代理可以将代理类和进行解耦,代理类已经不再关心被代理类时什么,它只关心需要给被代理的类增强那些功能。
总结:使用动态代理的一般步骤如下:
1、 创建代理类,代理类需要实现InvocationHandler接口;
2、 创建代理类工厂(也可以不创建),该工厂通过Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException方法创建代理对象(也可以通过Proxy.getProxyClass(loader, interfaces).getConstructor(new Class[] { InvocationHandler.class }).newInstance(new Object[] { handler });)方法创建);
3、 将被代理对象传给代理类和代理类工厂即可。
附件中是JDK的部分源码和例子的中源代码

猜你喜欢

转载自michael8335.iteye.com/blog/1562117
今日推荐