SpringBoot中yml与properties配置文件及bean取值赋值

简介: SpringBoot中yml与properties配置文件及bean取值赋值

① SpringBoot的配置文件

SpringBoot使用一个全局的配置文件,配置文件名是固定的 :


  • application.properties
  • application.yml

配置文件的作用 : 修改SpringBoot自动配置的默认值(SpringBoot在底层已经配置好的属性)。


② yml 是什么?

YAML(YAML Ain’t Markup Language)

YAML A Markup Language:是一个标记语言

YAML isn’t Markup Language:不是一个标记语言;


YAML:以数据为中心,比json、xml等更适合做配置文件。

配置例子-更改Tomcat默认端口 :

server:
  port:8081


③ YAML语法

YAML使用缩进表示层级关系,缩进时不允许使用Tab键,只允许使用空格。缩进的空格的数目不重要,只要相同层级的元素左侧对齐即可。


YAML支持三种数据结构 :

  • 对象 : 键值对的集合;
  • 数组 : 一组按次序排列的值;
  • 字面量 : 单个的不可分割的值。


语法格式如下 :

k:(空格)v;表示一对键值对,空格必须有。
server:
  port: 8082

④ YAML值的写法

一般值有三种类型 : 字面量,即普通的值(数字,字符串,布尔);对象、map(键值对);数组(list set)。

(4.1)字面量

k: v:字面直接来写;

字符串默认不用加上单引号或者双引号;

“”:双引号;不会转义字符串里面的特殊字符;特殊字符会作为本身想表示的意思

name: "zhangsan \n lisi";输出:zhangsan 换行 lisi;

‘’:单引号;会转义特殊字符,特殊字符最终只是一个普通的字符串数据

name: ‘zhangsan \n lisi’; 输出:zhangsan \n lisi

(4.2)对象、map

k: v:在下一行来写对象的属性和值的关系;注意缩进

对象还是k: v的方式

friends:
  lastName: zhangsan
  age: 20

行内写法:

friends: {lastName: zhangsan,age: 18}

(4.3)数组

用- 值表示数组中的一个元素

pets:
‐ cat
‐ dog
‐ pig

行内写法

pets: [cat,dog,pig]

⑤ 使用yml为bean赋值

(5.1) 添加文件处理器依赖


官网如下 :

https://docs.spring.io/spring-boot/docs/2.0.2.RELEASE/reference/html/configuration-metadata.html#configuration-metadata-annotation-processor

<!‐‐导入配置文件处理器,配置文件进行绑定就会有提示‐‐>
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-configuration-processor</artifactId>
  <optional>true</optional>
</dependency>


(5.2)JavaBean注解配置

@ConfigurationProperties:告诉SpringBoot将本类中的所有属性和配置文件中相关的配置进行绑定。


prefix = "person":配置文件中哪个下面的所有属性进行一一映射。

该注解默认从全局配置文件中取值。

/**
* 将配置文件中配置的每一个属性的值,映射到这个组件中
* @ConfigurationProperties:告诉SpringBoot将本类中的所有属性
* 和配置文件中相关的配置进行绑定;
* prefix = "person":配置文件中哪个下面的所有属性进行一一映射
* 只有这个组件是容器中的组件,才能容器提供的@ConfigurationProperties功能;
*/
@Component
@ConfigurationProperties(prefix = "person")
public class Person {
  private String lastName;
  private Integer age;
  private Boolean boss;
  private Date birth;
  private Map<String,Object> maps;
  private List<Object> lists;
  private Dog dog;
  //...
  // 注意,@ConfigurationProperties该注解时不用再使用@Value!!!
}

(5.3)编写yml配置文件

server:
  port: 8082
person:
  lastName: hello
  age: 18
  boss: false
  birth: 2017/12/12
  maps: {k1: v1,k2: 12}
  lists:
    ‐ lisi
    ‐ zhaoliu
  dog:
    name: 小狗
    age: 12

(5.4)使用SpringBoot 测试

@RunWith(SpringRunner.class)
@SpringBootTest
public class SpringbooHelloworldQuickApplicationTests {
  @Autowired
  Person person;
  @Test
  public void contextLoads() {
    System.out.println(person);
  }
}

测试结果如下所示 :

Spring容器中的bean被yml中的配置正确赋值!


(5.5)使用@ConfigurationProperties注解的同时使用@Value

@Component
@ConfigurationProperties(prefix = "person")
public class Person {
    @Value("${person.age}")
    private String lastName;
    //...
}

如上所示,使用@Value为属性赋值别的变量的值。测试结果表明仍然为@ConfigurationProperties该注解为lastName赋值,@Value注解此时不会起作用。


⑥ properties配置文件(仍旧使用@ConfigurationProperties)

properties配置文件是以前项目中常用的配置文件,SpringBoot同样保留了该种类型的配置文件。


如下所示,在properties文件中为person赋值:

# idea
person.age=21
person.birth=2018/11/11
person.last-name=小明
person.boss=true
person.dog.name=dog
person.dog.age=1
person.maps.k1=v1
person.maps.k2=v2
person.lists=a,b,c

运行测试 :

正常获取到值,但是中文乱码,为什么?


