zoukankan      html  css  js  c++  java
  • JDK动态代理与Cglib库

    JDK动态代理

      代理模式是常用的java设计模式,他的特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后处理消息等。代理类与委托类之间通常会存在关联关系,一个代理类的对象与一个委托类的对象关联,代理类的对象本身并不真正实现服务,而是通过调用委托类的对象的相关方法,来提供特定的服务。 
      按照代理的创建时期,代理类可以分为两种。 
      静态代理:由程序员创建或特定工具自动生成源代码,再对其编译。在程序运行前,代理类的.class文件就已经存在了。 
      动态代理:在程序运行时,运用反射机制动态创建而成。 

      为什么使用动态代理?因为动态代理可以对请求进行任何处理。
      哪些地方需要动态代理?不允许直接访问某些类;对访问要做特殊处理等。
      目前Java开发包中包含了对动态代理的支持,但是其实现只支持对接口的的实现。 其实现主要通过java.lang.reflect.Proxy类和java.lang.reflect.InvocationHandler接口。 Proxy类主要用来获取动态代理对象,InvocationHandler接口用来约束调用者实现。

      以下为模拟案例,通过动态代理实现在方法调用前后向控制台输出两句字符串。

      定义一个HelloWorld接口:

    [java] view plaincopy
     
    1. package com.ljq.test;  
    2.   
    3.  /** 
    4.  * 定义一个HelloWorld接口 
    5.  *  
    6.  * @author jiqinlin 
    7.  * 
    8.  */  
    9.  public interface HelloWorld {  
    10.     public void sayHelloWorld();  
    11. }  

      类HelloWorldImpl是HelloWorld接口的实现:

    [java] view plaincopy
     
    1. package com.ljq.test;  
    2.   
    3.  /** 
    4.  * 类HelloWorldImpl是HelloWorld接口的实现 
    5.  *  
    6.  * @author jiqinlin 
    7.  * 
    8.  */  
    9.  public class HelloWorldImpl implements HelloWorld{  
    10.   
    11.     public void sayHelloWorld() {  
    12.         System.out.println("HelloWorld!");  
    13.     }  
    14.   
    15. }  

      HelloWorldHandler是 InvocationHandler接口实现:

    [java] view plaincopy
     
    1. package com.ljq.test;  
    2.   
    3.  import java.lang.reflect.InvocationHandler;  
    4.  import java.lang.reflect.Method;  
    5.   
    6.  /** 
    7.  * 实现在方法调用前后向控制台输出两句字符串 
    8.  *  
    9.  * @author jiqinlin 
    10.  * 
    11.  */  
    12.  public class HelloWorldHandler implements InvocationHandler{  
    13.     //要代理的原始对象  
    14.      private Object obj;  
    15.       
    16.     public HelloWorldHandler(Object obj) {  
    17.         super();  
    18.         this.obj = obj;  
    19.     }  
    20.   
    21.     /** 
    22.      * 在代理实例上处理方法调用并返回结果 
    23.      *  
    24.      * @param proxy 代理类 
    25.      * @param method 被代理的方法 
    26.      * @param args 该方法的参数数组 
    27.      */  
    28.     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {  
    29.         Object result = null;  
    30.         //调用之前  
    31.          doBefore();  
    32.         //调用原始对象的方法  
    33.         result=method.invoke(obj, args);  
    34.         //调用之后  
    35.         doAfter();  
    36.         return result;  
    37.     }  
    38.       
    39.     private void doBefore(){  
    40.         System.out.println("before method invoke");  
    41.     }  
    42.       
    43.     private void doAfter(){  
    44.         System.out.println("after method invoke");  
    45.     }  
    46.       
    47. }  

      测试类:

    [java] view plaincopy
     
    1. package com.ljq.test;  
    2.   
    3. import java.lang.reflect.InvocationHandler;  
    4. import java.lang.reflect.Proxy;  
    5.   
    6.   
    7. public class HelloWorldTest {  
    8.   
    9.     public static void main(String[] args) {  
    10.         HelloWorld helloWorld=new HelloWorldImpl();  
    11.         InvocationHandler handler=new HelloWorldHandler(helloWorld);  
    12.   
    13.         //创建动态代理对象  
    14.         HelloWorld proxy=(HelloWorld)Proxy.newProxyInstance(  
    15.             helloWorld.getClass().getClassLoader(),  
    16.             helloWorld.getClass().getInterfaces(),  
    17.             handler);  
    18.         proxy.sayHelloWorld();  
    19.     }  
    20. }  

      运行结果为:

    [plain] view plaincopy
     
    1. before method invoke  
    2. HelloWorld!  
    3. after method invoke  

      基本流程:用Proxy类创建目标类的动态代理,创建时需要指定一个自己实现InvocationHandler接口的回调类的对象,这个回调类中有一个invoke()用于拦截对目标类各个方法的调用。创建好代理后就可以直接在代理上调用目标对象的各个方法。

      JDK自从1.3版本开始,就引入了动态代理,并且经常被用来动态地创建代理。JDK的动态代理用起来非常简单,但它有一个限制,就是使用动态代理的对象必须实现一个或多个接口。比如上面的HelloWorldImpl类,实现了HelloWorld接口,所以可以用JDK的动态代理。如果想代理没有实现接口的继承的类,该怎么办? CGLIB就是最好的选择(https://github.com/cglib/cglib,使用apache license 2.0)。其他比较有名的还有从JBoss项目衍生出来的Javassist(https://github.com/jboss-javassist/javassist),这里介绍Cglib。

    Cglib代码生成库

      CGlib是一个强大的,高性能,高质量的Code生成类库。它可以在运行期扩展Java类与实现Java接口。其底层是通过小而快的字节码处理框架ASM(http://forge.ow2.org/projects/asm,使用BSD License)来转换字节码并生成新的类。大部分功能实际上是asm所提供的,CGlib只是封装了asm,简化了asm的操作,实现了在运行期动态生成新的class。

      CGlib被许多AOP的框架使用,例如Spring AOP和dynaop,为他们提供方法的interception(拦截);最流行的OR Mapping工具hibernate也使用CGLIB来代理单端single-ended(多对一和一对一)关联(对集合的延迟抓取,是采用其他机制实现的);EasyMock和jMock是通过使用模仿(moke)对象来测试java代码的包,它们都通过使用CGLIB来为那些没有接口的类创建模仿(moke)对象。

      CGLIB包的基本代码很少,但学起来有一定的困难,主要是缺少文档,API描述过于简单,这也是开源软件的一个不足之处。目前CGLIB的版本是cglib-2.2.jar,主要由一下部分组成:
      (1)net.sf.cglib.core:底层字节码处理类,他们大部分与ASM有关系。
      (2)net.sf.cglib.transform:编译期或运行期类和类文件的转换。
      (3)net.sf.cglib.proxy :实现创建代理和方法拦截器的类。
      (4)net.sf.cglib.reflect :实现快速反射和C#风格代理的类。
      (5)net.sf.cglib.util:集合排序工具类。
      (6)net.sf.cglib.beans:JavaBean相关的工具类。

      CGLIB包是在ASM之上的一个高级别的层。对代理那些没有实现接口的类非常有用。本质上,它是通过动态的生成一个子类去覆盖所要代理类的不是final的方法,并设置好callback,则原有类的每个方法调用就会转变成调用用户定义的拦截方法(interceptors),这比JDK动态代理方法快多了。可见,Cglib的原理是对指定的目标类动态生成一个子类,并覆盖其中方法实现增强,但因为采用的是继承,所以不能对final修饰的类和final方法进行代理。

    用Cglib创建动态代理

      下图表示Cglib常用到的几类。

    图1 Cglib主要的接口

      创建一个具体类的代理时,通常要用到的CGLIB包的APIs:

      net.sf.cglib.proxy.Callback接口:在CGLIB包中是一个很关键的接口,所有被net.sf.cglib.proxy.Enhancer类调用的回调(callback)接口都要继承这个接口。

      net.sf.cglib.proxy.MethodInterceptor接口:是最通用的回调(callback)类型,它经常被AOP用来实现拦截(intercept)方法的调用。这个接口只定义了一个方法。

    [java] view plaincopy
     
    1. public Object intercept(Object object, java.lang.reflect.Method method, Object[] args, MethodProxy proxy) throws Throwable;  

      当net.sf.cglib.proxy.MethodInterceptor做为所有代理方法的回调 (callback)时,当对基于代理的方法调用时,在调用原对象的方法的之前会调用这个方法,如图下图所示。第一个参数是代理对像,第二和第三个参数分别 是拦截的方法和方法的参数。原来的方法可能通过使用java.lang.reflect.Method对象的一般反射调用,或者使用 net.sf.cglib.proxy.MethodProxy对象调用。net.sf.cglib.proxy.MethodProxy通常被首选使用,因为它更快。在这个方法中,我们可以在调用原方法之前或之后注入自己的代码。

    图1

      net.sf.cglib.proxy.MethodInterceptor能够满足任何的拦截(interception )需要,当对有些情况下可能过度。为了简化和提高性能,CGLIB包提供了一些专门的回调(callback)类型。例如:

      net.sf.cglib.proxy.FixedValue:为提高性能,FixedValue回调对强制某一特别方法返回固定值是有用的。

      net.sf.cglib.proxy.NoOp:NoOp回调把对方法调用直接委派到这个方法在父类中的实现。

      net.sf.cglib.proxy.LazyLoader:当实际的对象需要延迟装载时,可以使用LazyLoader回调。一旦实际对象被装载,它将被每一个调用代理对象的方法使用。

      net.sf.cglib.proxy.Dispatcher:Dispathcer回调和LazyLoader回调有相同的特点,不同的是,当代理方法被调用时,装载对象的方法也总要被调用。

       net.sf.cglib.proxy.ProxyRefDispatcher:ProxyRefDispatcher回调和Dispatcher一样,不同的是,它可以把代理对象作为装载对象方法的一个参数传递。

      代理类的所以方法经常会用到回调(callback),当然你也可以使用net.sf.cglib.proxy.CallbackFilter 有选择的对一些方法使用回调(callback),这种考虑周详的控制特性在JDK的动态代理中是没有的。在JDK代理中,对 java.lang.reflect.InvocationHandler方法的调用对代理类的所有方法都有效。

      CGLIB的代理包也对net.sf.cglib.proxy.Mixin提供支持。基本上,它允许多个对象被绑定到一个单一的大对象。在代理中对方法的调用委托到下面相应的对象中。

      接下来我们看看如何使 用CGLIB代理APIs创建代理。

      1、创建一个简单的代理

      CGLIB代理最核心类net.sf.cglib.proxy.Enhancer, 为了创建一个代理,最起码你要用到这个类。首先,让我们使用NoOp回调创建一个代理。

    [java] view plaincopy
     
    1. /**  
    2.  
    3. * Create a proxy using NoOp callback. The target class  
    4. * must have a default zero-argument constructor 
    5. *  
    6. * @param targetClass the super class of the proxy  
    7. * @return a new proxy for a target class instance  
    8. */   
    9. public Object createProxy(Class targetClass) {   
    10.     Enhancer enhancer = new Enhancer();  
    11.     enhancer.setSuperclass(targetClass);  
    12.     enhancer.setCallback(NoOp.INSTANCE);  
    13.     return enhancer.create();  
    14. }   

      返回值是target类一个实例的代理。在这个例子中,我们为net.sf.cglib.proxy.Enhancer 配置了一个单一的回调(callback)。我们可以看到很少直接创建一个简单的代理,而是创建一个net.sf.cglib.proxy.Enhancer的实例,在net.sf.cglib.proxy.Enhancer类中你可使用静态帮助方法创建一个简单的代理。一般推荐使用上面例子的方法创建代理,因为它允许你通过配置net.sf.cglib.proxy.Enhancer实例很好的控制代理的创建。

      要注意的是,target类是作为产生的代理的父类传进来的。不同于JDK的动态代理,它不能在创建代理时传target对象,target对象必须被CGLIB包来创建。在这个例子中,默认的无参数构造器时用来创建target实例的。如果你想用CGLIB来创建有参数的实例,用net.sf.cglib.proxy.Enhancer.create(Class[], Object[])方法替代net.sf.cglib.proxy.Enhancer.create()就可以了。方法中第一个参数定义了参数的类型,第 二个是参数的值。在参数中,基本类型应被转化成类的类型。

      2、使用MethodInterceptor创建一个代理

      为了更好的使用代理,我们可以使用自己定义的MethodInterceptor类型回调(callback)来代替net.sf.cglib.proxy.NoOp回调。当对代理中所有方法的调用时,都会转向MethodInterceptor类型的拦截(intercept)方法,在拦截方法中再调用底层对象相应的方法。下面我们举个例子,假设你想对目标对象的所有方法调用进行权限的检查,如果没有经过授权,就抛出一个运行时的异常AuthorizationException。其中AuthorizationService.java接口的代码如下:

    [java] view plaincopy
     
    1. package com.lizjason.cglibproxy;   
    2.   
    3. import java.lang.reflect.Method;   
    4.   
    5. /**  
    6.  * A simple authorization service for illustration purpose.  
    7.  * @author Jason Zhicheng Li (jason@lizjason.com)  
    8.  */   
    9. public interface AuthorizationService {   
    10.     void authorize(Method method);   
    11. }  

      对net.sf.cglib.proxy.MethodInterceptor接口的实现的类AuthorizationInterceptor.java代码如下:

    [java] view plaincopy
     
    1. package com.lizjason.cglibproxy.impl;  
    2. import java.lang.reflect.Method;  
    3. import net.sf.cglib.proxy.MethodInterceptor;  
    4. import net.sf.cglib.proxy.MethodProxy;  
    5.   
    6. import com.lizjason.cglibproxy.AuthorizationService;  
    7.   
    8. /** 
    9.  * A simple MethodInterceptor implementation to 
    10.  * apply authorization checks for proxy method calls. 
    11.  */  
    12. public class AuthorizationInterceptor implements MethodInterceptor {  
    13.   
    14.     private AuthorizationService authorizationService;  
    15.   
    16.     /** 
    17.      * Create a AuthorizationInterceptor with the given AuthorizationService 
    18.      */  
    19.     public AuthorizationInterceptor (AuthorizationService authorizationService) {  
    20.         this.authorizationService = authorizationService;  
    21.     }  
    22.   
    23.     /** 
    24.      * Intercept the proxy method invocations to inject authorization check. * The original 
    25.      * method is invoked through MethodProxy. 
    26.      */  
    27.     public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {  
    28.         if (authorizationService != null) {  
    29.             //may throw an AuthorizationException if authorization failed  
    30.             authorizationService.authorize(method);  
    31.         }  
    32.         return methodProxy.invokeSuper(object, args);  
    33.     }  
    34. }  

      我们可以看到在拦截方法中,首先进行权限的检查,如果通过权限的检查,拦截方法再调用目标对象的原始方法。由于性能的原因,对原始方法的调用我们使用CGLIB的net.sf.cglib.proxy.MethodProxy对象,而不是反射中一般使用java.lang.reflect.Method对象。

      下面是一个完整的使用MethodInterceptor的例子。

    [java] view plaincopy
     
    1. package cglibexample;  
    2.   
    3. import java.lang.reflect.Method;  
    4. import net.sf.cglib.proxy.Enhancer;  
    5. import net.sf.cglib.proxy.MethodInterceptor;  
    6. import net.sf.cglib.proxy.MethodProxy;  
    7.   
    8. /** 
    9.  * 定义一个HelloWorld类,没有实现接口 
    10.  * 
    11.  */  
    12. class HelloWorld {  
    13.   
    14.     public void sayHelloWorld() {  
    15.         System.out.println("HelloWorld!");  
    16.     }  
    17. }  
    18.   
    19. /** 
    20.  * 通过Cglib实现在方法调用前后向控制台输出两句字符串 
    21.  * 
    22.  */  
    23. class CglibProxy implements MethodInterceptor {  
    24.   
    25.     //要代理的原始对象  
    26.     private Object obj;  
    27.   
    28.     public Object createProxy(Object target) {  
    29.         this.obj = target;  
    30.         Enhancer enhancer = new Enhancer();  
    31.         // 设置要代理的目标类,以扩展它的功能  
    32.         enhancer.setSuperclass(this.obj.getClass());  
    33.         // 设置单一回调对象,在回调中拦截对目标方法的调用  
    34.         enhancer.setCallback(this);  
    35.         //设置类装载器  
    36.         enhancer.setClassLoader(target.getClass().getClassLoader());  
    37.         //创建代理对象  
    38.         return enhancer.create();  
    39.     }  
    40.   
    41.     /** 
    42.      * 回调方法:在代理实例上拦截并处理目标方法的调用,返回结果 
    43.      * 
    44.      * @param proxy 代理类 
    45.      * @param method 被代理的方法 
    46.      * @param params 该方法的参数数组 
    47.      * @param methodProxy 
    48.      */  
    49.     @Override  
    50.     public Object intercept(Object proxy, Method method, Object[] params,  
    51.             MethodProxy methodProxy) throws Throwable {  
    52.         Object result = null;  
    53.         // 调用之前  
    54.         doBefore();  
    55.         // 调用目标方法,用methodProxy,  
    56.         // 而不是原始的method,以提高性能  
    57.         result = methodProxy.invokeSuper(proxy, params);  
    58.         // 调用之后  
    59.         doAfter();  
    60.         return result;  
    61.     }  
    62.   
    63.     private void doBefore() {  
    64.         System.out.println("before method invoke");  
    65.     }  
    66.   
    67.     private void doAfter() {  
    68.         System.out.println("after method invoke");  
    69.     }  
    70. }  
    71.   
    72. public class TestCglib {  
    73.   
    74.     public static void main(String[] args) {  
    75.         CglibProxy cglibProxy = new CglibProxy();  
    76.         HelloWorld hw = (HelloWorld) cglibProxy.createProxy(new HelloWorld());  
    77.         hw.sayHelloWorld();  
    78.     }  
    79. }  

      输出结果:

    [plain] view plaincopy
     
    1. before method invoke  
    2. HelloWorld!  
    3. after method invoke  

      基本流程:需要自己写代理类,它实现MethodInterceptor接口,有一个intercept()回调方法用于拦截对目标方法的调用,里面使用methodProxy来调用目标方法。创建代理对象要用Enhance类,用它设置好代理的目标类、有intercept()回调的代理类实例、最后用create()创建并返回代理实例。

      3、使用CallbackFilter在方法层设置回调

      net.sf.cglib.proxy.CallbackFilter允许我们在方法层设置回调(callback)。假如你有一个PersistenceServiceImpl类,它有两个方法:save和load,其中方法save需要权限检查,而方法load不需要权限检查。

    [java] view plaincopy
     
    1. import com.lizjason.cglibproxy.PersistenceService;  
    2. import java.lang.reflect.Method;  
    3. import net.sf.cglib.proxy.CallbackFilter;  
    4.   
    5. /** 
    6.  * A simple implementation of PersistenceService interface 
    7.  */  
    8. class PersistenceServiceImpl implements PersistenceService {  
    9.   
    10.     //需要权限检查  
    11.     public void save(long id, String data) {  
    12.         System.out.println(data + " has been saved successfully.");  
    13.     }  
    14.   
    15.     //不需要权限检查  
    16.     public String load(long id) {  
    17.         return "Test CGLIB CallBackFilter";  
    18.     }  
    19. }  
    20.   
    21. /** 
    22.  * An implementation of CallbackFilter for PersistenceServiceImpl 
    23.  */  
    24. public class PersistenceServiceCallbackFilter implements CallbackFilter {   
    25.     //callback index for save method  
    26.     private static final int SAVE = 0;  
    27.     //callback index for load method  
    28.     private static final int LOAD = 1;  
    29.   
    30.     /** 
    31.      * Specify which callback to use for the method being invoked.  
    32.      * @param method the method being invoked. 
    33.      * @return  
    34.      */  
    35.     @Override  
    36.     public int accept(Method method) {  
    37.         //指定各方法的代理回调索引  
    38.         String name = method.getName();  
    39.         if ("save".equals(name)) {  
    40.             return SAVE;  
    41.         }  
    42.         // for other methods, including the load method, use the  
    43.         // second callback  
    44.         return LOAD;  
    45.     }  
    46. }  

      accept方法中对代理方法和回调进行了匹配,返回的值是某方法在回调数组中的索引。下面是PersistenceServiceImpl类代理的实现。

    [java] view plaincopy
     
    1. ...  
    2. Enhancer enhancer = new Enhancer();  
    3. enhancer.setSuperclass(PersistenceServiceImpl.class);  
    4. //设置回调过滤器  
    5. CallbackFilter callbackFilter = new PersistenceServiceCallbackFilter();  
    6. enhancer.setCallbackFilter(callbackFilter);  
    7. //创建各个目标方法的代理回调  
    8. AuthorizationService authorizationService = ...  
    9. Callback saveCallback = new AuthorizationInterceptor(authorizationService);  
    10. Callback loadCallback = NoOp.INSTANCE;  
    11. //顺序要与指定的回调索引一致  
    12. Callback[] callbacks = new Callback[]{saveCallback, loadCallback };  
    13. enhancer.setCallbacks(callbacks);  //设置回调  
    14. ...  
    15. return (PersistenceServiceImpl)enhancer.create();  //创建代理对象  

      在这个例子中save方法使用了AuthorizationInterceptor实例,load方法使用了NoOp实例。此外,你也可以通过net.sf.cglib.proxy.Enhancer.setInterfaces(Class[])方法指定代理对象所实现的接口。

      除了为net.sf.cglib.proxy.Enhancer指定回调数组,你还可以通过net.sf.cglib.proxy.Enhancer.setCallbackTypes(Class[]) 方法指定回调类型数组。当创建代理时,如果你没有回调实例的数组,就可以使用回调类型。象使用回调一样,你必须使用net.sf.cglib.proxy.CallbackFilter为每一个方法指定一个回调类型索引。

      4、使用Mixin

      Mixin通过代理方式将多种类型的对象绑定到一个大对象上,这样对各个目标类型中的方法调用可以直接在这个大对象上进行。下面是一个例子。

    [java] view plaincopy
     
    1. import net.sf.cglib.proxy.Mixin;  
    2.   
    3. interface MyInterfaceA {  
    4.   
    5.     public void methodA();  
    6. }  
    7.   
    8. interface MyInterfaceB {  
    9.   
    10.     public void methodB();  
    11. }  
    12.   
    13. class MyInterfaceAImpl implements MyInterfaceA {  
    14.   
    15.     @Override  
    16.     public void methodA() {  
    17.         System.out.println("MyInterfaceAImpl.methodA()");  
    18.     }  
    19. }  
    20.   
    21. class MyInterfaceBImpl implements MyInterfaceB {  
    22.   
    23.     @Override  
    24.     public void methodB() {  
    25.         System.out.println("MyInterfaceBImpl.methodB()");  
    26.     }  
    27. }  
    28.   
    29. public class Main {  
    30.   
    31.     public static void main(String[] args) {  
    32.         //各个对象对应的类型  
    33.         Class[] interfaces = new Class[]{MyInterfaceA.class, MyInterfaceB.class};  
    34.         //各个对象  
    35.         Object[] delegates = new Object[]{new MyInterfaceAImpl(), new MyInterfaceBImpl()};  
    36.         //将多个对象绑定到一个大对象上  
    37.         Object obj = Mixin.create(interfaces, delegates);  
    38.         //直接在大对象上调用各个目标方法  
    39.         ((MyInterfaceA)obj).methodA();  
    40.         ((MyInterfaceB)obj).methodB();  
    41.     }  
    42. }  

    动态生成Bean

      我们知道,Java Bean包含一组属性字段,用这些属性来存储和获取值。通过指定一组属性名和属性值的类型,我们可以使用Cglib的BeanGenerator和BeanMap来动态生成Bean。下面是一个例子。

    [java] view plaincopy
     
    1. import java.lang.reflect.Method;  
    2. import java.util.HashMap;  
    3. import java.util.Iterator;  
    4. import java.util.Map;  
    5. import java.util.Set;  
    6. import net.sf.cglib.beans.BeanGenerator;  
    7. import net.sf.cglib.beans.BeanMap;  
    8.   
    9. /** 
    10.  * 动态实体bean 
    11.  * 
    12.  * @author cuiran 
    13.  * @version 1.0 
    14.  */  
    15. class CglibBean {  
    16.   
    17.     //Bean实体Object  
    18.     public Object object = null;  
    19.     //属性map  
    20.     public BeanMap beanMap = null;  
    21.   
    22.     public CglibBean() {  
    23.         super();  
    24.     }  
    25.   
    26.     @SuppressWarnings("unchecked")  
    27.     public CglibBean(Map<String, Class> propertyMap) {  
    28.         //用一组属性生成实体Bean  
    29.         this.object = generateBean(propertyMap);  
    30.         //用实体Bean创建BeanMap,以便可以设置和获取Bean属性的值  
    31.         this.beanMap = BeanMap.create(this.object);  
    32.     }  
    33.   
    34.     /** 
    35.      * 给bean中的属性赋值 
    36.      * 
    37.      * @param property 属性名 
    38.      * @param value 值 
    39.      */  
    40.     public void setValue(String property, Object value) {  
    41.         beanMap.put(property, value);  
    42.     }  
    43.   
    44.     /** 
    45.      * 获取bean中属性的值 
    46.      * 
    47.      * @param property 属性名 
    48.      * @return 值 
    49.      */  
    50.     public Object getValue(String property) {  
    51.         return beanMap.get(property);  
    52.     }  
    53.   
    54.     /** 
    55.      * 得到该实体bean对象 
    56.      * 
    57.      * @return 
    58.      */  
    59.     public Object getObject() {  
    60.         return this.object;  
    61.     }  
    62.   
    63.     @SuppressWarnings("unchecked")  
    64.     private Object generateBean(Map<String, Class> propertyMap) {  
    65.         //根据一组属性名和属性值的类型,动态创建Bean对象  
    66.         BeanGenerator generator = new BeanGenerator();  
    67.         Set keySet = propertyMap.keySet();  
    68.         for (Iterator i = keySet.iterator(); i.hasNext();) {  
    69.             String key = (String) i.next();  
    70.             generator.addProperty(key, (Class) propertyMap.get(key));  
    71.         }  
    72.         return generator.create();  //创建Bean  
    73.     }  
    74. }  
    75.   
    76. /** 
    77.  * Cglib测试类 
    78.  * 
    79.  * @author cuiran 
    80.  * @version 1.0 
    81.  */  
    82. public class CglibTest {  
    83.   
    84.     @SuppressWarnings("unchecked")  
    85.     public static void main(String[] args) throws ClassNotFoundException { // 设置类成员属性  
    86.         HashMap<String, Class> propertyMap = new HashMap<>();  
    87.         propertyMap.put("id", Class.forName("java.lang.Integer"));  
    88.         propertyMap.put("name", Class.forName("java.lang.String"));  
    89.         propertyMap.put("address", Class.forName("java.lang.String")); // 生成动态Bean  
    90.         CglibBean bean = new CglibBean(propertyMap);  
    91.         // 给Bean设置值  
    92.         bean.setValue("id", 123);  //Auto-boxing  
    93.         bean.setValue("name", "454");  
    94.         bean.setValue("address", "789");  
    95.         // 从Bean中获取值,当然获得值的类型是Object  
    96.         System.out.println(" >> id = " + bean.getValue("id"));  
    97.         System.out.println(" >> name = " + bean.getValue("name"));  
    98.         System.out.println(" >> address = " + bean.getValue("address"));  
    99.         // 获得bean的实体  
    100.         Object object = bean.getObject();  
    101.         // 通过反射查看所有方法名  
    102.         Class clazz = object.getClass();  
    103.         Method[] methods = clazz.getDeclaredMethods();  
    104.         for (Method curMethod : methods) {  
    105.             System.out.println(curMethod.getName());  
    106.         }  
    107.     }  
    108. }  

      输出结果:

    [java] view plaincopy
     
    1.  >> id = 123  
    2.  >> name = 454  
    3.  >> address = 789  
    4. getAddress  
    5. getName  
    6. getId  
    7. setName  
    8. setId  
    9. setAddress  

    CGLIB轻松实现延迟加载

      通过使用LazyLoader,可以实现延迟加载,即在没有访问对象的字段或方法之前并不加载对象,只有当要访问对象的字段或方法时才进行加载。下面是一个例子。

    [java] view plaincopy
     
    1. import net.sf.cglib.proxy.Enhancer;  
    2. import net.sf.cglib.proxy.LazyLoader;  
    3.   
    4. class TestBean {  
    5.     private String userName;  
    6.   
    7.     /** 
    8.      * @return the userName 
    9.      */  
    10.     public String getUserName() {  
    11.         return userName;  
    12.     }  
    13.   
    14.     /** 
    15.      * @param userName the userName to set 
    16.      */  
    17.     public void setUserName(String userName) {  
    18.         this.userName = userName;  
    19.     }  
    20. }  
    21.   
    22. //延迟加载代理类  
    23. class LazyProxy implements LazyLoader {  
    24.   
    25.     //拦截Bean的加载,本方法会延迟处理  
    26.     @Override  
    27.     public Object loadObject() throws Exception {  
    28.         System.out.println("开始延迟加载!");  
    29.         TestBean bean = new TestBean(); //创建实体Bean  
    30.         bean.setUserName("test");  //给一个属性赋值  
    31.         return bean;  //返回Bean  
    32.     }  
    33. }  
    34.   
    35. public class BeanTest {  
    36.   
    37.     public static void main(String[] args) {  
    38.         //创建Bean类型的延迟加载代理实例  
    39.         TestBean bean = (TestBean) Enhancer.create(TestBean.class, new LazyProxy());  
    40.         System.out.println("------");  
    41.         System.out.println(bean.getUserName());  
    42.     }  
    43. }  

      输出结果:

    [java] view plaincopy
     
    1. ------  
    2. 开始延迟加载!  
    3. test  

      我们创建TestBean类的延迟代理,通过LazyLoader中的loadObject()方法的拦截,实现对TestBean类的对象进行延迟加载。从输出可以看出,当创建延迟代理时,并没有立刻加载目标对象(因为还有输出“开始延迟加载!”),当通过代理访问目标对象的getUserName()方法时,就会加载目标对象。可见loadObject()是延迟执行的。

  • 相关阅读:
    Longest Palindromic Substring
    PayPal MLSE job description
    Continuous Median
    Remove Duplicates From Linked List
    Valid IP Address
    Longest substring without duplication
    Largest range
    Subarray sort
    Multi String Search
    Suffix Trie Construction
  • 原文地址:https://www.cnblogs.com/bendantuohai/p/4703885.html
Copyright © 2011-2022 走看看