Spring和Spring Boot中的注解让人眼花缭乱,要搞懂SpringBoot的启动过程,不得不先搞清楚常用的注解有哪些,常规用户是怎么样的。Spring Boot的注解水很深,从JDK的注解到Spring框架的注解再到Spring Boot的注解,用法出神入化了。
1、JDK中的注解
包java.lang.annotation.*
@Target @since 1.5
@Retention @since 1.5
@Inherited @since 1.5
@Documented @since 1.5
@Repeatable @since 1.8
@Native @since 1.8
2、JSR-250规范中的注解
包jakarta.annotation
@Resource
@Nonnull
@Nullable
@PostConstruct
@PreDestroy
@Priority
@Resources
@Generated
什么是JSR-250呢?访问这个链接:https://jcp.org/en/jsr/detail
@Resource注解属于JDK扩展包,所以不在JDK当中,需要引入依赖。如果是JDK8的话不需要额外引入依赖。高于JDK11或低于JDK8需要引入以下依赖。
jakarta.annotation
jakarta.annotation-api
2.1.1
3、JSR-330 规范中的注解(JAVA依赖注入标准)
包jakarta.inject
@Named
@Scope
@Qualifier
@Singleton
上述注解属于JDK扩展包,所以不在JDK当中,需要引入类似如下依赖:
jakarta.inject
jakarta.inject-api
2.0.1
4、Spring的注解(sprig-bean-x.y.z包)
以 spring-bean.6.0.12.jar为例,包org.springframework.beans.factory.annotation有如下几个注解:
@Autowired
@Configurable
@Lookup
@Qualifier
@Value
5、Spring的注解(spring-core-x.y.z包)
以spring-core-6.0.12.jar包为例来拆解,
包路径为:org.springframework.core.annotation;
@AliasFor
Spring框架4.2版本之后才引入的,用于注解的属性之上,为属性声明一个别名,使用非常广泛。我们常用的@SpringBootApplication注解就用到了,@SpringBootApplication注解相当于继承了如下3个注解 @SpringBootConfiguration 、@SpringBootConfiguration、@ComponentScan,在@SpringBootApplication注解的各属性中都用到了 @AliasFor 注解,相当于给3个继承的注解的对应属性进行赋值。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@SpringBootConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
@AliasFor(annotation = EnableAutoConfiguration.class)
Class>[] exclude() default {};
@AliasFor(annotation = EnableAutoConfiguration.class)
String[] excludeName() default {};
@AliasFor(annotation = ComponentScan.class, attribute = "basePackages")
String[] scanBasePackages() default {};
@AliasFor(annotation = ComponentScan.class, attribute = "basePackageClasses")
Class>[] scanBasePackageClasses() default {};
@AliasFor(annotation = ComponentScan.class, attribute = "nameGenerator")
Class extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;
@AliasFor(annotation = Configuration.class)
boolean proxyBeanMethods() default true;
}
@Order
Spring框架2.0版本就已经引入的,定义Spring IOC容器中Bean的执行顺序的优先级
6、Spring的注解(spring-context-x.y.z包的原型注解)
以 spring- context.6.0.12.jar为例,有两个包下都有涉及到注解,分别为包org.springframework.stereotype,和包org.springframework.context.annotation。
6.1、包org.springframework.stereotype下的原型注解
@Component
@Controller
@Repository
@Service
@Indexed
其中前4个注解的含义都是说明被注解的类要被Spring容器管理,为了连带说明被@Component注解的类在分层模型中的位置就又拆分为3个子注解,分别是@Repository表示数据层的DAO,@Service表示业务层的Service,@Controller表示WEB层的controller,实际上直接用@Component也可以实现一样的作用。
最后的一个@Indexed注解是spring5.0以后新加入的,主要是在编译打包的时候把类的注解提前解析出来,避免Spring应用启动的时候来扫描jar包,加速启动速度的。毕竟现在的应用依赖的东西都很多,启动的时候一个一个的jar来扫描找到注解也是很耗时的,能够提前解析好,确实可以大大提速的。
6.2、包org.springframework.context.annotation下的注解
@Bean 用到方法上表示方法会创建对象,并且对象需要由spring容器来管理。
@ComponentScan 根据定义的扫描路径,把符合扫描规则的类装配到spring容器中,扫描规则默认包含了@Component@Controller@Repository@Servicezhe这4个原型注解,具体规则可以再详细制定。可以添加多个 @ComponentScan 来添加多个扫描规则
@ComponentScans Spring4.3以后才引入的,可以一次声明多个 @ComponentScan 注释作为参数。
@Conditional Spring4.0以后才引入的,条件装配注解,可以用在任何类型或者方法上,以指定的条件限制bean的创建;即只有当所有条件都满足时,被 @Conditional 标注的目标才会被spring容器处理。@ConditionalOnBean@ConditionalOnClass等众多的条件注解都是基于本注解来定义的。属于很基础的注解,常用于定义注解。
@Configuration 表示被注解的类是一个配置类,
@DependsOn
@Description
@EnableAspectJAutoProxy
@EnableLoadTimeWeaving
@EnableMBeanExport
@Import
@ImportResource
@ImportRuntimeHints
@Lazy
@Primary
@Profile
@PropertySource
@PropertySources
@Role
@Scope
7、Springboot中常用的注解
SpringBoot中常用的注解在spring-boot-autoconfigure-x.y.z.jar中,这里以spring-boot-autoconfigure-6.1.2.jar为例来拆解,注解涉及到的源代码存放在两个包下面,分别为org.springframework.boot.autoconfigure和org.springframework.boot.autoconfigure.condition。
7.1)包org.springframework.boot.autoconfigure下的注解
@AutoConfiguration @since 2.7.0
@AutoConfigurationPackage @since 2.3.0
@AutoConfigureAfter @since 1.0.0
@AutoConfigureBefore @since 1.0.0
@AutoConfigureOrder @since 1.3.0
@EnableAutoConfiguration @since 1.0.0
@ImportAutoConfiguration @since 1.3.0
@SpringBootApplication @since 1.2.0
看这些注解引入的版本就知道注解的整个演变过程了。
其中SpringBoot2.7版本新增了一个自动配置注解 @AutoConfiguration,用来代替之前的 @Configuration,用于标识新自动配置注册文件中的顶级自动配置类,由 @AutoConfiguration 注解嵌套、导入进来的其他配置类可以继续使用 @Configuration 注解。
另外,为方便起见,@AutoConfiguration 注解还支持 after, afterNames, before 和 beforeNames 属性进行自动配置排序,用于代替之前的 @AutoConfigureAfter 和 @AutoConfigureBefore 注解。
新增了一个自动配置注解 @AutoConfiguration,用来代替之前的 @Configuration,用于标识新自动配置注册文件中的顶级自动配置类,由 @AutoConfiguration 注解嵌套、导入进来的其他配置类可以继续使用 @Configuration 注解。
另外,为方便起见,@AutoConfiguration 注解还支持 after, afterNames, before 和 beforeNames 属性进行自动配置排序,用于代替之前的 @AutoConfigureAfter 和 @AutoConfigureBefore 注解。
下面以ElasticMetricsExportAutoConfiguration.java的注解来简单说明下各注解含义:
@AutoConfiguration(
before = { CompositeMeterRegistryAutoConfiguration.class, SimpleMetricsExportAutoConfiguration.class },
after = MetricsAutoConfiguration.class)
@ConditionalOnBean(Clock.class)
@ConditionalOnClass(ElasticMeterRegistry.class)
@ConditionalOnEnabledMetricsExport("elastic")
@EnableConfigurationProperties(ElasticProperties.class)
public class ElasticMetricsExportAutoConfiguration {
}
上述@AutoConfiguration表示ElasticMetricsExportAutoConfiguration.class类的bean应该在如下类MetricsAutoConfigurationa.class的bean被创建之后才被创建,并且需要在类CompositeMeterRegistryAutoConfiguration.class, SimpleMetricsExportAutoConfiguration.class的bean被创建之前,清楚的表明了bean被创建的先后顺序。
上述@ConditionalOnBean(Clock.class)表示存在类 Clock.class的bean才创建ElasticMetricsExportAutoConfiguration.class的bean。
上述@ConditionalOnClass(ElasticMeterRegistry.class)表示classpath中存在类ElasticMeterRegistry.class才创建ElasticMetricsExportAutoConfiguration.class的bean。
上述@ConditionalOnEnabledMetricsExport("elastic") 表示需要在引入了 elastic 并且 actuator 暴露了 elastic 端口的情况下才创建ElasticMetricsExportAutoConfiguration.class的bean。
上述@EnableConfigurationProperties(ElasticProperties.class)表示让@ConfigurationProperties 注解的类ElasticProperties.class被加载生效。
7.2)包org.springframework.boot.autoconfigure.condition下的注解
@ConditionalOnBean(CqlSession.class) 配置的CqlSession.class类的bean存在时,才会创建这个bean;
@ConditionalOnMissingBean 配置的bean不存在时,才会创建这个bean;
@ConditionalOnClass(RabbitTemplate.class) Classpath中存在配置的类RabbitTemplate.class,才会创建这个bean;
@ConditionalOnMissingClasses Classpath中不存在配置的类,才会创建这个bean;
@ConditionalOnJava JDK版本在范围以内,才会创建这个bean;
@ConditionalOnExpression 指定的SpEL表达式结果为true,才会创建这个bean;
@ConditionalOnWebApplication 是一个WEB应用程序,才会创建这个bean;
@ConditionalOnNotWebApplication 不是一个WEB应用程序,才会创建这个bean;
@ConditionalOnCloudPlatform 仅当我们在某个云平台上运行时才加载bean:
@ConditionalOnJndi("java:comp/env/ejb/myEJB") 仅当通过JNDI提供某个资源时才加载bean:
@ConditionalOnProperty(prefix = "management.auditevents", name = "enabled") 仅在存在环境属性(management.auditevents.enabled)且配置的值不等于false才创建bean。
@ConditionalOnProperty(prefix = "management.health.readinessstate", name = "enabled", havingValue = "true")仅在存在环境属性(management.health.readinessstate.enabled)且配置的值为true才创建bean。
@ConditionalOnResource(resources = "${spring.info.build.location:classpath:META-INF/build-info.properties}") 仅当指定的资源文件出现在classpath中才创建bean
关于@AutoConfigureBefore和@AutoConfigureAfter的用法
以下为案例:
@AutoConfigureBefore(Test1.class)
@AutoConfigureAfter(Test2.class)
public class TestConfig {
}
表示TestConfig.class应该在Test2.class加载后即自动加载,并且要在类Test1.class加载之前。
条件注解发源自注解Conditional,该注解传入的参数是Condition。
注解:org.springframework.context.annotation.Conditional.class
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Conditional {
Classextends Condition>[] value();
}
接口org.springframework.context.annotation.Condition.class,是一个函数式接口:
@FunctionalInterface
public interface Condition {
boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata);
}
以条件注解@ConditionalOnBean为例来看具体的实现:
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnBeanCondition.class)
public @interface ConditionalOnBean {
Class[] value() default {};
String[] type() default {};
Classextends Annotation>[] annotation() default {};
String[] name() default {};
SearchStrategy search() default SearchStrategy.ALL;
Class[] parameterizedContainer() default {};
}
从上述代码可以看到,真正来判定Bean是否存在的条件是由类OnBeanCondition.class来完成的。
参考如下,OnBeanCondition.java 是来实现ConfigurationCondition.java接口的,ConfigurationCondition.java是继承自Condition.java 这个函数式接口,真正的判定条件匹配是在boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata);方法中来实现的。
@Order(Ordered.LOWEST_PRECEDENCE)
class OnBeanCondition extends FilteringSpringBootCondition implements ConfigurationCondition {
}
Spring和Spring Boot的注解水很深,用法出神入化了。