cglib大名在java界如雷贯耳,众多优秀的开源项目均使用其来实现各自的功能(spring aop,hibernate等等),这里主要简单介绍一下cglib的使用,对比一下java原生的proxy还有javaassist.
说到cglib第一印象就是动态代理(啥是动态代理?请百度<设计模式>),没错,这是它牛逼的功能之一,
使用cglib构建动态代理核心类就是Enhancer,作用如其名:增强.它能够对目标类的方法进行增强.上代码:
/** * Created by wally on 3/1/18. */ public abstract class Animal { private String name; public Animal() { } public Animal(String name) { this.name = name; } public abstract void eat(); public void live(){ System.out.println("i'm " + name); eat(); } } public class Duck extends Animal { public Duck() { } public Duck(String name) { super(name); } @Override public void eat() { System.out.println("i'm eat fish!"); } } /** * Created by wally on 3/1/18. */ public class ProxyCreator { public static <T> T createProxy(Class<T> target,T instance){ Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(target); CallbackHelper callbackHelper = new CallbackHelper(Duck.class,new Class[0]) { @Override protected Object getCallback(Method method) { if(method.getName().equals("live")){ return NoOp.INSTANCE; } return new DuckProxy(); } }; enhancer.setCallbackFilter(callbackHelper); enhancer.setCallbacks(callbackHelper.getCallbacks()); return (T)enhancer.create(new Class[]{String.class},new String[]{"唐老鸭"}); } }
/**
* Created by wally on 3/1/18.
*/
public class Starter {
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
Duck duck = ProxyCreator.createProxy(Duck.class,null);
duck.live();
}
}
上述栗子的输出:
i'm 唐老鸭
say gua gua!
i'm eat fish!
可以看到Duck类的eat行为发生变化了。在不调整原有代码的基础上改变原有对象的行为(严格意义上原有行为并没有变,只是包装了一层,是的,装饰者模式可以看做一种特殊的代理!)。这有什么用呢,我们来看一下大家都是怎么用的。
在hibernate中,使用cglib对bean进行代理,从而实现延迟加载,我们会发现,hibernate正真执行sql的时候往往不是我们调用查询方法的时候,而是在调用查询结果的时候采取真正执行。
在spring中,当我们通过注解@EnableAspectJAutoProxy开启aop时,可以指定是使用java原生proxy还是使用cglib,通过设置proxyTargetClass=true/false(使用cglib/使用java proxy).
通过代理,可以将业务逻辑与通用逻辑解耦,例如日志打印,数据库链接等等。
cglib除了强大的动态代理,还有一个功能:动态生成对象
/** * Created by wally on 3/1/18. */ public class Beangenerator { public static Object generateBean(Map<String, Class<?>> params, Class<?> superClass) { BeanGenerator generator = new BeanGenerator(); if (params != null && params.size() != 0) { params.forEach(generator::addProperty); } if (superClass != null) generator.setSuperclass(superClass); return generator.create(); } }
cglib是对ASM的封装,ASM是一个字节码生成工具,所以cglib可以实现动态字节码生成,上面栗子通过BeanGenerator实例来生成指定属性的对象,该对象会自带setter和getter方法,可以通过反射获取。
值得一提的是,由于cglib是生成字节码,我们知道java的类信息释放在permanent edition中的,通过cglib生成的类信息也不例外,所以在使用cglib生成代理类或者bean时,需要注意,平凡的创建有可能导致pem 区的oom。
java proxy对比cglib:
java proxy由于是java自身支持的,在生成代理类时速度比cglib快,但是cglib是通过插入字节码来实现代理的,在调用速度上cglib要比java proxy快
在功能上,java proxy仅仅支持接口级别的代理,而cglib没有这个限制,目标类可以是接口可以是普通类,并且可以精确到方法级别(上面的栗子可以看出)。