现在的位置: 首页 > 综合 > 正文

CGlib简单介绍

2013年10月05日 ⁄ 综合 ⁄ 共 15040字 ⁄ 字号 评论关闭

原文地址:http://www.blogjava.net/stone2083/archive/2008/03/16/186615.html

CGlib概述:
cglib(Code Generation Library)是一个强大的,高性能,高质量的Code生成类库。它可以在运行期扩展Java类与实现Java接口。
cglib封装了asm,可以在运行期动态生成新的class。
cglib用于AOP,jdk中的proxy必须基于接口,cglib却没有这个限制。

CGlib应用:
以一个实例在简单介绍下cglib的应用。
我们模拟一个虚拟的场景,关于信息的管理。

1)原始需求是任何人可以操作信息的create,update,delete,query操作。
InfoManager.java--封装对信息的操作

public class InfoManager {
    
// 模拟查询操作
    public void query() {
        System.out.println(
"query");
    }
    
// 模拟创建操作
    public void create() {
        System.out.println(
"create");
    }
    
// 模拟更新操作
    public void update() {
        System.out.println(
"update");
    }
    
// 模拟删除操作
    public void delete() {
        System.out.println(
"delete");
    }
}

InfoManagerFactory.java--工厂类

public class InfoManagerFactory {
    
private static InfoManager manger = new InfoManager();
    
/**
     * 创建原始的InfoManager
     * 
     * 
@return
     
*/
    
public static InfoManager getInstance() {
        
return manger;
    }
}

client.java--供客户端调用

public class Client {

    public static void main(String[] args) {
        Client c 
= new Client();
        c.anyonecanManager();
    }

    /**
     * 模拟:没有任何权限要求,任何人都可以操作
     
*/
    
public void anyonecanManager() {
        System.out.println(
"any one can do manager");
        InfoManager manager 
= InfoManagerFactory.getInstance();
        doCRUD(manager);
        separatorLine();
    }

    /**
     * 对Info做增加/更新/删除/查询操作
     * 
     * 
@param manager
     
*/
    
private void doCRUD(InfoManager manager) {
        manager.create();
        manager.update();
        manager.delete();
        manager.query();
    }

    /**
     * 加一个分隔行,用于区分
     
*/
    
private void separatorLine() {
        System.out.println(
"################################");
    }

}

至此,没有涉及到cglib的内容,因为需求太简单了,但是接下来,需求发生了改变,要求:

2)只有一个叫“maurice”的用户登录,才允许对信息进行create,update,delete,query的操作。
怎么办?难道在每个方法前,都加上一个权限判断吗?这样重复逻辑太多了,于是乎想到了Proxy(代理模式),但是原先的InfoManager也没有实现接口,不能采用jdk的proxy。那么cglib在这边就要隆重登场。
一旦使用cgblig,只需要添加一个MethodInterceptor的类以及修改factory代码就可以实现这个需求。
AuthProxy.java--权限校验代理类

public class AuthProxy implements MethodInterceptor {

    private String name; // 会员登录名

    
public AuthProxy(String name) {
        
this.name = name;
    }

    /**
     * 权限校验,如果会员名为:maurice,则有权限做操作,否则提示没有权限
     
*/
    @Override
    
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        
if (!"maurice".equals(this.name)) {
            System.out.println(
"AuthProxy:you have no permits to do manager!");
            
return null;
        }
        
return proxy.invokeSuper(obj, args);
    }

    public String getName() {
        
return name;
    }

    public void setName(String name) {
        
this.name = name;
    }

}

InfoManagerFactory.java--代码变动如下:

public class InfoManagerFactory {

    /**
     * 创建带有权限检验的InfoManager
     * 
     * 
@param auth
     * 
@return
     
*/
    
public static InfoManager getAuthInstance(AuthProxy auth) {
        Enhancer enhancer 
= new Enhancer();
        enhancer.setSuperclass(InfoManager.
class);
        enhancer.setCallback(auth);
        
return (InfoManager) enhancer.create();
    }

    
}

client.java--代码修改如下

public class Client {

