原文地址:https://www.cnblogs.com/xrq730/p/6661692.html
Cglib是一个强大的、高性能的代码生成包,它广泛被许多AOP框架使用,为他们提供方法的拦截。下图是我网上找到的一张Cglib与一些框架和语言的关系:
对此图总结一下:
详细请参照代理模式详解之Cglib动态代理:https://mp.csdn.net/postedit/88715234
下面演示一下Cglib代码示例----对类做代理。首先定义一个Dao类,里面有一个select()方法和一个update()方法:
再扩展一点点,比方说在AOP中我们经常碰到的一种复杂场景是:我们想对类A的B方法使用一种拦截策略、类A的C方法使用另外一种拦截策略。
如果我们想对同一个类的两个方法使用不同的拦截策略,例子如下:
创建一个Dao代理,实现MethodInterceptor接口,目标是在update()方法与select()方法调用前后输出两句话:
public class Dao {
public void update() {
System.out.println("PeopleDao.update()");
}
public void select() {
System.out.println("PeopleDao.select()");
}
}
intercept方法的参数名并不是原生的参数名,我做了自己的调整,几个参数的含义为:
public class DaoProxy implements MethodInterceptor {
@Override
public Object intercept(Object object, Method method, Object[] objects, MethodProxy proxy) throws Throwable {
System.out.println("Before Method Invoke");
proxy.invokeSuper(object, objects);
System.out.println("After Method Invoke");
return object;
}
}
再定义一个新的proxy类
public class DaoAnotherProxy implements MethodInterceptor {
@Override
public Object intercept(Object object, Method method, Object[] objects, MethodProxy proxy) throws Throwable {
System.out.println("StartTime=[" + System.currentTimeMillis() + "]");
method.invoke(object, objects);
System.out.println("EndTime=[" + System.currentTimeMillis() + "]");
return object;
}
}
方法调用前后输出一下开始时间与结束时间。为了实现我们的需求,实现一下CallbackFilter:
public class DaoFilter implements CallbackFilter {
@Override
public int accept(Method method) {
if ("select".equals(method.getName())) {
return 0;
}
return 1;
}
}
返回的数值表示顺序,结合下面的代码解释,测试代码:
public class CglibTest {
@Test
public void testCglib() {
DaoProxy daoProxy = new DaoProxy();
DaoAnotherProxy daoAnotherProxy = new DaoAnotherProxy();
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(Dao.class);
enhancer.setCallbacks(new Callback[]{daoProxy, daoAnotherProxy, NoOp.INSTANCE});
enhancer.setCallbackFilter(new DaoFilter());
Dao dao = (Dao)enhancer.create();
dao.update();
dao.select();
}
}
意思是CallbackFilter的accept方法返回的数值表示的是顺序,顺序和setCallbacks里面Proxy的顺序是一致的。再解释清楚一点,Callback数组中有三个callback,那么:
因此,方法的执行结果为:
符合我们的预期,因为update()方法不是方法名为"select"的方法,因此返回1,返回1使用DaoAnotherProxy,即打印时间;select()方法是方法名为"select"的方法,因此返回0,返回0使用DaoProxy,即方法调用前后输出两句话。
这里要额外提一下,Callback数组中我特意定义了一个NoOp.INSTANCE,这表示一个空Callback,即如果不想对某个方法进行拦截,可以在DaoFilter中返回2,具体效果可以自己尝试一下。
如果Update()方法与select()方法在构造函数中被调用,那么也是会对这两个方法进行相应的拦截的,现在我想要的是构造函数中调用的方法不会被拦截,那么应该如何做?先改一下Dao代码,加一个构造方法Dao(),调用一下update()方法:
public class Dao {
public Dao() {
update();
}
public void update() {
System.out.println("PeopleDao.update()");
}
public void select() {
System.out.println("PeopleDao.select()");
}
}
如果想要在构造函数中调用update()方法时,不拦截的话,Enhancer中有一个setInterceptDuringConstruction(boolean interceptDuringConstruction)方法设置为false即可,默认为true,即构造函数中调用方法也是会拦截的。那么测试方法这么写:
public class CglibTest {
@Test
public void testCglib() {
DaoProxy daoProxy = new DaoProxy();
DaoAnotherProxy daoAnotherProxy = new DaoAnotherProxy();
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(Dao.class);
enhancer.setCallbacks(new Callback[]{daoProxy, daoAnotherProxy, NoOp.INSTANCE});
enhancer.setCallbackFilter(new DaoFilter());
enhancer.setInterceptDuringConstruction(false);
Dao dao = (Dao)enhancer.create();
dao.update();
dao.select();
}
}
运行结果为: