前言
在SpringBoot中,经常可以看到许多以 @Enable 开头的注解,例如:@EnableAutoConfiguration,@EnableAsync……,那么我们是否可以自己定义一个注解呢?
其实自定义注解最终都是利用到了 ImportBeanDefinitionRegistrar 这个类,通过手动的方式,将一个类注册成为 Bean,然后在进行一系列的操作。相比SPI机制, 该实现相当于手动装配
实现
假设现在有一个需求是要写一个切面,这个切面负责打印controller 的log。但是可能某些系统有自己的日志格式,不太需要这个切面AOP,所以希望可以增加一个开关,然后是按需引用。
下面就来开始写一个这样的demo,项目结构如下:
1 2 3 4 5 6 7 8 9 10 11 12 13
| ├── pom.xml └── src ├── main │ ├── java │ │ └── com │ │ ├── anno │ │ │ ├── EnableLog.java │ │ │ ├── LogAop.java │ │ │ └── LogRegister.java │ │ └── example │ │ ├── App.java │ │ ├── controller │ │ │ └── AnnoController.java
|
在这里为什么会初始化两个文件夹,是因为 @SpringBootApplication 这个注解会默认将当前目录以及它的下级目录下的 Bean 注入到容器中,所以新建一个同级目录anno 就是为了不让 @SpringBootApplication 加载切面。
Controller
1 2 3 4 5 6 7 8 9 10 11 12 13 14
|
@RestController @RequestMapping("/anno") public class AnnoController {
@GetMapping("/hello") public Object hello() { return "hello world"; } }
|
然后请求 http://localhost:8080/anno/hello
, 可以看到在控制台没有任何输出。那就说明自定义的切面还没有生效。此时在 Application 上添加我们的自定义注解,添加后代码如下:
1 2 3 4 5 6 7
| @EnableLog @SpringBootApplication public class App { public static void main(String[] args) { SpringApplication.run(App.class, args); } }
|
此时在此启动项目,你会发现已经已经在控制台有我们的日志log了。
自定义注解实现
首先我们需要注册到spring容器的是aop, 这里先实现AOP切面。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
|
@Slf4j @Aspect public class LogAop {
@Pointcut("execution(public * com.example.controller.*.*(..))") public void req() {}
@Around("req()") public Object around(ProceedingJoinPoint point) throws Throwable { log.info("start:{}", System.currentTimeMillis()); Object proceed = point.proceed(); log.info("end:{}", System.currentTimeMillis()); return proceed; }
}
|
然后实现自动注入配置(需要@Import的类)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
|
public class LogRegister implements ImportBeanDefinitionRegistrar {
@Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { BeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition(LogAop.class).getBeanDefinition(); registry.registerBeanDefinition("logAspect", beanDefinition); } }
|
最后定义注入开关注解
1 2 3 4 5 6
| @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE}) @Documented @Import({LogRegister.class}) public @interface EnableLog { }
|
再次访问 http://localhost:8080/anno/hello
可以看到aop已经生效了。
1 2
| 2021-02-28 16:24:31.567 INFO 58297 --- [nio-8080-exec-2] com.anno.LogAop : start:1614500671567 2021-02-28 16:24:31.576 INFO 58297 --- [nio-8080-exec-2] com.anno.LogAop : end:1614500671576
|
最后
本文到此结束,感谢阅读。如果您觉得不错,请关注公众号【当我遇上你】,您的支持是我写作的最大动力。
参考