JDK动态代理的理解可以帮助我们很好的去理解Spring的AOP思想以及实现。废话不多说,先来个小例子。
1. 定义一个接口,因为JDK动态代理代理的是实现了接口的类
public interface PersonalTest {
void printString(String str);
void printString1(String str);
}
2.其实现类
public class PersonalTestImpl implements PersonalTest {
@Override
public void printString(String str) {
System.out.println("print value = "+str);
}
@Override
public void printString1(String str) {
System.out.println("print value1 = "+str);
}
}
3.要能使用代理对象,需要实现InvocationHandle接口
public class PersonalInvocation implements InvocationHandler {
private Object target;
public PersonalInvocation(Object target) {
this.target = target;
}
/**
* 代理类执行
* @param proxy
* @param method
* @param args
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("before invocation");
//调用目标类的方法
Object returnVal = method.invoke(target,args);
System.out.println("after invocation");
return returnVal;
}
/**
* 获取代理对象
* @return
*/
public Object getTargetProxy(){
return Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),target.getClass().getInterfaces(),this);
}
}
4.测试main
public class TestA {
public static void main(String[] args) throws Exception{
PersonalTest p = new PersonalTestImpl();
PersonalInvocation in = new PersonalInvocation(p);
PersonalTest proxy = (PersonalTest) in.getTargetProxy();
proxy.printString("hello!!!");
proxy.printString1("hello???");
}
}
5.输出
before invocation
print value = hello!!!
after invocation
before invocation
print value1 = hello???
after invocation
以上是一个简单的测试用例,我觉得一开始接触的人会有疑问?至少我一直都是有点糊里糊涂的,至少没有一个明确的结论。
1.为什么proxy在调用printString的时候会执行到invoke方法上去?也可以这么想,invoke是谁在调用?
2.InvocationHandler中invoke方法内的第一个参数proxy是什么?
带着疑问我们可以做一些简单的测试:
public class TestA {
public static void main(String[] args) throws Exception{
PersonalTest p = new PersonalTestImpl();
PersonalInvocation in = new PersonalInvocation(p);
PersonalTest proxy = (PersonalTest) in.getTargetProxy();
System.out.print("是不是Proxy的实例: ");
System.out.println(proxy instanceof Proxy);
System.out.println("Class是什么: "+proxy.getClass().toString());
System.out.println("继承的父类是什么: "+proxy.getClass().getSuperclass());
Class<?>[] t = proxy.getClass().getInterfaces();
System.out.print("实现的接口是什么: ");
for(Class<?> each: t){
System.out.print(each.getName()+", ");
}
System.out.println();
Method[] method=proxy.getClass().getDeclaredMethods();
System.out.print("内部方法是什么: ");
for(Method m:method){
System.out.print(m.getName()+", ");
}
}
}
是不是Proxy的实例: true
Class是什么: class com.sun.proxy.$Proxy0
继承的父类是什么: class java.lang.reflect.Proxy
实现的接口是什么: com.wayne.sam.PersonalTest,
内部方法是什么: equals, toString, hashCode, printString, printString1,
上面是一些简单的测试,应该能从中获取一些感想吧,有没有发现Class是一个procy.$Proxy0的玩意儿,这玩意儿是怎么出来的?接下来我们去内部看下源码,最后再分析上面两个问题。
1. 我么从PersonalInvocation中的getProxy方法入手newProxyInstance,它最终生成的是代理类的一个实例对象,用于最后的操作,也就是上例中的printString操作。
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException {
if (h == null) {
throw new NullPointerException();
}
/*
* Look up or generate the designated proxy class.
*/
//寻找或者生成代理对象,主要的操作在这里面
Class<?> cl = getProxyClass0(loader, interfaces); // stack walk magic: do not refactor
/*
* Invoke its constructor with the designated invocation handler.
*/
try {
//调用代理对象的构造方法(也就是$Proxy0的构造方法,参数是InvocationHandler h)
final Constructor<?> cons = cl.getConstructor(constructorParams);
final InvocationHandler ih = h;
SecurityManager sm = System.getSecurityManager();
if (sm != null && Proxy.ProxyAccessHelper.needsNewInstanceCheck(cl)) {
// create proxy instance with doPrivilege as the proxy class may
// implement non-public interfaces that requires a special permission
return AccessController.doPrivileged(new PrivilegedAction<Object>() {
public Object run() {
return newInstance(cons, ih);
}
});
} else {
//生成代理类的实例,参数是我们自己的PersonalInvocation
return newInstance(cons, ih);
}
} catch (NoSuchMethodException e) {
throw new InternalError(e.toString());
}
}
2.简化了一些无关紧要的代码,基本的策略就是生成代理对象的字节码,然后最终构造出代理对象的实例返回。
private static Class<?> getProxyClass0(ClassLoader loader,
Class<?>... interfaces) {
...
//代理对象对应的Class对象
Class<?> proxyClass = null;
/* collect interface names to use as key for proxy class cache */
String[] interfaceNames = new String[interfaces.length];
// for detecting duplicates
Set<Class<?>> interfaceSet = new HashSet<>();
//变量目标类所实现的接口
for (int i = 0; i < interfaces.length; i++) {
/*
* Verify that the class loader resolves the name of this
* interface to the same Class object.
*/
//接口名称
String interfaceName = interfaces[i].getName();
Class<?> interfaceClass = null;
try {
//反射拿到接口
interfaceClass = Class.forName(interfaceName, false, loader);
} catch (ClassNotFoundException e) {
}
//...验证操作
//将接口的Class对象放入Set
interfaceSet.add(interfaceClass);
interfaceNames[i] = interfaceName;
}
/*
* Using string representations of the proxy interfaces as
* keys in the proxy class cache (instead of their Class
* objects) is sufficient because we require the proxy
* interfaces to be resolvable by name through the supplied
* class loader, and it has the advantage that using a string
* representation of a class makes for an implicit weak
* reference to the class.
*/
List<String> key = Arrays.asList(interfaceNames);
/*
* Find or create the proxy class cache for the class loader.
*/
Map<List<String>, Object> cache;
synchronized (loaderToCache) {
//先从缓存中获取
cache = loaderToCache.get(loader);
if (cache == null) {
cache = new HashMap<>();
loaderToCache.put(loader, cache);
}
}
synchronized (cache) {
do {
//从Cache中获取
Object value = cache.get(key);
if (value instanceof Reference) {
proxyClass = (Class<?>) ((Reference) value).get();
}
if (proxyClass != null) {
return proxyClass;
} else if (value == pendingGenerationMarker) {
try {
cache.wait();
} catch (InterruptedException e) {
}
continue;
} else {
cache.put(key, pendingGenerationMarker);
break;
}
} while (true);
}
try {
//...准备一些代理对象的名称和包名等等信息
/*
* Generate the specified proxy class. 生成代理对象字节码
*/
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
proxyName, interfaces);
try {
//根据代理类的字节码生成代理类的实例
proxyClass = 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());
//...
return proxyClass;
}
3.生成代理对象的字节码,用于返回代理对象的实例
public static byte[] generateProxyClass(final String var0, Class[] var1) {
ProxyGenerator var2 = new ProxyGenerator(var0, var1);
//动态生成字节码,
final byte[] var3 = var2.generateClassFile();
if (saveGeneratedFiles) { //如果是true的话会生成字节码到本地去
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
try {
FileOutputStream var1 = new FileOutputStream(ProxyGenerator.dotToSlash(var0) + ".class");
var1.write(var3);
var1.close();
return null;
} catch (IOException var2) {
throw new InternalError("I/O exception saving generated file: " + var2);
}
}
});
}
//返回代理类的字节码
return var3;
}
4.生成的代理类反编译出来的效果,答案都在里面
//有没有发现生成的代理类继承的是Procy类,实现了我们定义的接口PersonalTest
public final class $Proxy11 extends Proxy implements PersonalTest
{
private static Method m1;
private static Method m4;
private static Method m3;
private static Method m0;
private static Method m2;
public $Proxy11(InvocationHandler paramInvocationHandler)
{
//构造方法的入参就是InvocationHandler,记得上面的代码不???
super(paramInvocationHandler);
}
public final boolean equals(Object paramObject)
{
try
{
return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
//代理类里面实现了这个接口
public final void printString(String paramString)
{
try
{
//底层调用的是h(h就是InvocationHandler接口),
//又因为我们实现了这个接口,所以本质上是调用我们自定义InvocationHandler中的invoke方法
//参数解析 this --> 当年代理类自身
// m4 --> 可以从下面得知是printString这个方法
this.h.invoke(this, m4, new Object[] { paramString });
return;
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
public final void printString1(String paramString)
throws
{
try
{
this.h.invoke(this, m3, new Object[] { paramString });
return;
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
public final int hashCode()
throws
{
try
{
return ((Integer)this.h.invoke(this, m0, null)).intValue();
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
public final String toString()
throws
{
try
{
return (String)this.h.invoke(this, m2, null);
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
static
{
try
{
//在这边进行的静态初始化,都在这儿了,看见
m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
m4 = Class.forName("com.wayne.sam.PersonalTest").getMethod("printString", new Class[] { Class.forName("java.lang.String") });
m3 = Class.forName("com.wayne.sam.PersonalTest").getMethod("printString1", new Class[] { Class.forName("java.lang.String") });
m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
return;
}
catch (NoSuchMethodException localNoSuchMethodException)
{
throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
}
catch (ClassNotFoundException localClassNotFoundException)
{
throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
}
}
现在我觉得可以回答上面两个问题了:
1.为什么proxy在调用printString的时候会执行到invoke方法上去?也可以这么想,invoke是谁在调用?
-->invoke其实就是代理对象在调用,因为最终生成的$Proxy0实例强制转换成了PersonalTest,而我们调用PersonalTest的对代理对象proxy的printString方法时,会去调用$Proxy0内的invoke方法,进而调用Proxy中Invocation的invoke方法,从而最终调用到PersonalInvocation中的invoke方法。
2.InvocationHandler中invoke方法内的第一个参数proxy是什么?
-->代理对象本身,但是源码里并没有使用,估计是留着如果要使用代理对象的一些信息。
增加一个问题:为什么JDK动态代理只能代理实现了接口的类
-->有没有发现最终生成的$Proxy0这个代理类呢?它继承了Proxy,没错,Java只能允许单继承,而允许多实现接口,就是这么简单。换句话说,它没法再去继承另外一个类了。