spring自定义注解失效的原因

时间:2024-04-05 21:56:06

自定义注解如何写,这个百度就很多了,这里就不重复了。这里重点说一下为什么有时候自定义注解不起作用,失效的原因,以及解决办法。

一、自定义注解失效的例子

@Component
public class Run1 implements CommandLineRunner {

    @Autowired
    Run1 run;
    @Autowired
    TestRecord testRecord;

    @Record("run1->run")
    @Override
    public void run(String... args) throws Exception {
        //因为是直接用this来调用的,没走代理调用,@Record无效
        test();
        run.test();
        testRecord.test();
    }

    @Record("run1->test")
    public void test(){
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    @Component
    public class TestRecord {

        @Record("run1->TestRecord->test")
        public void test() {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    @Target({ElementType.METHOD})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface Record {
        String value() default "";
    }

    @Aspect
    @Component
    public class RecordPointCut {
        @Pointcut("@annotation(com.example.demo.annotation.Run1.Record)")
        public void myAnnotationPointcut(){

        }

        @Before("myAnnotationPointcut()")
        public void before(JoinPoint joinPoint){
            System.out.println(joinPoint.getTarget() + " begin:" + System.currentTimeMillis());
        }

        @After("myAnnotationPointcut()")
        public void after(JoinPoint joinPoint){
            System.out.println(joinPoint.getTarget() + " end:" + System.currentTimeMillis());
        }
    }
}

其中run.test()和testRecord.test()上的自定义注解都成功了,为什么呢?因为run和testRecord都是使用的@Autowired自动注入的,相当于被spring代理了,当调用其中的方法的时候,spring不是通过new这个类的对象出来调用的,而是通过代理去调用的,这时候AOP就会起作用,注解就能生效了。大家可能就会疑问,为什么run方法就可以生效呢?这是因为,spring做了这个回调工作,回调的时候也是用的代理调用。

二、解决自定义注解失效的两个办法

1.使用ApplicationContext拿到相应的bean再调用那个方法

@Component
public class Run2 implements CommandLineRunner, ApplicationContextAware {

    @Autowired
    Run2 run;
    @Autowired
    TestRecord testRecord;
    ApplicationContext applicationContext;

    @Record("run2->run")
    @Override
    public void run(String... args) throws Exception {
        //因为是代理调用,@Record有效
        applicationContext.getBean(Run2.class).test();
        run.test();
        testRecord.test();
    }

    @Record("run2->test")
    public void test(){
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    @Component
    public class TestRecord {

        @Record("run2->TestRecord->test")
        public void test() {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    @Target({ElementType.METHOD})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface Record {
        String value() default "";
    }

    @Aspect
    @Component
    public class RecordPointCut {
        @Pointcut("@annotation(com.example.demo.annotation.Run2.Record)")
        public void myAnnotationPointcut(){

        }

        @Before("myAnnotationPointcut()")
        public void before(JoinPoint joinPoint){
            System.out.println(joinPoint.getTarget() + " begin:" + System.currentTimeMillis());
        }

        @After("myAnnotationPointcut()")
        public void after(JoinPoint joinPoint){
            System.out.println(joinPoint.getTarget() + " end:" + System.currentTimeMillis());
        }
    }
}

2.使用AopContext去拿到当前代理类,再去调用那个方法

@Component
//必须要将exposeProxy属性设为TRUE
@EnableAspectJAutoProxy(exposeProxy = true)
public class Run3 implements CommandLineRunner {

    @Autowired
    Run3 run;
    @Autowired
    TestRecord testRecord;

    @Record("run3->run")
    @Override
    public void run(String... args) throws Exception {
        //因为是代理调用,@Record有效
        ((Run3)AopContext.currentProxy()).test();
        run.test();
        testRecord.test();
    }

    @Record("run3->test")
    public void test(){
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    @Component
    public class TestRecord {

        @Record("run3->TestRecord->test")
        public void test() {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    @Target({ElementType.METHOD})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface Record {
        String value() default "";
    }

    @Aspect
    @Component
    public class RecordPointCut {
        @Pointcut("@annotation(com.example.demo.annotation.Run3.Record)")
        public void myAnnotationPointcut(){

        }

        @Before("myAnnotationPointcut()")
        public void before(JoinPoint joinPoint){
            System.out.println(joinPoint.getTarget() + " begin:" + System.currentTimeMillis());
        }

        @After("myAnnotationPointcut()")
        public void after(JoinPoint joinPoint){
            System.out.println(joinPoint.getTarget() + " end:" + System.currentTimeMillis());
        }
    }
}

用这个方法,要将exposeProxy设置为true,让代理类可以暴露出来。
下图的这个方法位于DynamicAdvisedInterceptor类中。
spring自定义注解失效的原因

三、如何让普通的对象调用也能使注解生效

目前我也不知道~,留个TODO