动态代理
代理模式的解释:为其他对象提供一种代理以控制对这个对象的访问,增强一个类中的某个方法,对程序进行扩展。
比如,现在存在一个UserService类:
public class UserService {
public void test() {
System.out.println("test...");
}
}
此时,我们new一个UserService对象,然后执行test()方法,结果是显而易见的。
如果我们现在想在不修改UserService类的源码前提下,给test()增加额外逻辑,那么就可以使用动态代理机制来创建UserService对象了,比如:
// 被代理的对象target
UserService target = new UserService();
// 通过cglib技术
Enhancer enhancer = new Enhancer();
// 设置代理哪个类UserService
enhancer.setSuperclass(UserService.class);
// 设置方法拦截器 定义额外逻辑,也就是代理逻辑
enhancer.setCallbacks(new Callback[]{new MethodInterceptor() {
@Override
// 当前代理对象正在执行的方法method,入参objects 这里是空
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("before...");
Object result = methodProxy.invoke(target, objects);
method.invoke(target,)
System.out.println("after...");
return result;
}
}});
// 动态代理所创建出来的UserService对象
UserService userService = (UserService) enhancer.create();
// 执行这个userService的test方法时,就会额外会执行一些其他逻辑
userService.test();
得到的都是UserService对象,但是执行test()方法时的效果却不一样了,这就是代理所带来的效果。
上面是通过cglib来实现的代理对象的创建,是基于父子类的,被代理类(UserService)是父类,代理类是子类,代理对象就是代理类的实例对象,代理类是由cglib创建的,对于程序员来说不用关心。
CGLIB源码分析
阅读代理
这个对应的Method对象就是UserService这个类的test方法
cglib在58行会拿到那个方法拦截器然后在65行执行方法拦截器的intercept方法。
一开始肯定拿到是个空,所以在60行才会获得真正的方法拦截器,传入的参数是代理对象
传进来的就是UserService的代理对象,然后强转成代理对象,一开始进去if就把false改为true,就不再进来了
ThreadLocal
从当前线程ThreadLocal拿到CALLBACKWIntercept过滤器,然后给属性赋值
后续就可以使用,执行他的intercept方法
在这个方法给ThreadLocal赋值的,那这个方法在哪调用的?
在各种newInstance里面
=============================================================================
newInstance()
newInstance()这个方法在CGLIB提供的Factory接口提供的。相当于某个代理对象UserService实现了Factory工厂
相当于可以拿这个Factory生成另一个代理对象。【用得少】
=============================================================================
其实ThreadLocal都在这个Ehancer的create()方法里设置的
Enhancer是个增强器,目前只是设置了要去产生代理对象的参数。
利用参数去创建代理对象
一开始把传进去的参数封装成对象key【包含了要代理的参数 等下要进行缓存 存到个map里去的】
这个key对应的value就是最后所产生的代理类【不是代理对象】
根据设置的代理参数,去缓存一个代理类【不可能每次调用都去产生一个代理类 因为参数不会变化】
所以后续create就直接从缓存拿到代理类。
紧接着调用create方法,最终返回结果就是代理对象了
这里传进去的obj就是我们刚才所生成的代理类,
然后传进来的代理类就会执行firstInstance()方法
根据代理类创建我们的代理对象
传进的参数是我们刚才new Enhancer()里面设置的Callback和代理类
这个是我们刚才找的Callback方法
一旦执行这个方法,就会设置进ThreadLocal里面【代理类生成后,代理对象生成之前】
小总结:
传入代理类和你在Enhancer里面设置的方法拦截器传进去,
然后把刚才那个设置ThreadLocal的方法名字,和代理类传进去,找到Method对象,
然后用反射,因为是static,所以obj是null,把方法拦截器座位参数callbacks传进去
最后就成功设置到ThreadLocal这个属性上面去
有值之后,接下来就会产生代理对象,代理对象执行test方法
就会拿到刚才设置的Callback,然后执行Callback里的方法拦截器方法,从而执行到我们所设置的intercept方法里面去
代理类怎么产生的?
这里的obj就是代理类
默认会去找缓存,如果设置不开缓存
那每次都会走gennerate方法去生成代理类。
设置了缓存,直接从缓存里get
拿到了就直接返回。
没有拿到就走createEntry方法。最终还是会调用到生成代理类的那个generate方法
apply()方法
来看看cglib是怎么生成代理类的?
Generate()方法
如果有设置superClass就设置代理类前缀
底层用ASM技术,生成具体的类的名字,构造方法,属性啊 ,类里面各种各样的方法啊【直接去生成字节码 这里其实是经过idea反编译之后 这里其实是生产字节码的指令】
大总结
①首先通过Ehancer对象设置要代理的类【supperClass】,拦截器方法【methodInterceptor】,然后调用create方法,根据这些参数生成一个Object类型的key【包含了要代理的参数 等下要进行缓存 存到个map里去的】这个key对应的value就是最后所产生的代理类【不是代理对象】根据设置的代理参数,去缓存一个代理类【不可能每次调用都去产生一个代理类 因为参数不会变化】然后调用create()方法。
代理类哪里来的?
默认会去找缓存,如果设置不开缓存,那每次都会走gennerate方法去生成代理类。如果有设置superClass就设置代理类前缀,底层是用ASM技术,会生成具体的类的名字,构造方法,属性啊 ,类里面各种各样的方法啊【直接去生成字节码 这里其实是经过idea反编译之后 这里其实是生产字节码的指令】
②create()方法里面,会传入代理类【从缓存取或者gennerate】并执行firstInstance()方法。然后会调用代理类的static方法去setThreadLocal【3个参数 new Enhancer()里面设置的方法拦截器和代理类和设置代理类的ThreadLocal的方法名字】,然后用反射,最后就成功把方法拦截器设置到ThreadLocal这个属性上面去。然后根据代理类创建我们的代理对象
最后执行代理对象的test()方法然后就会拿到刚才ThreadLocal里设置的Callback,然后执行Callback里的方法拦截器方法,从而执行到我们所设置的intercept方法里面去,实现对方法的增强
=============
MethodProxy
methodProxy代理的就是你当前正在执行的方法method
代理类里其实是有2个test的方法的,执行test就会执行增强逻辑,
执行CGLIB$test$4就不会执行增强逻辑【直接执行UserService的test方法】。
method其实代理了两个方法【名字是固定的,test方法和CGLIB$test$4方法】
invoke他就只会执行test()方法,invokeSuper就会执行CGLIB$test$4()方法,他不关心你传的对象里有没有这个方法
第二个会报错,因为被代理的对象里头没有CGLIB$test$4这个方法。
但是实际上报错是报的不能转换
换成对象o【(即代理类的实例),这个类是目标类的子类】就能正常执行不报错,相当于执行super.test()只会执行一次。
这个会报错,25行又会执行增强的test方法,里面又会执行方法拦截器,相当于递归了。
MethodProxy对象什么时候创建的?
对象在这里传进去的
代理类生成的时候里面的静态代码块构造出来的
test方法和增强的test方法返回值都是void,所以传一个就好了
构造MethodProxy对象,
用方法名字和返回值生成2个方法签名对象赋值给MethodProxy对象,
createInfo存放代理类和被代理类的信息,MethodProxy只关心test方法,其他方法不关心的。
MethodProxy 对象具体关心的是被代理对象的单个方法。当你创建一个CGLIB代理时,对于被代理对象的每个方法,CGLIB会生成一个相应的MethodProxy实例。
在代理对象调用test()方法时候就会用到MethodProxy,就能用它的invoke方法和invokeSuper方法
传进来后用它的invoke和invokeSuper方法
invoke()方法实现
invokeSuper()方法
两个方法很像,区别一个是f1,i1
一个是f2,i2
就是对应的一个是invoke一个是invokeSuper
fastClassInfo这个属性一开始是空的
在init()方法赋值
Init()方法详解
用了MethodProxy对象后就会多2个类出来,这2个类都会继承FastClass类
都是FastClass的代理类
- 被代理类的FastClass:这个FastClass是针对原始被代理类生成的。它包含了被代理类所有方法的索引,并能够快速地调用这些方法。
- 代理类的FastClass:CGLIB还会为代理类自身生成一个FastClass。由于代理类重写了被代理类的所有方法,在这些重写的方法中通常会调用MethodInterceptor,代理类的FastClass同样包含了这些重写方法的索引,并可以快速调用它们。
如果用反射的话,首先得生成test方法和CGLIB$test$4方法的Method对象再去通过反射调用,效率比较低。
那FastClass如何加速?
FastClass该类会针对UserService的各个方法去返回对应的下标。传入方法签名,拿到下标
helper方法会生成对应的类
然后调用getIndex方法拿到test方法和CGLIB$test$4方法签名的下标索引是什么
找到下标后就设置到fastClassInfo属性里去
然后调用MethodProxy对象的invoke方法时候,就会传入test()方法或者CGLIB$test$4()方法对应的下标。
然后直接调用的你传入对象的test()方法或者CGLIB$test$4()方法,所以会更快
但是invokeSuper方法在执行CGLIB$test$4()方法,也就是会执行你想要代理的那个对象的原本的test方法前会强制转换
强制转换成UserService的代理类【里面才有CGLIB$test$4()方法】
所以这里才会报错转换失败,因为target是你自己new的需要被代理的那个对象,不能把UserService转成UserService的代理类型 ,因为UserService是父类。你只能把UserService的代理类型 转成UserService。
MethodProxy总结
代理类生成的时候里面的静态代码块构造出来的MethodProxy对象,当你用MethodProxy对象调用invoke或者invokeSuper方法的时候,会传入代理对象o或者被代理对象【自己new出来的需要被代理的那个类 比如UserService】和参数,然后调用init()方法,里面的helper方法会生成
2个FastClass的代理类【继承了FastClass的两个类 代理类的FastClass和被代理类的FastClass】,然后传入方法签名【静态代码块的create方法里就赋值好的方法前面 比如test方法】拿到索引下标。然后调用MethodProxy对象的invoke方法时候,就会传入test()方法或者CGLIB$test$4()方法对应的下标,就会去执行下标对应的方法,如果是执行invokeSuper还会强制转换成xxx的代理类型,如果你传的是xxx【父类】强转成子类肯定报错。底层不是用的反射,所以更快叫FastClass
JDK动态代理源码分析
除开cglib技术,jdk本身也提供了一种创建代理对象的动态代理机制,但是它只能代理接口,也就是UserService得先有一个接口才能利用jdk动态代理机制来生成一个代理对象,比如:
public interface UserInterface {
public void test();
}
public class UserService implements UserInterface {
public void test() {
System.out.println("test...");
}
}
利用JDK动态代理来生成一个代理对象:
UserService target = new UserService();
// UserInterface接口的代理对象 传入类加载器 你要代理的接口 new一个InvocationHandler 里面写你要增强的方法
Object proxy = Proxy.newProxyInstance(UserService.class.getClassLoader(), new Class[]{UserInterface.class}, new InvocationHandler() {
@Override
// proxy代理对象,method当前执行的方法,args参数
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("before...");
Object result = method.invoke(target, args);
System.out.println("after...");
return result;
}
});
UserInterface userService = (UserInterface) proxy;
userService.test();
如果你把new Class[]{UserInterface.class},替换成new Class[]{UserService.class},允许代码会直接报错:
Exception in thread "main" java.lang.IllegalArgumentException: com.zhouyu.service.UserService is not an interface
表示一定要是个接口。
由于这个限制,所以产生的代理对象的类型是UserInterface,而不是UserService,这是需要注意的。
如何根据接口构造代理对象的?
生成代理类cl
拿到代理类的构造器和Handler对象,传递给cons的构造方法。
把你自己写的Handler传给代理类的构造方法去生成代理对象。
代理类如何产生?
从缓存里拿的
key就是要代理的接口
value就是去生成代理类,作为value存起来
key就是ClassLoader,Parameter就是接口
这个key就是最终缓存的key,根据接口生产的subKey
根据接口个数返回key名字
每个ClassLoader有个单独的小的map去缓存某个我这个ClassLoader里面 某个接口所对应的代理类
传入ClassLoader,接口,key,缓存的map都传进去
根据ClassLoader和接口传给这个方法生成代理类最后装进缓存map中。
apply()生产代理类
遍历每个接口,拿到方法,然后包信息啊各种信息拿到后
去生成具体的class文件
会继承Proxy类实现你传进去的那个接口
然后调用代理类的构造方法,传进你写的Handler那个类
调用的是super()也就是JDK本身提供的类,赋值给h属性
然后调用h属性的invoke方法,也就是自己写的那个Handler的invoke方法,把代理对象自己传进去,m3就是接口的test方法的Method对象,test方法没有参数,所以传的null。
JDK动态代理源码总结
ASM技术
可以直接操作字节码,用的字节码指令
javasist这个工具比ASM更好用【Dubbo】