    public static void main(String[] args) {
        Client c 
= new Client();
        c.haveNoAuthManager();
        c.haveAuthManager();
    }

    /**
     * 模拟:登录会员没有权限
     
*/
    
public void haveNoAuthManager() {
        System.out.println(
"the loginer's name is not maurice,so have no permits do manager");
        InfoManager noAuthManager 
= InfoManagerFactory.getAuthInstance(new AuthProxy("maurice1"));
        doCRUD(noAuthManager);
        separatorLine();
    }

    /**
     * 模拟:登录会员有权限
     
*/
    
public void haveAuthManager() {
        System.out.println(
"the loginer's name is maurice,so have permits do manager");
        InfoManager authManager 
= InfoManagerFactory.getAuthInstance(new AuthProxy("maurice"));
        doCRUD(authManager);
        separatorLine();
    }

    /**
     * 对Info做增加/更新/删除/查询操作
     * 
     * 
@param manager
     
*/
    
private void doCRUD(InfoManager manager) {
        manager.create();
        manager.update();
        manager.delete();
        manager.query();
    }

    /**
     * 加一个分隔行,用于区分
     
*/
    
private void separatorLine() {
        System.out.println(
"################################");
    }

}

执行下代码,发现这时client端中已经加上了权限校验。
同样是InfoManager,为什么这时能多了权限的判断呢?Factory中enhancer.create()返回的到底是什么对象呢?这个疑问将在第三部分CGlib中解释。
这边的代码,其实是介绍了cglib中的enhancer功能.

到这里,参照上面的代码,就可以使用cglib带来的aop功能了。但是为了更多介绍下cglib的功能,模拟需求再次发生变化:

3)由于query功能用户maurice才能使用,招来其他用户的强烈的抱怨,所以权限再次变更,只有create,update,delete,才需要权限保护,query任何人都可以使用。
怎么办?采用AuthProxy,使得InfoManager中的所有方法都被代理,加上了权限的判断。当然,最容易想到的办法,就是在AuthProxy的intercept的方法中再做下判断,如果代理的method是query,不需要权限验证。这么做,可以,但是一旦逻辑比较复杂的时候,intercept这个方法要做的事情会很多,逻辑会异常的复杂。
幸好,cglib还提供了CallbackFilter。使用CallbackFilter,可以明确表明,被代理的类(InfoManager)中不同的方法,被哪个拦截器(interceptor)拦截。
AuthProxyFilter.java

public class AuthProxyFilter implements CallbackFilter {

    private static final int AUTH_NEED     = 0;
    
private static final int AUTH_NOT_NEED = 1;

    /**
     * <pre>
     * 选择使用的proxy
     * 如果调用query函数,则使用第二个proxy
     * 否则,使用第一个proxy
     * </pre>
     
*/
    @Override
    
public int accept(Method method) {
        
if ("query".equals(method.getName())) {
            
return AUTH_NOT_NEED;
        }
        
return AUTH_NEED;
    }

}

这段代码什么意思?其中的accept方法的意思是说,如果代理的方法是query(),那么使用第二个拦截器去拦截,如果代理的方法不是query(),那么使用第一个拦截器去拦截。所以我们只要再写一个拦截器,不做权限校验就行了。(其实,cglib中的NoOp.INSTANCE就是一个空的拦截器,只要配置上这个就可以了。)
InfoManagerFactory.java--代码修改如下:(配置不同的拦截器和filter)

public class InfoManagerFactory {

    /**
     * 创建不同权限要求的InfoManager
     * 
     * 
@param auth
     * 
@return
     
*/
    
public static InfoManager getSelectivityAuthInstance(AuthProxy auth) {
        Enhancer enhancer 
= new Enhancer();
        enhancer.setSuperclass(InfoManager.
class);
        enhancer.setCallbacks(
new Callback[] { auth, NoOp.INSTANCE });
        enhancer.setCallbackFilter(
new AuthProxyFilter());
        
return (InfoManager) enhancer.create();
    }

}

记住:setCallbacks中的拦截器(interceptor)的顺序,一定要和CallbackFilter里面指定的顺序一致!!切忌。

