-----菜鸟一只,看了许多文章,自己综合的,所以会看到一样的.........
动态代理:
就是在程序运行的过程中,动态的生成一个类(代理类),这个类要代理 目标业务对象(被代理类,也称为委托类) ,并且可以动态生成代理类的对象.
作用:
主要用来做方法的增强,让你可以在不修改源码的情况下,增强一些方法
一.基于接口的动态代理(jdk官方的proxy)---被代理类需要实现接口
(1).相关类:
调用处理器:invocationHandler ---方法:invoke()
动态代理类: proxy ---方法:newProxyInstance()
(2).方法参数:
1.newProxyInstance():
第一个参数和第二个参数都是由被代理类获得
xxx.getClass().getClassLoader();
xxx.getInterfaces();
2.invoke(Object proxy,Method method,Object[] args):
proxy:为"代理类"对象
method:为"被代理类"的方法的Method对象
args:method方法的参数
(3). 问题:
当代理对象调用被代理对象的方法时,为什么其会自动的跳转到代理对象关联的 InvocationHandler接 口对象的invoke方法来进行调用?
代码:
Proxy.newProxyInstance(loader,interfaces,new InvocationHandler(){
public Object invoke(Object proxy, Method method, Object[] args){
}});
xxx.xxx();
原因:
因为JDK生成的最终真正的代理类(starProxy),它继承自Proxy并实现了我们定义的Star接口,实 现Star接口方法的内部,通过反射调用了handler的invoke方法.
代码:
public final String Say(String paramString){
try {
return (String)this.h.invoke(this, m4, new Object[] { paramString });
}
(4).代码过程
解析newProxyInstance()源码:
1.IvocationHandler handler = new MyInvocationHandler(...);
// 通过实现 InvocationHandler 接口创建自己的调用处理器;
2.Class<?> cl = getProxyClass0(loader, intfs);
//通过为 Proxy 类指定 ClassLoader 对象和一组 interface来创建 动态代理 "类"
3.final Constructor<?> cons = cl.getConstructor(constructorParams);
//通过反射机制获得动态代理类的构造函数,其唯一参数类型是调用处理器接口类型;
4.return cons.newInstance(new Object[]{h});
//通过构造函数创建动态代理类 "实例" ,构造时调用处理器对象作为参数被传入。
二.基于子类的动态代理(cglib的enhancer)---被代理类不能被final修饰
(0).原理:
利用了CGLIB,在运行时期生成被代理对象的子类,来作为代理对象,同时重写了被代理对象的方法.
(1).相关类:
拦截器:MethodInterceptor---方法intercept()
字节码增强器:Enhancer(对想要处理的类进行扩展)---方法create()
(2).方法参数:
1. intercept (Object obj,method m,Object[] args,MethodProxy proxy)
--obj:为"代理类"对象
--m:为"被代理类"的方法的Method对象
--args:method方法的参数
--proxy:"被代理对象方法" 的MethodProxy对象(生成的代理类对方法的代理引用--这句不懂)
2. proxy.invokeSuper(obj,args):调用代理类实例上的proxy方法 的 父类方法(即被代理类中对应 的方法)
或
m.invoke(new Target(),args) //不建议使用,反射效率低----(不知道这个方法对不对,老师是这么 讲的)
(3).从其他文章看到的部分源码解析:
1. 代理类会为每个委托方法都生成两个方法,以xxx方法为例:(xxx方法为被代理类的方法)
一个是重写的xxx方法;一个是CGLIB$xxx$0方法,该方法直接调用委托类的add方法.
2. 当执行代理对象的xxx方法时,会先判断是否存在实现了MethodInterceptor接口的对象 cglib$CALLBACK_0, 如果存在,则调用MethodInterceptor对象的intercept方法(就是自己实现的 intercept方法).----这里就解释了为何在执行代理对象的xxx方法时,intercept()会被调用.
3. Enhancer.create(Class type, Callback callback)的源码:
public static Object create(Class type, Callback callback) {
Enhancer e = new Enhancer();
e.setSuperclass(type); //首先将被代理类设置成父类
e.setCallback(callback);//然后设置拦截器MyInterceptor(实现MethodInterceptor)
return e.create();//最后执行enhancer.create()动态生成一个代理类
}
4. MethodProxy proxy
每个被代理的方法都对应一个MethodProxy对象,methodProxy.invokeSuper方法最终调用委 托类的xxx方法
(1). invokeSuper()源码: 最终调用的是委托类的xxx()方法,下面说
public Object invokeSuper(Object obj, Object[] args) throws Throwable {
try {
init(); //作用看下面的源码
FastClassInfo fci = fastClassInfo;
return fci.f2.invoke(fci.i2, obj, args);
} catch (InvocationTargetException e) {
throw e.getTargetException();
}
}
(2).init()源码:实质上是生成一个FastClassInfo//作用看下面的源码
private void init() {
if (fastClassInfo == null){
synchronized (initLock){
if (fastClassInfo == null){
CreateInfo ci = createInfo;
FastClassInfo fci = new FastClassInfo();
fci.f1 = helper(ci, ci.c1);
fci.f2 = helper(ci, ci.c2);
fci.i1 = fci.f1.getIndex(sig1);
fci.i2 = fci.f2.getIndex(sig2);
fastClassInfo = fci;
createInfo = null;
}
}
}
}
(3).fastClassInfo源码:里面存放了两个FastClass f1和f2,还有两个方法索引的值i1和i2 (f1和f2分别为委托类对象和代理类对象;i1和i2分别为xxx()方法和CGLIB$xxx$0方法在对象中索 引位置(代理类会为每个委托方法都生成的两个方法)))
private static class FastClassInfo {
FastClass f1;
FastClass f2;
int i1;
int i2;
}
5.FastClass介绍:
fastClass机制就是对一个类的方法建立索引,通过索引来直接调用相应的方法
下面通过一个例子了解一下FastClass的实现机制
目标类:
class Target {
public void f(){
System.out.println("f method");
}
public void g(){
System.out.println("g method");
}
}
Fast类:
class FastTest {
public int getIndex(String signature){
switch(signature.hashCode()){
case xxxxx:
return 1;
case xxxxx:
return 2;
}
return -1;
}
public Object invoke(int index, Object o, Object[] ol){
Target t = (Target) o;
switch(index){
case 1:
t.f();
return null;
case 2:
t.g();
return null;
}
return null;
}
}
测试类:
public static void main(String[] args){
Target target= new Target();
FastTest ft = new FastTest();
int index = ft.getIndex("f()V");
fc.invoke(index, target, null);//invokeSuper()源码中
}
在FastTest中有两个方法,getIndex中对Target类的每个方法根据hash建立索引,invoke根据指 定的索引,直接调用目标方法-----------------所以当调用methodProxy.invokeSuper方法时,实际上 是调用代理类的CGLIB$xxx$0方法,CGLIB$xxx$0直接调用了委托类的xxx方法.
6. proxy.invokeSuper(obj,args)是否可以改为proxy.invoke(obj,args)
不可以,原因是会栈溢出报错,OOM问题
7. jdk采用反射机制调用委托类的方法(反射的效率低),cglib采用类似索引的方式直接调用委托类方法