前言
之前对动态代理了解仅仅在于表层,一直觉得高不可攀,今天点开了 Proxy 类,欲知故事如何,需 Read The Source Code,再加上看一些别人的文章,对照着自己对源码的理解,形成此文,通俗易懂,保你看后对动态代理又有了更加深入的理解
先看一个例子熟悉一下吧
先定义接口,之后我们再看,为什么JDK不能代理类,只能代理接口
public interface AddService {
/**
* <p>Test method</p>
*
* @param a number a
* @param b number b
* @return sum of a and b
*/
int add(int a, int b);
}
实现类
public class AddServiceImpl implements AddService {
@Override
public int add(int a, int b) {
return a + b;
}
}
Handler,继承自InvocationHandler,该接口只有一个方法 invoke,你只需要实现它,然后利用反射 Object invoke = method.invoke(addService, args); 返回接口return invoke; 其他的你想干什么都行,当然你也完全改变这个这个实现,返回一些别的啥,坏事也是可以的。getProxy方法里面调用Proxy.newProxyInstance 获取一个代理对象
public class AddServiceHandler implements InvocationHandler {
private AddService addService;
public AddServiceHandler(AddService addService) {
this.addService = addService;
}
public AddService getProxy() {
return (AddService) Proxy.newProxyInstance(addService.getClass().getClassLoader(),
addService.getClass().getInterfaces(), this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("before");
Object invoke = method.invoke(addService, args);
System.out.println("after");
return invoke;
}
}
使用,首先获取创建实例对象,然后构造一个Handler,再通过Handler获取Proxy 对象,再调用接口的方法。我们一切的疑问,可能就在 getProxy,他到底返回了什么东西,能够让我们再调用接口方法的时候,执行的却是 实现的Service 的方法,并且加了一些其它实现,聪明的你可能会说,这用静态代理依然能够实现,并且要比动态代理来得简单,为什么还要这样复杂的实现。我现在能想到的是,静态代理的话,你可能需要为每一个代理接口实现一个代理 Handler,然而 InvocationHandler 的话,你只需要为类似的请求实现一个Handler,为程序的扩展提供了大大的空间。
@Test
public void dynamicProxyTest() {
AddService service = new AddServiceImpl();
AddServiceHandler addServiceHandler = new AddServiceHandler(service);
AddService proxy = addServiceHandler.getProxy();
Assert.assertEquals(3, proxy.add(1, 2));
}
看到这里,我们有很多疑问
- Proxy.newProxyInstance() 返回的是什么东西
- invoke 方法到底在哪调用
- 我们在 target 里面没有看到其它任何附带生成的 class,系统到底是怎么做的呢
那我们就要好好看看这些方法的实现原理
Proxy.newProxyInstance
public class Proxy implements java.io.Serializable{}
private 的构造方法,里面有一个 InvocationHandler,这个就是我们传入的 Handler,另外还有一个 proxyClassCache,一个代理类的缓存对象,我暂时不打算展开讲这个东西,还没搞明白,现在需要记住,这个存放这系统帮我们生成的代理类,用了WeakReference 实现, GC 的时候会被回收。
里面有两个参数传过去,KeyFactory() 先不管,ProxyClassFactory() 这个很重要,我们之后遇到了再说
/** parameter types of a proxy class constructor */
private static final Class<?>[] constructorParams =
{ InvocationHandler.class };
/**
* a cache of proxy classes
*/
private static final WeakCache<ClassLoader, Class<?>[], Class<?>>
proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());
/**
* the invocation handler for this proxy instance.
* @serial
*/
protected InvocationHandler h;
/**
* Prohibits instantiation.
*/
private Proxy() {
}
为了理解方便,我将一些无关精要的代码剔除,留下最重要的两个方法,getProxyClass0(loader, intfs) 根据loader和intfs 获取代理类,通过这个方法我们获得一个新的类字节码,这个类是运行时生成的,通过这个代理类,getConstructor 获取构造对象,调用newInstance创建一个实例对象,newInstance是可以传参的,只需要调用 constructor 的构造方法参数必须是 InvocationHandler.class,所以我们传的是 this 对象。
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
/*
* Look up or generate the designated proxy class.
*/
Class<?> cl = getProxyClass0(loader, intfs);
final Constructor<?> cons = cl.getConstructor(constructorParams);
return cons.newInstance(new Object[]{h});
}
65535 限制,这个get 比较高级,直接从缓存拿,刚开始看到可能觉得有点纳闷,这个 proxyClassCache 之前遇到过了
private static Class<?> getProxyClass0(ClassLoader loader,
Class<?>... interfaces) {
if (interfaces.length > 65535) {
throw new IllegalArgumentException("interface limit exceeded");
}
// If the proxy class defined by the given loader implementing
// the given interfaces exists, this will simply return the cached copy;
// otherwise, it will create the proxy class via the ProxyClassFactory
return proxyClassCache.get(loader, interfaces);
}
get方法首先创建一个 valuesMap,获取subKey,里面比较重要的就是subKeyFactory.apply(key, parameter),这个方法会帮我们生成代理类的subKey,另外之后会建立一个Factory,当使用get 的时候,便是真正生成 代理类的时候
public V get(K key, P parameter) {
Objects.requireNonNull(parameter);
expungeStaleEntries();
Object cacheKey = CacheKey.valueOf(key, refQueue);
// lazily install the 2nd level valuesMap for the particular cacheKey
ConcurrentMap<Object, Supplier<V>> valuesMap = map.get(cacheKey);
if (valuesMap == null) {
ConcurrentMap<Object, Supplier<V>> oldValuesMap
= map.putIfAbsent(cacheKey,
valuesMap = new ConcurrentHashMap<>());
if (oldValuesMap != null) {
valuesMap = oldValuesMap;
}
}
// create subKey and retrieve the possible Supplier<V> stored by that
// subKey from valuesMap
Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));
Supplier<V> supplier = valuesMap.get(subKey);
Factory factory = null;
while (true) {
if (supplier != null) {
// supplier might be a Factory or a CacheValue<V> instance
V value = supplier.get();
if (value != null) {
return value;
}
}
// else no supplier in cache
// or a supplier that returned null (could be a cleared CacheValue
// or a Factory that wasn't successful in installing the CacheValue)
// lazily construct a Factory
if (factory == null) {
factory = new Factory(key, parameter, subKey, valuesMap);
}
if (supplier == null) {
supplier = valuesMap.putIfAbsent(subKey, factory);
if (supplier == null) {
// successfully installed Factory
supplier = factory;
}
// else retry with winning supplier
} else {
if (valuesMap.replace(subKey, supplier, factory)) {
// successfully replaced
// cleared CacheEntry / unsuccessful Factory
// with our Factory
supplier = factory;
} else {
// retry with current supplier
supplier = valuesMap.get(subKey);
}
}
}
}
ProxyClassFactory apply 不可不看,首先加载接口,然后使用ProxyGenerator.generateProxyClass 生成Class 字节码文件,最后再调用 defineClass0 对其加载后返回关键字,作为key,之后再根据这个key获取到真正的class 对象,到这里,Proxy类已经生成好,并且加载好了,直接返回,这个类是动态生成的,留在内存的数据
private static final class ProxyClassFactory
implements BiFunction<ClassLoader, Class<?>[], Class<?>>
{
// prefix for all proxy class names
private static final String proxyClassNamePrefix = "$Proxy";
// next number to use for generation of unique proxy class names
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) {
/*
* Verify that the class loader resolves the name of this
* interface to the same Class object.
*/
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");
}
/*
* Verify that the Class object actually represents an
* interface.
*/
if (!interfaceClass.isInterface()) {
throw new IllegalArgumentException(
interfaceClass.getName() + " is not an interface");
}
/*
* Verify that this interface is not a duplicate.
*/
if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {
throw new IllegalArgumentException(
"repeated interface: " + interfaceClass.getName());
}
}
String proxyPkg = null; // package to define proxy class in
int accessFlags = Modifier.PUBLIC | Modifier.FINAL;
/*
* 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 (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) {
// if no non-public proxy interfaces, use com.sun.proxy package
proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
}
/*
* Choose a name for the proxy class to generate.
*/
long num = nextUniqueNumber.getAndIncrement();
String proxyName = proxyPkg + proxyClassNamePrefix + num;
/*
* Generate the specified proxy class.
*/
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
proxyName, interfaces, accessFlags);
try {
return defineClass0(loader, proxyName,
proxyClassFile, 0, proxyClassFile.length);
} catch (ClassFormatError e) {
/*
* A ClassFormatError here means that (barring bugs in the
* proxy class generation code) there was some other
* invalid aspect of the arguments supplied to the proxy
* class creation (such as virtual machine limitations
* exceeded).
*/
throw new IllegalArgumentException(e.toString());
}
}
}
那我们可以调用ProxyGenerator.generateProxyClass
来看一次下这个生成的类,把它写到文件里,这个类大概就是这个样子,就是我们通过getProxy
获取到的实际类,之后就简单了,可以清楚的看到里面熟悉的add
方法,是通过调用了 invoke
来实现的
public final class $Proxy11 extends Proxy implements Service {
private static Method m1;
private static Method m2;
private static Method m3;
private static Method m0;
public $Proxy11(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 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 int add(int var1, int var2) throws {
try {
return (Integer)super.h.invoke(this, m3, new Object[]{var1, var2});
} catch (RuntimeException | Error var4) {
throw var4;
} catch (Throwable var5) {
throw new UndeclaredThrowableException(var5);
}
}
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"));
m2 = Class.forName("java.lang.Object").getMethod("toString");
m3 = Class.forName("proxy.Service").getMethod("add", Integer.TYPE, Integer.TYPE);
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}
至此,大部分逻辑已经搞清楚了,那我们大概知道了为什么这个过程要比直接创建对象要慢,那是因为他第一次的时候需要动态的去创建字节码,然后进行加载,初始化......虽然有缓存,但是由于使用了 WeakReference,GC后有可能会被回收,那么就得重新加载,一定程度上会降低效率,所以一般情况下,我们尽量避免这种动态生成类的方式,而是用在编译时生成类的方式取代,这便是 APT 技术的精髓。
小结
上面三个小问题都已经弄清楚了吧,现在对动态代理更加了解了些,另外一类 CGLIB,更改字节码的技术之后有时间会再去看
欢迎讨论~~~