应该项目中都遇到这样的例子,读取properties文件中的中文乱码。以前解决办法就是将properties文件中的中文转换为Unicode形式,如\u822a\u6bcd。或者将其重新编码再解码。

idea中解决方法如下图:

  • 将其转换为ASCII,其与Unicode是可以相互转换的。

再次测试:


⑦ @Value为bean赋值

在以前的项目中常用为bean赋值(从properties文件中获取值)的方法有两种。

第一种-xml配置

示例如下

 <bean id="urlModel" class="com.hh.core.model.UrlModel" >
      <property name="url" value="${url}"></property>
  </bean>   

第二种-@Value

@Component
public class Person {
    /**
     * <bean class="Person">
     *      <property name="lastName" value="字面量/${key}从环境变量、配置文件中获取值/#{SpEL}"></property>
     * <bean/>
     */
   //lastName必须是邮箱格式
   // @Email// 不支持JSR303校验
    @Value("${person.last-name}")//键必须与properties文件中的一致
    private String lastName;
    @Value("#{11*2}")//支持SpELl语法
    private Integer age;
    @Value("true")
    private Boolean boss;
    private Date birth;
   // @Value("${person.maps}")//不支持复杂类型封装
    private Map<String,Object> maps;
    private List<Object> lists;
    private Dog dog;
    //...
}


@Value默认从系统环境中加载属性变量。比如application.yml中配置了person.lastName属性,那么就可以使用@Value直接为Person的lastName赋值。


@Value注入map或者list

配置文件如下:

list: topic1,topic2,topic3
maps: "{key1: 'value1', key2: 'value2'}"


注入实例如下:

@Value("#{'${list}'.split(',')}")
private List<String> list;
@Value("#{${maps}}")  
private Map<String,String> maps;

⑧ @Value获取值和@ConfigurationProperties获取值比较

松散绑定 :

–person.firstName:使用标准方式
–person.first-name:大写用-
–person.first_name:大写用_
–PERSON_FIRST_NAME:
.推荐系统属性使用这种写法


上面几种写法在@ConfigurationProperties环境下都可以对应到person对象的firstName属性。@Value则必须保证取的键与properties文件中一致。


JSR303校验:

@Component
@ConfigurationProperties(prefix = "person")
@Validated
public class Person {
    //lastName必须是邮箱格式
//    @Value("${person.last-name}")
    @Email
    private String lastName;
    //...
}

@Value 不支持JSR3030校验,但是支持SpELl语法,@ConfigurationProperties则相反。


对比总结如下:


总结如下:


配置文件yml还是properties他们都能获取到值;


如果说,我们只是在某个业务逻辑中需要获取一下配置文件中的某项值,使用@Value;


如果说,我们专门编写了一个javaBean来和配置文件进行映射,我们就直接使用@ConfigurationProperties;


那么能否在业务逻辑类中使用@ConfigurationProperties呢?

不建议使用,@ConfigurationProperties会将本类中的所有属性和配置文件中相关的配置进行绑定。尤其@ConfigurationProperties支持松散语法。


⑨ @PropertySource加载额外的配置文件

将一切配置全部写在全局配置文件中,是不可想象的。项目中不可避免存在多个配置文件。


@PropertySource就可以根据需要加载指定的配置文件(@ConfigurationProperties 默认从全局配置文件获取配置),将配置文件中的属性注入到系统环境中。


这里将person的属性配置单独写在person.properties文件中,并从全局配置文件中注释掉person的属性配置。

20180514162534602.jpg

Person中使用@Value为属性赋值:

@PropertySource(value = {"classpath:person.properties"})
@Component
public class Person {
    /**
     * <bean class="Person">
     *      <property name="lastName" value="字面量/${key}从环境变量、配置文件中获取值/#{SpEL}"></property>
     * <bean/>
     */
   //lastName必须是邮箱格式
   // @Email
    @Value("${person.last-name}")
    private String lastName;
    @Value("${person.age}")
    private Integer age;
    @Value("true")
    private Boolean boss;
  @Value("${person.birth}")
    private Date birth;
    @Value("${person.maps}")
    private Map<String,Object> maps;
    @Value("${person.lists}")
    private List<Object> lists;
    @Value("${person.dog}")
    private Dog dog;


为了对比person的属性从不同配置文件赋值,这里将全局配置文件中保留person.lastName属姓配置。


测试如下:



20180514162649214.jpg

分析可知,默认从全局配置文件中为person赋值,这里为lastName赋值小明。person的其他属性从person.properties文件中获取。


同时使用@PropertySource和@ConfigurationProperties注解,则默认属性仍旧从全局配置文件寻找,其次从@PropertySource指定的配置文件寻找。而且Person中的属性 不用 再使用@Value为其赋值。

@PropertySource(value = {"classpath:person.properties"})
@Component
@ConfigurationProperties(prefix = "person")
public class Person {
    private String lastName;
    private Integer age;
    private Boolean boss;
  //...
}

application.properties如下图:

person.properties如下图:

测试结果如下图:


⑩ 多个配置文件同属性配置@Value取值



如果项目中不同配置文件中配置同属性,使用@Value该如何取值呢?这就涉及到了配置文件加载优先级的问题。

如图一:


application.properties与person.properties同时配置了属性person.last-name,如图中所示,使用@Value取值,此时取到的值为application.properties中的值。


若将application.properties中的person.last-name注释掉,则取的为person.properties(Person类配置了@PropertySource)值。



那么是否说明,默认从全局配置文件取还是按照上下顺序依次检查呢?

如图二所示:


修改application.properties为tapplication.properties文件,将会从application.yml到tapplication.properties依次查找,如找到该属性则取其值。

那么是否能够说明了配置文件的加载次序呢?


(11)@ImportResource导入Spring配置xml文件

Spring Boot里面没有Spring的配置文件,我们自己编写的配置文件,也不能自动识别。那么如何使用我们自己编写的配置文件呢?


在主配置类添加@ImportResource注解,如下图:

配置文件内容如下:

