Cglib和jdk动态代理
说到cglib技术,使用的就是增强类Enhancer,底层使用的ASM技术.
下面举例说明
共同的类,一个接口(JDK动态代理准备的,CGlib不需要接口),一个接口的实现类
public class Dao implements IDao{
public void select() {
System.out.println("select方法:);
insert();
}
public void insert() {
System.out.println("insert方法");
}
}
public interface IDao {
public void select();
public void insert();
}
cglib需要的类
public class DaoProxy implements MethodInterceptor {
//使用invoke方法,可以将对象传进来
private Object object;
// public DaoProxy(Object object) {
// this.object = object;
// }
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("DaoProxy begin intercept");
//invokeSuper方法调用目标类的方法
// proxy.invoke (object, args);
//这里涉及到incokeSuper和invoke的区别,简单理解这里的obj是代理后的对象,invoke传入代理对象,依然执行的是代理方法,还是当前所在的intercept方法,这样就是一个死循环
//invokeSuper调用的是被代理对象的真是方法,当调用select方法的时候,select又调用了insert方法,会继续被代理
// 所以使用invoke的是要把真实对象传进来,可以使用构造器入参的方式传进来,当调用方法的时候,就是传进来的对象调用select方法,当select 方法调用insert方法是,只是普通的调用
//还有就是obj是object,代理之后的类,是继承关系,所以obj放到invoke方法里,不会抛类型转换错误异常
proxy.invokeSuper(obj, args);
System.out.println("DaoProxy end intercept");
return obj;
}
}
public class Client {
public static void main(String[] args) {
//将代理类存到本地磁盘
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "d:\\");
//实例化增强器
Enhancer enhancer = new Enhancer();
//设置需要代理的目标类
enhancer.setSuperclass(Dao.class);
//设置拦截对象 回调的实现类,如果要在DaoProxy的intercept方法中使用invoke,可以new DaoProxy(new Dao())
//这里也看出来enhancer,增强类是不需要接口的,这是两者最大的区别
enhancer.setCallback(new DaoProxy());
//使用create 返回Object 生成代理类并返回实例
Dao dao = (Dao) enhancer.create();
//select优先级高 使用DaoProxy
dao.select();
}
}
在DaoProxy中我们也介绍了invoke和invokeSuper两个方法的区别,具体的可以看这篇文章,重点呢就是在执行方法的时候是执行的代理对象的方法,还是被代理对象的方法,
Invoke调用的是被代理对象的方法,那就是我们平时方法的简单调用,说到这里大家也就会明白 select 方法调用insert方法的时候,用invoke的时候,select调用insert方法,只是简单的方法之间的调用,不会再次走拦截器,invokeSuper执行,insert就会再次被拦截,这点需要注意
https://blog.csdn.net/z69183787/article/details/106878203/
这个方法里面介绍invoke方法的时候,把obj传进来的图片上,实际上没有传进obj对象,
怎么把真是对象方法传进来呢,可以用构造器的方式传进来.
我们在详细的说一下Enhancer
Enhancer的两个重要的参数
private CallbackFilter filter;
private Callback[] callbacks;
一个是过滤器的数据,一个过滤器的filter,这两个要结合使用,filter返回是数字,是callbacks的
下角标,决定执行某个方法的时候,使用哪个过滤器.
Enhancer执行create方法的时候调用createHelper,在调用validate方法,在validate方法中我们发现了这段代码
if (this.filter == null) {
if (this.callbackTypes.length > 1) {
throw new IllegalStateException("Multiple callback types possible but no filter specified");
}
this.filter = ALL_ZERO;
}
当我们没有执行filter的时候,使用的是默认的ALL_ZERO 看方法名可以知道一直返回0,
也就是一直调用第一个过滤器
点进ALL_ZERO方法,看到了accept(Method method)方法的返回值 return 0
当然我们可以自定义,自己的filter,在accept的时候,利用method得到方法名称,根据方法名称来决定调用哪个过滤器,这里提前说一下,spring用cglib代理的时候,就通过方法上是否标注了@Bean注解,决定方法调用是否需要被代理,这个一会会说到.也会说到aop代理@Configuration注解的类,得到的代理类是什么样的
我们在来说说jdk的动态代理,接口和接口的实现来是公用的
public class DaoJdkProxy implements InvocationHandler {
private Object targetObject;
public DaoJdkProxy(Object target) {
this.targetObject=target;
}
public DaoJdkProxy() {
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("代理开始");
return targetObject==null?null: method.invoke(targetObject, args);
}
}
public static void main(String[] args) {
System.setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles","true");
IDao dao = (IDao) Proxy.newProxyInstance(
Dao.class.getClassLoader(), // 代理类加载器
Dao.class.getInterfaces(), // 代理的接口
new DaoJdkProxy(new Dao()));
dao.select();
}
}
执行的时候发现,select调用insert的时候,也只是普通方法之间的调用,通过enhancer的介绍,我们会明白为什么,因为targetObject就是个普通的对象.
那我们再关注一下spring中cglib代理是什么样的,
我们都知道,下面这段代码呢,因为A类标注了@ Configuration ,A是完全配置类,默认会被cglib增强,getC只会执行一次,流程大概是,getB调用getC时,会将C放到容器中,beanName是getC,当@Bean执行getC的时候,发现容器中已经存在了,则不会再执行一边getC方法.这样也是为了保证容器中的B中的C和IOC容器中是同一个对象
@Configuration
public
class A {
@Bean
public B getB() {
B b = new B ();
C c = getC ();
b.setC (c);
return b;
}
@Bean
public C getC() {
return new C ();
}
}
那根据上面介绍的Enhancer,那我们是不是猜想,A依然标注了@Configuration注解,getB和getC没有标注@Bean,从容器中获取A,a调用getB方法,getB中调用getC方法,getC是不是也走了代理呢,其实不是的,这里就用到了我们上面说到的filter,spring自定义了filter,判断方法是否含有@Bean注解
首先看一下,标注了@Configuration在哪被增强了
ConfigurationClassPostProcessor#enhanceConfigurationClasses方法
Class<?> enhancedClass = enhancer.enhance(configClass, this.beanClassLoader);
不过这里生成不是代理对象,而是代理对象对应的类,(比如User.class)把这个类放到bean定义的beanClass里,在后续的doCreateBean的时候,真正创建代理类(比如new User()),这里介绍这么多,更多的大家可以关注spring源码课程
那我们关心这个enhancer的filter和Callback[]
ConfigurationClassEnhancer类的CALLBACKS成员变量,这就是标注了@Configuration注解之后的过滤器数组
private static final Callback[] CALLBACKS = new Callback[] {
new BeanMethodInterceptor(),
new BeanFactoryAwareMethodInterceptor(),
NoOp.INSTANCE
};
这就是filter
private static final ConditionalCallbackFilter CALLBACK_FILTER = new ConditionalCallbackFilter(CALLBACKS);
继续查看accept方法
@Override
public int accept(Method method) {
for (int i = 0; i < this.callbacks.length; i++) {
Callback callback = this.callbacks[i];
//遍历上面说的3个过滤器数组,如果类型不是ConditionalCallback调用过滤器数组中的
//调用过滤器的isMatch方法,哪个过滤器返回ture就决定使用这个过滤器
if (!(callback instanceof ConditionalCallback) || ((ConditionalCallback) callback).isMatch(method)) {
return i;
}
}
throw new IllegalStateException("No callback available for method " + method.getName());
}
BeanMethodInterceptor中会判断是否标注了@Bean注解
@Override
public boolean isMatch(Method candidateMethod) {
return (candidateMethod.getDeclaringClass() != Object.class &&
!BeanFactoryAwareMethodInterceptor.isSetBeanFactory(candidateMethod) &&
BeanAnnotationHelper.isBeanAnnotated(candidateMethod));
}
BeanFactoryAwareMethodInterceptor的isMatch主要是判断关于FacrotyBean的一些条件,大家可以点进去看一下.
如果这两个都不满足的话,就调用最后一个NoOp.INSTANCE什么也不做,所以a调用getB 调用getC其实就是普通方法的调用.
关于BeanMethodInterceptor的intercept方法参考下面这个文章,什么时候执行原方法(普通调用),什么时候从容器中获取对应的bean
https://www.e-learn.cn/topic/3451142
网友评论