CGLIB动态代理
原理:
运行时动态的生成一个被代理类的子类(通过ASM字节码处理框架实现),子类重写了被代理类中所有非final的方法。在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势植入横切逻辑。
先来看一段代码:
//被代理的类 模仿数据库的service层
public class ServiceBean {
public void create(){
System.out.println("creating!!");
}
public void delete(){
System.out.println("deleting!!");
}
public void update(){
System.out.println("updating!!");
}
public void query(){
System.out.println("querying!!");
}
}
我们如果需要在后续给每个方法拓展一些功能,比如说权限功能,日志功能。去修改每个方法显然是不明智的。这时候就可以使用到动态代理。而上篇文章已经说到过,jdk的动态代理是需要实现接口的,明显在这里不太适合,我们选用另外一种方式实现动态代理
下面程序实现的效果是 :除了query()方法外,其他的方法都需要boss权限才能调用,而query()所有人都能调用
//使用cglib实现的代理类需要实现MethodInterceptor 拦截器
public class MyCglibProxy implements MethodInterceptor {
private Logger logger = LoggerFactory.getLogger(MyCglibProxy.class);
private String name;
//Enhancer 适用于创建代理对象的一个工具类
public Enhancer enhancer = new Enhancer();
//给这个代理设置一个名字
public MyCglibProxy(String name) {
this.name = name;
}
public Object getObjectBean(Class cls){
enhancer.setSuperclass(cls);
enhancer.setCallbacks(new Callback[]{this, NoOp.INSTANCE});
/**
* 在此设置过滤器(这个可以根据需求设置)
* MyProxyFilter 实现过滤器 CallbackFilter
* 他的作用是可以明确表示,被代理中的类的不同方法 被哪拦截器所拦截
*
* 对于setCallbacks方法
* setCallbacks中定义了所使用的拦截器,其中NoOp.INSTANCE是CGlib所提供的实际是一个没有任何操作的拦截器,
* 他们是有序的,一定要和CallbackFilter(在这里是MyProxyFilter)里面的顺序一致。上面return返回(0/1)的就是返回的顺序。也就是说如果调用query方法就使用NoOp.INSTANCE进行拦截。
*/
enhancer.setCallbackFilter(new MyProxyFilter());
return enhancer.create();
}
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
logger.info("调用方法:"+method.getName());
if(!"boss".equals(name)){
logger.info("您没有权限");
return null;
}
//代理子类对象生成以后 会调用复写的方法,传入对应得参数
Object result = methodProxy.invokeSuper(o,objects);
return result;
}
}
public class MyProxyFilter implements CallbackFilter {
@Override
public int accept(Method method) {
//return 的顺序和enhancer.setCallbacks(new Callback[]{proxy, NoOp.INSTANCE});中的顺序是对应的
//如果返回的是0,则使用的是proxy拦截器 如果返回的是1 则使用的是NoOp.INSTANCE,这个拦截器什么都没有做
if(!"query".equalsIgnoreCase(method.getName())){
return 0;
}
return 1;
}
}
//方法调用
public class Main {
public static void main(String[] args) {
ServiceBean serviceBean1 = (ServiceBean)(new MyCglibProxy("boss").getObjectBean(ServiceBean.class));
serviceBean1.create();
ServiceBean serviceBean2 = (ServiceBean)(new MyCglibProxy("jonh").getObjectBean(ServiceBean.class));
//虽然是jonh 但是query()是允许调用的
serviceBean2.query();
ServiceBean serviceBean3 = (ServiceBean)(new MyCglibProxy("jonh").getObjectBean(ServiceBean.class));
//不是boss 无权限调用create()
serviceBean3.create();
}
}
与JDK不同的是 cglib动态生成了一个子类。子类重写了被代理类中所有非final的方法。在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势植入横切逻辑。