CGLIB 详解
欢迎光临我的博客[http://poetize.cn],前端使用Vue2,聊天室使用Vue3,后台使用Spring Boot
依赖
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.2.12</version>
</dependency>
简介
CGLIB(Code Generator Library)是一个强大的、高性能的代码生成库。
其被广泛应用于AOP框架(Spring)中,用以提供方法拦截操作。
CGLIB代理主要通过对字节码的操作,以控制对象的访问。
CGLIB底层使用了ASM(一个短小精悍的字节码操作框架)来操作字节码生成新的类。
CGLIB相比于JDK动态代理更加强大:
JDK动态代理虽然简单易用,但只能对接口进行代理。
如果要代理的类为一个普通类,没有接口,那么Java动态代理就没法使用了。
Java动态代理使用Java原生的反射API进行操作(运行期),在生成类上比较高效。
CGLIB使用ASM框架直接对字节码进行操作(编译期),在类的执行过程中比较高效
Enhancer 介绍
Enhancer:
Enhancer既能够代理普通的class,也能够代理接口。
Enhancer创建一个被代理对象的子类并且拦截所有的方法调用(包括从Object中继承的toString和hashCode方法)。
Enhancer不能够拦截final类与方法。
Enhancer.setSuperclass(Class superclass);
用来设置父类型
Enhancer.setCallback(Callback callback);
Enhancer.setCallback(new InvocationHandler(){});
Enhancer.setCallback(new MethodInterceptor(){});
增强
Enhancer.create(Class type, Callback callback);
Enhancer.create(Class superclass, Class[] interfaces, Callback callback);
Enhancer.create(Class[] argumentTypes, Object[] arguments);
方法是用来创建代理对象,其提供了很多不同参数的方法用来匹配被增强类的不同构造方法。
Callback
Callback是一个空的接口,在Cglib中它的实现类有以下几种:
MethodInterceptor
NoOp
LazyLoader
Dispatcher
InvocationHandler
FixedValue
MethodInterceptor:
它可以实现类似于AOP编程中的环绕增强(around-advice)。
它只有一个方法:
public Object intercept(Object obj, java.lang.reflect.Method method, Object[] args, MethodProxy proxy)
代理类的所有方法调用都会转而执行这个接口中的intercept方法而不是原方法。
如果需要在intercept方法中执行原方法可以使用参数method进行反射调用,
或者使用参数proxy 一 proxy.invokeSuper(obj, args);
后者会快一些(反射调用比正常的方法调用的速度慢很多)。
MethodInterceptor允许我们完全控制被拦截的方法,并且提供了手段对原方法进行调用,
因为 MethodInterceptor的效率不高,它需要产生不同类型的字节码,
并且需要生成一些运行时对象(InvocationHandler就不需要),所以Cglib提供了其它的接口供我们选择。
InvocationHandler:
它的使用方式和MethodInterceptor差不多。
需要注意的一点是,所有对invoke()方法的参数proxy对象的方法调用都会被委托给同一个InvocationHandler,
所以可能会导致无限循环。
NoOp:
这个接口只是简单地把方法调用委托给了被代理类的原方法,
不做任何其它的操作。
LazyLoader,它也提供了一个方法:
Object loadObject()
loadObject()方法会在第一次被代理类的方法调用时触发,它返回一个代理类的对象,
这个对象会被存储起来然后负责所有被代理类方法的调用,一种lazy模式。
如果被代理类或者代理类的对象的创建比较麻烦,而且不确定它是否会被使用,那么可以选择使用这种lazy模式来延迟生成代理。
Dispatcher:
Dispatcher和LazyLoader接口相同,也是提供了loadObject()方法。
不过它们之间不同的地方在于,
Dispatcher的loadObject()方法在每次发生对原方法的调用时都会被调用并返回一个代理对象来调用原方法。
也就是说Dispatcher的loadObject()方法返回的对象并不会被存储起来,
可以类比成Spring中的Prototype类型,而LazyLoader则是lazy模式的Singleton。
ImmutableBean 不可变Bean
ImmutableBean允许创建一个原来对象的包装类,
这个包装类是不可变的,任何改变底层对象的包装类操作都会抛出IllegalStateException。
SampleBean bean = new SampleBean();
bean.setValue("Hello world");
SampleBean immutableBean = (SampleBean) ImmutableBean.create(bean); //创建不可变类
immutableBean.setValue("Hello cglib"); //直接修改将throw exception
bean.setValue("Hello world, again"); //可以通过底层对象来进行修改
BeanGenerator
运行时动态的创建一个bean
BeanGenerator beanGenerator = new BeanGenerator();
beanGenerator.addProperty("value",String.class);
Object myBean = beanGenerator.create();
Method setter = myBean.getClass().getMethod("setValue",String.class);
setter.invoke(myBean,"Hello cglib");
Method getter = myBean.getClass().getMethod("getValue");
BeanCopier
从一个bean复制到另一个bean中,还提供了一个转换器,用来在转换的时候对bean的属性进行操作。
BeanCopier.create(Class source, Class target, boolean useConverter)
BeanCopier copier = BeanCopier.create(Bean1.class, Bean2.class, false); //设置为true,则使用converter
Bean1 bean1 = new Bean1();
bean1.setValue("Hello cglib");
Bean2 bean2 = new Bean2();
copier.copy(bean1, bean2, null); //设置为true,则传入converter,指明怎么进行转换
BeanMap
BeanMap类实现了Java Map,将一个bean对象中的所有属性转换为一个<String,Obejct>的Java Map
BeanMap map = BeanMap.create(bean); //将对象转为BeanMap
public abstract class BeanMap implements Map {
protected Object bean;
public Object get(Object key) {
return this.get(this.bean, key);
}
public Object put(Object key, Object value) {
return this.put(this.bean, key, value);
}
public void setBean(Object bean) {
this.bean = bean;
}
public Object getBean() {
return this.bean;
}
}
参考
https://blog.csdn.net/danchu/article/details/70238002
https://blog.csdn.net/Q_AN1314/article/details/79724334