深入分析Spring源码(二)----代理模式(jdk动态代理)
代理模式:
特点:1.有执行者,被代理人两个角色
2.对于被代理人来说,这件事情是一定要做的,但自己又不想做或者没有时间做,找代理
3.需要获取被代理人的个人资料
首先来看一下如何使用JDK动态代理。JDK提供了java.lang.reflect.Proxy类来实现动态代理的,可通过它的newProxyInstance来获得代理实现类。同时对于代理的接口的实际处理,是一个java.lang.reflect.InvocationHandler,它提供了一个invoke方法供实现者提供相应的代理逻辑的实现。可以对实际的实现进行一些特殊的处理,像Spring AOP中的各种advice。
//静态代理
public interface Person{
void sing();
String getName();
String getSex();
}
public class Star implements Person{
private String name = "Gongxiaoxiao";
private String sex = "female";
@Override
public void sing(){
System.out.println("===Star====Sing===");
}
}
public class Agent implements Person{
private Person p;
Agent(Person p ){
this.p = p;
}
@Override
public void sing(){
System.out.println("====唱歌前谈价钱===");
p.sing();
System.out.println("======唱歌后护送离场====");
}
}
public static void main(String[] args){
Person p = new Star();
Agent aa = new Agent(p);
aa.sing();
}
//动态代理
public interface Person{
public void sing();
}
public class Star implements Person{
@Override
public void sing(){
System.out.println("===Star====Sing===");
}
}
public class Agent implements InvocationHandler{
private Person target;
//获取被代理人的个人资料
public Object getInstance(Person target) throws Exception{
this.target = target;
//Proxy.newProxyInstance(ClassLoader loader,Class<?>
// []interfaces,InvocationHandler h);
Class clazz = target.getClass();
//参数1:被代理人对应的类加载器,参数2:被代理人对应实现的接口,参数3:代理人;返回代
//理对象
//类加载器(ClassLoader)用来加载动态代理类
//一个要实现接口的数组,从这点就可以看出,要想使用JDK动态代理,必须要有接口类
return
Proxy.newProxyInstance(clazz.getClassLoader(),clazz.getInterfaces(),this);
}
@Override
public Object invoke(Object proxy,Method method,Object[]args){
System.out.println("=====付出场费=====");
method.invoke(target,args);
System.out.println("=====护送离场=====");
}
}
public static void main(String[] args){
//设置sun.misc.ProxyGenerator.saveGeneratedFiles这个系统属性为true来把生成的class保存到
//本地文件来查看。
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true");
Person p = new Star();
Person aa = (Person)Agent.getInstance(p);
//该代理对象的引用aa的运行时类为 $Proxy0
System.out.println(aa.getClass());
aa.sing();
}
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException {
if (h == null) {
throw new NullPointerException();
}
final Class<?>[] intfs = interfaces.clone();
final SecurityManager sm = System.getSecurityManager();
if (sm != null) {
checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
}
// 这里是生成class的地方
Class<?> cl = getProxyClass0(loader, intfs);
// 使用我们实现的InvocationHandler作为参数调用构造方法来获得代理类的实例
try {
final Constructor<?> cons = cl.getConstructor(constructorParams);
final InvocationHandler ih = h;
if (sm != null && ProxyAccessHelper.needsNewInstanceCheck(cl)) {
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());
}
}
其中newInstance只是调用Constructor.newInstance来构造相应的代理类实例,这里重点是看getProxyClass0这个方法的实现:
private static Class<?> getProxyClass0(ClassLoader loader,
Class<?>... interfaces) {
// 代理的接口数量不能超过65535(没有这种变态吧)
if (interfaces.length > 65535) {
throw new IllegalArgumentException("interface limit exceeded");
}
// JDK对代理进行了缓存,如果已经存在相应的代理类,则直接返回,否则才会通过ProxyClassFactory来创建代理
return proxyClassCache.get(loader, interfaces);
}
其中代理缓存是使用WeakCache实现的,如下
private static final WeakCache<ClassLoader, Class<?>[], Class<?>>
proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());
具体的缓存逻辑这里暂不关心,只需要关心ProxyClassFactory是如何生成代理类的,ProxyClassFactory是Proxy的一个静态内部类,实现了WeakCache的内部接口BiFunction的apply方法:
private static final class ProxyClassFactory
implements BiFunction<ClassLoader, Class<?>[], Class<?>> {
// 所有代理类名字的前缀
private static final String proxyClassNamePrefix = "$Proxy";
// 用于生成代理类名字的计数器
private static final AtomicLong nextUniqueNumber = new AtomicLong();
@Override
public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
// 省略验证代理接口的代码……
String proxyPkg = null; // 生成的代理类的包名
// 对于非公共接口,代理类的包名与接口的相同
for (Class<?> intf : interfaces) {
int flags = intf.getModifiers();
if (!Modifier.isPublic(flags)) {
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");
}
}
}
// 对于公共接口的包名,默认为com.sun.proxy
if (proxyPkg == null) {
proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
}
// 获取计数
long num = nextUniqueNumber.getAndIncrement();
// 默认情况下,代理类的完全限定名为:com.sun.proxy.$Proxy0,com.sun.proxy.$Proxy1……依次递增
String proxyName = proxyPkg + proxyClassNamePrefix + num;
// 这里才是真正的生成代理类的字节码的地方
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
proxyName, interfaces);
try {
// 根据二进制字节码返回相应的Class实例
return defineClass0(loader, proxyName,
proxyClassFile, 0, proxyClassFile.length);
} catch (ClassFormatError e) {
throw new IllegalArgumentException(e.toString());
}
}
}
ProxyGenerator是sun.misc包中的类,它没有开源,但是可以反编译来一探究竟:
public static byte[] generateProxyClass(final String var0, Class[] var1) {
ProxyGenerator var2 = new ProxyGenerator(var0, var1);
final byte[] var3 = var2.generateClassFile();
// 这里根据参数配置,决定是否把生成的字节码(.class文件)保存到本地磁盘,我们可以通过把相应的class文件保存到本地,再反编译来看看具体的实现,这样更直观
if(saveGeneratedFiles) {
AccessController.doPrivileged(new PrivilegedAction() {
public Void run() {
try {
FileOutputStream var1 = new FileOutputStream(ProxyGenerator.dotToSlash(var0) + ".class");
var1.write(var3);
var1.close();
return null;
} catch (IOException var2) {
throw new InternalError("I/O exception saving generated file: " + var2);
}
}
});
}
return var3;
}
对生成的代理类进行反编译可得:
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package com.sun.proxy;
import com.example.demo1.dynamicProxy.Person;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
public final class $Proxy0 extends Proxy implements Person {
private static Method m1;
private static Method m4;
private static Method m3;
private static Method m2;
private static Method m5;
private static Method m0;
public $Proxy0(InvocationHandler var1) throws {
super(var1);
}
public final boolean equals(Object var1) throws {
try {
return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
public final String getSex() throws {
try {
return (String)super.h.invoke(this, m4, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final String getName() throws {
try {
return (String)super.h.invoke(this, m3, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final String toString() throws {
try {
return (String)super.h.invoke(this, m2, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final void sing() throws {
try {
super.h.invoke(this, m5, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final int hashCode() throws {
try {
return (Integer)super.h.invoke(this, m0, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
m4 = Class.forName("com.example.demo1.dynamicProxy.Person").getMethod("getSex");
m3 = Class.forName("com.example.demo1.dynamicProxy.Person").getMethod("getName");
m2 = Class.forName("java.lang.Object").getMethod("toString");
m5 = Class.forName("com.example.demo1.dynamicProxy.Person").getMethod("sing");
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}
生成的动态代理类$Proxy0的特性:
继承了Proxy类,实现了代理的接口,由于java不能多继承,这里已经继承了Proxy类了,不能再继承其他的类,所以JDK的动态代理不支持对实现类的代理,只支持接口的代理。
提供了一个使用InvocationHandler作为参数的构造方法。
生成静态代码块来初始化接口中方法的Method对象,以及Object类的equals、hashCode、toString方法。
重写了Object类的equals、hashCode、toString,它们都只是简单的调用了InvocationHandler的invoke方法,即可以对其进行特殊的操作,也就是说JDK的动态代理还可以代理上述三个方法。
代理类实现代理接口的sing方法中,只是简单的调用了InvocationHandler的invoke方法,我们可以在invoke方法中进行一些特殊操作,甚至不调用实现的方法,直接返回。
至此JDK动态代理的实现原理就分析的差不多了。同时我们可以想像一下Spring AOP提供的各种拦截该如何实现,就已经很明了了,如下所示:
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// BeforeAdvice
Object retVal = null;
try {
// AroundAdvice
retVal = method.invoke(target, args);
// AroundAdvice
// AfterReturningAdvice
}
catch (Throwable e) {
// AfterThrowingAdvice
}
finally {
// AfterAdvice
}
return retVal;
}
(源码分析部分来自于https://blog.csdn.net/mhmyqn/article/details/48474815)
还没有评论,来说两句吧...