Spring源码分析(三)Core
sschrodienger
2019/05/27
代理
代理的主要作用是使用一个代理类代理普通类,使得对普通类的功能进行扩展,参见代理模式(runoob.com)。
按照代理类生成的时间,可以分为静态代理和动态代理,静态代理特指代理类和委托类在代码运行前关系就确定了,也就是说在代理类的代码一开始就已经存在了;动态代理特指在运行时才生成代理类。
Java 静态代理
静态代理医用聚合的方式组成,UML 类图如下:
spring core
假如需要编写一个数据库操作类,实现数据库的连接和添加操作,类的接口和实现定义如下:
public interface DataOp {
public void connect() throws SQLException;
public void addData(Data data) throws SQLException;
}
public class DataOpImpl implements DataOp {
public void connect() throws SQLException {}
public void addData(Data data) throws SQLException {}
}
使用静态代理对 DataOpImpl 增加一个日志功能,在运行成功时打印成功、失败时打印失败,如下:
public class DataOpProxy implements DataOp {
private DataOp dataOp = new DataOpImpl();
public void connect() throws SQLException {
try {
dataOp.connect();
System.out.println("connect success");
} catch (SQLException e) {
System.out.println("connect failed");
throw new SQLException(e);
}
}
public void addData(Data data) throws SQLException {
try {
dataOp.addData(Data data);
System.out.println("add success");
} catch (SQLException e) {
System.out.println("add failed");
throw new SQLException(e);
}
}
}
note
- 代理模式和装饰器模式比较相似:代理类(proxy class)可以对它的客户隐藏一个对象的具体信息。因此,当使用代理模式的时候,我们常常在一个代理类中创建一个对象的实例。并且,当我们使用装饰器模式的时候,我们通常的做法是将原始对象作为一个参数传给装饰者的构造器。
- 即:使用代理模式,代理和真实对象之间的的关系通常在编译时就已经确定了,而装饰者能够在运行时递归地被构造。
手工写代理类麻烦且易出错,AspectJ 库可以通过注解的方式自动帮我们完成代理类的生成。
动态代理
动态代理主要有两种实现方式:JDK 代理 和 CGLIB 代理。
JDK 代理
JDK 代理主要依赖于类库 java.lang.reflect.Proxy。JDK 代理要求委托类必须实现接口,JDK 代理只会代理接口之下的方法。
对于如下的接口和接口实现:
interface Service {
void doSomething();
}
class ServiceImpl implements Service {
public void doSomething() {
System.out.println("class[" + this.getClass().getCanonicalName() + "] do something");
}
}
可以使用以下的方式获得代理类:
public class DynamicProxy {
public Object getProxyInstance(Service target) {
return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(),
(proxy, method, args) -> {
System.out.println("before...");
Object returnValue = method.invoke(target, args);
System.out.println("after...");
return null;
});
}
public static void main(String[] args) {
DynamicProxy dynamicProxy = new DynamicProxy();
Service service = (Service) dynamicProxy.getProxyInstance(new ServiceImpl());
System.out.println(service.getClass());
service.doSomething();
}
}
源码分析
JDK 代理最重要的一个方法是 Proxy.newProxyInstance,在这个方法中,玩成了最主要的三步工作,代码简写如下:
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException {
// step 1
Class<?> cl = getProxyClass0(loader, intfs);
// step 2
// constructorParams = { 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;
}
});
}
// step 3
return cons.newInstance(new Object[]{h});
}
其中第一步主要是完成代理类的创建,第二步找到代理类的构造函数、第三部将 InvocationHandler 作为构造函数的参数传入并返回。
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);
}
其中,完成代理类的创建主要在函数 proxyClassCache.get() 中,首先看该缓存中有没有当前代理类,有,则直接返回,没有的话进行创建。proxyClassCache 的值为 ProxyClassFactory,该类是 Proxy 的内部类,如果没有缓存,则调用 ProxyClassFactory 类的 apply 方法创建新的代理类。apply 函数的关键函数如下:
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
proxyName, interfaces, accessFlags);
try {
return defineClass0(loader, proxyName,
proxyClassFile, 0, proxyClassFile.length);
}
动态的生成继承了接口的 Proxy 的子类。
在如上 main 函数之前加入如下语句,可以将生成的代理类保存在 classPath 中,如下:
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true");
利用反编译查看生成的代理类如下:
package other;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
final class $Proxy0 extends Proxy implements Service {
private static Method m1;
private static Method m3;
private static Method m2;
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 void doSomething() throws {
try {
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 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"));
m3 = Class.forName("other.Service").getMethod("doSomething");
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());
}
}
}
由此可知,对于每一个代理类继承的函数,都是使用 InvocationHandler,利用反射机制实现代理。
note
- JDK 代理只能代理接口,因为生成的代理类自动继承
Proxy,当继承类时,因为 Java 不允许多继承,所以会报错。
cglib 代理
与 JDK 代理相比,cglib 直接在内存中根据委托类创建该委托类的子类,并且在子类中增加功能。
与 JDK 代理相比,cglib 可以实现类的代理而不仅仅是接口的代理。
由于是继承方式,对于标注了 static 方法,private 方法,final 方法等描述的方法是不能被代理的。











网友评论