介紹
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沒有這個限制,可以通過繼承類的方式代理類。
方法攔截器的參數依次為:
- obj,表示創建的代理對象
- method,代理的方法,如sing
- objects,方法參數
- 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));
}
}
運行時創建一個對象
