CGlib是一个强大的代码生成包,常被用于各种AOP框架,提供“拦截”功能。JDK本身就为控制要访问的对象提供了一种途径,动态代理Proxy。但是被代理的累必须实现一个或多个接口。如果想摆脱这个限制,为没有实现接口的类代理的话,可以使用CGlib。
下面是一个入门学习的小例子。
需求:InfoManager类有四个方法,query、del、create、update。要对这些方法进行访问控制。只有end用户可
以访问所有方法,而其他用户只能query。
<span style="font-size:18px;">public class InfoManager { public void query() { System.out.println("query method..."); } public void del() { System.out.println("del method..."); } public void create() { System.out.println("create method..."); } public void update() { System.out.println("update method..."); } } </span>
InfoManagerFactory类,用来生成InfoManager实例。
<span style="font-size:18px;">public class InfoManagerFactory { public static InfoManager getInstance(UserProxy proxy) { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(InfoManager.class);//被代理的类 enhancer.setCallbacks(new Callback[]{proxy, NoOp.INSTANCE});//两个callBack,第二个实则为空限制,实现某些方法的无限制访问 enhancer.setCallbackFilter(new UserProxyFilter());//CallBack过滤器 return (InfoManager) enhancer.create();//创建带有拦截过滤功能的InfoManager的子类实例 } } </span>
UserProxy实现MethodInterceptor接口,用来拦截。
<span style="font-size:18px;">public class UserProxy implements MethodInterceptor { private String name; public UserProxy(String name) { super(); this.name = name; } @Override public Object intercept(Object object, Method method, Object[] args, MethodProxy proxy) throws Throwable { if(!"end".equals(this.name))//非end用户,不能访问 { System.out.println(name + "没有权限访问方法:" + method.getName()); return null; } return proxy.invokeSuper(object, args);//通过拦截,则反射调用InfoManager的对应方法。 } }</span>
UserProxyFilter实现CallbackFilter,制定规则,决定采用前面的哪个CallBack。
<span style="font-size:18px;">public class UserProxyFilter implements CallbackFilter { public static final int USER_NEED = 0; public static final int USER_NOT_NEED = 1; @Override public int accept(Method method) { if("query".equals(method.getName())) { return USER_NOT_NEED; } return USER_NEED; } } </span>
这里要解释一下,accept方法返回值是int,就是这个值决定了采用哪个拦截器。看之前的这句代码:
<span style="font-size:18px;"><span style="font-size:18px;">enhancer.setCallbacks(new Callback[]{proxy, NoOp.INSTANCE});</span></span>
Enhancer设置了一个callBack数组,第一个为我们写的用户权限的拦截器,第二个为一个空的拦截器。当accept方法
发现被用户操作调用的方法是query时,应该都允许,所以返回值是1,所以采用了callBack数组下标为1的拦截器:空
拦截器,不进行验证拦截;当用户操作调用的方法是不query时,需要验证用户权限进行拦截,所以返回值是0,采用
callBack数组下标为0的拦截器:UserProxy。而UserProxy中,我们只允许end用户访问InfoManager类方法。至此,
我们就实现了需求中的功能。
InfoManagerFactory中,我们看到创建实例的方法中用到了Enhance,最后return了enhancer.create()的返回值。那么
在设置了各种属性后,enhancer.create()到底做了什么呢,使得InfoManager实例,按照我们的规则被调用和拒绝。
在文章最开始,我们说到CGlib是一个动态生成代码的包,上面的小例子,原理其实是这样的:cglib在代码运行期间
,根据UserProxyFilter的accept方法返回值,选择了相应的MethodInterceptor拦截器。然后动态生成了一个InfoMana
ger的子类,并在子类中覆写了InfoManager中的各个方法,去调用拦截器的intercept方法。所以,Factory得到是cglib
生成的子类,调用里面的方法时,都会去拦截器里进行验证。
CGlib小记