Spring/Springboot 註解失效的原因解析
現象:Spring中某些註解失效事務失效的
原因:Spring進行AOP是維護的是代理對象,而第一調用代理對象中的方法,如果你在這個方法中直接調用該類中的另一個方,會導致另一個方法的增強失敗,主要是由於第二調用是被代理對象去調用的。
失效代碼示例
調用add方法是會使得queryUser方法上的@Transactional失效
@Service
public class UserServiceImpl {
@Transactional
public String queryUser(String userId) {
System.out.println("UserServiceImpl1 ->" + userId);
return "UserServiceImpl1 ->" + userId;
}
@Transactional
public void add(String id) {
queryUser(id);
System.out.println("UserServiceImpl1 -> addxx");
}
}
Spring的AOP是Spring一個重要組成部分,在Spring的實現用的是動態代理,Spring在對象實例化的過程中會根據實例的有切面從而對對象進行動態代理,具體的過程是在這個需要進行增強的Bean實例化完成後進行動態代理,具體的入口在AbstractAutowireCapableBeanFactory的applyBeanPostProcessorsAfterInitialization()
。
該方法會遍歷所有實現了BeanPostProcessor接口
從而會調用到AspectJwareAdvisorAutoProxyCreator(AbstractAutoProxyCreator)類中的postProcessBeforeInstantiation方法
,此方法調用到wrapIfNecessary()又會調用到getAdvicesAndAdvisorsForBean(),該方法主要是封裝所有切面對象,並且匹配該類符合的切面然後進行返回,
然後在wrapIfNecessary()中會創建代理對象,具體會調用到createProxy(),該方法首先會創建一個代理工廠,注意這個工廠是多實例的,一個beanClass對應一個代理工廠,這個代理工廠會封裝被代理對象以及這個被代理對象中所有的切面(這個劃重點,代理工廠中是存在被代理對象的!!!!),然後通過代理工廠創建代理對象。
此時Spring會根據配置來構造是JdkDynamicAopProxy(jdk動態代理)還是生成ObjenesisCglibAopProxy(cglib代理),不管是哪一種均會把代理工廠封裝到兩種對象中,以jdkDynamicAopProxy爲例:在調用getProxy方法是,就會返回代理對象了。當用回調用代理對象時,回回調到JdkDynamicAopProxy的invoke()方法。
重點看invoke方法:在invoke方法主要關關注一下方法,第一個是判斷該方法是否存在切面,並且如何調用該方法的切面,Spring採用了一種過濾鏈的模式,剛纔我們把代理工廠封裝到了jdkDynamicAopProxy對象中了,所有我們可以通過對象獲取到被代理對象所有的切面,然後用這些切面跟方法進行匹配,匹配成功的切面Spring就會把切面中的增強封裝成MethodInterceptor類型的對象,爲什麼要封裝成MethodIntercepor對象呢,主要原因是由於增強的種類有五種,如果不封裝成統一類型對象,則會有大量的if-else判斷。上述過程主要在AdvisedSupport這個類中的getInterceptorsAndDynamicInterceptionAdvice()此方法中完成,找到了調用鏈後則會進行調用了。invoke()方法中會將目標對象目標對象方法目標方法參數封裝成ReflectiveMethodInvocation類型對象,調用該類型中的proceed()方法,再次方法中會是一個調用鏈的形式,進來先判斷切面方法是否執行完成,如果執行完成纔會通過反射調用到目標對象的目標方法,注意此時的目標對象是被代理對象!!!
通過對springAOP源碼的閱讀,我們就知道爲什麼在同一個方法中,我們在其中一個方法調用另一個方法時會導致另一個方法的註解失效。。。筆者通過自己寫的代碼將Spring的源碼的中生產的動態代理嘗試的類輸出到文件中一下:
i
mport com.sun.proxy..Proxy50;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
import org.aopalliance.aop.Advice;
import org.springframework.aop.Advisor;
import org.springframework.aop.TargetSource;
import org.springframework.aop.framework.AopConfigException;
public final class userServiceProxy extends Proxy implements Proxy50 {
private static Method m1;
private static Method m31;
private static Method m19;
private static Method m24;
private static Method m18;
private static Method m14;
private static Method m26;
private static Method m32;
private static Method m3;
private static Method m21;
private static Method m7;
private static Method m6;
private static Method m0;
private static Method m28;
private static Method m16;
private static Method m23;
private static Method m30;
private static Method m34;
private static Method m11;
private static Method m2;
private static Method m10;
private static Method m12;
private static Method m20;
private static Method m13;
private static Method m4;
private static Method m5;
private static Method m36;
private static Method m9;
private static Method m17;
private static Method m38;
private static Method m22;
private static Method m33;
private static Method m8;
private static Method m37;
private static Method m35;
private static Method m27;
private static Method m29;
private static Method m25;
private static Method m15;
public userServiceProxy(InvocationHandler var1) throws {
super(var1);
}
public final boolean equals(Object var1) throws {
try {
return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
public final InvocationHandler getInvocationHandler(Object var1) throws IllegalArgumentException {
try {
return (InvocationHandler)super.h.invoke(this, m31, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
public final void addAdvisor(Advisor var1) throws AopConfigException {
try {
super.h.invoke(this, m19, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
public final boolean isExposeProxy() throws {
try {
return (Boolean)super.h.invoke(this, m24, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final boolean isProxyTargetClass() throws {
try {
return (Boolean)super.h.invoke(this, m18, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final void removeAdvisor(int var1) throws AopConfigException {
try {
super.h.invoke(this, m14, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
public final Class[] getProxiedInterfaces() throws {
try {
return (Class[])super.h.invoke(this, m26, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final Class getProxyClass(ClassLoader var1, Class[] var2) throws IllegalArgumentException {
try {
return (Class)super.h.invoke(this, m32, new Object[]{var1, var2});
} catch (RuntimeException | Error var4) {
throw var4;
} catch (Throwable var5) {
throw new UndeclaredThrowableException(var5);
}
}
public final int indexOf(Advisor var1) throws {
try {
return (Integer)super.h.invoke(this, m3, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
public final TargetSource getTargetSource() throws {
try {
return (TargetSource)super.h.invoke(this, m21, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final void addAdvice(int var1, Advice var2) throws AopConfigException {
try {
super.h.invoke(this, m7, new Object[]{var1, var2});
} catch (RuntimeException | Error var4) {
throw var4;
} catch (Throwable var5) {
throw new UndeclaredThrowableException(var5);
}
}
public final void addAdvice(Advice var1) throws AopConfigException {
try {
super.h.invoke(this, m6, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
public final int hashCode() throws {
try {
return (Integer)super.h.invoke(this, m0, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final boolean isInterfaceProxied(Class var1) throws {
try {
return (Boolean)super.h.invoke(this, m28, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
public final boolean removeAdvice(Advice var1) throws {
try {
return (Boolean)super.h.invoke(this, m16, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
public final void setExposeProxy(boolean var1) throws {
try {
super.h.invoke(this, m23, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
public final Object newProxyInstance(ClassLoader var1, Class[] var2, InvocationHandler var3) throws IllegalArgumentException {
try {
return (Object)super.h.invoke(this, m30, new Object[]{var1, var2, var3});
} catch (RuntimeException | Error var5) {
throw var5;
} catch (Throwable var6) {
throw new UndeclaredThrowableException(var6);
}
}
public final void wait(long var1, int var3) throws InterruptedException {
try {
super.h.invoke(this, m34, new Object[]{var1, var3});
} catch (RuntimeException | InterruptedException | Error var5) {
throw var5;
} catch (Throwable var6) {
throw new UndeclaredThrowableException(var6);
}
}
public final void setTargetSource(TargetSource var1) throws {
try {
super.h.invoke(this, m11, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
public final String toString() throws {
try {
return (String)super.h.invoke(this, m2, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final Class getTargetClass() throws {
try {
return (Class)super.h.invoke(this, m10, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final Class getDecoratedClass() throws {
try {
return (Class)super.h.invoke(this, m12, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final void addAdvisor(int var1, Advisor var2) throws AopConfigException {
try {
super.h.invoke(this, m20, new Object[]{var1, var2});
} catch (RuntimeException | Error var4) {
throw var4;
} catch (Throwable var5) {
throw new UndeclaredThrowableException(var5);
}
}
public final boolean removeAdvisor(Advisor var1) throws {
try {
return (Boolean)super.h.invoke(this, m13, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
public final int indexOf(Advice var1) throws {
try {
return (Integer)super.h.invoke(this, m4, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
public final boolean isFrozen() throws {
try {
return (Boolean)super.h.invoke(this, m5, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final Class getClass() throws {
try {
return (Class)super.h.invoke(this, m36, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final void addxx(String var1) throws {
try {
super.h.invoke(this, m9, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
public final boolean replaceAdvisor(Advisor var1, Advisor var2) throws AopConfigException {
try {
return (Boolean)super.h.invoke(this, m17, new Object[]{var1, var2});
} catch (RuntimeException | Error var4) {
throw var4;
} catch (Throwable var5) {
throw new UndeclaredThrowableException(var5);
}
}
public final void notifyAll() throws {
try {
super.h.invoke(this, m38, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final void setPreFiltered(boolean var1) throws {
try {
super.h.invoke(this, m22, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
public final void wait() throws InterruptedException {
try {
super.h.invoke(this, m33, (Object[])null);
} catch (RuntimeException | InterruptedException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final String queryUser(String var1) throws {
try {
return (String)super.h.invoke(this, m8, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
public final void notify() throws {
try {
super.h.invoke(this, m37, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final void wait(long var1) throws InterruptedException {
try {
super.h.invoke(this, m35, new Object[]{var1});
} catch (RuntimeException | InterruptedException | Error var4) {
throw var4;
} catch (Throwable var5) {
throw new UndeclaredThrowableException(var5);
}
}
public final String toProxyConfigString() throws {
try {
return (String)super.h.invoke(this, m27, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final boolean isProxyClass(Class var1) throws {
try {
return (Boolean)super.h.invoke(this, m29, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
public final Advisor[] getAdvisors() throws {
try {
return (Advisor[])super.h.invoke(this, m25, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final boolean isPreFiltered() throws {
try {
return (Boolean)super.h.invoke(this, m15, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
m31 = Class.forName("com.sun.proxy.$Proxy50").getMethod("getInvocationHandler", Class.forName("java.lang.Object"));
m19 = Class.forName("com.sun.proxy.$Proxy50").getMethod("addAdvisor", Class.forName("org.springframework.aop.Advisor"));
m24 = Class.forName("com.sun.proxy.$Proxy50").getMethod("isExposeProxy");
m18 = Class.forName("com.sun.proxy.$Proxy50").getMethod("isProxyTargetClass");
m14 = Class.forName("com.sun.proxy.$Proxy50").getMethod("removeAdvisor", Integer.TYPE);
m26 = Class.forName("com.sun.proxy.$Proxy50").getMethod("getProxiedInterfaces");
m32 = Class.forName("com.sun.proxy.$Proxy50").getMethod("getProxyClass", Class.forName("java.lang.ClassLoader"), Class.forName("[Ljava.lang.Class;"));
m3 = Class.forName("com.sun.proxy.$Proxy50").getMethod("indexOf", Class.forName("org.springframework.aop.Advisor"));
m21 = Class.forName("com.sun.proxy.$Proxy50").getMethod("getTargetSource");
m7 = Class.forName("com.sun.proxy.$Proxy50").getMethod("addAdvice", Integer.TYPE, Class.forName("org.aopalliance.aop.Advice"));
m6 = Class.forName("com.sun.proxy.$Proxy50").getMethod("addAdvice", Class.forName("org.aopalliance.aop.Advice"));
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
m28 = Class.forName("com.sun.proxy.$Proxy50").getMethod("isInterfaceProxied", Class.forName("java.lang.Class"));
m16 = Class.forName("com.sun.proxy.$Proxy50").getMethod("removeAdvice", Class.forName("org.aopalliance.aop.Advice"));
m23 = Class.forName("com.sun.proxy.$Proxy50").getMethod("setExposeProxy", Boolean.TYPE);
m30 = Class.forName("com.sun.proxy.$Proxy50").getMethod("newProxyInstance", Class.forName("java.lang.ClassLoader"), Class.forName("[Ljava.lang.Class;"), Class.forName("java.lang.reflect.InvocationHandler"));
m34 = Class.forName("com.sun.proxy.$Proxy50").getMethod("wait", Long.TYPE, Integer.TYPE);
m11 = Class.forName("com.sun.proxy.$Proxy50").getMethod("setTargetSource", Class.forName("org.springframework.aop.TargetSource"));
m2 = Class.forName("java.lang.Object").getMethod("toString");
m10 = Class.forName("com.sun.proxy.$Proxy50").getMethod("getTargetClass");
m12 = Class.forName("com.sun.proxy.$Proxy50").getMethod("getDecoratedClass");
m20 = Class.forName("com.sun.proxy.$Proxy50").getMethod("addAdvisor", Integer.TYPE, Class.forName("org.springframework.aop.Advisor"));
m13 = Class.forName("com.sun.proxy.$Proxy50").getMethod("removeAdvisor", Class.forName("org.springframework.aop.Advisor"));
m4 = Class.forName("com.sun.proxy.$Proxy50").getMethod("indexOf", Class.forName("org.aopalliance.aop.Advice"));
m5 = Class.forName("com.sun.proxy.$Proxy50").getMethod("isFrozen");
m36 = Class.forName("com.sun.proxy.$Proxy50").getMethod("getClass");
m9 = Class.forName("com.sun.proxy.$Proxy50").getMethod("addxx", Class.forName("java.lang.String"));
m17 = Class.forName("com.sun.proxy.$Proxy50").getMethod("replaceAdvisor", Class.forName("org.springframework.aop.Advisor"), Class.forName("org.springframework.aop.Advisor"));
m38 = Class.forName("com.sun.proxy.$Proxy50").getMethod("notifyAll");
m22 = Class.forName("com.sun.proxy.$Proxy50").getMethod("setPreFiltered", Boolean.TYPE);
m33 = Class.forName("com.sun.proxy.$Proxy50").getMethod("wait");
m8 = Class.forName("com.sun.proxy.$Proxy50").getMethod("queryUser", Class.forName("java.lang.String"));
m37 = Class.forName("com.sun.proxy.$Proxy50").getMethod("notify");
m35 = Class.forName("com.sun.proxy.$Proxy50").getMethod("wait", Long.TYPE);
m27 = Class.forName("com.sun.proxy.$Proxy50").getMethod("toProxyConfigString");
m29 = Class.forName("com.sun.proxy.$Proxy50").getMethod("isProxyClass", Class.forName("java.lang.Class"));
m25 = Class.forName("com.sun.proxy.$Proxy50").getMethod("getAdvisors");
m15 = Class.forName("com.sun.proxy.$Proxy50").getMethod("isPreFiltered");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}
可以看到其實動態代理生產的類方法調用最後是調用到h.invoke()方法,h就是之前的JdkDynamicAopProxy類型的對象,h.invoke就是調用到前面我們所說的invoke方法,我們直接調用的方法的增強是生效的,但是你在方法內直接調用的該類中的其他方法中的增強是不生效的,原因是我們會調用到invoke中的 retVal=invocatin.proceed()方法,而該方法最終調用的是通過被代理對象去調用自己的方法及最終是執行method.invke(target,args)這個代碼,所以我們在目標方法直接調用本類中的另一個方法,會導致註解失效。總結來說就由於我們最終使用使用原對象去調用,而非代理對象去調用,所以導致增強失敗。解決方式很簡單,我們換成代理對象去調用就可以了。