动态代理的基本使用
动态代理是Java的一种基本技术手段,他能在运行期对一个对象的一些方法做一些修改,而得到一个新的对象,而不需要预先定义好对应的对象的class,下面我们先来看一下代理模式的简单使用方式:
先定义好一个接口:
public interface IPrint {
void print();
}
然后在定义这个接口的实现类:
public class Person implements IPrint {
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public void print() {
System.out.println(toString());
}
}
这是一个简单的实体类,并实现了接口IPrint
,我们之后代理的就是这个类的实例。
然后定义代理相关的核心类:
public class LogHandler<T> implements InvocationHandler {
private T targetObject;
public LogHandler(T targetObject) {
this.targetObject = targetObject;
}
public T newProxyInstance(T targetObject) {
return (T) Proxy.newProxyInstance(targetObject.getClass().getClassLoader(),
targetObject.getClass().getInterfaces(), this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] objects) throws Throwable {
System.out.println("invoke start");
Object result = method.invoke(targetObject, objects);
System.out.println("invoke end");
return result;
}
}
在这个类里,我们发现了一个我们不认识的方法:
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
他的源码解析我们放在下一章去解读,我们现在只需要知道,它的返回值是一个实现了传入的interfaces接口列表的每一个接口的类的实例,而这个代理类的每一个方法的实现均依赖于传入的h的invoke方法的具体实现,当代理类一个方法被调用的时候,相当于调用到h的invoke方法,而h的invoke方法一般会调用method.invoke(targetObject, objects),这相当于是在调用被代理对象的方法。
我们来看具体使用代理的方式:
Person tony = new Person("Tony", 1);
LogHandler<IPrint> logHandler = new LogHandler<>(tony);
IPrint iPrint = logHandler.newProxyInstance(tony);
iPrint.print();
iPrint.toString();
invoke start
Person{name='Tony', age=1}
invoke end
invoke start
invoke end
可以看到我们通过动态代理得到的对象是实现了IPrint
接口的,而接口里面的方法print被代理了,在调用的时候除了原始类本身的打印外,还有在代理实现的打印,而且代理类的继承于Object的方法也被代理了。
我们能够通过打印代理Class的参数列表等来看其的类的样子:
public final class com.sun.proxy.$Proxy0 extends java.lang.reflect.Proxy
{
public com.sun.proxy.$Proxy0(java.lang.reflect.InvocationHandler);
public final boolean equals(java.lang.Object);
public final java.lang.String toString();
public final int hashCode();
public final void print(){
h.invoke(this,m0, Object[] objects)
}
java.lang.reflect.Method m1;
java.lang.reflect.Method m3;
java.lang.reflect.Method m2;
java.lang.reflect.Method m0;
}
其中print的实现为猜想的结果。
动态代理的源码解读
我们学习了动态代理的基本使用之后,我们再来看看动态代理的源码:
先从
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
说起,他的实现如下(Android上的实现):
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
Objects.requireNonNull(h);
final Class<?>[] intfs = interfaces.clone();
// Android-removed: SecurityManager calls
/*
final SecurityManager sm = System.getSecurityManager();
if (sm != null) {
checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
}
*/
/*
* Look up or generate the designated proxy class.
*/
//获取代理类的Class对象
Class<?> cl = getProxyClass0(loader, intfs);
/*
* Invoke its constructor with the designated invocation handler.
*/
try {
// Android-removed: SecurityManager / permission checks.
/*
if (sm != null) {
checkNewProxyPermission(Reflection.getCallerClass(), cl);
}
*/
final Constructor<?> cons = cl.getConstructor(constructorParams);
final InvocationHandler ih = h;
if (!Modifier.isPublic(cl.getModifiers())) {
// BEGIN Android-changed: Excluded AccessController.doPrivileged call.
/*
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
cons.setAccessible(true);
return null;
}
});
*/
cons.setAccessible(true);
// END Android-removed: Excluded AccessController.doPrivileged call.
}
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);
}
}
他的逻辑很简单,就是通过传来的接口信息以及类加载器创建出代理类的Class对象,然后调用构造方法来创建出具体的对象,所以最关键的是,他创建出来的Class对象具体是啥样的,我们来具体看这个方法:
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);
}
他是通过调用WeakCache.get方法来得到的,而WeakCache类主要是做一些缓冲的工作的,他最终会掉到ProxyClassFactory的apply方法:
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 the default package.
proxyPkg = "";
}
{
// Android-changed: Generate the proxy directly instead of calling
// through to ProxyGenerator.
List<Method> methods = getMethods(interfaces);
Collections.sort(methods, ORDER_BY_SIGNATURE_AND_SUBTYPE);
validateReturnTypes(methods);
List<Class<?>[]> exceptions = deduplicateAndGetExceptions(methods);
Method[] methodsArray = methods.toArray(new Method[methods.size()]);
Class<?>[][] exceptionsArray = exceptions.toArray(new Class<?>[exceptions.size()][]);
/*
* Choose a name for the proxy class to generate.
*/
long num = nextUniqueNumber.getAndIncrement();
String proxyName = proxyPkg + proxyClassNamePrefix + num;
return generateProxy(proxyName, interfaces, loader, methodsArray,
exceptionsArray);
}
}
}
他分如下步骤:
- 做一些检验工作,分别是接口能否被类加载器加载,Class数组是不是都是接口,接口是否有重复,接口如果我们传入的类加载器和接口数组是使用具体的对象拿到的,这些校验肯定是能通过的;
- 然后获取代理类的包名,如果所有接口均是public的,代理类包名就为空,否则就是非public的接口的包名(如果有多个非public的接口,且包名不同,会抛出异常,当然我们代码不可能实现多个包下的非public的接口);
- 获取所有接口的方法列表,并检查是否有相同方法名以及参数列表,但是返回值不同的方法,有就抛出异常;
- 获取方法返回的异常列表;
- 然后调用native方法具体创建代理类对象。
以上做的这些检查,如果我们的类加载器和接口列表是通过targetObject.getClass().getClassLoader(),targetObject.getClass().getInterfaces()来取得的,那些检查是一定能通过的。
获取方法列表具体如下,它除了接口里面的方法外,还会添加equals、hashCode、toString三个方法,每个接口还会递归获取其父类的方法。
private static List<Method> getMethods(Class<?>[] interfaces) {
List<Method> result = new ArrayList<Method>();
try {
result.add(Object.class.getMethod("equals", Object.class));
result.add(Object.class.getMethod("hashCode", EmptyArray.CLASS));
result.add(Object.class.getMethod("toString", EmptyArray.CLASS));
} catch (NoSuchMethodException e) {
throw new AssertionError();
}
getMethodsRecursive(interfaces, result);
return result;
}
private static void getMethodsRecursive(Class<?>[] interfaces, List<Method> methods) {
for (Class<?> i : interfaces) {
getMethodsRecursive(i.getInterfaces(), methods);
Collections.addAll(methods, i.getDeclaredMethods());
}
}
创建Class对象的native方法如下:
private static native Class<?> generateProxy(String name, Class<?>[] interfaces,
ClassLoader loader, Method[] methods,
Class<?>[][] exceptions);
由于并没有代理类所对应的class文件,所以常规的创建Class方法行不通,这儿是通过native方法来实现的。
至此我们就解读完了代理类的创建过程。
我们从源码中能总结出如下几点:
- 动态代理只能够实现代理对象接口上的方法;
- 通过动态代理后,被代理对象的信息是不会出现在代理类的Class里面的,也就是说动态代理其实是正对接口列表来获取代理类,要与被代理对象关联,只能通过InvocationHandler.invoke来实现。
最后我们简单的站在动态代理的角度解读下Retrofit的创建使用过程。
相信使用过Retrofit的人对如下代码很熟悉:
api = Retrofit.Builder()
.baseUrl(BASE_URL)
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create())
.client(builder.build())
.build()
.create(Api::class.java)
他在最后一步,通过调用Retrofit的create方法,传入的是一个接口的Class对象,最后得到的是一个实例,我们能很简单的猜到它内部使用了动态代理来实现的。他的集体实现如下:
public <T> T create(final Class<T> service) {
Utils.validateServiceInterface(service);
if (validateEagerly) {
eagerlyValidateMethods(service);
}
return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service },
new InvocationHandler() {
private final Platform platform = Platform.get();
@Override public Object invoke(Object proxy, Method method, Object... args)
throws Throwable {
// If the method is a method from Object then defer to normal invocation.
if (method.getDeclaringClass() == Object.class) {
return method.invoke(this, args);
}
if (platform.isDefaultMethod(method)) {
return platform.invokeDefaultMethod(method, service, proxy, args);
}
ServiceMethod serviceMethod = loadServiceMethod(method);
OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);
return serviceMethod.callAdapter.adapt(okHttpCall);
}
});
}
可以看出他的实例就是通过动态代理来创建的。
在invoke方法里,先判断当前调用的是接口定义的方法还是属于Object的方法,Object的方法就直接调用,如果是接口定义的方法,就解析方法的注解信息,存到serviceMethod,然后调用请求接口。