`
ry.china
  • 浏览: 137140 次
  • 性别: Icon_minigender_1
  • 来自: 重庆
社区版块
存档分类
最新评论

CGlib简单介绍

    博客分类:
  • java
阅读更多
转载:http://hacker0825.blog.163.com/blog/static/34570677201051993121226/
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  !=   null goto  _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  !=   null goto  _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  !=   null goto  _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  !=   null goto  _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  !=   null goto  _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  !=   null goto  _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 <spa
分享到:
评论

相关推荐

    Spring AOP源码分析.mmap

    有关于Spring,我们最常用的两个功能就是IOC和AOP,前几篇文章从源码级别介绍了Spring容器如何为我们生成bean及bean之间的依赖关系 下面我们接着来看AOP的源码实现。 有关于AOP,我们在面试中也被无数次问到...

    JavaEE技术问题汇总.docx

    OSI模型:七层模型介绍 wait方法和Sleep方法简单对比描述一下 comparable与comparator的区别 GC垃圾回收机机制? Java序列化与反序列化是什么?如何实现Java序列化与反序列化.序列化和反序列化使用的API ...

    蚂蚁云客服机器人面试答案.docx

    1、自我介绍、自己做的项目和技术领域 开放题 2、项目中的监控:那个监控指标常见的有哪些? 答:CPU、内存、IO 等等。建议下载个nmon工具,里面有各个指标。 数据库:Mysql(缓存命中、索引、单条SQL性能、数据库...

    《MyEclipse 6 Java 开发中文教程》前10章

    第一章 安装配置开发环境 18 1.1系统需求 18 1.2 JDK 的下载,安装和配置(可...10.7.2 MyEclipse生成的Spring+Hibernate无法保存数据问题的解决方法2 - 用 CGLIB 来实现事务管理 258 10.7.3 Spring相关的参考资料 261

    Spring.3.x企业应用开发实战(完整版).part2

    6.2.3 CGLib动态代理 6.2.4 AOP联盟 6.2.5 代理知识小结 6.3 创建增强类 6.3.1 增强类型 6.3.2 前置增强 6.3.3 后置增强 6.3.4 环绕增强 6.3.5 异常抛出增强 6.3.6 引介增强 6.4 创建切面 6.4.1 切点类型 6.4.2 切面...

    Spring3.x企业应用开发实战(完整版) part1

    6.2.3 CGLib动态代理 6.2.4 AOP联盟 6.2.5 代理知识小结 6.3 创建增强类 6.3.1 增强类型 6.3.2 前置增强 6.3.3 后置增强 6.3.4 环绕增强 6.3.5 异常抛出增强 6.3.6 引介增强 6.4 创建切面 6.4.1 切点类型 6.4.2 切面...

    Spring 2.0 开发参考手册

    7.5.3. 基于JDK和CGLIB的代理 7.5.4. 对接口进行代理 7.5.5. 对类进行代理 7.5.6. 使用“全局”advisor 7.6. 简化代理定义 7.7. 使用ProxyFactory通过编程创建AOP代理 7.8. 操作被通知对象 7.9. 使用“自动...

    spring chm文档

    7.5.3. 基于JDK和CGLIB的代理 7.5.4. 对接口进行代理 7.5.5. 对类进行代理 7.5.6. 使用“全局”advisor 7.6. 简化代理定义 7.7. 使用ProxyFactory通过编程创建AOP代理 7.8. 操作被通知对象 7.9. 使用“自动...

    Spring-Reference_zh_CN(Spring中文参考手册)

    7.5.3. 基于JDK和CGLIB的代理 7.5.4. 对接口进行代理 7.5.5. 对类进行代理 7.5.6. 使用“全局”advisor 7.6. 简化代理定义 7.7. 使用ProxyFactory通过编程创建AOP代理 7.8. 操作被通知对象 7.9. 使用“自动代理...

    Spring中文帮助文档

    7.5.3. 基于JDK和CGLIB的代理 7.5.4. 对接口进行代理 7.5.5. 对类进行代理 7.5.6. 使用“全局”通知器 7.6. 简化代理定义 7.7. 使用ProxyFactory通过编程创建AOP代理 7.8. 操作被通知对象 7.9. 使用“自动...

    Spring API

    7.5.3. 基于JDK和CGLIB的代理 7.5.4. 对接口进行代理 7.5.5. 对类进行代理 7.5.6. 使用“全局”通知器 7.6. 简化代理定义 7.7. 使用ProxyFactory通过编程创建AOP代理 7.8. 操作被通知对象 7.9. 使用“自动...

Global site tag (gtag.js) - Google Analytics