SpringBoot注解的前世今生

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需要引入以下依赖。

<dependency>
    <groupId>jakarta.annotation</groupId>
    <artifactId>jakarta.annotation-api</artifactId>
    <version>2.1.1</version>
</dependency>

3、JSR-330 规范中的注解(JAVA依赖注入标准)

包jakarta.inject

@Named

@Scope

@Qualifier

@Singleton

上述注解属于JDK扩展包,所以不在JDK当中,需要引入类似如下依赖:

<dependency>
    <groupId>jakarta.inject</groupId>
    <artifactId>jakarta.inject-api</artifactId>
    <version>2.0.1</version>
</dependency>

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的注解水很深,用法出神入化了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值