关于java:Spring-Boot-常用注解和自动装配原理

38次阅读

共计 7872 个字符,预计需要花费 20 分钟才能阅读完成。

1. Spring 罕用注解

1.1 主动拆卸

1.1.1 @Configuration

Spring 3.0之后开始反对了一种更优的配置形式:基于 Java 类的配置形式 。通过减少 @Configuration 注解表明这是一个配置类,其底层为 @Component 即该类的 bean 对象也会被 Spring 容器接管)。

proxybeanMethods 属性作用:指定被 @Bean 注解润饰的办法是否被代理 ,true 示意该办法被代理(底层 cglib,即该办法不能为 private、final 润饰),每次对该办法进行调用将会间接取得容器中的 bean 对象,而非真正执行该办法;false 示意该办法不被代理,每次调用时都将取得一个新的对象( 对应 full 和 lite 模式)。

1.1.2 @Bean

配合 @Configuration 能够实现 XML 配置实现注入的性能,成果与

<beans>
    <bean>...</bean>
    <bean>...</bean>
</beans>

相似,在应用 @Bean 注解时,示意将该办法返回的对象加载进 Spring 容器,在应用 @Bean 润饰办法时须要留神:

  1. 办法带返回值,且返回类型为想要被治理的 bean 的类型;
  2. 办法名即为默认的 bean name,可应用 @Beanname 属性自定义;
  3. 办法可带参数,则此时传入的参数也必须是被 Spring 容器治理的 bean。

1.1.3 @Value

该注解的作用是从配置文件中读取属性,如 application.yaml 中有个配置

server:
        port: 8000

咱们应用该注解润饰办法参数 public void test(@Value("${server.port}") address),则该属性 8000 会主动被注入到 address 变量中。

@Value 定位属性有两种形式:

  • ${property: default_value}:读取配置文件(如 *.properties, *.yaml 中的值
  • #{obj.property?: default_value}:读取容器中治理的 bean 对象的属性值。

假如有一个对象申明如下:

@Component
public class ServerProperty {@Value("${server.url}")
private String url;

// getter and setter
// ...
}

@Component
public class Server {@Value("#{serverProperty.url}")
    private String url;
}

第一个 @Value 指代从配置文件中读取属性注入到名为 serverProperty 的 bean 对象中,而第二个 @Value 则是从名为 serverProperty 的 bean 对象中读取数据并注入到 server 中。

注:@Value 想设置 null 默认值给 String 类型的变量时需如此 @Value("${server.url: #{null}}")

1.1.4 @ConfigurationProperties

其作用是 将 Java 类属性的值与配置文件进行映射 ,即通过@ConfigurationProperties 注解,咱们能够应用 properties 或者 yaml 等配置文件对所有属性值进行治理,再映射加载到 Java 对象中。该注解的 prefix 属性则指定了配置文件中的惟一标识名。

1.1.5 @EnableConfigurationProperties

@ConfigurationProperties注解的作用仅仅是将配置文件的值映射载入到 Java 对象中,但该对象与一般 Java 对象无区别,并没有被 Spring 容器进行治理,@EnableConfigurationProperties的作用则是,使得这些被 @ConfigurationProperties 润饰的类转为被 Spring 容器治理的 bean 对象,在 @ConfigurationProperties 润饰的类上加 @Component 也能够使得其被 Spring 容器治理。

1.1,6 @Conditional

Spring4.0 开始提供了 @Conditional 注解,提供了 if...then...else 的语义来治理 Bean 对象的注册。

应用 @Conditional 注解,须要与一个名为 Condition 的接口进行联合才可能施展成果。Condition 接口只有一个办法matches

public interface Condition {boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata);
}

这个 matches 办法里即是对 if…then…else 语义逻辑的具体代码实现。

@Conditional 注解有一个属性 value,是一个类数组Class<? extends Condition>[],其逻辑为,当 value 里所有元素类的 macthes 办法执行后果均为 true 时,批准注册 Bean 对象。