 <bean id="helloService" class="com.web.service.HelloService"/>

测试如下:

在主配置类使用ImportResource引入自定义Spring配置文件,即可获取helloService bean。


(12)SpringBoot推荐给容器中添加组件的方式

SpringBoot推荐使用配置类的方式来给容器中添加组件。如下所示:

@Configuration
public class MyAppConfig {
    //将方法的返回值添加到容器中;容器中这个组件默认的id就是方法名
    @Bean
    public HelloService helloService(){
        System.out.println("配置类@Bean给容器中添加组件了...");
        return new HelloService();
    }
}


测试结果如下图:


(13)配置文件占位符

除了前面说的几种方式,还可以使用占位符的方式在配置文件中为属性赋值。

① 随机数

示例如下:

person.last‐name=张三${random.uuid}


② 占位符获取之前配置的值,如果没有可以是用:指定默认值

示例如下:

person.last‐name=张三${random.uuid}
person.age=${random.int}
person.birth=2017/12/15
person.boss=false
person.maps.k1=v1
person.maps.k2=14
person.lists=a,b,c
person.dog.name=${person.hello:hello}_dog
person.dog.age=15


${person.hello:hello}意思为如果person.hello没有值则默认赋值为hello。${person.hello}如果有值,则取值,无值则会当作字面量解析–${person.hello}原样赋值。


(14)@PropertySource和@ConfigurationProperties以及@Value

这三个注解究竟是什么,做了什么?

① @Value

源码如下:

/**
 * Annotation at the field or method/constructor parameter level
 * that indicates a default value expression for the affected argument.
 *
 * <p>Typically used for expression-driven dependency injection. Also supported
 * for dynamic resolution of handler method parameters, e.g. in Spring MVC.
 *
 * <p>A common use case is to assign default field values using
 * "#{systemProperties.myProp}" style expressions.
 *
 * <p>Note that actual processing of the {@code @Value} annotation is performed
 * by a {@link org.springframework.beans.factory.config.BeanPostProcessor
 * BeanPostProcessor} which in turn means that you <em>cannot</em> use
 * {@code @Value} within
 * {@link org.springframework.beans.factory.config.BeanPostProcessor
 * BeanPostProcessor} or
 * {@link org.springframework.beans.factory.config.BeanFactoryPostProcessor BeanFactoryPostProcessor}
 * types. Please consult the javadoc for the {@link AutowiredAnnotationBeanPostProcessor}
 * class (which, by default, checks for the presence of this annotation).
 *
 * @author Juergen Hoeller
 * @since 3.0
 * @see AutowiredAnnotationBeanPostProcessor
 * @see Autowired
 * @see org.springframework.beans.factory.config.BeanExpressionResolver
 * @see org.springframework.beans.factory.support.AutowireCandidateResolver#getSuggestedValue
 */
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Value {
  /**
   * The actual value expression: e.g. "#{systemProperties.myProp}".
   */
  String value();
}

言简意赅说一下,这个注解可以动态为属性赋值,处理过程是BeanPostProcessor!


② @PropertySource

源码如下:

/**
 * Annotation providing a convenient and declarative mechanism for adding a
 * {@link org.springframework.core.env.PropertySource PropertySource} to Spring's
 * {@link org.springframework.core.env.Environment Environment}. To be used in
 * conjunction with @{@link Configuration} classes.
 *
 * <h3>Example usage</h3>
 *
 * <p>Given a file {@code app.properties} containing the key/value pair
 * {@code testbean.name=myTestBean}, the following {@code @Configuration} class
 * uses {@code @PropertySource} to contribute {@code app.properties} to the
 * {@code Environment}'s set of {@code PropertySources}.
 *
 * <pre class="code">
 * &#064;Configuration
 * &#064;PropertySource("classpath:/com/myco/app.properties")
 * public class AppConfig {
 *     &#064;Autowired
 *     Environment env;
 *
 *     &#064;Bean
 *     public TestBean testBean() {
 *         TestBean testBean = new TestBean();
 *         testBean.setName(env.getProperty("testbean.name"));
 *         return testBean;
 *     }
 * }</pre>
 *
 * Notice that the {@code Environment} object is @{@link
 * org.springframework.beans.factory.annotation.Autowired Autowired} into the
 * configuration class and then used when populating the {@code TestBean} object. Given
 * the configuration above, a call to {@code testBean.getName()} will return "myTestBean".
 *
 * <h3>Resolving ${...} placeholders in {@code <bean>} and {@code @Value} annotations</h3>
 *
 * In order to resolve ${...} placeholders in {@code <bean>} definitions or {@code @Value}
 * annotations using properties from a {@code PropertySource}, one must register
 * a {@code PropertySourcesPlaceholderConfigurer}. This happens automatically when using
 * {@code <context:property-placeholder>} in XML, but must be explicitly registered using
 * a {@code static} {@code @Bean} method when using {@code @Configuration} classes. See
 * the "Working with externalized values" section of @{@link Configuration}'s javadoc and
 * "a note on BeanFactoryPostProcessor-returning @Bean methods" of @{@link Bean}'s javadoc
 * for details and examples.
 *
 * <h3>Resolving ${...} placeholders within {@code @PropertySource} resource locations</h3>
 *
 * Any ${...} placeholders present in a {@code @PropertySource} {@linkplain #value()
 * resource location} will be resolved against the set of property sources already
 * registered against the environment. For example:
 *
 * <pre class="code">
 * &#064;Configuration
 * &#064;PropertySource("classpath:/com/${my.placeholder:default/path}/app.properties")
 * public class AppConfig {
 *     &#064;Autowired
 *     Environment env;
 *
 *     &#064;Bean
 *     public TestBean testBean() {
 *         TestBean testBean = new TestBean();
 *         testBean.setName(env.getProperty("testbean.name"));
 *         return testBean;
 *     }
 * }</pre>
 *
 * Assuming that "my.placeholder" is present in one of the property sources already
 * registered, e.g. system properties or environment variables, the placeholder will
 * be resolved to the corresponding value. If not, then "default/path" will be used as a
 * default. Expressing a default value (delimited by colon ":") is optional.  If no
 * default is specified and a property cannot be resolved, an {@code
 * IllegalArgumentException} will be thrown.
 *
 * <h3>A note on property overriding with @PropertySource</h3>
 *
 * In cases where a given property key exists in more than one {@code .properties}
 * file, the last {@code @PropertySource} annotation processed will 'win' and override.
 *
 * For example, given two properties files {@code a.properties} and
 * {@code b.properties}, consider the following two configuration classes
 * that reference them with {@code @PropertySource} annotations:
 *
 * <pre class="code">
 * &#064;Configuration
 * &#064;PropertySource("classpath:/com/myco/a.properties")
 * public class ConfigA { }
 *
 * &#064;Configuration
 * &#064;PropertySource("classpath:/com/myco/b.properties")
 * public class ConfigB { }
 * </pre>
 *
 * The override ordering depends on the order in which these classes are registered
 * with the application context.
 * <pre class="code">
 * AnnotationConfigApplicationContext ctx =
 *     new AnnotationConfigApplicationContext();
 * ctx.register(ConfigA.class);
 * ctx.register(ConfigB.class);
 * ctx.refresh();
 * </pre>
 *
 * In the scenario above, the properties in {@code b.properties} will override any
 * duplicates that exist in {@code a.properties}, because {@code ConfigB} was registered
 * last.
 *
 * <p>In certain situations, it may not be possible or practical to tightly control
 * property source ordering when using {@code @ProperySource} annotations. For example,
 * if the {@code @Configuration} classes above were registered via component-scanning,
 * the ordering is difficult to predict. In such cases - and if overriding is important -
 * it is recommended that the user fall back to using the programmatic PropertySource API.
 * See {@link org.springframework.core.env.ConfigurableEnvironment ConfigurableEnvironment}
 * and {@link org.springframework.core.env.MutablePropertySources MutablePropertySources}
 * javadocs for details.
 *
 * @author Chris Beams
 * @author Juergen Hoeller
 * @author Phillip Webb
 * @since 3.1
 * @see PropertySources
 * @see Configuration
 * @see org.springframework.core.env.PropertySource
 * @see org.springframework.core.env.ConfigurableEnvironment#getPropertySources()
 * @see org.springframework.core.env.MutablePropertySources
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Repeatable(PropertySources.class)
public @interface PropertySource {
  /**
   * Indicate the name of this property source. If omitted, a name will
   * be generated based on the description of the underlying resource.
   * @see org.springframework.core.env.PropertySource#getName()
   * @see org.springframework.core.io.Resource#getDescription()
   */
  String name() default "";
  /**
   * Indicate the resource location(s) of the properties file to be loaded.
   * For example, {@code "classpath:/com/myco/app.properties"} or
   * {@code "file:/path/to/file"}.
   * <p>Resource location wildcards (e.g. *&#42;/*.properties) are not permitted;
   * each location must evaluate to exactly one {@code .properties} resource.
   * <p>${...} placeholders will be resolved against any/all property sources already
   * registered with the {@code Environment}. See {@linkplain PropertySource above}
   * for examples.
   * <p>Each location will be added to the enclosing {@code Environment} as its own
   * property source, and in the order declared.
   */
  String[] value();
  /**
   * Indicate if failure to find the a {@link #value() property resource} should be
   * ignored.
   * <p>{@code true} is appropriate if the properties file is completely optional.
   * Default is {@code false}.
   * @since 4.0
   */
  boolean ignoreResourceNotFound() default false;
  /**
   * A specific character encoding for the given resources, e.g. "UTF-8".
   * @since 4.3
   */
  String encoding() default "";
  /**
   * Specify a custom {@link PropertySourceFactory}, if any.
   * <p>By default, a default factory for standard resource files will be used.
   * @since 4.3
   * @see org.springframework.core.io.support.DefaultPropertySourceFactory
   * @see org.springframework.core.io.support.ResourcePropertySource
   */
  Class<? extends PropertySourceFactory> factory() default PropertySourceFactory.class;
}


