1. 程式人生 > 實用技巧 >CGLIB實現動態代理

CGLIB實現動態代理

介紹

cglib是一個強大的,受歡迎的,高效能的程式碼生成類庫,它的底層就是asm(位元組碼框架),可以用來動態修改class和建立class,Spring AOP實現動態代理的一種方式就是cglib,hibernate使用cglib實現懶載入功能。

實現動態代理

引入maven依賴

<dependency>
   <groupId>cglib</groupId>
   <artifactId>cglib</artifactId>
   <version>3.3.0</version>
</dependency>

實現

public class Client {
  public static void main(String[] args) {
//設定cglib生成的原始碼目錄
    System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D:\\");
    Enhancer enhancer = new Enhancer();
//設定父類
    enhancer.setSuperclass(Singer.class);
//設定方法攔截處理器
    enhancer.setCallback(new SingerMethodInterceptor());
//建立代理物件
    Singable singable = (Singable) enhancer.create();
    singable.sing();
  }

  public static class SingerMethodInterceptor implements MethodInterceptor {

    @Override
    public Object intercept(Object obj, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
      System.out.println("cglib before");
      Object result = methodProxy.invokeSuper(obj, objects);
      System.out.println("cglib after");
      return result;
    }
  }
}

輸出結果為

cglib before
I am singing...
cglib after

可以看到確實實現了動態代理的功能,JDK動態代理建立的類預設繼承Proxy類,所以不能繼承其他類,cglib沒有這個限制,可以通過繼承類的方式代理類。
方法攔截器的引數依次為:

  1. obj,表示建立的代理物件
  2. method,代理的方法,如sing
  3. objects,方法引數
  4. methodProxy,封裝了代理方法,代理物件和原物件

建立不可變物件

public class Client {
  public static void main(String[] args) {
    User user = new User("lisi");
//根據user物件建立一個不可變物件
    User immutableUser = (User) ImmutableBean.create(user);
    System.out.println(immutableUser);
//當我們修改了原物件,不可變物件也被修改了
    user.setName("lisi3");
    System.out.println(immutableUser);
    immutableUser.setName("lisi2");
    System.out.println(immutableUser);
  }

  @Getter
  @Setter
  @AllArgsConstructor
  @NoArgsConstructor
  @ToString
  public static class User {

    private String name;
  }
}

當我們修改不可變物件時,會丟擲異常

Exception in thread "main" java.lang.IllegalStateException: Bean is immutable
	at com.imooc.sourcecode.java.dynamicproxy.cglib.test2.Client$User$$ImmutableBeanByCGLIB$$98871c2c.setName(<generated>)
	at com.imooc.sourcecode.java.dynamicproxy.cglib.test2.Client.main(Client.java:17)

但是如果我們修改原物件,那麼不可變物件也會相應的被修改。這是因為不可變物件是通過繼承原類,然後重寫Getter,setter方法實現的,Getter直接呼叫原物件的Getter,setter直接丟擲異常。

物件生成器

public class Client {
  public static void main(String[] args) throws Exception {
    BeanGenerator beanGenerator = new BeanGenerator();
    beanGenerator.addProperty("name", String.class);
    Object bean = beanGenerator.create();
    Method setNameMethod = bean.getClass().getMethod("setName", String.class);
    setNameMethod.invoke(bean,"lisi");
    Method getNameMethod = bean.getClass().getMethod("getName");
    System.out.println(getNameMethod.invoke(bean));
  }
}

執行時建立一個物件