@Conditional 可用于润饰类(@Conponent 和 @Configuration 等)或办法(@Bean),并有许多拓展,如

  • @ConditionalOnBean:容器中存在指定 Bean 对象;
  • @ConditionalOnMissingBean:容器中不存在指定 Bean 对象;

1.1.7 @Import

该注解用来整合所有在 @Configuration 注解中定义的 bean 配置,作用相似咱们将多个 XML 配置文件导入到单个 XML 文件中。

@Import 联合 ImportSelector 接口,能够实现动静抉择导入的配置类(@Configuration 润饰类)。其应用与 @ConditionalCondition 接口的组合形式有点类似,如 SpringBoot 中正是应用 @Import(AutoConfigurationImportSelector.class) 加载了所有主动配置类。ImportSelector 接口提供如下办法:

public interface ImportSelector {
    // ...
    
    String[] selectImports(AnnotationMetadata importingClassMetadata);
}

该办法 selectImports 返回的字符串数组,即是 @Import 注解筹备导入的类。

1.1.8 @EnableAutoConfiguration

该注解被 @Import(AutoConfigurationImportSelector.class) 润饰,指定将要被主动装载到 Spring 容器中的一系列组件。

1.2 Web

1.2.1 @RequestMapping

将 HTTP 申请映射到该注解润饰的解决办法上,衍生出的注解有:

  • @GetMapping:实质是 @RequestMapping 指定了 GET 办法,接管 HTTP 的 GET 申请;
  • @PostMapping:实质是 @RequestMapping 指定了 POST 办法,接管 HTTP 的 POST 申请;
  • @PutMapping:实质是 @RequestMapping 指定了 PUT 办法,接管 HTTP 的 PUT 申请;
  • @DeleteMapping:实质是 @RequestMapping 指定了 DELETE 办法,接管 HTTP 的 DELETE 申请;

1.2.2 @RequestBody

该注解用于读取 Http 申请的申请体数据(当 content-typeapplication/json 时),而后将这部分数据绑定到该注解润饰的办法参数上,在同一个办法里最多只有一个参数可能被 @RequestBody 润饰。

注:无奈配合 GET 申请应用。

1.2.3 @ResponseBody

该注解可润饰办法和其余注解,示意将办法返回的对象转换成肯定格局,并写入响应报文的响应体中,个别用于返回 Json 数据。

1.2.4 RestController

该注解个别用于润饰类,实质是 @Controller + @ResponseBody,因而聚合了 @Controller 的路由性能和 @ResponseBody 的性能—类上的所有办法都会返回 Json 格局数据。

1.2.5 @RequestParam

该注解用于接管 Http 申请的查问参数,起源可分为两局部:

  • GET 办法的 URL
  • POST 办法(当 content-typeapplication/x-www-form-urlencoded 时)的 form data

1.2.6 @PathVariable

该注解用于解析 URL 模板,如果咱们设计 REST 格调的 URL 如下:

@RequestMapping("xxx.com/items/{id}")
public String getItemById(@PathVariable("id") String id) {}

当拜访 “xxx.com/items/100” 时,办法 getItemById 的办法参数能够间接解析失去 “100”。

2. Spring Boot 主动拆卸原理

Spring Boot 在启动时会主动拆卸一系列组件(bean 对象),因为其主动加载这些组件,开发者无需做什么配置即可应用 Spring Boot 提供的性能和服务,且能够在 application.properties 中笼罩默认配置。其实现的原理次要是通过 JavaConfig 和几个上述提到的罕用注解。

2.1 如何进行主动拆卸

