Spring 代理对象,cglib,jdk的问题思考,AOP 配置注解拦截 的一些问题.为什么不要注解在接口,以及抽象方法.
可以被继承
首先注解在类上是可以被继承的 在注解上用@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 也是获取不了注解的.因为方法不能被代理对象重写.