言简意赅说明一下,该注解将制定文件中的key/value形式的属性配置注入到了Environment中!


③ @ConfigurationProperties

@ConfigurationProperties源码如下:

/**
 * Annotation for externalized configuration. Add this to a class definition or a
 * {@code @Bean} method in a {@code @Configuration} class if you want to bind and validate
 * some external Properties (e.g. from a .properties file).
 * <p>
 * Note that contrary to {@code @Value}, SpEL expressions are not evaluated since property
 * values are externalized.
 *
 * @author Dave Syer
 * @see ConfigurationPropertiesBindingPostProcessor
 * @see EnableConfigurationProperties
 */
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ConfigurationProperties {
  /**
   * The name prefix of the properties that are valid to bind to this object. Synonym
   * for {@link #prefix()}.
   * @return the name prefix of the properties to bind
   */
  @AliasFor("prefix")
  String value() default "";
  /**
   * The name prefix of the properties that are valid to bind to this object. Synonym
   * for {@link #value()}.
   * @return the name prefix of the properties to bind
   */
  @AliasFor("value")
  String prefix() default "";
  /**
   * Flag to indicate that when binding to this object invalid fields should be ignored.
   * Invalid means invalid according to the binder that is used, and usually this means
   * fields of the wrong type (or that cannot be coerced into the correct type).
   * @return the flag value (default false)
   */
  boolean ignoreInvalidFields() default false;
  /**
   * Flag to indicate that when binding to this object fields with periods in their
   * names should be ignored.
   * @return the flag value (default false)
   */
  boolean ignoreNestedProperties() default false;
  /**
   * Flag to indicate that when binding to this object unknown fields should be ignored.
   * An unknown field could be a sign of a mistake in the Properties.
   * @return the flag value (default true)
   */
  boolean ignoreUnknownFields() default true;
  /**
   * Flag to indicate that an exception should be raised if a Validator is available,
   * the class is annotated with {@link Validated @Validated} and validation fails. If
   * it is set to false, validation errors will be swallowed. They will be logged, but
   * not propagated to the caller.
   * @return the flag value (default true)
   * @deprecated as of 1.5 since validation only kicks in when {@code @Validated} is
   * present
   */
  @Deprecated
  boolean exceptionIfInvalid() default true;
}

