CGLIB(Code Generation Library)是一个强大的、高性能的、高质量的Code生成类库,它可以在运行期扩展Java类和Java接口。它封装了asm,可以在运行期动态生成class,是Spring、Hibernate等框架依赖的核心包。Java动态代理基于接口,CGLIB没有此限制。
BookManager类:
- public class BookManager {
- private void create() {
- System.out.println("create...\n");
- }
- private void query() {
- System.out.println("query...\n");
- }
- private void update() {
- System.out.println("update...\n");
- }
- private void delete() {
- System.out.println("delete...\n");
- }
- // 为方便调用
- public void doCRUD() {
- create();
- query();
- update();
- delete();
- }
- }
BookFactory类:
- public class BookFactory {
- private static BookManager manager = new BookManager();
- public static BookManager getInstance() {
- return manager;
- }
- }
写一单元测试方法:
- @Test
- public void test1() {
- System.out.println("any one can do!\n");
- BookManager manager = BookFactory.getInstance();
- manager.doCRUD();
- }
上述是对CRUD操作无限制时的代码,未涉及CGLIB。但接下来需求发生变化:要求只有用户”xxx”才能进行CRUD操作。此时若在每个方法前都加上权限判断,会使重复逻辑太多,这时可使用代理模式,但原先的BookManager未实现接口,不能使用Java的动态代理,此时可使用CGLIB。使用CGLIB,需添加一个实现MethodInterceptor接口的类,代码如下:
- // 权限校验代理类,用户名为xxx时才有权限操作
- public class PermissionProxy implements MethodInterceptor {
- private String name;
- public PermissionProxy(String name) {
- this.name = name;
- }
- public String getName() {
- return name;
- }
- public void setName(String name) {
- this.name = name;
- }
- @Override
- public Object intercept(Object obj, Method method, Object[] args,
- MethodProxy proxy) throws Throwable {
- if (!"xxx".equals(name)) {
- return null;
- }
- return proxy.invokeSuper(obj, args);
- }
- }
此时在BookFactory类中添加如下代码:
- /**
- * 创建带有权限的BookManager
- */
- public static BookManager getPermissionInstance(PermissionProxy proxy) {
- Enhancer enhancer = new Enhancer();
- enhancer.setSuperclass(BookManager.class);
- enhancer.setCallback(proxy);
- return (BookManager) enhancer.create();
- }
单元测试方法代码如下:
- @Test
- public void test2() {
- System.out.println("恭喜您,您有权限进行操作!!!");
- BookManager permission = BookFactory
- .getPermissionInstance(new PermissionProxy("xxx"));
- permission.doCRUD();
- System.out.println("不存在的用户名,您没有权限进行操作!!!");
- BookManager noPermission = BookFactory
- .getPermissionInstance(new PermissionProxy("xyz"));
- noPermission.doCRUD();
- }
上述代码使用到CGLIB中的Enhancer功能,下面模拟需求再次发生变化:query任何人都可以操作,其余操作只有用户”xxx”具有权限。此时,若采用PermissionProxy,使得BookManager中所有方法都被代理,最容易想到的办法是,在PermissionProxy中的intercept方法中再做下判断,如果method是query方法,不需要权限验证。这么做,一旦逻辑比较复杂,intercept方法要做的事情会很多,使得逻辑非常复杂。CGLIB提供了CallBackFilter接口,使用此接口,可明确表明被代理的类(BookManager)中的不同方法,被哪个拦截器拦截。写一实现CallBackFilter接口的类:
- public class PermissionProxyFilter implements CallbackFilter {
- private static final int NEED_PERMISSION = 0;
- private static final int NO_NEED_PERMISSION = 1;
- @Override
- public int accept(Method method) {
- if ("query".equals(method.getName())) {
- return NO_NEED_PERMISSION;
- }
- return NEED_PERMISSION;
- }
- }
此时在BookFactory类中添加如下代码:
- /**
- * 创建仅有查询权限的BookManager
- */
- public static BookManager getQueryInstance(PermissionProxy proxy) {
- Enhancer enhancer = new Enhancer();
- enhancer.setSuperclass(BookManager.class);
- enhancer.setCallbacks(new Callback[] { proxy, NoOp.INSTANCE });
- enhancer.setCallbackFilter(new PermissionProxyFilter());
- return (BookManager) enhancer.create();
- }
注意:setCallBacks中的拦截器(interceptor)的顺序要和CallBackFilter中指定的顺序一致,NoOp.INSTANCE是CGLIB中的空拦截器。再写一单元测试方法:
- @Test
- public void test3() {
- System.out.println("您仅有查询权限!!!");
- BookManager manager = BookFactory
- .getQueryInstance(new PermissionProxy("xyy"));
- manager.doCRUD();
- }
上面的例子,CGLIB在代码运行期,动态生成了BookManager的子类,并根据CallBackFilter的accept方法,覆盖了BookManager中的所有方法,去执行相应的MethodInterceptor的intecept方法。