自定义注解如何写,这个百度就很多了,这里就不重复了。这里重点说一下为什么有时候自定义注解不起作用,失效的原因,以及解决办法。
一、自定义注解失效的例子
@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类中。
三、如何让普通的对象调用也能使注解生效
目前我也不知道~,留个TODO