您的位置:首页 > 编程语言 > Java开发

jdk动态代理实现总结和范例

2016-05-11 23:20 483 查看
最近在研究spring aop,其中代理在aop扮演着一个很重要的角色,现在来总结一下动态代理(这里只总结动态代理,代理模式和静态代理略)。

动态代理是跟静态代理的目的都一样,代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后处理消息等。代理类与委托类之间通常会存在关联关系,代理类的对象本身并不真正实现服务,而是通过调用委托类的对象的相关方法,来提供特定的服务。

先来看看动态代理的一个简单的例子:

业务接口ITalk

public interface ITalk {
void talk();
}


业务实现类 People

public class People implements ITalk {
@Override
public void talk() {
System.out.println("people talk");
}
}


代理类MyProxy

public class MyProxy implements InvocationHandler {
private Object target;

public Object bind(Object target) throws NoSuchMethodException, SecurityException{
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("切面之前业务");
result = method.invoke(target, args);
System.out.println("切面之后业务");
return result;
}
}


客户端代码:

public static void main(String[] args)  {
ITalk talk = (ITalk)new MyProxy().bind(new People());
talk.talk();

}


输出结果为:

主业务之前做的事

people talk

主业务之后做的事

执行完毕,似乎一切都很神奇,为什么People的talk方法的多输出了‘主业务之前做的事’和‘主业务之后做的事’?不是只有‘people talk’吗?这就是动态代理的威力······

调试一下程序,可以发现客户端代码中的talk引用的对象是$Proxy0,由此我们可以知道,这个对象跟Proxy有很大的联系。

jdk中动态代理的实现有两个重要的角色:java.lang.reflect.Proxy类和java.lang.reflect.InvocationHandler接口。

InvocationHandler接口:

public interface InvocationHandler {
public Object invoke(Object proxy,Method method,Object[] args) throws Throwable;
}


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);
}
Class<?> cl = getProxyClass0(loader, intfs);

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});
}


神奇的事情都发生在Proxy的newProxyInstance方法,里面调用了一个重要的方法getProxyClass0()

private static Class<?> getProxyClass0(ClassLoader loader,
Class<?>... interfaces) {
if (interfaces.length > 65535) {
throw new IllegalArgumentException("interface limit exceeded");
}
return proxyClassCache.get(loader, interfaces);
}


继续往proxyClassCache.get(loader, interfaces)里探究,最后发现Proxy的一个内部类ProxyClassFactory中的apply方法

public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {

Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
for (Class<?> intf : interfaces) {
Class<?> interfaceClass = null;
try {
interfaceClass = Class.forName(intf.getName(), false, loader);
} catch (ClassNotFoundException e) {
}

if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {
throw new IllegalArgumentException(
"repeated interface: " + interfaceClass.getName());
}
}

String proxyPkg = null;     // package to de
4000
fine proxy class in
int accessFlags = Modifier.PUBLIC | Modifier.FINAL;

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) {
proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
}

long num = nextUniqueNumber.getAndIncrement();
String proxyName = proxyPkg + proxyClassNamePrefix + num;

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

throw new IllegalArgumentException(e.toString());
}
}
}


细细研究,其实可以发现Proxy.newProxyInstance的作用如下:

1、根据传入的interfaces参数动态产生一个类,并实现interfaces中的接口,上面的例子就是生成的动态类实现了ITalk。并且继承了Proxy类,重写hashcode,equals,toString方法,具体实现在ProxyGenerator.generateProxyClass(…); 并生成了$Proxy0类

2,通过传入的classloader将刚产生的类加载到jvm中。

3,、第三个参数为InvocationHandler,调用新产生的$Proxy0的构造函数,Proxy(InvocationHandle h),并且用interfaces参数遍历其所有接口的方法,并生成Method对象初始化对象的几个Method成员变量

4、将Proxy0的实例返回给客户端。

因为Proxy0实现了ITalk,所以可以转化为ITalk类型,并且调用ITalk的talk方法,而这里,其实也就是调用了InvocationHandle中的invoke方法:

在$Proxy0中的talk方法:

public final void talk{
try {
super.h.invoke(this, m3, null); //该段则执行了InvocationHandler.invoke();  super.h既是InvocationHandler
return;
} catch (Error e) {
} catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}


至此,就可以接受不同的委托而进行动态的代理了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  java