Spring Boot 的启动类上有一个组合注解 @SpringBootApplication

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
        @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {// ...}

疏忽 Java 的元注解之后,次要剩下这三个注解:

  • @SpringBootConfiguration:标注这是一个配置类,实质是一个@Configuration
  • @EnableAutoConfiguration将符合条件的 bean 主动注入到 Spring 容器中
  • @ComponentScan:指定须要扫描包的门路。

主动拆卸的外围次要是通过 @EnableAutoConfiguration 来实现,该注解的源码如下:

@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {// ...}

这里有一个 @ImportImportSelector 接口实现类的组合,查看 AutoConfigurationImportSelector.classselectImports 办法:

public String[] selectImports(AnnotationMetadata annotationMetadata) {
    // 查看主动拆卸是否被禁用,即 "spring.boot.enableautoconfiguration" 的值是否为 true
    if (!isEnabled(annotationMetadata)) {return NO_IMPORTS;}
    AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
    return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}

getAutoConfigurationEntry 办法中,调用了 getCandidateConfigurations 办法,其中应用了SpringFactoriesLoader(基于Java SPI 机制拓展)读取 spring-boot-autoconfigure-xxx.jar 包中的 META-INF/spring.factories 文件内容,失去主动拆卸类的全限定名并进行加载。

spring.factories 文件的内容如下,SpringFactoriesLoader将其解析为一个 Map<String, List<String>> 构造,其中 key 依据组件作用分类,而 value 则是对应的一系列主动拆卸类的全限定名。

# Initializers
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\
org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener

# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.boot.autoconfigure.BackgroundPreinitializer

# Auto Configuration Import Listeners
org.springframework.boot.autoconfigure.AutoConfigurationImportListener=\
org.springframework.boot.autoconfigure.condition.ConditionEvaluationReportAutoConfigurationImportListener

# Auto Configuration Import Filters
org.springframework.boot.autoconfigure.AutoConfigurationImportFilter=\
org.springframework.boot.autoconfigure.condition.OnBeanCondition,\
org.springframework.boot.autoconfigure.condition.OnClassCondition,\
org.springframework.boot.autoconfigure.condition.OnWebApplicationCondition

# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
...

# Failure analyzers
org.springframework.boot.diagnostics.FailureAnalyzer=\
org.springframework.boot.autoconfigure.data.redis.RedisUrlSyntaxFailureAnalyzer,\
...

# Template availability providers
org.springframework.boot.autoconfigure.template.TemplateAvailabilityProvider=\
org.springframework.boot.autoconfigure.freemarker.FreeMarkerTemplateAvailabilityProvider,\
...

因为 @SpringBootApplication@EnableAutoConfiguration注解润饰,因而这里会加载 EnableAutoConfiguration 对应的所有主动拆卸类(XXXXAutoConfiguration.class)注入到 Spring 容器中。

2.2 主动拆卸类的属性如何注入

开发者在 application.properties 中笼罩默认配置的性能其实就是通过 将配置文件的内容映射到主动拆卸类上的成员 来实现的,主动拆卸类(XXXXAutoConfiguration.class)注入属性的过程利用了上述提到的几个注解:@ConfigurationProperties@EnableConfigurationProperties。以 KafkaAutoConfiguration.class 为例:

@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(KafkaTemplate.class)
@EnableConfigurationProperties(KafkaProperties.class)
@Import({KafkaAnnotationDrivenConfiguration.class, KafkaStreamsAnnotationDrivenConfiguration.class})
public class KafkaAutoConfiguration {
    private final KafkaProperties properties;
    // ...
}

该类本身是一个配置类,外部有许多被 @Bean 注解润饰的办法用于生成被 Spring 容器治理的组件,其被 @EnableConfigurationProperties 润饰,注入了 KafkaProperties.class 的 bean 对象,而 KafkaProperties.class 又被 @ConfigurationProperties 润饰,即所有属性最终都由配置文件提供,这也解释了开发者为什么可能在 application.properties 中笼罩默认配置。

@ConfigurationProperties(prefix = "spring.kafka")
public class KafkaProperties {// ...}

3. 参考资料

  1. 一文理解 @Conditional 注解阐明和应用
  2. @Import、ImportSelector 注解应用及源码剖析
  3. @ResponseBody 与 @RestController 的作用与区别
  4. springboot(四)——@EnableConfigurationProperties 是如何起作用的你晓得吗

正文完
 0