Client.java

public class Client {

    public static void main(String[] args) {
        Client c 
= new Client();
        c.selectivityAuthManager();
    }
    
    
/**
     * 模拟:没有权限的会员,可以作查询操作
     
*/
    
public void selectivityAuthManager() {
        System.out.println(
"the loginer's name is not maurice,so have no permits do manager except do query operator");
        InfoManager authManager 
= InfoManagerFactory.getSelectivityAuthInstance(new AuthProxy("maurice1"));
        doCRUD(authManager);
        separatorLine();
    }

    /**
     * 对Info做增加/更新/删除/查询操作
     * 
     * 
@param manager
     
*/
    
private void doCRUD(InfoManager manager) {
        manager.create();
        manager.update();
        manager.delete();
        manager.query();
    }

    /**
     * 加一个分隔行,用于区分
     
*/
    
private void separatorLine() {
        System.out.println(
"################################");
    }

}

此时,对于query的权限校验已经被去掉了。

通过一个模拟需求,简单介绍了cglib aop功能的使用。
CGlib应用非常广,在spring,hibernate等框架中,被大量的使用。


CGlib原理:
cglib神奇吗?其实一旦了解cglib enhancer的原理,一切就真相大白了。
刚才在第二部分中,有个疑问:enhancer.create()到底返回了什么对象?

其实在刚才的例子中,cglib在代码运行期,动态生成了InfoManager的子类,并且根据CallbackFilter的accept方法,覆写了InfoManager中的所有方法--去执行相应的MethodInterceptor的intercept方法。

有兴趣的朋友可以看看我反编译的InfoManager的子类,就可以很明白知道具体的实现了。(需要有耐心才可以)
InfoManager$$EnhancerByCGLIB$$de624598.jad

// Decompiled by Jad v1.5.8e. Copyright 2001 Pavel Kouznetsov.
// Jad home page: http://www.geocities.com/kpdus/jad.html
// Decompiler options: packimports(3) 
// Source File Name:   <generated>

package cn.eulic.codelab.cglib;

import java.lang.reflect.Method;
import net.sf.cglib.core.Signature;
import net.sf.cglib.proxy.*;

// Referenced classes of package cn.eulic.codelab.cglib:
//            InfoManager

public class CGLIB.BIND_CALLBACKS extends InfoManager
    
