最近在自己做一个网关的项目,平时做一些业务开发,对代理模式的认知不够深刻,在此对cglib的代理用法做一些总结,方便项目的进行
CGLIB是一个功能强大,高性能的代码生成包。它为没有实现接口的类提供代理,为JDK的动态代理提供了很好的补充。通常可以使用Java的动态代理创建代理,但当要代理的类没有实现接口或者为了更好的性能,CGLIB是一个好的选择。
CGLIB原理:动态生成一个要代理类的子类,子类重写要代理的类的所有不是final的方法。在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑。它比使用java反射的JDK动态代理要快。'
如果没有代理模式,代码就会缺少灵魂 如果没有代理模式,可能就看不到Spring中的AOP切面编程了,看不到mybatis这样的ORM框架了,各种基于代理实现的各种技术中间件
CGLIB缺点:对于final方法,无法进行代理。
1. 方法拦截MethodInterceptor
引入依赖
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
/**
*
* 被代理的类,不需要实现接口,这也是相比于InvocatinHandler的优势
* @Author: dingyawu
* @Description: TODO
* @Date: Created in 21:38 2023/2/11
*/
public class ConcreteClassNoInterface {
public String getConcreteMethodA(String str){
System.out.println("ConcreteMethod A ... "+str);
return str;
}
public int getConcreteMethodB(int n){
System.out.println("ConcreteMethod B ... "+n);
return n+10;
}
}
/**
*
* 拦截器, CGLib会回调MethodInterceptor接口方法拦截,来实现你自己的代理逻辑
* @Author: dingyawu
* @Description: TODO
* @Date: Created in 21:39 2023/2/11
*/
public class ConcreteClassInterceptor implements MethodInterceptor {
/**
* Object为由CGLib动态生成的代理类实例
* Method为上文中实体类所调用的被代理的方法引用
* Object[]为参数值列表
* MethodProxy为生成的代理类对方法的代理引用
*/
public Object intercept(Object obj, Method method, Object[] arg, MethodProxy proxy) throws Throwable {
System.out.println("Before:"+method);
Object object=proxy.invokeSuper(obj, arg);
System.out.println("After:"+method);
return object;
}
@Test
public void test(){
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(ConcreteClassNoInterface.class);
enhancer.setCallback(new ConcreteClassInterceptor());
ConcreteClassNoInterface ccni=(ConcreteClassNoInterface)enhancer.create();
ccni.getConcreteMethodA("shensy");
ccni.getConcreteMethodB(0);
}
}
2. 接口生成器InterfaceMaker
/**
* InterfaceMaker会动态生成一个接口,该接口包含指定类定义的所有方法。
* 此处让Object生成的代理类实现了由InterfaceMaker生成的接口,但是由于Object类并没有覆写其中的方法,
* 因此,每当对生成接口内方法进行MethodInterceptor方法拦截时,都返回一个字符串,并在最后打印出来。
* @Author: dingyawu
* @Description: TODO
* @Date: Created in 21:31 2023/2/11
*/
public class CreateTest {
@Test
public void test() throws Exception {
InterfaceMaker im=new InterfaceMaker();
im.add(ConcreteClassNoInterface.class);
Class interfaceOjb=im.create();
System.out.println(interfaceOjb.isInterface());
System.out.println(interfaceOjb.getName());
Method[] methods = interfaceOjb.getMethods();
for(Method method:methods){
System.out.println(method.getName());
}
Object obj = Enhancer.create(Object.class, new Class[]{ interfaceOjb },
new MethodInterceptor() {
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
return "intercept!";
}
});
Method method = obj.getClass().getMethod("getConcreteMethodA", new Class[]{String.class});
System.out.println(method.invoke(obj, new Object[]{"12345"}));
}
}
3. 设置多个callback,但生不生效由ConcreteClassCallbackFilter决定索引
public class ConcreteClassNoInterface1 {
public String getConcreteMethodA(String str){
System.out.println("ConcreteMethod A ... "+str);
return str;
}
public int getConcreteMethodB(int n){
System.out.println("ConcreteMethod B ... "+n);
return n+10;
}
public int getConcreteMethodFixedValue(int n){
System.out.println("getConcreteMethodFixedValue..."+n);
return n+10;
}
}
/**
* 回调过滤器CallbackFilter
* 在CGLib回调时可以设置对不同方法执行不同的回调逻辑,或者根本不执行回调。
* 其中return值为被代理类的各个方法在回调数组Callback[]中的位置索引
* @Author: dingyawu
* @Description: TODO
* @Date: Created in 22:09 2023/2/11
*/
public class ConcreteClassCallbackFilter implements CallbackFilter {
public int accept(Method method) {
if("getConcreteMethodB".equals(method.getName())){
//Callback callbacks[0]
return 0;
}else if("getConcreteMethodA".equals(method.getName())){
//Callback callbacks[1]
return 1;
}else if("getConcreteMethodFixedValue".equals(method.getName())){
//Callback callbacks[2]
return 2;
}
return 1;
}
}
/**
* @Author: dingyawu
* @Description: TODO
* @Date: Created in 22:11 2023/2/11
*/
public class ConcreteClassFixedValue implements FixedValue {
public Object loadObject() throws Exception {
System.out.println("ConcreteClassFixedValue loadObject ...");
Object object=999;
return object;
}
}
/**
* 回调过滤CallbackFilter
* MethodInterceptor:方法拦截器
* NoOp.INSTANCE:这个NoOp表示no operator,即什么操作也不做,代理类直接调用被代理的方法不进行拦截
* FixedValue:表示锁定方法返回值,无论被代理类的方法返回什么值,回调方法都返回固定值。
*
* @Author: dingyawu
* @Description: TODO
* @Date: Created in 22:10 2023/2/11
*/
public class CreateProxyTest {
@Test
public void test(){
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(ConcreteClassNoInterface1.class);
CallbackFilter filter = new ConcreteClassCallbackFilter();
enhancer.setCallbackFilter(filter);
Callback interceptor = new ConcreteClassInterceptor();
Callback noOp = NoOp.INSTANCE;
Callback fixedValue = new ConcreteClassFixedValue();
Callback[] callbacks = new Callback[]{ interceptor, noOp, fixedValue };
enhancer.setCallbacks(callbacks);
ConcreteClassNoInterface1 proxyObject=(ConcreteClassNoInterface1)enhancer.create();
//接上文...
System.out.println("*** NoOp Callback ***");
proxyObject.getConcreteMethodA("abcde");
System.out.println("*** MethodInterceptor Callback ***");
proxyObject.getConcreteMethodB(1);
System.out.println("*** FixedValue Callback ***");
int fixed1=proxyObject.getConcreteMethodFixedValue(128);
System.out.println("fixedValue1:"+fixed1);
int fixed2=proxyObject.getConcreteMethodFixedValue(256);
System.out.println("fixedValue2:"+fixed2);
}
}
4. 延迟加载LazyLoader
/**
* 首先定义一个实体类LoaderBean,该Bean内有一个需要延迟加载的属性PropertyBean。
* @Author: dingyawu
* @Description: TODO
* @Date: Created in 00:07 2023/2/12
*/
public class LoaderBean {
private String loaderName;
private int loaderValue;
private PropertyBean propertyBean;
public LoaderBean(){
this.loaderName="loaderNameA";
this.loaderValue=123;
this.propertyBean = createPropertyBean();
}
protected PropertyBean createPropertyBean(){
Enhancer enhancer=new Enhancer();
//设置要被代理的类
enhancer.setSuperclass(PropertyBean.class);
//第一个参数就是被代理的类的class, 第二个参数是callback的实现类
return (PropertyBean)enhancer.create(PropertyBean.class,new ConcreteClassLazyLoader());
}
public String getLoaderName() {
return loaderName;
}
public void setLoaderName(String loaderName) {
this.loaderName = loaderName;
}
public int getLoaderValue() {
return loaderValue;
}
public void setLoaderValue(int loaderValue) {
this.loaderValue = loaderValue;
}
public PropertyBean getPropertyBean() {
return propertyBean;
}
public void setPropertyBean(PropertyBean propertyBean) {
this.propertyBean = propertyBean;
}
}
/**
* @Author: dingyawu
* @Description: TODO
* @Date: Created in 00:08 2023/2/12
*/
public class PropertyBean {
private String propertyName;
private int propertyValue;
public String getPropertyName() {
return propertyName;
}
public void setPropertyName(String propertyName) {
this.propertyName = propertyName;
}
public int getPropertyValue() {
return propertyValue;
}
public void setPropertyValue(int propertyValue) {
this.propertyValue = propertyValue;
}
}
/**
* @Author: dingyawu
* @Description: TODO
* @Date: Created in 00:09 2023/2/12
*/
public class ConcreteClassLazyLoader implements LazyLoader {
public Object loadObject() {
System.out.println("LazyLoader loadObject() ...");
PropertyBean bean = new PropertyBean();
bean.setPropertyName("lazy-load object propertyName!");
bean.setPropertyValue(11);
return bean;
}
@Test
public void test(){
LoaderBean loader=new LoaderBean();
System.out.println(loader.getLoaderName());
System.out.println(loader.getLoaderValue());
PropertyBean propertyBean=loader.getPropertyBean();//访问延迟加载对象
System.out.println(propertyBean.getPropertyName());
System.out.println(propertyBean.getPropertyValue());
System.out.println("after...");
//当再次访问延迟加载对象时,就不会再执行回调了
System.out.println(propertyBean.getPropertyName());
}
}
/**
* 第一次获取property bean的属性时,会触发代理类回调方法。
* 第二次再获取property bean的属性时,就直接返回属性值而不会再次触发代理类回调方法了
* @Author: dingyawu
* @Description: TODO
* @Date: Created in 01:03 2023/2/12
*/
public class ProxyTest {
@Test
public void test(){
LoaderBean loader = new LoaderBean();
System.out.println(loader.getLoaderName());
System.out.println(loader.getLoaderValue());
//访问延迟加载对象
PropertyBean propertyBean=loader.getPropertyBean();
System.out.println(propertyBean.getPropertyName());
System.out.println(propertyBean.getPropertyValue());
System.out.println("after...");
//当再次访问延迟加载对象时,就不会再执行回调了
System.out.println(propertyBean.getPropertyName());
}
}
5. 延迟加载Dispatcher
/**
* @Author: dingyawu
* @Description: TODO
* @Date: Created in 01:11 2023/2/12
*/
public class ConcreteClassDispatcher implements Dispatcher {
public Object loadObject() throws Exception {
System.out.println("Dispatcher loadObject ...");
PropertyBean object = new PropertyBean();
object.setPropertyName("PropertyBeanName!");
object.setPropertyValue(1);
return object;
}
}
/**
* @Author: dingyawu
* @Description: TODO
* @Date: Created in 01:11 2023/2/12
*/
public class DispatcherBean {
private String name;
private String value;
private PropertyBean propertyBean;
public DispatcherBean(){
this.name="DispatcherBean";
this.value="abc";
this.propertyBean=createDispatcherBean();
}
protected PropertyBean createDispatcherBean(){
Enhancer enhancer=new Enhancer();
enhancer.setSuperclass(PropertyBean.class);
return (PropertyBean)enhancer.create(PropertyBean.class,new ConcreteClassDispatcher());
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
public PropertyBean getPropertyBean() {
return propertyBean;
}
public void setPropertyBean(PropertyBean propertyBean) {
this.propertyBean = propertyBean;
}
}
/**
* 每次获取property bean的属性都会自动触发回调方法加载对象
* @Author: dingyawu
* @Description: TODO
* @Date: Created in 01:22 2023/2/12
*/
public class ProxyTest {
public void test(){
DispatcherBean dispatcherBean=new DispatcherBean();
System.out.println(dispatcherBean.getName());
System.out.println(dispatcherBean.getValue());
PropertyBean pb=dispatcherBean.getPropertyBean();
System.out.println(pb.getPropertyName());
//在每次访问时都要进行回调
System.out.println(pb.getPropertyValue());
}
}
6. 自己产生接口,自己创建代理
/**
* 自己通过InterfaceMaker 产生一个接口
* 然后通过enhancer产生一个代理类,被代理的类实现了产生的接口
*/
public class CglibTest implements MethodInterceptor {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
return JSON.toJSONString(objects);
}
@Test
public void test_cglib() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException, NoSuchFieldException {
// 定义接口
InterfaceMaker interfaceMaker = new InterfaceMaker();
interfaceMaker.add(new Signature("sayHi", Type.getType(String.class), new Type[]{Type.getType(String.class)}), null);
Class<?> interfaceClass = interfaceMaker.create();
Method[] methods = interfaceClass.getMethods();
Arrays.stream(methods).forEach(System.out::println);
// 创建代理
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(Object.class);
enhancer.setInterfaces(new Class[]{interfaceClass});
enhancer.setCallback(this);
Object obj = enhancer.create();
// 调用方法, String.class是表示区分重构
Method method = obj.getClass().getMethod("sayHi", String.class);
Object result = method.invoke(obj,"hi yawu");
System.out.println(result);
}
}