面向切面的编程(AOP)和 IOC作为Spring的两个最重要的模块,Spring其他的模块都是建立在它们之上。正好最近在学习Spring的相关知识,看到AOP知识中的动态代理
时用到了CGLIB的相关知识,隧上网整理一下其相关知识,待以后备用。
cglib基本信息
- cglib的官方网站: http://cglib.sourceforge.net/
- 官网的samples : http://cglib.sourceforge.net/xref/samples/
- CGLIB学习文档:http://www.iteye.com/topic/799827;http://www.open-open.com/doc/view/0212ea10f7eb4535a971b4f62d581aae
使用CGLIB创建动态代理
1、先看一个简单的代理示例
public class CGLIBSample {
public static void main(String args[]){
Enhancer en = new Enhancer();
en.setSuperclass(CGLIBSample.class);//CGLIB是动态生成目标类的子类,此处设置动态代理的父类
en.setCallback(new CGLIBMethodIntercept());//设置回调
CGLIBSample samp = (CGLIBSample)en.create();//创建目标代理子类
samp.print();
}
public void print(){
System.out.println("CGLIB学习示例");
}
}
class CGLIBMethodIntercept implements MethodInterceptor{
/**
* obj 代理目标类对象
* arg1 拦截方法,即目标类的方法,一般用不到
* arg2 方法的参数
* proxyMethod 代理类方法,通常首选使用,应为他更快
*/
public Object intercept(Object obj, Method arg1, Object[] arg2,
MethodProxy proxyMethod) throws Throwable {
System.out.println("前置增强(通知)");//在调用方法前我们可以做一些事情,比如说Spring的前置通知
//通过代理子类访问目标类的方法,此处也可以使用arg1直接调用
//但是这两种方法是有区别
Object result = proxyMethod.invokeSuper(obj, arg2);
System.out.println("后置增强(通知)");
return result;
}
}
2、接下来我们看一下创建代理使用的主要类图
1、Callback接口从图中可以看出是一个很重要的接口,所有被Enhancer类调用的回调(callback)接口都实现了这个接口
2、MethodInterceptor 接口是最常用的回调接口,它经常被基于代理的AOP用来拦截方法的调用。当它作为代理方法的回调时,当对基于代理方法的调用时,在调用原对象的方法之前会调用这个方法,如下图所示。
3、FixedValue 为提高性能,FixedValue回调对强制某一特别方法返回固定值是有用的。
4、NoOp NoOp回调把对方法的调用直接委派到这个方法在父类中的实现
5、LazyLoader 当实际的对象需要延迟加载时,可以使用它回调。一旦实际对象被装载,它将被每一个调用代理对象的方法使用
6、Dispatcher Dispatcher回调和LazyLoder回调有相同的特点,不同的是,当代理方法被调用时,装载对象的方法也总要被调用
7、ProxyRefDispatcher ProxyRefDispatcher回调和Dispatcher一样,不同的是,它可以吧代理对象作为装载对象方法的一个参数传递
8、CallbackFilter 有选择的对一些方法使用回调
3、使用CallbackFilter在方法层设置回调
CallbackFilter 允许我们在方法层设置回调。比如一个类中有两个两个方法,一个需要权限检查,一个不需要。我们就可以使用CallbackFilter回调实现。(Spring实例化策略
下面示例中UserService类中identifyMethod方法调用前需要进行权限验证
public class CallbackFilterTest {
public static void main(String[] args){
Enhancer en = new Enhancer();
en.setSuperclass(UserService.class);
CallbackFilter filter = new UserServiceCallbackFilter();
en.setCallbackFilter(filter);
Callback identifyCallBack = new IdentifyMethodInterceptor();
Callback otherCallBack = NoOp.INSTANCE;//直接使用NoOper回调 for 不需要权限验证的方法
Callback[] callbacks = new Callback[]{identifyCallBack,otherCallBack};//数组中的位置必须和CallbackFilter中的一致
en.setCallbacks(callbacks);
UserService userService = (UserService)en.create();
userService.identifyMethod();
userService.ordinaryMethod();
}
}
class UserService{
public void identifyMethod(){
System.out.println("访问这个访问需要权限!");
}
public void ordinaryMethod(){
System.out.println("这是一个普通方法,不需要验证访问权限!");
}
}
/**
* 为需要进行权限验证的回调类
*/
class IdentifyMethodInterceptor implements MethodInterceptor{
public Object intercept(Object arg0, Method arg1, Object[] arg2,
MethodProxy arg3) throws Throwable {
System.out.println("进行访问权限验证……!");
return arg3.invokeSuper(arg0, arg2);
}
}
class UserServiceCallbackFilter implements CallbackFilter{
private static final int IDENTIFY = 0;//回调数组下标,必须与enhance设置的回调数组下标对应
private static final int ORDINARY = 1;
public int accept(Method interceptorMethod) {
if(interceptorMethod.getName().equals("identifyMethod"))
return IDENTIFY;
return ORDINARY;
}
}
4、使用Mixing(可以整合多个接口)
如果接口数组中有相同的方法,则代理类总是调用接口数组中首先声明的接口中的那个方法
public class MixinTest {
public static void main(String[] args){
Class[] interfaces = new Class[]{A.class,B.class};//代理目标类接口数组
Object[] delegates = new Object[]{new AImplement(),new BImplement()};//代理目标类实现数组,需要和上边的接口数组顺序对应
Object obj = Mixin.create(interfaces, delegates);
((A)obj).a();
((B)obj).b();
}
}
interface A{
public void a();
}
interface B{
public void b();
}
class AImplement implements A{
public void a(){
System.out.println("调用A.a()");
}
}
class BImplement implements B{
public void b(){
System.out.println("调用B.b()");
}
}
5、使用LazyLoader实现延迟加载
public class LazyLoaderTest {
public static void main(String[] args){
Enhancer en = new Enhancer();
MyService ser = (MyService)en.create(MyService.class,new Lazy());//此时MyService对象其实还没有加载,只有在调用方法时才去真正示例化
System.out.println("调用MyService.f()!");
ser.f();
}
}
class Lazy implements LazyLoader{
public Object loadObject() throws Exception {
System.out.println("延迟加载开始……");
Thread.currentThread().sleep(5000);//当前线程sleep 5秒,模拟对象加载
return new MyService();
}
}
class MyService{
public void f(){
System.out.println("调用服务");
}
}
CGLIB主要用到的知识在这里就记录的差不多了,下面在粘上Spring中使用CGLIB技术实现对bean的实例化。
/**
* An inner class so we don't have a CGLIB dependency in core.
*/
private static class CglibSubclassCreator {
private static final Log logger = LogFactory.getLog(CglibSubclassCreator.class);
private final RootBeanDefinition beanDefinition;
private final BeanFactory owner;
public CglibSubclassCreator(RootBeanDefinition beanDefinition, BeanFactory owner) {
this.beanDefinition = beanDefinition;
this.owner = owner;
}
public Object instantiate(Constructor ctor, Object[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(this.beanDefinition.getBeanClass());
enhancer.setCallbackFilter(new CallbackFilterImpl());
enhancer.setCallbacks(new Callback[] {
NoOp.INSTANCE,
new LookupOverrideMethodInterceptor(),
new ReplaceOverrideMethodInterceptor()
});
return (ctor == null) ?
enhancer.create() :
enhancer.create(ctor.getParameterTypes(), args);
}
/**
* Class providing hashCode and equals methods required by CGLIB to
* ensure that CGLIB doesn't generate a distinct class per bean.
* Identity is based on class and bean definition.
*/
private class CglibIdentitySupport {
/**
* Exposed for equals method to allow access to enclosing class field
*/
protected RootBeanDefinition getBeanDefinition() {
return beanDefinition;
}
@Override
public boolean equals(Object other) {
return (other.getClass().equals(getClass()) &&
((CglibIdentitySupport) other).getBeanDefinition().equals(beanDefinition));
}
@Override
public int hashCode() {
return beanDefinition.hashCode();
}
}
/**
* CGLIB MethodInterceptor to override methods, replacing them with an
* implementation that returns a bean looked up in the container.
*/
private class LookupOverrideMethodInterceptor extends CglibIdentitySupport implements MethodInterceptor {
public Object intercept(Object obj, Method method, Object[] args, MethodProxy mp) throws Throwable {
// Cast is safe, as CallbackFilter filters are used selectively.
LookupOverride lo = (LookupOverride) beanDefinition.getMethodOverrides().getOverride(method);
return owner.getBean(lo.getBeanName());
}
}
/**
* CGLIB MethodInterceptor to override methods, replacing them with a call
* to a generic MethodReplacer.
*/
private class ReplaceOverrideMethodInterceptor extends CglibIdentitySupport implements MethodInterceptor {
public Object intercept(Object obj, Method method, Object[] args, MethodProxy mp) throws Throwable {
ReplaceOverride ro = (ReplaceOverride) beanDefinition.getMethodOverrides().getOverride(method);
// TODO could cache if a singleton for minor performance optimization
MethodReplacer mr = (MethodReplacer) owner.getBean(ro.getMethodReplacerBeanName());
return mr.reimplement(obj, method, args);
}
}
/**
* CGLIB object to filter method interception behavior.
*/
private class CallbackFilterImpl extends CglibIdentitySupport implements CallbackFilter {
public int accept(Method method) {
MethodOverride methodOverride = beanDefinition.getMethodOverrides().getOverride(method);
if (logger.isTraceEnabled()) {
logger.trace("Override for '" + method.getName() + "' is [" + methodOverride + "]");
}
if (methodOverride == null) {
return PASSTHROUGH;
}
else if (methodOverride instanceof LookupOverride) {
return LOOKUP_OVERRIDE;
}
else if (methodOverride instanceof ReplaceOverride) {
return METHOD_REPLACER;
}
throw new UnsupportedOperationException(
"Unexpected MethodOverride subclass: " + methodOverride.getClass().getName());
}
}
}