可以被繼承
首先注解在類上是可以被繼承的 在注解上用@Inherited
/** * Created by laizhenwei on 17:49 2017-10-14 */ @Target({ElementType.TYPE,ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Inherited public @interface Mark { String desc() default ""; }
注解在方法上(jdk代理對象/cglib代理對象/非代理對象)
注解在方法中,沒有所謂繼承問題,只有重寫問題(什么時候會被重寫,除了人為重寫,還有產生代理對象的時候會被重寫)
如果注解在父類方法中,如果方法沒有被子類重寫,那么調用的是父類的方法,那么注解是存在的,如果方法被子類重寫,子類方法沒有注解,那么調用子類方法就獲取不了注解
測試前請確保開啟了(exposeProxy = true),此參數暴露代理對象,否則AopContext.currentProxy()會拋出以下異常
java.lang.IllegalStateException: Cannot find current proxy: Set 'exposeProxy' property on Advised to 'true' to make it available.
不過就算開啟了exposeProxy = true,在非代理對象中使用AopContext.currentProxy(),同樣會拋出
在SpringBoot中使用注解開啟
@SpringBootApplication @EnableAspectJAutoProxy(exposeProxy = true) public class AopApplication
繼承抽象類非代理對象
非代理對象測試代碼:
package com.example.aop.aopproxy.cglibproxy; import com.example.aop.annotation.aopproxy.Mark; /** * Created by laizhenwei on 17:56 2017-10-14 */ @Mark(desc = "我是被注解在抽象類中的Mark!") public abstract class AbstractClass { @Mark public abstract void sysout(); /** * 此方法繼承以后,子類沒有重寫,所以可以獲取到注解(此結論僅針對非代理對象,以及jdk動態代理對象(不針對cglib代理對象)) */ @Mark public void sysout2(){ System.out.println("sysout2"); } public abstract boolean isAopProxy(); public abstract boolean isCglibProxy(); public abstract boolean isJdkDynamicProxy(); }
package com.example.aop.aopproxy.cglibproxy.impl; import com.example.aop.aopproxy.cglibproxy.AbstractClass; import org.springframework.aop.framework.AopContext; import org.springframework.aop.support.AopUtils; /** * Created by laizhenwei on 17:57 2017-10-14 */ public class ClassImplNoProxy extends AbstractClass { @Override public void sysout() { } @Override public boolean isAopProxy(){ return AopUtils.isAopProxy(AopContext.currentProxy()); } @Override public boolean isCglibProxy(){ return AopUtils.isCglibProxy(AopContext.currentProxy()); } @Override public boolean isJdkDynamicProxy(){ return AopUtils.isJdkDynamicProxy(AopContext.currentProxy()); } }
JunitTest
package com.example.aop.aopproxy.cglibproxy; import com.example.aop.AopApplication; import com.example.aop.annotation.aopproxy.Mark; import com.example.aop.aopproxy.cglibproxy.impl.ClassImplNoProxy; import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; /** * Created by laizhenwei on 17:58 2017-10-14 * 非代理類測試,繼承抽象類 */ @RunWith(SpringRunner.class) @SpringBootTest(classes = AopApplication.class) public class AbstractClassTestsNoProxy { /** * 實現類接收 * 這里直接new 出來一個非代理對象,不采用Spring 注入, */ private ClassImplNoProxy notProxyClassImpl = new ClassImplNoProxy(); /** * 抽象類接收 * 這里直接new 出來一個非代理對象,不采用Spring 注入, */ private AbstractClass abstractClass = new ClassImplNoProxy(); /** * 測試是否代理對象,這里采用new,這里必然會拋出異常 */ @Test public void isProxy(){ try { System.out.println( notProxyClassImpl.isAopProxy()); }catch (Throwable throwable){ Assert.assertNotNull(throwable); return; } Assert.assertTrue(false);//如果代碼能執行到這里,斷言失敗 } /** * 測試是否cglib代理對象,這里采用new,這里必然會拋出異常 */ @Test public void isCglibProxy(){ try { System.out.println(notProxyClassImpl.isCglibProxy()); }catch (Throwable throwable){ Assert.assertNotNull(throwable); return; } Assert.assertTrue(false);//如果代碼能執行到這里,斷言失敗 } /** * 測試是否JDK動態代理對象,這里采用new,這里必然會拋出異常 */ @Test public void isJdkDynamicProxy(){ try { System.out.println(notProxyClassImpl.isJdkDynamicProxy());; }catch (Throwable throwable){ Assert.assertNotNull(throwable); return; } Assert.assertTrue(false);//如果代碼能執行到這里,斷言失敗 } //方法被重寫,mark 為null @Test public void getSysoutMark() throws NoSuchMethodException { Mark mark = notProxyClassImpl.getClass().getMethod("sysout",null).getAnnotation(Mark.class); Assert.assertNull(mark); } //方法被重寫,mark 為null @Test public void getAbstractClassSysoutMark() throws NoSuchMethodException { Mark mark = abstractClass.getClass().getMethod("sysout",null).getAnnotation(Mark.class); Assert.assertNull("方法被重寫,mark 為null",mark); } //方法沒有重寫,測試通過 @Test public void getSysout2Mark() throws NoSuchMethodException { Mark mark = notProxyClassImpl.getClass().getMethod("sysout2",null).getAnnotation(Mark.class); Assert.assertNotNull(mark); } //方法沒有重寫,測試通過 @Test public void getAbstractClassSysout2Mark() throws NoSuchMethodException { Mark mark = abstractClass.getClass().getMethod("sysout2",null).getAnnotation(Mark.class); Assert.assertNotNull(mark); } //注解在類上是可以繼承的 @Test public void getImplClassMark(){ Mark mark = notProxyClassImpl.getClass().getAnnotation(Mark.class); Assert.assertNotNull(mark); } }
結果
代理對象(CGLIB):
我們來修改子類改為Spring注入方式
package com.example.aop.aopproxy.cglibproxy.impl; import com.example.aop.aopproxy.cglibproxy.AbstractClass; import org.springframework.aop.framework.AopContext; import org.springframework.aop.support.AopUtils; import org.springframework.stereotype.Service; /** * Created by laizhenwei on 18:25 2017-10-14 */ @Service("cglibClassImplProxy") public class CglibClassImplProxy extends AbstractClass { @Override public void sysout() { } @Override public boolean isAopProxy(){ return AopUtils.isAopProxy(AopContext.currentProxy()); } @Override public boolean isCglibProxy(){ return AopUtils.isCglibProxy(AopContext.currentProxy()); } @Override public boolean isJdkDynamicProxy(){ return AopUtils.isJdkDynamicProxy(AopContext.currentProxy()); } }
junitTest 看看有沒有驚喜
package com.example.aop.aopproxy.cglibproxy; import com.example.aop.AopApplication; import com.example.aop.annotation.aopproxy.Mark; import com.example.aop.aopproxy.cglibproxy.impl.CglibClassImplProxy; import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import javax.annotation.Resource; /** * Created by laizhenwei on 18:24 2017-10-14 * 代理對象 */ @RunWith(SpringRunner.class) @SpringBootTest(classes = AopApplication.class) public class AbstractClassTestsProxy { @Resource(name = "cglibClassImplProxy") private CglibClassImplProxy cglibClassProxy; //抽象類接收 @Resource(name = "cglibClassImplProxy") private AbstractClass abstractClass; /** * 測試是否代理對象,這里采用new,這里必然會拋出異常 */ @Test public void isProxy(){ try { System.out.println( cglibClassProxy.isAopProxy()); }catch (Throwable throwable){ Assert.assertNotNull(throwable); return; } Assert.assertTrue(false);//如果代碼能執行到這里,斷言失敗 } /** * 測試是否cglib代理對象,這里采用new,這里必然會拋出異常 */ @Test public void isCglibProxy(){ try { System.out.println(cglibClassProxy.isCglibProxy()); }catch (Throwable throwable){ Assert.assertNotNull(throwable); return; } Assert.assertTrue(false);//如果代碼能執行到這里,斷言失敗 } /** * 測試是否JDK動態代理對象,這里采用new,這里必然會拋出異常 */ @Test public void isJdkDynamicProxy(){ try { System.out.println(cglibClassProxy.isJdkDynamicProxy());; }catch (Throwable throwable){ Assert.assertNotNull(throwable); return; } Assert.assertTrue(false);//如果代碼能執行到這里,斷言失敗 } //方法被重寫,mark 為null @Test public void getSysoutMark() throws NoSuchMethodException { Mark mark = cglibClassProxy.getClass().getMethod("sysout",null).getAnnotation(Mark.class); Assert.assertNull(mark); } //方法被重寫,mark 為null @Test public void getAbstractClassSysoutMark() throws NoSuchMethodException { Mark mark = abstractClass.getClass().getMethod("sysout",null).getAnnotation(Mark.class); Assert.assertNull("方法被重寫,mark 為null",mark); } //方法沒有重寫,測試通過 @Test public void getSysout2Mark() throws NoSuchMethodException { Mark mark = cglibClassProxy.getClass().getMethod("sysout2",null).getAnnotation(Mark.class); Assert.assertNotNull(mark); } //方法沒有重寫,測試通過 @Test public void getAbstractClassSysout2Mark() throws NoSuchMethodException { Mark mark = abstractClass.getClass().getMethod("sysout2",null).getAnnotation(Mark.class); Assert.assertNotNull(mark); } //注解在類上是可以繼承的 @Test public void getImplClassMark(){ Mark mark = cglibClassProxy.getClass().getAnnotation(Mark.class); Assert.assertNotNull(mark); } }
結果
依然全部通過,這不科學,使用了Spring bean 注入方式,依然獲取不到代理對象(因為isProxy ,isCglibProxy , isJdkDynamicProxy依然拋出了異常)
原因是:spring會根據當前對象,判斷是否創建jdk動態代理的代理對象(針對接口代理),還是cglib的代理對象(針對實現類代理),或者不創建代理對象,在上面的測試中
子類,以及父類都沒有需要使用Spring代理對象的必要(所有方法都沒有使用到SpringAop),所以沒有創建,這里需要一個代理對象來測試,所以給方法加上事務注解就好了
OK,知道原因那就好辦,我們在子類或者父類隨便一個方法加上事務注解這里在抽象類加上事務注解
package com.example.aop.aopproxy.cglibproxy; import com.example.aop.annotation.aopproxy.Mark; import org.springframework.transaction.annotation.Transactional; /** * Created by laizhenwei on 17:56 2017-10-14 */ @Mark(desc = "我是被注解在抽象類中的Mark!") public abstract class AbstractClass { @Mark @Transactional public abstract void sysout(); /** * 此方法繼承以后,子類沒有重寫,所以可以獲取到注解(此結論僅針對非代理對象,以及jdk動態代理對象(不針對cglib代理對象)) */ @Mark public void sysout2(){ System.out.println("sysout2"); } public abstract boolean isAopProxy(); public abstract boolean isCglibProxy(); public abstract boolean isJdkDynamicProxy(); }
修改測試類
package com.example.aop.aopproxy.cglibproxy; import com.example.aop.AopApplication; import com.example.aop.annotation.aopproxy.Mark; import com.example.aop.aopproxy.cglibproxy.impl.CglibClassImplProxy; import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import javax.annotation.Resource; /** * Created by laizhenwei on 18:24 2017-10-14 * 代理對象 */ @RunWith(SpringRunner.class) @SpringBootTest(classes = AopApplication.class) public class AbstractClassTestsProxy { @Resource(name = "cglibClassImplProxy") private CglibClassImplProxy cglibClassProxy; //抽象類接收 @Resource(name = "cglibClassImplProxy") private AbstractClass abstractClass; /** * 測試是否代理對象 */ @Test public void isProxy(){ Assert.assertTrue(cglibClassProxy.isAopProxy()); } /** * 測試是否cglib代理對象 */ @Test public void isCglibProxy(){ Assert.assertTrue(cglibClassProxy.isCglibProxy()); } /** * 測試是否JDK動態代理對象 */ @Test public void isJdkDynamicProxy(){ Assert.assertFalse(cglibClassProxy.isJdkDynamicProxy()); } //方法被重寫,mark 為null @Test public void getSysoutMark() throws NoSuchMethodException { Mark mark = cglibClassProxy.getClass().getMethod("sysout",null).getAnnotation(Mark.class); Assert.assertNull(mark); } //方法被重寫,mark 為null @Test public void getAbstractClassSysoutMark() throws NoSuchMethodException { Mark mark = abstractClass.getClass().getMethod("sysout",null).getAnnotation(Mark.class); Assert.assertNull("方法被重寫,mark 為null",mark); } //方法沒有人為重寫,測試不通過,因為 cglib代理對象已經被重寫 @Test public void getSysout2Mark() throws NoSuchMethodException { Mark mark = cglibClassProxy.getClass().getMethod("sysout2",null).getAnnotation(Mark.class); Assert.assertNotNull(mark); } //方法沒有人為重寫,測試不通過,因為 cglib代理對象已經被重寫 @Test public void getAbstractClassSysout2Mark() throws NoSuchMethodException { Mark mark = abstractClass.getClass().getMethod("sysout2",null).getAnnotation(Mark.class); Assert.assertNotNull(mark); } //注解在類上是可以繼承的 @Test public void getImplClassMark(){ Mark mark = cglibClassProxy.getClass().getAnnotation(Mark.class); Assert.assertNotNull(mark); } }
JunitTest結果,這下成功生成代理對象,但是出現了兩個預期外的結果.(測試代理對象三個方法的斷言已經被修改,所以測試通過)
我們在子類中,並沒有人為重寫這個方法,為何注解就獲取不到了?無論用實現類接收,還是抽象類接收.
原因:繼承抽象類的bean,Spring注入,無論如何都是cglib代理,針對實現類代理,所以會把父類的方法在子類中重寫,這時並沒有帶上注解.所以代理類中無論如何都獲取不了注解
(這也就是為什么注解在抽象類的方法中,aop攔截注解會失效的原因)
實現接口(非代理對象 new)
package com.example.aop.aopproxy.jdkproxy; import com.example.aop.annotation.aopproxy.Mark; /** * Created by laizhenwei on 17:43 2017-10-14 */ public interface IClass { @Mark void sysout(); boolean isAopProxy(); boolean isCglibProxy(); boolean isJdkDynamicProxy(); }
實現類
package com.example.aop.aopproxy.jdkproxy.impl; import com.example.aop.aopproxy.jdkproxy.IClass; import org.springframework.aop.framework.AopContext; import org.springframework.aop.support.AopUtils; /** * Created by laizhenwei on 17:44 2017-10-14 */ public class ClassImplNoProxy implements IClass { @Override public void sysout() { } @Override public boolean isAopProxy() { return AopUtils.isAopProxy(AopContext.currentProxy()); } @Override public boolean isCglibProxy() { return AopUtils.isCglibProxy(AopContext.currentProxy()); } @Override public boolean isJdkDynamicProxy() { return AopUtils.isJdkDynamicProxy(AopContext.currentProxy()); } }
junitTest
package com.example.aop.aopproxy.jdkproxy; import com.example.aop.AopApplication; import com.example.aop.annotation.aopproxy.Mark; import com.example.aop.aopproxy.jdkproxy.impl.ClassImplNoProxy; import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; /** * Created by laizhenwei on 17:46 2017-10-14 */ @RunWith(SpringRunner.class) @SpringBootTest(classes = AopApplication.class) public class IClassTestsNoProxy { /** * 實現類接收 * 這里直接new 出來一個非代理對象,不采用Spring 注入, */ private ClassImplNoProxy notProxyClassImpl = new ClassImplNoProxy(); /** * 接口類接收 * 這里直接new 出來一個非代理對象,不采用Spring 注入, */ private IClass iClass = new ClassImplNoProxy(); /** * 測試是否代理對象,這里采用new,這里必然會拋出異常 */ @Test public void isProxy(){ try { System.out.println( notProxyClassImpl.isAopProxy()); }catch (Throwable throwable){ Assert.assertNotNull(throwable); return; } Assert.assertTrue(false);//如果代碼能執行到這里,斷言失敗 } /** * 測試是否cglib代理對象,這里采用new,這里必然會拋出異常 */ @Test public void isCglibProxy(){ try { System.out.println(notProxyClassImpl.isCglibProxy()); }catch (Throwable throwable){ Assert.assertNotNull(throwable); return; } Assert.assertTrue(false);//如果代碼能執行到這里,斷言失敗 } /** * 測試是否JDK動態代理對象,這里采用new,這里必然會拋出異常 */ @Test public void isJdkDynamicProxy(){ try { System.out.println(notProxyClassImpl.isJdkDynamicProxy());; }catch (Throwable throwable){ Assert.assertNotNull(throwable); return; } Assert.assertTrue(false);//如果代碼能執行到這里,斷言失敗 } //方法被重寫,mark 為null @Test public void getSysoutMark() throws NoSuchMethodException { Mark mark = notProxyClassImpl.getClass().getMethod("sysout",null).getAnnotation(Mark.class); Assert.assertNull(mark); } //方法被重寫,mark 為null @Test public void getAbstractClassSysoutMark() throws NoSuchMethodException { Mark mark = iClass.getClass().getMethod("sysout",null).getAnnotation(Mark.class); Assert.assertNull("方法被重寫,mark 為null",mark); } //注解在接口上,並不是繼承,所以獲取不了 @Test public void getImplClassMark(){ Mark mark = notProxyClassImpl.getClass().getAnnotation(Mark.class); Assert.assertNotNull(mark); } }
測試結果,其他都跟預期一樣,但是獲取不了實現類的注解, 想當然這是現實接口,不是繼承.所以獲取不了也合理.
實現接口(非代理對象 Spring 注入 bean) 注解@Service 讓Spring容器管理,注釋掉事務注解
package com.example.aop.aopproxy.jdkproxy.impl; import com.example.aop.aopproxy.jdkproxy.IClass; import org.springframework.aop.framework.AopContext; import org.springframework.aop.support.AopUtils; import org.springframework.stereotype.Service; /** * Created by laizhenwei on 19:37 2017-10-14 */ @Service("classImplProxy") public class ClassImplProxy implements IClass { @Override // @Transactional public void sysout() { } @Override public boolean isAopProxy() { return AopUtils.isAopProxy(AopContext.currentProxy()); } @Override public boolean isCglibProxy() { return AopUtils.isCglibProxy(AopContext.currentProxy()); } @Override public boolean isJdkDynamicProxy() { return AopUtils.isJdkDynamicProxy(AopContext.currentProxy()); } }
JUNIT TEST 用注解獲取bean
package com.example.aop.aopproxy.jdkproxy; import com.example.aop.AopApplication; import com.example.aop.annotation.aopproxy.Mark; import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import javax.annotation.Resource; /** * Created by laizhenwei on 19:46 2017-10-14 */ @RunWith(SpringRunner.class) @SpringBootTest(classes = AopApplication.class) public class IClassTestsProxy { /** * 實現類接收 */ // @Resource //// @Autowired // private ClassImplProxy classImplProxy; /** * 接口類接收 */ // @Resource(name = "classImplProxy") @Resource(name = "classImplProxy") private IClass iClass; /** * 測試是否代理對象 */ @Test public void isProxy(){ Assert.assertTrue(iClass.isAopProxy()); } /** * 測試是否cglib代理對象 */ @Test public void isCglibProxy(){ Assert.assertTrue(iClass.isCglibProxy()); } /** * 測試是否JDK動態代理對象 */ @Test public void isJdkDynamicProxy(){ Assert.assertTrue(iClass.isJdkDynamicProxy()); } //方法被重寫,mark 為null @Test public void getSysoutMark() throws NoSuchMethodException { Mark mark = iClass.getClass().getMethod("sysout",null).getAnnotation(Mark.class); Assert.assertNull(mark); } //方法被重寫,mark 為null @Test public void getAbstractClassSysoutMark() throws NoSuchMethodException { Mark mark = iClass.getClass().getMethod("sysout",null).getAnnotation(Mark.class); Assert.assertNull("方法被重寫,mark 為null",mark); } //注解在接口上,並不是繼承,所以獲取不了 @Test public void getImplClassMark(){ Mark mark = iClass.getClass().getAnnotation(Mark.class); Assert.assertNotNull(mark); } //注解在接口上,並不是繼承,所以獲取不了 @Test public void getClassMark(){ Mark mark = iClass.getClass().getAnnotation(Mark.class); Assert.assertNotNull(mark); } }
測試結果(這里並沒有創代理對象,因為沒有方法中並沒有使用到Aop 的方法,Spring判定沒有必要創建代理對象)
加上事務注解
package com.example.aop.aopproxy.jdkproxy.impl; import com.example.aop.aopproxy.jdkproxy.IClass; import org.springframework.aop.framework.AopContext; import org.springframework.aop.support.AopUtils; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; /** * Created by laizhenwei on 19:37 2017-10-14 */ @Service("classImplProxy") //@Scope(proxyMode = ScopedProxyMode.INTERFACES) public class ClassImplProxy implements IClass { @Override @Transactional public void sysout() { } @Override public boolean isAopProxy() { return AopUtils.isAopProxy(AopContext.currentProxy()); } @Override public boolean isCglibProxy() { return AopUtils.isCglibProxy(AopContext.currentProxy()); } @Override public boolean isJdkDynamicProxy() { return AopUtils.isJdkDynamicProxy(AopContext.currentProxy()); } }
測試結果(出乎意料,居然是cglib代理,這里我做過幾個測試,哪怕是實現了接口,依然是cglib代理,無解)
強制指定根據接口創建代理類,@Scope(proxyMode = ScopedProxyMode.INTERFACES)
package com.example.aop.aopproxy.jdkproxy.impl; import com.example.aop.aopproxy.jdkproxy.IClass; import org.springframework.aop.framework.AopContext; import org.springframework.aop.support.AopUtils; import org.springframework.context.annotation.Scope; import org.springframework.context.annotation.ScopedProxyMode; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; /** * Created by laizhenwei on 19:37 2017-10-14 */ @Service("classImplProxy") @Scope(proxyMode = ScopedProxyMode.INTERFACES) public class ClassImplProxy implements IClass { @Override @Transactional public void sysout() { } @Override public boolean isAopProxy() { return AopUtils.isAopProxy(AopContext.currentProxy()); } @Override public boolean isCglibProxy() { return AopUtils.isCglibProxy(AopContext.currentProxy()); } @Override public boolean isJdkDynamicProxy() { return AopUtils.isJdkDynamicProxy(AopContext.currentProxy()); } }
測試結果 (沒有變化)
但是就已經不能使用實現類接收
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'com.example.aop.aopproxy.jdkproxy.IClassTestsProxy': Injection of resource dependencies failed; nested exception is org.springframework.beans.factory.BeanNotOfRequiredTypeException: Bean named 'classImplProxy' is expected to be of type 'com.example.aop.aopproxy.jdkproxy.impl.ClassImplProxy' but was actually of type 'com.sun.proxy.$Proxy61'
基於全都是CGLIB 代理,可能有人懷疑我設置了強制走CGLIB 的代理參數,實際上並沒有
@SpringBootApplication @EnableAspectJAutoProxy(exposeProxy = true) public class AopApplication { @Bean @Primary @ConfigurationProperties(prefix = "spring.datasource") public DataSource dataSource() { return DataSourceBuilder.create().build(); } public static void main(String[] args) { SpringApplication.run(AopApplication.class, args); } } @Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Import({AspectJAutoProxyRegistrar.class}) public @interface EnableAspectJAutoProxy { boolean proxyTargetClass() default false; boolean exposeProxy() default false; }
結論:
本來想測試針對接口現實的jdk動態代理的bean,是可以注解在接口,注入對象能夠獲取到注解.但因為全程了cglib那么就沒辦法測了.
為什么建議注解都在實現類方法上, 是因為有那么多不穩定的因素.
是否實現了接口就一定是jdk 代理, 不一定,還可能不創建
在一個類中,如果沒有涉及aop的方法操作,那么Spring不會創建代理對象
如果繼承了一個類,又現實了接口是什么情況呢?我測試過,也是cglib,但是現在也不敢保證一定是.
注解在static 方法,以及final aop 也是獲取不了注解的.因為方法不能被代理對象重寫.