功能自己看源码上面的javadoc,这里需要注意的是ConfigurationPropertiesBindingPostProcessor。


很熟悉吧,又是一个后置处理器!

ConfigurationPropertiesBindingPostProcessor源码如下:

public class ConfigurationPropertiesBindingPostProcessor implements BeanPostProcessor,
    BeanFactoryAware, EnvironmentAware, ApplicationContextAware, InitializingBean,
    DisposableBean, ApplicationListener<ContextRefreshedEvent>, PriorityOrdered {
  /**
   * The bean name of the configuration properties validator.
   */
  public static final String VALIDATOR_BEAN_NAME = "configurationPropertiesValidator";
  private static final String[] VALIDATOR_CLASSES = { "javax.validation.Validator",
      "javax.validation.ValidatorFactory",
      "javax.validation.bootstrap.GenericBootstrap" };
  private static final Log logger = LogFactory
      .getLog(ConfigurationPropertiesBindingPostProcessor.class);
  private ConfigurationBeanFactoryMetaData beans = new ConfigurationBeanFactoryMetaData();
  private PropertySources propertySources;
  private Validator validator;
  private volatile Validator localValidator;
  private ConversionService conversionService;
  private DefaultConversionService defaultConversionService;
  private BeanFactory beanFactory;
  private Environment environment = new StandardEnvironment();
  private ApplicationContext applicationContext;
  private List<Converter<?, ?>> converters = Collections.emptyList();
  private List<GenericConverter> genericConverters = Collections.emptyList();
  private int order = Ordered.HIGHEST_PRECEDENCE + 1;
  /**
   * A list of custom converters (in addition to the defaults) to use when converting
   * properties for binding.
   * @param converters the converters to set
   */
  @Autowired(required = false)
  @ConfigurationPropertiesBinding
  public void setConverters(List<Converter<?, ?>> converters) {
    this.converters = converters;
  }
  /**
   * A list of custom converters (in addition to the defaults) to use when converting
   * properties for binding.
   * @param converters the converters to set
   */
  @Autowired(required = false)
  @ConfigurationPropertiesBinding
  public void setGenericConverters(List<GenericConverter> converters) {
    this.genericConverters = converters;
  }
  /**
   * Set the order of the bean.
   * @param order the order
   */
  public void setOrder(int order) {
    this.order = order;
  }
  /**
   * Return the order of the bean.
   * @return the order
   */
  @Override
  public int getOrder() {
    return this.order;
  }
  /**
   * Set the property sources to bind.
   * @param propertySources the property sources
   */
  public void setPropertySources(PropertySources propertySources) {
    this.propertySources = propertySources;
  }
  /**
   * Set the bean validator used to validate property fields.
   * @param validator the validator
   */
  public void setValidator(Validator validator) {
    this.validator = validator;
  }
  /**
   * Set the conversion service used to convert property values.
   * @param conversionService the conversion service
   */
  public void setConversionService(ConversionService conversionService) {
    this.conversionService = conversionService;
  }
  /**
   * Set the bean meta-data store.
   * @param beans the bean meta data store
   */
  public void setBeanMetaDataStore(ConfigurationBeanFactoryMetaData beans) {
    this.beans = beans;
  }
  @Override
  public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
    this.beanFactory = beanFactory;
  }
  @Override
  public void setEnvironment(Environment environment) {
    this.environment = environment;
  }
  @Override
  public void setApplicationContext(ApplicationContext applicationContext) {
    this.applicationContext = applicationContext;
  }
  @Override
  public void afterPropertiesSet() throws Exception {
    if (this.propertySources == null) {
      this.propertySources = deducePropertySources();
    }
    if (this.validator == null) {
      this.validator = getOptionalBean(VALIDATOR_BEAN_NAME, Validator.class);
    }
    if (this.conversionService == null) {
      this.conversionService = getOptionalBean(
          ConfigurableApplicationContext.CONVERSION_SERVICE_BEAN_NAME,
          ConversionService.class);
    }
  }
  @Override
  public void onApplicationEvent(ContextRefreshedEvent event) {
    freeLocalValidator();
  }
  @Override
  public void destroy() throws Exception {
    freeLocalValidator();
  }
  private void freeLocalValidator() {
    try {
      Validator validator = this.localValidator;
      this.localValidator = null;
      if (validator != null) {
        ((DisposableBean) validator).destroy();
      }
    }
    catch (Exception ex) {
      throw new IllegalStateException(ex);
    }
  }
  private PropertySources deducePropertySources() {
    PropertySourcesPlaceholderConfigurer configurer = getSinglePropertySourcesPlaceholderConfigurer();
    if (configurer != null) {
      // Flatten the sources into a single list so they can be iterated
      return new FlatPropertySources(configurer.getAppliedPropertySources());
    }
    if (this.environment instanceof ConfigurableEnvironment) {
      MutablePropertySources propertySources = ((ConfigurableEnvironment) this.environment)
          .getPropertySources();
      return new FlatPropertySources(propertySources);
    }
    // empty, so not very useful, but fulfils the contract
    logger.warn("Unable to obtain PropertySources from "
        + "PropertySourcesPlaceholderConfigurer or Environment");
    return new MutablePropertySources();
  }
  private PropertySourcesPlaceholderConfigurer getSinglePropertySourcesPlaceholderConfigurer() {
    // Take care not to cause early instantiation of all FactoryBeans
    if (this.beanFactory instanceof ListableBeanFactory) {
      ListableBeanFactory listableBeanFactory = (ListableBeanFactory) this.beanFactory;
      Map<String, PropertySourcesPlaceholderConfigurer> beans = listableBeanFactory
          .getBeansOfType(PropertySourcesPlaceholderConfigurer.class, false,
              false);
      if (beans.size() == 1) {
        return beans.values().iterator().next();
      }
      if (beans.size() > 1 && logger.isWarnEnabled()) {
        logger.warn("Multiple PropertySourcesPlaceholderConfigurer "
            + "beans registered " + beans.keySet()
            + ", falling back to Environment");
      }
    }
    return null;
  }
  private <T> T getOptionalBean(String name, Class<T> type) {
    try {
      return this.beanFactory.getBean(name, type);
    }
    catch (NoSuchBeanDefinitionException ex) {
      return null;
    }
  }
  @Override
  public Object postProcessBeforeInitialization(Object bean, String beanName)
      throws BeansException {
    ConfigurationProperties annotation = AnnotationUtils
        .findAnnotation(bean.getClass(), ConfigurationProperties.class);
    if (annotation != null) {
      postProcessBeforeInitialization(bean, beanName, annotation);
    }
    annotation = this.beans.findFactoryAnnotation(beanName,
        ConfigurationProperties.class);
    if (annotation != null) {
      postProcessBeforeInitialization(bean, beanName, annotation);
    }
    return bean;
  }
  @Override
  public Object postProcessAfterInitialization(Object bean, String beanName)
      throws BeansException {
    return bean;
  }
  @SuppressWarnings("deprecation")
  private void postProcessBeforeInitialization(Object bean, String beanName,
      ConfigurationProperties annotation) {
    Object target = bean;
    PropertiesConfigurationFactory<Object> factory = new PropertiesConfigurationFactory<Object>(
        target);
    factory.setPropertySources(this.propertySources);
    factory.setValidator(determineValidator(bean));
    // If no explicit conversion service is provided we add one so that (at least)
    // comma-separated arrays of convertibles can be bound automatically
    factory.setConversionService(this.conversionService == null
        ? getDefaultConversionService() : this.conversionService);
    if (annotation != null) {
      factory.setIgnoreInvalidFields(annotation.ignoreInvalidFields());
      factory.setIgnoreUnknownFields(annotation.ignoreUnknownFields());
      factory.setExceptionIfInvalid(annotation.exceptionIfInvalid());
      factory.setIgnoreNestedProperties(annotation.ignoreNestedProperties());
      if (StringUtils.hasLength(annotation.prefix())) {
        factory.setTargetName(annotation.prefix());
      }
    }
    try {
      factory.bindPropertiesToTarget();
    }
    catch (Exception ex) {
      String targetClass = ClassUtils.getShortName(target.getClass());
      throw new BeanCreationException(beanName, "Could not bind properties to "
          + targetClass + " (" + getAnnotationDetails(annotation) + ")", ex);
    }
  }
  private String getAnnotationDetails(ConfigurationProperties annotation) {
    if (annotation == null) {
      return "";
    }
    StringBuilder details = new StringBuilder();
    details.append("prefix=").append(annotation.prefix());
    details.append(", ignoreInvalidFields=").append(annotation.ignoreInvalidFields());
    details.append(", ignoreUnknownFields=").append(annotation.ignoreUnknownFields());
    details.append(", ignoreNestedProperties=")
        .append(annotation.ignoreNestedProperties());
    return details.toString();
  }
  private Validator determineValidator(Object bean) {
    Validator validator = getValidator();
    boolean supportsBean = (validator != null && validator.supports(bean.getClass()));
    if (ClassUtils.isAssignable(Validator.class, bean.getClass())) {
      if (supportsBean) {
        return new ChainingValidator(validator, (Validator) bean);
      }
      return (Validator) bean;
    }
    return (supportsBean ? validator : null);
  }
  private Validator getValidator() {
    if (this.validator != null) {
      return this.validator;
    }
    if (this.localValidator == null && isJsr303Present()) {
      this.localValidator = new ValidatedLocalValidatorFactoryBean(
          this.applicationContext);
    }
    return this.localValidator;
  }
  private boolean isJsr303Present() {
    for (String validatorClass : VALIDATOR_CLASSES) {
      if (!ClassUtils.isPresent(validatorClass,
          this.applicationContext.getClassLoader())) {
        return false;
      }
    }
    return true;
  }
  private ConversionService getDefaultConversionService() {
    if (this.defaultConversionService == null) {
      DefaultConversionService conversionService = new DefaultConversionService();
      this.applicationContext.getAutowireCapableBeanFactory().autowireBean(this);
      for (Converter<?, ?> converter : this.converters) {
        conversionService.addConverter(converter);
      }
      for (GenericConverter genericConverter : this.genericConverters) {
        conversionService.addConverter(genericConverter);
      }
      this.defaultConversionService = conversionService;
    }
    return this.defaultConversionService;
  }
  /**
   * {@link LocalValidatorFactoryBean} supports classes annotated with
   * {@link Validated @Validated}.
   */
  private static class ValidatedLocalValidatorFactoryBean
      extends LocalValidatorFactoryBean {
    private static final Log logger = LogFactory
        .getLog(ConfigurationPropertiesBindingPostProcessor.class);
    ValidatedLocalValidatorFactoryBean(ApplicationContext applicationContext) {
      setApplicationContext(applicationContext);
      setMessageInterpolator(new MessageInterpolatorFactory().getObject());
      afterPropertiesSet();
    }
    @Override
    public boolean supports(Class<?> type) {
      if (!super.supports(type)) {
        return false;
      }
      if (AnnotatedElementUtils.hasAnnotation(type, Validated.class)) {
        return true;
      }
      if (type.getPackage() != null && type.getPackage().getName()
          .startsWith("org.springframework.boot")) {
        return false;
      }
      if (getConstraintsForClass(type).isBeanConstrained()) {
        logger.warn("The @ConfigurationProperties bean " + type
            + " contains validation constraints but had not been annotated "
            + "with @Validated.");
      }
      return true;
    }
  }
  /**
   * {@link Validator} implementation that wraps {@link Validator} instances and chains
   * their execution.
   */
  private static class ChainingValidator implements Validator {
    private Validator[] validators;
    ChainingValidator(Validator... validators) {
      Assert.notNull(validators, "Validators must not be null");
      this.validators = validators;
    }
    @Override
    public boolean supports(Class<?> clazz) {
      for (Validator validator : this.validators) {
        if (validator.supports(clazz)) {
          return true;
        }
      }
      return false;
    }
    @Override
    public void validate(Object target, Errors errors) {
      for (Validator validator : this.validators) {
        if (validator.supports(target.getClass())) {
          validator.validate(target, errors);
        }
      }
    }
  }
  /**
   * Convenience class to flatten out a tree of property sources without losing the
   * reference to the backing data (which can therefore be updated in the background).
   */
  private static class FlatPropertySources implements PropertySources {
    private PropertySources propertySources;
    FlatPropertySources(PropertySources propertySources) {
      this.propertySources = propertySources;
    }
    @Override
    public Iterator<PropertySource<?>> iterator() {
      MutablePropertySources result = getFlattened();
      return result.iterator();
    }
    @Override
    public boolean contains(String name) {
      return get(name) != null;
    }
    @Override
    public PropertySource<?> get(String name) {
      return getFlattened().get(name);
    }
    private MutablePropertySources getFlattened() {
      MutablePropertySources result = new MutablePropertySources();
      for (PropertySource<?> propertySource : this.propertySources) {
        flattenPropertySources(propertySource, result);
      }
      return result;
    }
    private void flattenPropertySources(PropertySource<?> propertySource,
        MutablePropertySources result) {
      Object source = propertySource.getSource();
      if (source instanceof ConfigurableEnvironment) {
        ConfigurableEnvironment environment = (ConfigurableEnvironment) source;
        for (PropertySource<?> childSource : environment.getPropertySources()) {
          flattenPropertySources(childSource, result);
        }
      }
      else {
        result.addLast(propertySource);
      }
    }
  }
}

