为什么想到这个?来源于http://blog.csdn.net/y943623901/article/details/50847334
这篇文章关于JDK动态代理的演示让我有点迷惑,又重新看了下动态代理
1.首先JDK动态代理是针对接口的,
用提供的被代理对象获得该对象所有实现了的接口,重新生成的一个类
针对这个,像spring注解事务应该放在接口的方法上,这个代理类实现接口方法时是可以获得接口上方法的注释的
2.仔细想,在invoke方法里执行method.invoke(target, args),即使里面有嵌套方法,也是通过target对象调用的,而不是代理对象,所以嵌套方法上的事务注解是无效的
3.为什么CGLIB里的嵌套方法有效
// CGLIB代理类具体实现
public class HelloConcrete$$EnhancerByCGLIB$$e3734e52 extends HelloConcrete
implements Factory
{
...
private MethodInterceptor CGLIB$CALLBACK_0; // ~~
...
public final String sayHello(String paramString)
{
...
MethodInterceptor tmp17_14 = CGLIB$CALLBACK_0;
if (tmp17_14 != null) {
// 将请求转发给MethodInterceptor.intercept()方法。
return (String)tmp17_14.intercept(this,
CGLIB$sayHello$0$Method,
new Object[] { paramString },
CGLIB$sayHello$0$Proxy);
}
return super.sayHello(paramString);
}
...
public class BookFacadeCglib implements MethodInterceptor {
private Object target;
/**
* 创建代理对象
*
* @param target
* @return
*/
public Object getInstance(Object target) {
this.target = target;
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(this.target.getClass());
// 回调方法
enhancer.setCallback(this);
// 创建代理对象
return enhancer.create();
}
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
Annotation annotation = obj.getClass()
.getSuperclass()
.getDeclaredMethod(method.getName(), method.getParameterTypes())
.getAnnotation(Transaction.class);
System.out.println("invoke");
if (annotation == null) {
proxy.invoke(target, args);
} else {
System.out.println("事务开始");
proxy.invokeSuper(obj, args); //这里明显去调用object父类也就是被代理对象的方法
System.out.println("事务结束");
}
return null;
}
}
可以看到,CGLIB生成的类为原始类的子类,第一次经过的方法会打印一次invoke,然后调用原对象方法,如果这个方法里又嵌套了方法,这时,子类也复写了这个方法,所以仍然通过代理对象调用,所以会再走一次invoke4.我们在自定义连接池,创建时动态代理要求用这种new Class[]{Connection.class} 方式获取接口集合,原因在于,数据库驱动返回的connection对象继承了某个类,而这个类或这个类的父类之前实现的接口通过connection对象的getInterfaces()是获取不到的,可以测试下,那么问题来到了,如果这个Class数组是个空数组,创建的代理对象是怎样的
// 对con创建其代理对象
Connection proxy = (Connection) Proxy.newProxyInstance(con.getClass().getClassLoader(), // 类加载器
//con.getClass().getInterfaces(),
new Class[]{Connection.class}, // 目标对象实现的接口
new InvocationHandler() {
for(Class c:con.getClass().getInterfaces())
{
System.out.println(c);
}
System.out.println(con.getClass().getInterfaces().length);// 这边输出是0
})
4.具体不做分析,主要追究代理对象生成的源码,一般情况下,interface[]length不为0,生成的代理类如下
而,像connection这种集合为0的情况,生成的类,只会$Proxy0 extends Proxy ,不实现任何接口,这样自然不能像接口转型,会报cast异常
main方法中加入System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true"),这样就会把生成的代理类Class文件保存在本地磁盘上,然后再反编译可以得到代理类的源码
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D:\\class"); --该设置用于输出cglib动态代理产生的类
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true"); --该设置用于输出jdk动态代理产生的类