implements Factory
{

    static void CGLIB$STATICHOOK1()
    {
        Class class1;
        ClassLoader classloader;
        CGLIB$THREAD_CALLBACKS 
= new ThreadLocal();
        classloader 
= (class1 = Class.forName("cn.eulic.codelab.cglib.InfoManager$$EnhancerByCGLIB$$de624598")).getClassLoader();
        classloader;
        CGLIB$emptyArgs 
= new Object[0];
        CGLIB$delete$
0$Proxy = MethodProxy.create(classloader, (CGLIB$delete$0$Method = Class.forName("cn.eulic.codelab.cglib.InfoManager").getDeclaredMethod("delete"new Class[0])).getDeclaringClass(), class1, "()V""delete""CGLIB$delete$0");
        CGLIB$create$
1$Proxy = MethodProxy.create(classloader, (CGLIB$create$1$Method = Class.forName("cn.eulic.codelab.cglib.InfoManager").getDeclaredMethod("create"new Class[0])).getDeclaringClass(), class1, "()V""create""CGLIB$create$1");
        CGLIB$query$
2$Proxy = MethodProxy.create(classloader, (CGLIB$query$2$Method = Class.forName("cn.eulic.codelab.cglib.InfoManager").getDeclaredMethod("query"new Class[0])).getDeclaringClass(), class1, "()V""query""CGLIB$query$2");
        CGLIB$update$
3$Proxy = MethodProxy.create(classloader, (CGLIB$update$3$Method = Class.forName("cn.eulic.codelab.cglib.InfoManager").getDeclaredMethod("update"new Class[0])).getDeclaringClass(), class1, "()V""update""CGLIB$update$3");
        CGLIB$finalize$
4$Proxy = MethodProxy.create(classloader, (CGLIB$finalize$4$Method = Class.forName("java.lang.Object").getDeclaredMethod("finalize"new Class[0])).getDeclaringClass(), class1, "()V""finalize""CGLIB$finalize$4");
        CGLIB$hashCode$
5$Proxy = MethodProxy.create(classloader, (CGLIB$hashCode$5$Method = Class.forName("java.lang.Object").getDeclaredMethod("hashCode"new Class[0])).getDeclaringClass(), class1, "()I""hashCode""CGLIB$hashCode$5");
        CGLIB$clone$
6$Proxy = MethodProxy.create(classloader, (CGLIB$clone$6$Method = Class.forName("java.lang.Object").getDeclaredMethod("clone"new Class[0])).getDeclaringClass(), class1, "()Ljava/lang/Object;""clone""CGLIB$clone$6");
        CGLIB$equals$
7$Proxy = MethodProxy.create(classloader, (CGLIB$equals$7$Method = Class.forName("java.lang.Object").getDeclaredMethod("equals"new Class[] {
            Class.forName(
"java.lang.Object")
        })).getDeclaringClass(), class1, 
"(Ljava/lang/Object;)Z""equals""CGLIB$equals$7");
        CGLIB$toString$
8$Proxy = MethodProxy.create(classloader, (CGLIB$toString$8$Method = Class.forName("java.lang.Object").getDeclaredMethod("toString"new Class[0])).getDeclaringClass(), class1, "()Ljava/lang/String;""toString""CGLIB$toString$8");
        
return;
    }

    final void CGLIB$delete$0()
    {
        
super.delete();
    }

    public final void delete()
    {
        CGLIB$CALLBACK_0;
        
if(CGLIB$CALLBACK_0 != nullgoto _L2; else goto _L1
_L1:
        JVM INSTR pop ;
        CGLIB$BIND_CALLBACKS(
this);
        CGLIB$CALLBACK_0;
_L2:
        JVM INSTR dup ;
        JVM INSTR ifnull 
37;
           
goto _L3 _L4
_L3:
        
break MISSING_BLOCK_LABEL_21;
_L4:
        
break MISSING_BLOCK_LABEL_37;
        
this;
        CGLIB$delete$
0$Method;
        CGLIB$emptyArgs;
        CGLIB$delete$
0$Proxy;
        intercept();
        
return;
        
super.delete();
        
return;
    }

    final void CGLIB$create$1()
    {
        
super.create();
    }

    public final void create()
    {
        CGLIB$CALLBACK_0;
        
if(CGLIB$CALLBACK_0 != nullgoto _L2; else goto _L1
_L1:
        JVM INSTR pop ;
        CGLIB$BIND_CALLBACKS(
this);
        CGLIB$CALLBACK_0;
_L2:
        JVM INSTR dup ;
        JVM INSTR ifnull 
37;
           
goto _L3 _L4
_L3:
        
break MISSING_BLOCK_LABEL_21;
_L4:
        
break MISSING_BLOCK_LABEL_37;
        
this;
        CGLIB$create$
1$Method;
        CGLIB$emptyArgs;
        CGLIB$create$
1$Proxy;
        intercept();
        
return;
        
super.create();
        
return;
    }

    final void CGLIB$query$2()
    {
        
super.query();
    }

    public final void query()
    {
        CGLIB$CALLBACK_0;
        
if(CGLIB$CALLBACK_0 != nullgoto _L2; else goto _L1
_L1:
        JVM INSTR pop ;
        CGLIB$BIND_CALLBACKS(
this);
        CGLIB$CALLBACK_0;
_L2:
        JVM INSTR dup ;
        JVM INSTR ifnull 
37;
           
goto _L3 _L4
_L3:
        
break MISSING_BLOCK_LABEL_21;
_L4:
        
break MISSING_BLOCK_LABEL_37;
        
this;
        CGLIB$query$
2$Method;
        CGLIB$emptyArgs;
        CGLIB$query$
2$Proxy;
        intercept();
        
return;
        
super.query();
        
return;
    }

    final void CGLIB$update$3()
    {
        
super.update();
    }

    public final void update()
    {
        CGLIB$CALLBACK_0;
        
if(CGLIB$CALLBACK_0 != nullgoto _L2; else goto _L1
_L1:
        JVM INSTR pop ;
        CGLIB$BIND_CALLBACKS(
this);
        CGLIB$CALLBACK_0;
_L2:
        JVM INSTR dup ;
        JVM INSTR ifnull 
37;
           
goto _L3 _L4
_L3:
        
break MISSING_BLOCK_LABEL_21;
_L4:
        
break MISSING_BLOCK_LABEL_37;
        
this;
        CGLIB$update$
3$Method;
        CGLIB$emptyArgs;
        CGLIB$update$
3$Proxy;
        intercept();
        
return;
        
super.update();
        
return;
    }

    final void CGLIB$finalize$4()
        
throws Throwable
    {
        
super.finalize();
    }

    protected final void finalize()
        
throws Throwable
    {
        CGLIB$CALLBACK_0;
        
if(CGLIB$CALLBACK_0 != nullgoto _L2; else goto _L1
_L1:
        JVM INSTR pop ;
        CGLIB$BIND_CALLBACKS(
this);
        CGLIB$CALLBACK_0;
_L2:
        JVM INSTR dup ;
        JVM INSTR ifnull 
37;
           
goto _L3 _L4
_L3:
        
break MISSING_BLOCK_LABEL_21;
_L4:
        
break MISSING_BLOCK_LABEL_37;
        
this;
        CGLIB$finalize$
4$Method;
        CGLIB$emptyArgs;
        CGLIB$finalize$
4$Proxy;
        intercept();
        
return;
        
super.finalize();
        
return;
    }

    final int CGLIB$hashCode$5()
    {
        
return super.hashCode();
    }

    public final int hashCode()
    {
        CGLIB$CALLBACK_0;
        
if(CGLIB$CALLBACK_0 != nullgoto _L2; else goto _L1
_L1:
        JVM INSTR pop ;
        CGLIB$BIND_CALLBACKS(
this);
        CGLIB$CALLBACK_0;
_L2:
        JVM INSTR dup ;
        JVM INSTR ifnull 
52;
           
goto _L3 _L4
_L3:
        
this;
        CGLIB$hashCode$
5$Method;
        CGLIB$emptyArgs;
        CGLIB$hashCode$
5$Proxy;
        intercept();
        JVM INSTR dup ;
        JVM INSTR ifnonnull 
45;
           
goto _L5 _L6
_L5:
        JVM INSTR pop ;
        
0;
          
goto _L7
_L6:
        (Number);
        intValue();
_L7:
        
return;
_L4:
        
return super.hashCode();
    }

    final Object CGLIB$clone$6()
        
throws CloneNotSupportedException
    {
        
return super.clone();
    }

    protected final Object clone()
        
throws CloneNotSupportedException
    {
        CGLIB$CALLBACK_0;
        
if(CGLIB$CALLBACK_0 != nullgoto _L2; else goto _L1
_L1:
        JVM INSTR pop ;
        CGLIB$BIND_CALLBACKS(
this);
        CGLIB$CALLBACK_0;
_L2:
        JVM INSTR dup ;
        JVM INSTR ifnull 
37;
           
goto _L3 _L4
_L3:
        
this;
        CGLIB$clone$
6$Method;
        CGLIB$emptyArgs;
        CGLIB$clone$
6$Proxy;
        intercept();
        
return;
_L4:
        
return super.clone();
    }

    final boolean CGLIB$equals$7(Object obj)
    {
        
return super.equals(obj);
    }

    public final boolean equals(Object obj)
    {
        CGLIB$CALLBACK_0;
        
if(CGLIB$CALLBACK_0 != nullgoto _L2; else goto _L1
_L1:
        JVM INSTR pop ;
        CGLIB$BIND_CALLBACKS(
this);
        CGLIB$CALLBACK_0;
_L2:
        JVM INSTR

【上篇】
【下篇】

抱歉!评论已关闭.