可以发现这个类实现了我们很多眼熟的接口:

public class ConfigurationPropertiesBindingPostProcessor implements 
BeanPostProcessor,  BeanFactoryAware, EnvironmentAware, 
ApplicationContextAware, InitializingBean,DisposableBean, 
ApplicationListener<ContextRefreshedEvent>, PriorityOrdered {
//...
}

(15) springboot下外部xml文件引用application.properties配置

① 不适用context:property-placeholder或者PropertyPlaceholderConfigurer

如下所示,spring.xml文件尝试引用XXXX.properites中属性配置。

<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc"
  xmlns:context="http://www.springframework.org/schema/context"
  xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
  xsi:schemaLocation="http://www.springframework.org/schema/beans 
    http://www.springframework.org/schema/beans/spring-beans-3.2.xsd 
    http://www.springframework.org/schema/mvc 
    http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd 
    http://www.springframework.org/schema/context 
    http://www.springframework.org/schema/context/spring-context-3.2.xsd 
    http://www.springframework.org/schema/aop 
    http://www.springframework.org/schema/aop/spring-aop-3.2.xsd 
    http://www.springframework.org/schema/tx 
    http://www.springframework.org/schema/tx/spring-tx-3.2.xsd ">
  <bean id="sysUser" class="com.appraiseteach.entity.SysUser">
    <property name="field1" value="${com.jane.appid.test}"/>
  </bean>
