恶魔必须死汉化版(Demons Must Die)
82.8MB · 2025-10-20
在我们日常开发中,我们经常会引入第三方框架。使用的时候需要我们在启动类上增加@Enable
开头的注解,以标记当前服务或者插件的启用。如:
@EnableRetry
@EnableScheduling
@EnableDubboConfig
@EnableAsync
它们就像一个个精巧的“开关”,轻轻一拨,便能将复杂的功能模块集成到应用中。
这些开关注解里面做了什么东西呢?我们一起一探究竟!
Spring Boot的核心设计理念是“约定优于配置”,旨在减少开发者在XML和样板代码上的负担,让开发者能够快速构建生产级的应用。
每个开关都提供了一整套默认的、经过实践检验的配置。例如,@EnableRetry
默认会使用SimpleRetryPolicy
和FixedDelayBackOffPolicy
。开发者无需配置任何参数即可使用这些合理的默认值。当有特殊需求时(如更改重试次数、延迟时间),又可以通过注解的参数或配置属性(application.properties
)进行自定义,完美平衡了开箱即用和灵活性。
开关的设计是细粒度的资源控制。正如spring-boot-dependencies
一样,管理所有的的依赖,但并不会直接引入,需要开发者按需引入,否则项目的jar
包就是很大。而开关的设计,则是要么控制资源是否需要加载到Spring
容器中,如果不启动开关,资源就不会被Spring
容器加载,启动之后,才会加载。这样可以防止无关的资源占用Spring
容器的资源。
这些「开关」并非魔法,其背后是Spring框架强大的**@Import
注解和导入选择器(ImportSelector
)**、配置类(Configuration) 机制。
@EnableRetry
@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
)进行重试。
@EnableScheduling
@EnableScheduling
使用用来开启定时任务的开关。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import(SchedulingConfiguration.class)
@Documented
public @interface EnableScheduling {
}
@EnableScheduling
里面连属性都没有,同样依赖@import
导入的类(SchedulingConfiguration.class
)
内部只是实例化了一个Bean
,并交给Spring
容器管理。看看Bean对象的继承关系
ScheduledAnnotationBeanPostProcessor
同样实现了Spring
的诸多扩展接口。
实例化后,注册ScheduledTaskRegistrar
然后调用afterPropertiesSet()
方法,来增加调度任务。
核心代码
@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
的位置不在启动类的扫描范围内,所以该类便不会加载。我们通过开关通知启动类是否需要加载。
开关类:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import(MyBeanPostProcessor.class)
public @interface EnableStatTime {
}
启动类增加开关
结果
这种“开关”式设计为Spring Boot生态系统带来了巨大优势:
@Configuration
类或ImportSelector
中,结构清晰,易于维护和扩展。@Enable*
模式,降低了学习成本,提供了优雅一致的用户体验。Spring Boot中的@Enable*
系列注解绝非简单的语法糖,它们是Spring框架“容器化”和“模块化”设计思想的集大成者。
通过声明式编程和自动装配机制,它们将复杂的技术实现细节完美地隐藏在一个个简洁的注解之后,让开发者能够专注于业务逻辑本身,从而真正实现了Spring Boot“快速开发”和“轻松集成”的承诺。
理解和掌握这些“开关”背后的设计哲学,有助于我们更好地使用Spring Boot,乃至设计和实现自己的 Starter 与自动配置模块。
82.8MB · 2025-10-20
140.10M · 2025-10-20
64.66M · 2025-10-20