看完了SpringAop,明白了Spring底层封装了cglib和jdk动态代理,并且默认使用了Jdk动态代理来实现aop技术,可以说没有动态代理就没有SpringAop,这下好了,Spring必须叫Jdk动态代理为大哥!!
既然jdk动态代理地位如此重要,那么Jdk动态代理到底是如何实现的?代理类到底是如何生成的?
咱们还是先回顾下动态代理的使用姿势吧,先以LoginService接口的login方法为例
1、Jdk动态代理使用步骤一实现代理逻辑
实现InvocationHandler接口,实现相关代理逻辑
//实现invoke方法,实现代理逻辑
class ProxyInvocationHandler implements InvocationHandler {
//原对象-被代理对象
private Object source;
public ProxyInvocationHandler(Object source) {
this.source = source;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("代理类:"+ proxy.getClass() + "被代理类:" + source.getClass());
System.out.println("代理开始执行方法=====" + method.getName());
Object result = method.invoke(source, args);
System.out.println("代理结束执行方法=====" + method.getName());
return result;
}
}
2、Jdk动态代理使用步骤二创建代理对象
public static LoginService createProxy() {
LoginService loginService = (LoginService) Proxy.newProxyInstance(LoginService.class.getClassLoader(), new Class[]{
LoginService.class}, new ProxyInvocationHandler(new LoginServiceImpl()));
System.out.println(loginService.getClass().toGenericString());
return loginService;
}
3、Jdk动态代理使用步骤三使用代理对象
public static void main(String[] args) throws InterruptedException {
//使用代理,和原对象一样的使用
LoginService loginServiceProxy = createProxy();
loginServiceProxy.login("周杰伦");
}
执行代理逻辑
上面完整演示了使用jdk动态代理过程。
Jdk动态代理使用起来还是很方便的,使用代理类和被代理类一样,生成代理类有两个重要的api,一个是InvocationHandler,一个是Proxy类
- InvocationHandler接口比较简单,里面只有一个invoke方法,实现类需要处理代理逻辑。
- 另外一个Proxy就是重点了,如果要弄明白代理到底是如何实现的,必须看看Proxy.newProxyInstance的源码。
Proxy.newProxyInstance的源码
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
{
//1、获取被代理类的接口
final Class<?>[] intfs = interfaces.clone();
//2、生成代理类
Class<?> cl = getProxyClass0(loader, intfs);
//3、获取构造函数
final Constructor<?> cons = cl.getConstructor(constructorParams);
//4、代理逻辑类
final InvocationHandler ih = h;
//5、创建代理类的实例
return cons.newInstance(new Object[]{
h});
}
方法逻辑非常清晰,主要是通过java.lang.reflect.Proxy#getProxyClass0方法生成了一个类,这个就是代理类,但是我们看不到它生成的类具体是什么样子的, 在生成代理类的方法中,jdk开发者给我们留了一个开关,这个开关可以配置是否保存代理类的class文件。
生成代理class文件
//获取保存代理类class的开关
boolean saveGeneratedFiles = (Boolean)AccessController.doPrivileged(new GetBooleanAction("sun.misc.ProxyGenerator.saveGeneratedFiles"));
public static byte[] generateProxyClass(final String var0, Class<?>[] var1, int var2) {
ProxyGenerator var3 = new ProxyGenerator(var0, var1, var2);
final byte[] var4 = var3.generateClassFile();
//开关打开了
if (saveGeneratedFiles) {
int var1 = var0.lastIndexOf(46);
Path var2;
//将代理类写到文件中
Files.write(var2, var4, new OpenOption[0]);
return null;
}
return var4;
}
那么我们可以配置系统参数sun.misc.ProxyGenerator.saveGeneratedFiles,打开这个开关
public static void main(String[] args) throws InterruptedException {
//打开保存生成代理类开关
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
LoginService loginServiceProxy = createProxy();
loginServiceProxy.login("周杰伦");
}
这样运行我们的测试类后,会将代理类的class文件生成到com.sun.proxy包中:
查看代理类class文件
public final class $Proxy0 extends Proxy implements LoginService {
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 login(String var1) throws {
try {
super.h.invoke(this, m3, 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 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("cn.supfox.proxy.service.LoginService").getMethod("login", 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());
}
}
}
代理类总结
- 代理类被final修饰,类名称是$Proxy开头,数字结尾
- 代理类都继承了Proxy类
- 实现了被代理类的所有方法并且都是final修饰
- 实现的被代理类的方法,里面都是通过invocation.invoke方法触发方法调用,再看看咱们写的ProxyInvocationHandler类,这就能明白了代理类会通过invoke方法来代理具体的方法
//实现invoke方法,实现代理逻辑
class ProxyInvocationHandler implements InvocationHandler {
//原对象-被代理对象
private Object source;
public ProxyInvocationHandler(Object source) {
this.source = source;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("代理类:"+ proxy.getClass() + "被代理类:" + source.getClass());
System.out.println("代理开始执行方法=====" + method.getName());
Object result = method.invoke(source, args);
System.out.println("代理结束执行方法=====" + method.getName());
return result;
}
}
代理类思考总结
- jdk动态代理为啥只能代理接口不能代理普通class类?,因为代理对象已经实现了Proxy类,没法实现其他类了。
- 代理类通过持有ProxyInvocationHandler的引用,间接实现代理逻辑的功能。