</beans>


因为application.properties中配置了spring.profiles.active=env ,那么就会从application-env.properties查找com.jane.appid.test配置。否则,从application.properties中查找。


② 使用context:property-placeholder

<context:property-placeholder location="classpath:application-test.properties" />
<bean id="sysUser" class="com.appraiseteach.entity.SysUser">
  <property name="field1" value="${com.jane.appid}"/>
</bean>


此时的查找顺序

  • ① 从application-env.properties查找,如果找到不再往下查找;
  • ② 从application-test.properties中查找;
  • ③ 如果①没有找到,②找到了,则使用②的值。否则使用①的值。
目录
相关文章
|
4天前
|
机器学习/深度学习 JSON Java
SpringBoot是如何加载配置文件的?
SpringBoot是如何加载配置文件的?
|
7天前
|
Java 数据库连接 开发者
SpringBoot实用开发篇第一章(yml配置文件操作)
SpringBoot实用开发篇第一章(yml配置文件操作)
|
18天前
|
Java Spring
解决Springboot集成ElasticSearch 报错:A bean with that name has already been defined in null and overriding
解决Springboot集成ElasticSearch 报错:A bean with that name has already been defined in null and overriding
|
4天前
|
Java
springboot Test 测试类中如何排除一个bean类
springboot Test 测试类中如何排除一个bean类
7 0
|
4天前
|
JavaScript Java 数据库连接
【Spring Boot】掌握Spring Boot:深入解析配置文件的使用与管理
【Spring Boot】掌握Spring Boot:深入解析配置文件的使用与管理
12 0
|
29天前
|
Java 开发者 Spring
Springboot中的@Bean用法以及常见问题
【5月更文挑战第27天】@Bean 注解是Spring框架中用于声明Spring应用上下文中的bean的一种方式。在Spring Boot中,@Bean注解通常与@Configuration注解一起使用,在配置类(Configuration class)中定义bean。
36 2
|
1月前
|
Java 应用服务中间件 Maven
SpringBoot概述&SpringBoot基础配置&yml的使用&多环境启动
SpringBoot概述&SpringBoot基础配置&yml的使用&多环境启动
38 2
|
1月前
|
XML Java 数据库
【SpringBoot:详解Bean装配】
【SpringBoot:详解Bean装配】
14 3
|
1月前
|
Java 容器 Spring
【SpringBoot:详解依赖注入和使用配置文件】
【SpringBoot:详解依赖注入和使用配置文件】
20 2
|
1月前
|
Java Spring 容器
SpringBoot中bean的生命周期
Spring Boot的Bean生命周期涉及实例化、属性注入、初始化和销毁。在实例化后,Spring通过构造函数或Setter注入属性,然后调用初始化方法(@PostConstruct、InitializingBean接口)。Bean在应用中使用后,当容器关闭时,会调用销毁方法(@PreDestroy、DisposableBean接口)。依赖注入、配置管理、组件扩展和切面编程是其常见应用场景。示例代码展示了如何通过实现BeanNameAware、BeanFactoryAware等接口以及使用@PostConstruct注解来控制Bean的初始化。
50 2
SpringBoot中bean的生命周期