设计模式之动态代理

之前写了一篇代理模式的文章,同时介绍了使用代理模式的好处,但是那种代理模式也存在一定的弊端:代理类和被代理类紧紧的耦合在一起了,一个代理类只能为一个代理类服务。这种显然是不愿意看到的,下面用一个例子介绍一下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的部分源码和例子的中源代码
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值