之前我们通过JDK中的Proxy实现了动态代理,Proxy用起来是比较简便的,但理解起来不是那么清晰,是因为我们并没有看见代理类是怎么生成的,代理类怎么调用的被代理类方法,所以下面我们进入源码看一下。
首先进入创建代理对象的方法Proxy.newProxyInstance()
中:
@CallerSensitive
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
//InvocationHandler对象非空校验
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);
//调用代理类构造器生成代理对象
try {
if (sm != null) {
//一些权限校验
checkNewProxyPermission(Reflection.getCallerClass(), cl);
}
//获取代理类的构造器,该构造器参数类型为InvocationHandler.class
//constructorParams是Proxy类的一个静态常量,值为{ InvocationHandler.class }
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;
}
});
}
//用上面获取的代理类构造器创建一个代理类对象并返回,参数为传入的InvocationHandler对象
return cons.newInstance(new Object[]{h});
} catch (IllegalAccessException|InstantiationException e) {
throw new InternalError(e.toString(), e);
} catch (InvocationTargetException e) {
Throwable t = e.getCause();
if (t instanceof RuntimeException) {
throw (RuntimeException) t;
} else {
throw new InternalError(t.toString(), t);
}
} catch (NoSuchMethodException e) {
throw new InternalError(e.toString(), e);
}
}
Proxy.newProxyInstance()
方法做的事情已经在注释中说明了,其中最重要的一行代码就是获取代理类Class> cl = getProxyClass0(loader, intfs)
,我们进入这个方法看一下:
private static Class<?> getProxyClass0(ClassLoader loader,
Class<?>... interfaces) {
if (interfaces.length > 65535) {
throw new IllegalArgumentException("interface limit exceeded");
}
//如果实现了接口interfaces的代理类已经被类加载器loader加载过了,那就
//将缓存中的代理类返回,否则,通过ProxyClassFactory生成代理类
return proxyClassCache.get(loader, interfaces);
}
proxyClassCache.get()
会尝试从缓存寻找代理类,找不到的话就调用ProxyClassFactory的apply()
生成代理类,我们看一下ProxyClassFactory里是怎么生成代理类的:
private static final class ProxyClassFactory
implements BiFunction<ClassLoader, Class<?>[], Class<?>>
{
//定义所有代理类名字前缀为"$Proxy"
private static final String proxyClassNamePrefix = "$Proxy";
//代理类名字为"$Proxy"+序号,用原子类来生成代理类的序号,确保代理类名字唯一
private static final AtomicLong nextUniqueNumber = new AtomicLong();
@Override
public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
for (Class<?> intf : interfaces) {
//验证intf是否是由指定的类加载器loader加载的
Class<?> interfaceClass = null;
try {
interfaceClass = Class.forName(intf.getName(), false, loader);
} catch (ClassNotFoundException e) {
}
if (interfaceClass != intf) {
throw new IllegalArgumentException(
intf + " is not visible from class loader");
}
//验证intf是否是接口
if (!interfaceClass.isInterface()) {
throw new IllegalArgumentException(
interfaceClass.getName() + " is not an interface");
}
//验证intf在interfaces中是否有重复
if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {
throw new IllegalArgumentException(
"repeated interface: " + interfaceClass.getName());
}
}
//代理类的包名
String proxyPkg = null;
//代理类访问标志,默认是public final
int accessFlags = Modifier.PUBLIC | Modifier.FINAL;
//如果接口有一个是非public的,就设置代理类包名与接口包名相同
for (Class<?> intf : interfaces) {
int flags = intf.getModifiers();
if (!Modifier.isPublic(flags)) {
//更改代理类访问标志为final
accessFlags = Modifier.FINAL;
//获取接口的全限定名
String name = intf.getName();
int n = name.lastIndexOf('.');
//如果接口全限定名中除去接口名后的包名
String pkg = ((n == -1) ? "" : name.substring(0, n + 1));
if (proxyPkg == null) {
//第一次遇到非public的接口,将代理类包名设置与接口包名相同
proxyPkg = pkg;
} else if (!pkg.equals(proxyPkg)) {
//如果代理类实现了不同包的非public接口,这里会报错
throw new IllegalArgumentException(
"non-public interfaces from different packages");
}
}
}
if (proxyPkg == null) {
//如果接口全是public的,代理类放在默认包下:com.sun.proxy
proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
}
//生成代理类的序号
long num = nextUniqueNumber.getAndIncrement();
//生成代理类的全限定名:包名+"$Proxy"+序号
String proxyName = proxyPkg + proxyClassNamePrefix + num;
//---------------------此处是核心---------------------
//【核心】生成代理类的字节码
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
proxyName, interfaces, accessFlags);
try {
//根据字节码文件生成Class对象即代理类
return defineClass0(loader, proxyName,
proxyClassFile, 0, proxyClassFile.length);
} catch (ClassFormatError e) {
throw new IllegalArgumentException(e.toString());
}
}
}
通过上面代码看到ProxyClassFactory主要是确定了代理类的全限定名、要实现的接口、访问标志,然后调用ProxyGenerator类的generateProxyClass()
方法来生成代理类的字节码文件,根据字节码文件获取代理类,具体字节码文件怎么生成的我们留到下一篇专门介绍,这里我们重点关注下生成的代理类是什么样的,为了一睹代理类的真容,我们直接调用generateProxyClass()
方法生成代理类的字节码并写入到文件中:
byte[] classFile = ProxyGenerator.generateProxyClass("$Proxy0", FontProviderFromDisk.class.getInterfaces());
String path = "D:\\ideaProject\\remote\\designpattern\\src\\com\\codezhao\\designpattern\\proxypattern\\dynamicproxy\\FontProviderProxy.class";
try(FileOutputStream fos = new FileOutputStream(path)) {
fos.write(classFile);
fos.flush();
System.out.println("代理类class文件写入成功");
} catch (Exception e) {
System.out.println("写文件错误");
}
打开我们生成的代理类$Proxy0
,看看里面到底有什么:
import com.codezhao.designpattern.proxypattern.FontProvider;
import java.awt.Font;
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 FontProvider {
private static Method m1;
private static Method m3;
private static Method m2;
private static Method m0;
//代理类的构造器需要传递InvocationHandler类型的参数,所以我们大概就可以猜到
//为什么代理对象调用方法都是执行了InvocationHandler对象的invoke()方法
//这里的super()调用了父类Proxy的构造器:
//protected Proxy(InvocationHandler h) {
// Objects.requireNonNull(h);
// this.h = h;
// }
//所以代理类中有个h属性即InvocationHandler对象
public $Proxy1(InvocationHandler var1) throws {
super(var1);
}
//调用代理类的getFont()方法,直接调用了h即InvocationHandler对象的invoke()方法
//所以就清楚了,代理对象关联了InvocationHandler对象,InvocationHandler对象关联了
//被代理对象。InvocationHandler对象就像一个中介,代理对象通过它间接地调用被代理对象
//的方法,这个中介还可以完成一些额外的工作,我们的例子中就是缓存功能。
public final Font getFont(String var1) throws {
try {
return (Font)super.h.invoke(this, m3, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
static {
//这个静态代码块中通过反射获得了getFont方法
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
m3 = Class.forName("com.codezhao.designpattern.proxypattern.FontProvider").getMethod("getFont", Class.forName("java.lang.String"));
m2 = Class.forName("java.lang.Object").getMethod("toString");
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
//为节省篇幅,这里将equals()、toString()、hashCode()省略了
}
在代理类$Proxy0
的代码中,我们可以看到它是如何调用的被代理类,上面的注释写的很清楚了。
从最初的Proxy.newProxyInstance()
走到这里,我们应该知道了Proxy.newProxyInstance()
的功能就是生成了代理类$Proxy0
的对象,我们直接用这个代理类$Proxy0
做测试,结果同调用Proxy.newProxyInstance()
的效果相同。
public class CachedProviderHandlerTest {
public static void main(String[] args) {
FontProvider fontProvider = new FontProviderFromDisk();
CachedProviderHandler cachedProviderHandler = new CachedProviderHandler(fontProvider);
//生成代理对象,直接new一个我们生成的代理类$Proxy0,不用Proxy.newProxyInstance()
$Proxy0 fontProviderProxy = new $Proxy0(cachedProviderHandler);
fontProviderProxy.getFont("xxx");
}
}
另外想要保存代理类class文件的话,还可以通过在测试类中加上下面这行代码来实现,生成的代理类会保存到com.sun.proxy包下,这个包就是上面ProxyClassFactory源码分析中看到的代理类保存的默认包名。
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
1.根据代理类的类加载器和要实现的接口生成代理类;
2.获取构造器参数类型为InvocationHandler.class的代理类构造器;
3.根据代理类构造器和传入的InvocationHandler对象创建代理类对象;
其中第1步又做了这些事:
1.确定代理类的全限定名、要实现的接口、访问标志;
2.生成代理类的字节码文件;
3.根据代理类的字节码文件获取代理类;
具体代理类的字节码文件是怎么生成的,我们留到下一篇专门介绍。
1.InvocationHandler就像一个中介,代理对象中关联了InvocationHandler对象,InvocationHandler对象中关联了被代理对象。代理对象调用方法时直接调用InvocationHandler对象的invoke()方法,在InvocationHandler的invoke()方法中再调用被代理对象的方法,同时在invoke()中可以做一些额外的工作,我们的例子中就是缓存功能;
2.代理类继承了Proxy类,由于Java只支持单继承,所以Proxy+InvocationHandler方式只能针对接口做代理;
3.动态代理的实现依赖于反射机制,而Spring的AOP就是通过代理实现的,所以反射机制对于Spring框架是至关重要的,我们再一次体会到了反射的强大;
4.Spring AOP中对接口的代理其实就是基于Proxy+InvocationHandler实现的,而对类的代理是通过Cglib;
下一篇我们分析ProxyGenerator是如何生成代理类的字节码文件。
欢迎关注我的公众号【codeZhao】,获取更多技术干货和职业思考。