关注我的公众号:【编程朝花夕拾】,可获取首发内容。

01 引言
在我们日常开发中,我们经常会引入第三方框架。使用的时候需要我们在启动类上增加@Enable
开头的注解,以标记当前服务或者插件的启用。如:
@EnableRetry
@EnableScheduling
@EnableDubboConfig
@EnableAsync
- ......
它们就像一个个精巧的"开关",轻轻一拨,便能将复杂的功能模块集成到应用中。
这些开关注解里面做了什么东西呢?我们一起一探究竟!
02 核心理念
Spring Boot的核心设计理念是"约定优于配置",旨在减少开发者在XML和样板代码上的负担,让开发者能够快速构建生产级的应用。
每个开关都提供了一整套默认的、经过实践检验的配置。例如,@EnableRetry
默认会使用SimpleRetryPolicy
和FixedDelayBackOffPolicy
。开发者无需配置任何参数即可使用这些合理的默认值。当有特殊需求时(如更改重试次数、延迟时间),又可以通过注解的参数或配置属性(application.properties
)进行自定义,完美平衡了开箱即用和灵活性。
开关的设计是细粒度的资源控制。正如spring-boot-dependencies
一样,管理所有的的依赖,但并不会直接引入,需要开发者按需引入,否则项目的jar
包就是很大。而开关的设计,则是要么控制资源是否需要加载到Spring
容器中,如果不启动开关,资源就不会被Spring
容器加载,启动之后,才会加载。这样可以防止无关的资源占用Spring
容器的资源。
03 案例源码分析
这些「开关」并非魔法,其背后是Spring框架强大的**@Import
注解和 导入选择器(ImportSelector
)**、配置类(Configuration) 机制。
3.1 @EnableRetry
java
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@EnableAspectJAutoProxy(proxyTargetClass = false)
@Import(RetryConfiguration.class)
@Documented
public @interface EnableRetry {
@AliasFor(annotation = EnableAspectJAutoProxy.class)
boolean proxyTargetClass() default false;
int order() default Ordered.LOWEST_PRECEDENCE - 1;
}
@EnableRetry
本身只是一个标记。其核心是通过@Import(RetryConfiguration.class)
导入了另一个配置类。
我们先看一下org.springframework.retry.annotation.RetryConfiguration
的继承关系

我们可以看到,该类实现了众多Spring
的扩展接口,如BeanFactoryAware
、InitializingBean
、SmartInitializingSingleton
、ImportAware
等。
关键代码块:

我们知道afterPropertiesSet
是InitializingBean
接口的方法,在初始化Bean
对象之后自动执行,也就是说会自动执行到此处。
画框的地方,正式通过@Retryable
注解构造AOP
的切点和通知等。这样一起项目启动之后,当调用该方法时,代理会介入执行,按照预设的重试策略(Retry Policy
)和回退策略(Backoff Policy
)进行重试。
3.2 @EnableScheduling
@EnableScheduling
使用用来开启定时任务的开关。
java
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import(SchedulingConfiguration.class)
@Documented
public @interface EnableScheduling {
}
@EnableScheduling
里面连属性都没有,同样依赖@import
导入的类(SchedulingConfiguration.class
)

内部只是实例化了一个Bean
,并交给Spring
容器管理。看看Bean对象的继承关系
ScheduledAnnotationBeanPostProcessor

同样实现了Spring
的诸多扩展接口。
实例化后,注册ScheduledTaskRegistrar

然后调用afterPropertiesSet()
方法,来增加调度任务。

3.3 手搓Bean初始化耗时开关
核心代码
java
@Component
public class MyBeanPostProcessor implements BeanPostProcessor {
Map<String, Long> map = new ConcurrentHashMap<>();
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
map.put(beanName, System.nanoTime());
return BeanPostProcessor.super.postProcessBeforeInitialization(bean, beanName);
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
long end = System.nanoTime();
System.out.println("初始化Bean【" + beanName + "】,耗时:" + (end - map.get(beanName)) + "ns");
return BeanPostProcessor.super.postProcessAfterInitialization(bean, beanName);
}
}
由于Bean
的位置不在启动类的扫描范围内,所以该类便不会加载。我们通过开关通知启动类是否需要加载。
开关类:
java
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import(MyBeanPostProcessor.class)
public @interface EnableStatTime {
}
启动类增加开关

结果

04 优势
这种"开关"式设计为Spring Boot生态系统带来了巨大优势:
- 低侵入性:功能模块与业务代码解耦,添加或移除功能只需操作一个注解,无需修改核心业务逻辑。
- 降低使用门槛:开发者无需深入理解模块的内部实现细节,只需知道注解的存在和基本用法即可快速集成高级功能。
- 可维护性强 :每个功能的配置和装配逻辑被封装在独立的
@Configuration
类或ImportSelector
中,结构清晰,易于维护和扩展。 - 一致性体验 :无论是什么功能,其启用方式都是统一的
@Enable*
模式,降低了学习成本,提供了优雅一致的用户体验。
05 小结
Spring Boot中的@Enable*
系列注解绝非简单的语法糖,它们是Spring框架"容器化"和"模块化"设计思想的集大成者。
通过声明式编程和自动装配机制,它们将复杂的技术实现细节完美地隐藏在一个个简洁的注解之后,让开发者能够专注于业务逻辑本身,从而真正实现了Spring Boot"快速开发"和"轻松集成"的承诺。
理解和掌握这些"开关"背后的设计哲学,有助于我们更好地使用Spring Boot,乃至设计和实现自己的 Starter 与自动配置模块。