SpringBoot学习笔记-02-深入解析自动装配

自动装配原理

​ springboot中的自动装配是通过xxxAutoConfiguration自动装配类,进行配置类xxxProperties装载。

xxxAutoConfiguration自动装配类

xxxAutoConfiguration自动装配类,可以看下面HttpEncodingAutoConfiguration这个类示例,@Configuration注解表示其为一个spring配置类,@EnableConfigurationProperties注解的作用则是将使用@ConfigurationProperties注解的类进行注入,这里注入的即是配置类。因为这些xxxProperties没有使用@Component注解,导致配置类没有注入到容器中,必须通过自动装配类xxxAutoConfiguration完成配置类自动注入。

@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(ServerProperties.class)
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
@ConditionalOnClass(CharacterEncodingFilter.class)
@ConditionalOnProperty(prefix = "server.servlet.encoding", value = "enabled", matchIfMissing = true)
public class HttpEncodingAutoConfiguration {

   private final Encoding properties;

   public HttpEncodingAutoConfiguration(ServerProperties properties) {
      this.properties = properties.getServlet().getEncoding();
   }
...
####  @ConditionalOnxxx注解

那么为什么不直接使用@Component进行自动配置注入呢?因为需要在特定环境下加载制定配置类,通过@ConditionalOnxxx注解进行实现,SpringBoot提供了以下的@ConditionalOnxxx注解:

  • @ConditionalOnBean(仅仅在当前上下文中存在某个对象时,才会实例化一个Bean)

  • @ConditionalOnClass(某个class位于类路径上,才会实例化一个Bean),该注解的参数对应的类必须存在,否则不解析该注解修饰的配置类;

  • @ConditionalOnExpression(当表达式为true的时候,才会实例化一个Bean)

  • @ConditionalOnMissingBean(仅仅在当前上下文中不存在某个对象时,才会实例化一个Bean), 该注解表示,如果存在它修饰的类的bean,则不需要再创建这个bean;可以给该注解传入参数例如@ConditionOnMissingBean(name = “example”),这个表示如果name为“example”的bean存在,这该注解修饰的代码块不执行。

  • @ConditionalOnMissingClass(某个class类路径上不存在的时候,才会实例化一个Bean)

  • @ConditionalOnNotWebApplication(不是web应用)

  • @ConditionalOnProperty是指在application.yml里配置的属性是否为true,其他的几个都是对class的判断

    这些@ConditionalOnxxx注解实现了不使用硬编码就可以对不同条件进行动态解析创建当前条件下所需要的bean

xxxProperties配置类

​ 观察和HttpEncodingAutoConfiguration自动装配类绑定的ServerProperties配置类,可以发现该配置类使用了@ConfigurationProperties去获取默认配置文件(application.properties 或者 application.yml)中的配置,我们可以使用配置文件去设置配置来修改默认的配置设定。

​ @ConfigurationProperties其中参数prefix限定了去获取指定前缀对应的值,例如server.port对应的就是ServerProperties的port属性值。

@ConfigurationProperties(prefix = "server", ignoreUnknownFields = true)
public class ServerProperties {

   /**
    * Server HTTP port.
    */
   private Integer port;

   /**
    * Network address to which the server should bind.
    */
   private InetAddress address;

   @NestedConfigurationProperty
   private final ErrorProperties error = new ErrorProperties();

   /**
    * Strategy for handling X-Forwarded-* headers.
    */
   private ForwardHeadersStrategy forwardHeadersStrategy;

@ConfigurationProperties注解

​ 在编写项目代码时,我们要求更加灵活的配置,更好的模块化整合。在 Spring Boot 项目中,为满足以上要求,我们将大量的参数配置在 application.properties 或 application.yml 文件中,通过 @ConfigurationProperties 注解,我们可以方便的获取这些参数值。

使用@ConfigurationProperties配置模块

​ 假设我们正在搭建一个发送邮件的模块。在本地测试,我们不想该模块真的发送邮件,所以我们需要一个参数来「开关」 disable 这个功能。另外,我们希望为这些邮件配置一个默认的主题,这样,当我们查看邮件收件箱,通过邮件主题可以快速判断出这是测试邮件。

​ 在application.properties文件中创建这些参数:

myapp.mail.enable=true
myapp.mail.default-subject=This is a Test

​ 我们可以使用 @Value 注解或着使用Spring Environment bean访问这些属性,是这种注入配置方式有时显得很笨重。我们将使用更安全的方式(@ConfigurationProperties)来获取这些属性。

@Data
@ConfigurationProperties(prefix = "myapp.mail")
public class MailModuleProperties {
    private Boolean enabled = Boolean.TRUE;
    private String defaultSubject;
}

​ @ConfigurationProperties 的基本用法非常简单:我们为每个要捕获的外部属性提供一个带有字段的类。请注意以下几点:

  • 前缀定义了哪些外部属性将绑定到类的字段上
  • 根据 Spring Boot 宽松的绑定规则,类的属性名称必须与外部属性的名称匹配
  • 我们可以简单地用一个值初始化一个字段来定义一个默认值
  • 类本身可以是包私有的
  • 类的字段必须有公共 setter 方法

​ 如果我们将 MailModuleProperties 类型的 bean 注入到另一个 bean 中,这个 bean 现在可以以类型安全的方式访问那些外部配置参数的值。但是,我们仍然需要让 Spring 知道我们的 @ConfigurationProperties 类存在,以便将其加载到应用程序上下文中。

激活@ConfigurationProperties

​ 对于 Spring Boot,创建一个 MailModuleProperties 类型的 bean,我们可以通过下面几种方式将其添加到应用上下文中。

​ 首先,我们可以通过添加 @Component 注解让 Component Scan 扫描到

@Component
@ConfigurationProperties(prefix = "myapp.mail")
public class MailModuleProperties {
    private Boolean enabled = Boolean.TRUE;
    private String defaultSubject;
}

​ 很显然,只有当类所在的包被 Spring @ComponentScan 注解扫描到才会生效,默认情况下,该注解会扫描在主应用类下的所有包结构。

​ 我们也可以通过 Spring 的 Java Configuration 特性实现同样的效果:

@Configuration
public class PropertiesConfig {
    @Bean
    public MailModuleProperties mailModuleProperties(){
        return new MailModuleProperties();
    }
}

​ 只要 MailModuleConfiguration 类被 Spring Boot 应用扫描到,我们就可以在应用上下文中访问 MailModuleProperties bean。

​ 我们还可以使用 @EnableConfigurationProperties 注解让我们的类被 Spring Boot 所知道,在该注解中其实是用了@Import(EnableConfigurationPropertiesImportSelector.class) 实现,大家可以看一下。

@Configuration
@EnableConfigurationProperties(MailModuleProperties.class)
public class PropertiesConfig {
}

激活一个@ConfigurationProperties类的最佳方式是什么?

​ 所有上述方法都同样有效。然而,我建议模块化你的应用程序,并让每个模块提供自己的@ConfigurationProperties 类,只提供它需要的属性,就像我们在上面的代码中对邮件模块所做的那样。这使得在不影响其他模块的情况下重构一个模块中的属性变得容易。

​ 因此,我不建议在应用程序类本身上使用 @EnableConfigurationProperties,如许多其他教程中所示,是在特定于模块的 @Configuration 类上使用@EnableConfigurationProperties,该类也可以利用包私有的可见性对应用程序的其余部分隐藏属性。

参考 @ConfigurationProperties 注解使用姿势,这一篇就够了 https://www.cnblogs.com/jimoer/p/11374229.html

posted @ 2021-02-19 22:50  小白兔云  阅读(79)  评论(0编辑  收藏  举报