扫描文末二维码或者微信搜索公众号
菜鸟飞呀
飞,便可关注微信公众号,阅读更多Spring源码分析文章java
在阅读本文以前,能够先思考一下下面几个问题。数组
public interface UserService {
int insert();
String query();
}
复制代码
public class UserServiceImpl implements UserService{
@Override
public int insert() {
System.out.println("insert");
return 0;
}
@Override
public String query() {
System.out.println("query");
return null;
}
}
复制代码
public class UserServiceInvocationHandler implements InvocationHandler {
// 持有目标对象
private Object target;
public UserServiceInvocationHandler(Object target){
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("invocation handler");
// 经过反射调用目标对象的方法
return method.invoke(target,args);
}
}
复制代码
public class MainApplication {
public static void main(String[] args) {
// 指明一个类加载器,要操做class文件,怎么少得了类加载器呢
ClassLoader classLoader = MainApplication.class.getClassLoader();
// 为代理对象指定要是实现哪些接口,这里咱们要为UserServiceImpl这个目标对象建立动态代理,因此须要为代理对象指定实现UserService接口
Class[] classes = new Class[]{UserService.class};
// 初始化一个InvocationHandler,并初始化InvocationHandler中的目标对象
InvocationHandler invocationHandler = new UserServiceInvocationHandler(new UserServiceImpl());
// 建立动态代理
UserService userService = (UserService) Proxy.newProxyInstance(classLoader, classes, invocationHandler);
// 执行代理对象的方法,经过观察控制台的结果,判断咱们是否对目标对象(UserServiceImpl)的方法进行了加强
userService.insert();
}
}
复制代码
阅读到这里,咱们基本知道了JDK动态代理的写法,功能虽然实现了,可是对于喜欢研究源码的同窗,可能会思考:为何调用Proxy.newProxyInstance()方法后,就产生了代理对象,对目标对象的方法进行了加强?原理是什么呢?微信
public static void main(String[] args) throws IOException {
String proxyName = "com.tiantang.study.$Proxy0";
Class[] interfaces = new Class[]{UserService.class};
int accessFlags = Modifier.PUBLIC;
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
proxyName, interfaces, accessFlags);
// 将字节数组写出到磁盘
File file = new File("/Users/liujinkun/Downloads/dynamic/$Proxy0.class");
OutputStream outputStream = new FileOutputStream(file);
outputStream.write(proxyClassFile);
}
复制代码
/Users/liujinkun/Downloads/dynamic/$Proxy0.class
找到生成的文件,因为是一个class文件,因此咱们须要把它有反编译器编译一下,例如:在idea中,将文件放到target目录下,打开文件就能看到反编译后的代码了。System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true")
,就能实现将程序运行过程当中产生的动态代理对象的class文件写入到磁盘。以下示例:public class MainApplication {
public static void main(String[] args) {
// 让代理对象的class文件写入到磁盘
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
// 指明一个类加载器,要操做class文件,怎么少得了类加载器呢
ClassLoader classLoader = MainApplication.class.getClassLoader();
// 为代理对象指定要是实现哪些接口,这里咱们要为UserServiceImpl这个目标对象建立动态代理,因此须要为代理对象指定实现UserService接口
Class[] classes = new Class[]{UserService.class};
// 初始化一个InvocationHandler,并初始化InvocationHandler中的目标对象
InvocationHandler invocationHandler = new UserServiceInvocationHandler(new UserServiceImpl());
// 建立动态代理
UserService userService = (UserService) Proxy.newProxyInstance(classLoader, classes, invocationHandler);
// 执行代理对象的方法,经过观察控制台的结果,判断咱们是否对目标对象(UserServiceImpl)的方法进行了加强
userService.insert();
}
}
复制代码
$Proxy0.class
。在idea打开,发现就是所产生代理类的源代码。经过上面两种方法获取到代理对象的源代码以下:异步
package com.sun.proxy;
import com.tiantang.study.UserService;
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 UserService {
private static Method m1;
private static Method m3;
private static Method m4;
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 int insert() throws {
try {
return (Integer)super.h.invoke(this, m3, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final String query() throws {
try {
return (String)super.h.invoke(this, m4, (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("com.tiantang.study.UserService").getMethod("insert");
m4 = Class.forName("com.tiantang.study.UserService").getMethod("query");
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());
}
}
}
复制代码
经过源码咱们发现,$Proxy0
类继承了Proxy类,同时实现了UserService接口。到这里,咱们的问题一就能解释了,为何JDK的动态代理只能基于接口实现,不能基于继承来实现?由于Java中不支持多继承,而JDK的动态代理在建立代理对象时,默认让代理对象继承了Proxy类,因此JDK只能经过接口去实现动态代理。ide
$Proxy0
实现了UserService接口,因此重写了接口中的两个方法($Proxy0
同时还重写了Object类中的几个方法)。因此当咱们调用query()方法时,先是调用到$Proxy0.query()
方法,在这个方法中,直接调用了super.h.invoke()方法,父类是Proxy,父类中的h
就是咱们定义的InvocationHandler,因此这儿会调用到UserServiceInvocationHandler.invoke()方法。所以当咱们经过代理对象去执行目标对象的方法时,会先通过InvocationHandler的invoke()方法,而后在经过反射method.invoke()去调用目标对象的方法,所以每次都会先打印invocation handler
这句话。工具
public class UserServiceInvocationHandler implements InvocationHandler {
// 持有目标对象
private Object target;
public UserServiceInvocationHandler(Object target){
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("invocation handler");
// 经过反射调用目标对象的方法
return method.invoke(target,args);
}
}
复制代码
public class UserServiceImpl implements UserService{
@Override
public int insert() {
System.out.println("insert");
query();
return 0;
}
@Override
public String query() {
System.out.println("query");
return null;
}
}
复制代码
public class MainApplication {
public static void main(String[] args) {
// 让代理对象的class文件写入到磁盘
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
// 指明一个类加载器,要操做class文件,怎么少得了类加载器呢
ClassLoader classLoader = MainApplication.class.getClassLoader();
// 为代理对象指定要是实现哪些接口,这里咱们要为UserServiceImpl这个目标对象建立动态代理,因此须要为代理对象指定实现UserService接口
Class[] classes = new Class[]{UserService.class};
// 初始化一个InvocationHandler,并初始化InvocationHandler中的目标对象
InvocationHandler invocationHandler = new UserServiceInvocationHandler(new UserServiceImpl());
// 建立动态代理
UserService userService = (UserService) Proxy.newProxyInstance(classLoader, classes, invocationHandler);
// 执行代理对象的方法,经过观察控制台的结果,判断咱们是否对目标对象(UserServiceImpl)的方法进行了加强
userService.insert();
}
}
复制代码
$Proxy0
代理类对UserServiceImpl中的方法都进行了加强,每次调用UserServiceImpl中类的方法时,应该都会通过InvocationHandler中的invoke()方法,每次都会打印invocation handler
这句话。因此当咱们调用userService.insert()
时,打印结果应该是:invocation handler
insert
invocation handler
query
复制代码
invocation handler
。在调用query()方法时,并无去执行InvocationHandler中的invoke()方法。为何呢?缘由就在这个地方:public int insert() {
System.out.println("insert");
query();
return 0;
}
复制代码
$Proxy0
这个代理对象,只有在调用代理对象的query()方法时,才会通过InvocationHandler.invoke()方法,因此此时只会打印一次invocation handler
。因此第二个问题的答案就是:在调用过程当中,this的指向发生了变化。扫描下方二维码便可关注微信公众号
菜鸟飞呀飞
,一块儿阅读更多Spring源码。源码分析