Spring事務Transactional和動態代理(二)-cglib動態代理
阿新 • • 發佈:2020-03-05
系列文章索引:
1. [Spring事務Transactional和動態代理(一)-JDK代理實現](http://www.itrensheng.com/archives/spring_transaction_jdk_proxy)
2. [Spring事務Transactional和動態代理(二)-cglib動態代理](Spring事務Transactional和動態代理(一)-JDK代理實現)
3. [Spring事務Transactional和動態代理(三)-事務失效的場景](http://www.itrensheng.com/archives/spring_transactional_uneffect)
### 什麼是cglib
Cglib是一個強大的、高效能的程式碼生成包,它廣泛被許多AOP框架使用,為他們提供方法的攔截。它為沒有實現介面的類提供代理,為JDK的動態代理提供了很好的補充。JDK必須強制基於interface介面型別:[Spring事務Transactional和動態代理(上)-JDK代理實現](http://www.itrensheng.com/archives/spring_transaction_jdk_proxy)
### cglib的應用
cglib應用很廣泛,根據cglib在Github上的描述([cglib](https://github.com/cglib/cglib/wiki)),存在以下應用:
1. [ Byte Code Engineering Library ](http://commons.apache.org/proper/commons-bcel/)
也就是JavaClass位元組碼檔案,這個庫可以很方便的分析,建立和操作位元組碼檔案
2. [XORM ](http://xorm.sourceforge.net/)
是一個可擴充套件的ORM框架,使用cglib來生成持久化物件,為RDBMS提供了對映到介面的持久Entity,讓開發人員專注於業務物件模型
3. [Hibernate](http://hibernate.sourceforge.net/)
Hibernate是一個又一個強大的、超高效能的Java物件/關係永續性框架。可以開發持久物件,包括關聯、繼承、多型性、組合和Java集合框架
4. [The Java Class File Editor ](http://staff.develop.com/halloway/code/gd.html)
Java類檔案編輯器,允許使用者在磁碟上或在執行時載入類時讀取/修改Class檔案,也它可以動態地建立新類
5. [Nanning Aspects](http://nanning.codehaus.org/)
是一個基於java的簡介AOP框架
6. [Spring ](https://spring.io/)
7. iBatis/Mybatis
8. ASM
9. [Proxool](http://proxool.sourceforge.net/)
基於java的連線池
10. [Guice ](https://code.google.com/p/google-guice/)
11. [ModelMapper](http://modelmapper.org/)
### cglib的使用
使用cglib需要先引入jar包,在maven中新增依賴:
```java
cglib
cglib
3.3.0
```
新建一個目標類,其中一個為final方法,一個為非final方法,用於對比cglib對於兩種方法的織入結果:
```java
public class Student {
public void study(){
System.out.println("study");
}
public final void eat(){
System.out.println("eat");
}
}
```
Interceptor 代理類如下:
```java
public class CglibInterceptor implements MethodInterceptor {
//織入前的處理
private void beforeInvoke(Method method){
System.out.println("before " + method.getName());
}
//織入後的處理
private void afterInvoke(Method method){
System.out.println("after " + method.getName());
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
beforeInvoke(method);
//呼叫cglib的invokeSuper而不是invoke方法
Object object = methodProxy.invokeSuper(o,objects);
afterInvoke(method);
return object;
}
}
```
測試類的呼叫順序為
1. 建立增強建Enhancer例項
2. 通過setSuperclass方法來設定目標類
3. 通過setCallback設定Interceptor攔截
4. 呼叫Enhancer的create方法生成代理類
程式碼如下:
```java
public class CglibTesst {
public static void main(String[] args) {
//把生產的代理類儲存到磁碟指定資料夾
System.getProperties().put(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, ".");
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(Student.class);
enhancer.setCallback(new CglibInterceptor());
Student studentProxy = (Student) enhancer.create();
studentProxy.study();
studentProxy.eat();
}
}
```
其中的輸出如下,可以看到只有非final方法study織入了before和after邏輯,而final方法eat是沒有的:
```java
before study
study
after study
eat
```
#### cglib生成的代理class檔案分析
通過在測試類中加入了
> System.getProperties().put(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, ".");
程式碼之後,本地就多出來了一些.class檔案如下:
![](https://img2020.cnblogs.com/blog/314515/202003/314515-20200304204249287-1394358571.png)
首先看一下Student$EnhancerByCGLIB$92f3e3f6,繼承了Student並且實現了Factory介面(介面方法主要是newInstance,setCallback和getCallbacks),該類中的程式碼太多,以下程式碼是節選:
```java
public class Student$EnhancerByCGLIB$92f3e3f6 extends Student implements Factory {
//靜態初始化類
static void CGLIB$STATICHOOK1() {
CGLIB$THREAD_CALLBACKS = new ThreadLocal();
CGLIB$emptyArgs = new Object[0];
Class var0 = Class.forName("com.randy.dynamicproxy.cglib.Student$$EnhancerByCGLIB$$92f3e3f6");
Class var1;
Method[] var10000 = ReflectUtils.findMethods(new String[]{"equals", "(Ljava/lang/Object;)Z", "toString", "()Ljava/lang/String;", "hashCode", "()I", "clone", "()Ljava/lang/Object;"}, (var1 = Class.forName("java.lang.Object")).getDeclaredMethods());
CGLIB$equals$1$Method = var10000[0];
CGLIB$equals$1$Proxy = MethodProxy.create(var1, var0, "(Ljava/lang/Object;)Z", "equals", "CGLIB$equals$1");
CGLIB$toString$2$Method = var10000[1];
CGLIB$toString$2$Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/String;", "toString", "CGLIB$toString$2");
CGLIB$hashCode$3$Method = var10000[2];
CGLIB$hashCode$3$Proxy = MethodProxy.create(var1, var0, "()I", "hashCode", "CGLIB$hashCode$3");
CGLIB$clone$4$Method = var10000[3];
CGLIB$clone$4$Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/Object;", "clone", "CGLIB$clone$4");
CGLIB$study$0$Method = ReflectUtils.findMethods(new String[]{"study", "()V"}, (var1 = Class.forName("com.randy.dynamicproxy.cglib.Student")).getDeclaredMethods())[0];
CGLIB$study$0$Proxy = MethodProxy.create(var1, var0, "()V", "study", "CGLIB$study$0");
}
static {
CGLIB$STATICHOOK1();
}
final void CGLIB$study$0() {
super.study();
}
public final void study() {
MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
//檢查當前Callback攔截物件
if (var10000 == null) {
CGLIB$BIND_CALLBACKS(this);
var10000 = this.CGLIB$CALLBACK_0;
}
//根據是否存在判定是通過攔截類來呼叫還是直接呼叫父類Student的study方法
if (var10000 != null) {
var10000.intercept(this, CGLIB$study$0$Method, CGLIB$emptyArgs, CGLIB$study$0$Proxy);
} else {
super.study();
}
}
final boolean CGLIB$equals$1(Object var1) {
return super.equals(var1);
}
public final boolean equals(Object var1) {
...
}
final String CGLIB$toString$2() {
return super.toString();
}
public final String toString() {
...
}
final int CGLIB$hashCode$3() {
return super.hashCode();
}
public final int hashCode() {
...
}
final Object CGLIB$clone$4() throws CloneNotSupportedException {
return super.clone();
}
protected final Object clone() throws CloneNotSupportedException {
...
}
}
```
可以看到該生成類中除了實現Factory介面的方法以外,都複寫了Student類以及超類Object中的非final方法(對於Student中的final方法eat和Object中的final方法wati,notify,notifyAll等方法都沒有複寫),**這也就是為什麼cglib無法對final方法進行代理,因為java不允許複寫final方法**
另外兩個類 Student$EnhancerByCGLIB$92f3e3f6$FastClassByCGLIB$1d02f934 和 Student$FastClassByCGLIB$ec571eb6 都繼承了cglib的抽象類FastClass,
主要是實現了FastClass的一下幾個方法
```java
public abstract int getIndex(String var1, Class[] var2);
public abstract int getIndex(Class[] var1);
public abstract Object invoke(int var1, Object var2, Object[] var3) throws InvocationTargetException;
public abstract Object newInstance(int var1, Object[] var2) throws InvocationTargetException;
public abstract int getIndex(Signature var1);
public abstract int getMaxIndex();
```
其中的
### cglib的原理
cglib動態生成一個要代理類的子類,子類重寫要代理的類的所有不是final的方法(**cglib無法對final方法進行代理**)。在子類中採用方法攔截的技術攔截所有父類方法的呼叫,順勢織入橫切邏輯。
CGLIB底層使用位元組碼處理框架ASM,來轉換位元組碼並生成新的類。關於java位元組碼請檢視:[The Java class File Format](https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html)
#### Enhancer類原始碼分析
```java
public class Enhancer extends AbstractClassGenerator {
//設定目標類作為父類,也就是對應生成的Student$$EnhancerByCGLIB$$92f3e3f6類繼承了Student
public void setSuperclass(Class superclass) {
if (superclass != null && superclass.isInterface()) {
this.setInterfaces(new Class[]{superclass});
} else if (superclass != null && superclass.equals(Object.class)) {
this.superclass = null;
} else {
this.superclass = superclass;
}
}
//通過Enhancer來建立代理類
public Object create() {
this.classOnly = false;
this.argumentTypes = null;
return this.createHelper();
}
private Object createHelper() {
this.preValidate();
//根據當前設定的父類等信心構造一個唯一的key
Object key = KEY_FACTORY.newInstance(this.superclass != null ? this.superclass.getName() : null, ReflectUtils.getNames(this.interfaces), this.filter == ALL_ZERO ? null : new WeakCacheKey(this.filter), this.callbackTypes, this.useFactory, this.interceptDuringConstruction, this.serialVersionUID);
this.currentKey = key;
Object result = super.create(key);
return result;
}
}
protected Object create(Object key) {
try {
ClassLoader loader = this.getClassLoader();
Map cache = CACHE;
//首先從快取中查詢key,如果就生成一個
AbstractClassGenerator.ClassLoaderData data = (AbstractClassGenerator.ClassLoaderData)cache.get(loader);
if (data == null) {
Class var5 = AbstractClassGenerator.class;
synchronized(AbstractClassGenerator.class) {
cache = CACHE;
data = (AbstractClassGenerator.ClassLoaderData)cache.get(loader);
if (data == null) {
Map newCache = new WeakHashMap(cache);
//核心是呼叫了AbstractClassGenerator的generate來生成位元組碼檔案,並通過ReflectUtils.defineClass返回
data = new AbstractClassGenerator.ClassLoaderData(loader);
//加入快取
newCache.put(loader, data);
CACHE = newCache;
}
}
}
this.key = key;
Object obj = data.get(this, this.getUseCache());
//如果是類就通過firstInstance初始化,而firstInstance在AbstractClassGenerator類中是一個抽象方法,具體實現如下
//firstInstance和nextInstance都是通過cglib的ReflectUtils.newInstance來建立例項的
return obj instanceof Class ? this.firstInstance((Class)obj) : this.nextInstance(obj);
} catch (RuntimeException var9) {
throw var9;
} catch (Error var10) {
throw var10;
} catch (Exception var11) {
throw new CodeGenerationException(var11);
}
}
```
#### MethodProxy
當所生成的代理類被呼叫的時候,MethodProxy會在所設定的CallBack中呼叫intercept方法。而在上面的CglibInterceptor類的intercept方法中就是使用的MethodProxy.invokeSuper方法,原始碼如下:
```java
public Object invokeSuper(Object obj, Object[] args) throws Throwable {
try {
//單例初始化
init();
FastClassInfo fci = fastClassInfo;
return fci.f2.invoke(fci.i2, obj, args);
} catch (InvocationTargetException e) {
throw e.getTargetException();
}
}
```
#### init方法:
init()方法是一個經典的雙重檢查單例設計模式,初始判斷物件是否已經初始化了,如果沒有就加鎖並再次判空。初始化的內容主要是FastClassInfo物件及其屬性
```java
private final Object initLock = new Object();
private void init()
{
if (fastClassInfo == null)
{
synchronized (initLock)
{
if (fastClassInfo == null)
{
CreateInfo ci = createInfo;
FastClassInfo fci = new FastClassInfo();
fci.f1 = helper(ci, ci.c1);
fci.f2 = helper(ci, ci.c2);
//通過getIndex來查詢到指定方法的索引
fci.i1 = fci.f1.getIndex(sig1);
fci.i2 = fci.f2.getIndex(sig2);
fastClassInfo = fci;
createInfo = null;
}
}
}
}
```
#### FastClass機制
FastClass機制就是對一個類的方法建立索引,通過索引來直接呼叫相應的方法,在上述的其中的invokeSuper中init初始化的主要就是FastClassInfo(內部類,持有兩個FastClass型別的變數)。
```java
private static class FastClassInfo
{
//目標類的FastClass
FastClass f1;
//代理類的FastClass
FastClass f2;
//目標類方法的索引
int i1;
//代理類方法的索引
int i2;
}
```
在上一篇[JDK代理實現](http://www.itrensheng.com/archives/spring_transaction_jdk_proxy) 中提到JDK攔截物件是通過InvocationHandler反射的機制來呼叫被攔截方法的,反射的效率比較低。
而cglib是對一個類的方法建立索引,通過索引來直接呼叫相應的方法。
如生成的Student$FastClassByCGLIB$ec571eb6就是繼承了FastClass,通過getIndex(Signature)通過方法簽名來定位一個索引,
```java
public int getIndex(Signature var1) {
String var10000 = var1.toString();
switch(var10000.hashCode()) {
case -1310345955:
if (var10000.equals("eat()V")) {
return 1;
}
break;
case 1826985398:
if (var10000.equals("equals(Ljava/lang/Object;)Z")) {
return 2;
}
break;
case 1876544780:
if (var10000.equals("study()V")) {
return 0;
}
break;
case 1913648695:
if (var10000.equals("toString()Ljava/lang/String;")) {
return 3;
}
break;
case 1984935277:
if (var10000.equals("hashCode()I")) {
return 4;
}
}
return -1;
}
```
在根據獲取的的Index位置來呼叫invoke方法,invoke方法在FastClass類中是一個抽象方法,子類(也就是生成的Student$FastClassByCGLIB$ec571eb6繼承FastClass)具體實現如下:
```java
public Object invoke(int var1, Object var2, Object[] var3) throws InvocationTargetException {
Student var10000 = (Student)var2;
int var10001 = var1;
try {
switch(var10001) {
case 0:
var10000.study();
return null;
case 1:
var10000.eat();
return null;
case 2:
return new Boolean(var10000.equals(var3[0]));
case 3:
return var10000.toString();
case 4:
return new Integer(var10000.hashCode());
}
} catch (Throwable var4) {
throw new InvocationTargetException(var4);
}
}
```
參考:
1. https://github.com/cglib/cglib/wiki
2. https://tech.meituan.com/2019/09/05/java-bytecode-enhancement.html
3. https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html
4. https://www.baeldung.com/cglib
5. https://www.ibm.com/developerworks/cn/java/j-lo-springaopcglib/ind