在学习Spring AOP 的时候我们知道Spring AOP 的实现方式是通过JDK 的动态代理和CGLIB的动态代理实现的,那么什么是CGLIB呢?
CGLIB其实就是封装了ASM(Java字节码操控框架)了的功能强大,高性能,高质量的Code生成类库。它可以在运行期扩展Java类与实现Java接口。
我们常见的就是Enhancer类。
它可以动态生成一个子类使方法可以被拦截;处理实现接口,该类还额可以动态生成父类中非final方法的子类,并提供回调用户自定义拦截器的钩子。原生的和普遍被使用的回调类型是MethodInterceptor ,它在AOP方面实现了环绕通知,也就是说你既可以在调用父类方法之前和之后调用自定义的增强代码。此外,你可以在调用父类方法前修改入参,甚至是根本就不调用父类方法。
虽然,MethodInterceptor 是通用的,足以满足任何拦截需求,但是它往往矫枉过正。为了简单性和性能增加了专门的回调类型 如LazyLoader,经常一个回调在每个拦截器中都没调用,你可以通过CallbackFilter 控制在每个方法上使用哪个回调。
有这样一个Service 提供了数据的增删改查功能,使用自定义注解标注每个方法被访问时要求的权限
/** *数据服务类 * @author zhangwei_david * @version $Id: TableDAO.java, v 0.1 2015年1月19日 下午8:16:14 zhangwei_david Exp $ */ public class MyService { @Permission("ADD") public void add() { System.out.println("create() is running !"); } @Permission({ "ADD", "QUERY", "UPDATE", "QUERY" }) public void query() { System.out.println("query() is running !"); } @Permission({ "UPDATE" }) public void update() { System.out.println("update() is running !"); } @Permission("DELETE") public void delete() { System.out.println("delete() is running !"); } }
/** * * @author zhangwei_david * @version $Id: permission.java, v 0.1 2015年1月19日 下午8:58:56 zhangwei_david Exp $ */ @Documented @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface Permission { String[] value(); }
/** *权限工具类 * @author zhangwei_david * @version $Id: AuthUtils.java, v 0.1 2015年1月19日 下午9:14:07 zhangwei_david Exp $ */ public class AuthUtils { /** * 判断当前用户角色是否具有当前方法的访问权限 * * @param method * @param role * @return */ public static boolean isGrantPermission(Method method, String role) { String[] roles = null == method.getAnnotation(Permission.class) ? null : method .getAnnotation(Permission.class).value(); if (roles == null || roles.length == 0) { return true; } for (String string : roles) { if (string.equals(role)) { return true; } } return false; } }
/** *安全代理类 * @author zhangwei_david * @version $Id: AuthProxy.java, v 0.1 2015年1月19日 下午8:20:36 zhangwei_david Exp $ */ public class AuthProxy implements MethodInterceptor { private String role; /** * @see net.sf.cglib.proxy.MethodInterceptor#intercept(java.lang.Object, java.lang.reflect.Method, java.lang.Object[], net.sf.cglib.proxy.MethodProxy) */ public Object intercept(Object arg0, Method method, Object[] arg2, MethodProxy arg3) throws Throwable { if (AuthUtils.isGrantPermission(method, role)) { return arg3.invokeSuper(arg0, arg2); } System.out.println("expected role is " + Arrays.toString(method.getAnnotation(Permission.class).value()) + " but now is " + role); return null; } public AuthProxy(String role) { super(); this.role = role; } }
/** * * @author zhangwei_david * @version $Id: TableDAOFactory.java, v 0.1 2015年1月19日 下午8:16:47 zhangwei_david Exp $ */ public class DAOFactory { public static MyService getInstance(AuthProxy authProxy) { Enhancer eh = new Enhancer(); eh.setSuperclass(MyService.class); eh.setCallback(authProxy); return (MyService) eh.create(); } }
/** * * @author zhangwei_david * @version $Id: Client.java, v 0.1 2015年1月19日 下午8:18:18 zhangwei_david Exp $ */ public class Client { /** * * @param args */ public static void main(String[] args) { MyService tableDAO = DAOFactory.getInstance(new AuthProxy("ADD")); tableDAO.add(); tableDAO.delete(); tableDAO.update(); tableDAO.query(); } }
结果是:
create() is running ! expected role is [DELETE] but now is ADD expected role is [UPDATE] but now is ADD query() is running !
发现在AuthUtils判断了一个类是否有权限控制的注解,我们可以想一下如果一个类都没有权限控制为什么还需要进入权限的拦截器中呢?
/** * * @author zhangwei_david * @version $Id: TableDAOFactory.java, v 0.1 2015年1月19日 下午8:16:47 zhangwei_david Exp $ */ public class DAOFactory { public static MyService getInstance(AuthProxy authProxy) { Enhancer eh = new Enhancer(); eh.setSuperclass(MyService.class); eh.setCallbacks(new Callback[] { authProxy, NoOp.INSTANCE }); eh.setCallbackFilter(new PermissionControlleFilter()); return (MyService) eh.create(); } }
/** * * @author zhangwei_david * @version $Id: PermissionControlleFilter.java, v 0.1 2015年1月19日 下午10:06:09 zhangwei_david Exp $ */ public class PermissionControlleFilter implements CallbackFilter { /** * @see net.sf.cglib.proxy.CallbackFilter#accept(java.lang.reflect.Method) */ public int accept(Method method) { if (method.getAnnotation(Permission.class) != null) { return 0; } return 1; } }
这样就是表示如果一个方法中没有使用权限注解标注改方法的权限的化就使用第二个拦截器,第二个拦截器
NoOp.INSTANCE
是CGLIB预定义的一个空个回调。