之前看jvm的时候看到CGLIB这个库,就想看看究竟是个什么玩意,结果一研究就是好几天,看的头大,记录一下学习路线,以后忘了好复习。
个人的理解,动态代理相比于静态代理(资料很多这里就不赘述了),优势在于动态代理不需要重新实现接口的所有函数,代码更为简练,以下是一个例子(主要 参考了文章:Java 动态代理作用是什么? - bravo1988的回答 - 知乎),注释里面有注意的点:
public interface Calculator {
public Integer add(Integer a , Integer b);
}
public class CalculatorImpl implements Calculator {
@Override
public Integer add(Integer a, Integer b) {
return a+b;
}
}
public static void main(String[] args) throws Exception {
Class proxyClazz = Proxy.getProxyClass(Demo.class.getClassLoader(),Calculator.class);
Calculator proxyCalculator =(Calculator) proxyClazz.getConstructor(InvocationHandler.class).newInstance(new InvocationHandler(){
//InvocationHandler 这个接口个人理解在于,代理类一切方法被调用时。
//默认就会调用这个接口里面的唯一方法invoke()
//从而实现封装
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//此处的proxy 就是调用方法的代理实例
System.out.println("鸡汤来咯"); //前部分封装的代码
//此处注意,要new一个或者使用一个非代理的原对象,而不是直接使用proxy。
//原因在于使用了proxy这个实例,就会导致再次进入Invocationhandler这个方法里面,无限递归
//最后栈溢出
Object result= method.invoke(new CalculatorImpl(),args);
System.out.println(result);
System.out.println("为什么不喝!!!");//后面封装的代码
return result;
}
});
System.out.println(proxyCalculator.add(3,5));
}
这一部分的代码可以用newInstance方法进一步精简,具体的上述文章里有,就不赘述了。
JDK动态代理的实质是最终生成一个继承于proxy实现了被代理类所有接口的一个类,由于java单继承的限制,因此动态代理对于没有接口的类是无法代理的,更具体的可参考文章:关于代理:为什么 JDK 动态代理只能为接口生成代理? - SegmentFault 思否
而CGLIB则没有了这个问题,CGLIB的实现原理,主要是参考了这篇文章:Cglib动态代理实现原理 - 两条闲鱼 - 博客园 (cnblogs.com),CGLIB在实现上直接继承了被代理类同时implement了Factory接口,因此可以对类进行代理。(特殊情况,当CGLIB传入接口的Class时候,CGLIB将不继承任何class而是直接空实现该类,参考:JAVA面试宝典—为什么CGlib方式可以对接口实现代理_哔哩哔哩_bilibili)
此外,CGLIB和JDK动态代理各有其优势,一般情况下,CGLIB构造出来的代理对象性能更高,但是JDK动态代理的构造代理对象的时间要短的多,因此,如果是单例的情况下,CGLIB更好,但要频繁new的情况下,JDK的动态代理更好。
CGLIB的一些基本操作代码:
MethodInterceptor版本:
@Test
public void testCGLibOne(){
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(CalculatorImpl.class);//设置要代理的类(父类)
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
//obj 与InvokeHandler相同 这里传的是代理后的类
//method 被拦截的方法
//args 参数
//method的MethodProxy版本,该类有invokeSuper方法用于调用被代理前对象的方法
System.out.println();
System.out.println("front deal");
//这的意思是调用父类 也就是原被代理对象的方法
//直接调用invoke会和InvokeHandler一样陷入死循环。
Object rs = proxy.invokeSuper(obj,args);
System.out.println("back deal");
return rs;
}
});
Calculator calculator = (Calculator)enhancer.create();
System.out.println(calculator.add(3,5));
}
CallBackFilter
@Test
public void testFilter() {
Enhancer enhancer = new Enhancer();
CallbackFilter filter = new CallbackFilter() {
//这里是设置返回一个int数,会变成下面CallBack数组中的索引
@Override
public int accept(Method method) {
if(method.getReturnType()==Integer.class)
return 0;
if(method.getReturnType()==String.class)
return 1;
return 2;
}
};
enhancer.setCallbackFilter(filter);
Callback callbackInt = new MethodInterceptor() {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("will return a int");
Object rs = proxy.invokeSuper(obj,args);
System.out.println("return int end");
return rs;
}
};
Callback callbackString = new FixedValue() {
//不执行原方法,返回常量
@Override
public Object loadObject() throws Exception {
return "输出你个头String啊!";
}
};
//对应上方CallbackFilter的返回值 0-> Integer,1->String,2->什么都不干
enhancer.setCallbacks(new Callback[]{callbackInt,callbackString,new NoOp(){}});
enhancer.setSuperclass(CalculatorImpl.class);
Calculator calculator = (Calculator) enhancer.create();
System.out.println(calculator.add(1,3));
System.out.println(calculator.toString());
System.out.println(calculator.getClass());
}
最后输出
will return a int
return int end
4
输出你个头String啊!
class tech.target.CalculatorImpl$$EnhancerByCGLIB$$c8aceca5
其它一些操作暂时就先不看了,不少于Bean的操作有关,需要的时候再继续研究