Spring除了支持读取自身配置文件外,也可以读取其他常见格式的配置(如properties、yaml等等),Spring对于配置管理提供了大量支持。

1.配置管理

1.1 @Value

@Value注解由Spring3.0提供,用于将外部的值动态注入到Bean中。@Value注解有@Value(“#{}”)和@Value(“${}”)两种形式,其中@Value(“#{}”)表示使用SpEL表达式,可以通过SpEL表达式获取Bean的属性和方法,SpEl表达式中也可以包含常量;@Value(“${}”)用于读取配置文件中定义的属性值。

1.1.1 使用@Value注解将外部的值动态注入到Bean中

  1. package com.example;
  2. import org.springframework.beans.factory.annotation.Value;
  3. import org.springframework.core.io.Resource;
  4. /**
  5. * 下面列举了@Value注解的几种方式使用场景,例如:
  6. * 注入普通字符串
  7. * 注入操作系统属性
  8. * 注入表达式结果
  9. * 注入其他Bean属性:注入beanInject对象的属性author
  10. * 注入文件资源
  11. * 注入URL资源
  12. */
  13. public class Project {
  14. @Value("normal")
  15. private String normal; // 注入普通字符串
  16. @Value("#{systemProperties['os.name']}")
  17. private String systemPropertiesName; // 注入操作系统属性
  18. @Value("#{ T(java.lang.Math).random() * 100.0 }")
  19. private double randomNumber; //注入表达式结果
  20. @Value("#{beanInject.author}")
  21. private String fromAnotherBean; // 注入其他Bean属性:注入beanInject对象的属性author,详情看BeanInject.class
  22. @Value("classpath:text.txt")
  23. private Resource resourceFile; // 注入文件资源,在项目的src/main/resources下创建text.txt
  24. @Value("http://www.baidu.com")
  25. private Resource testUrl; // 注入URL资源
  26. public String getNormal() {
  27. return normal;
  28. }
  29. public void setNormal(String normal) {
  30. this.normal = normal;
  31. }
  32. public String getSystemPropertiesName() {
  33. return systemPropertiesName;
  34. }
  35. public void setSystemPropertiesName(String systemPropertiesName) {
  36. this.systemPropertiesName = systemPropertiesName;
  37. }
  38. public double getRandomNumber() {
  39. return randomNumber;
  40. }
  41. public void setRandomNumber(double randomNumber) {
  42. this.randomNumber = randomNumber;
  43. }
  44. public Resource getResourceFile() {
  45. return resourceFile;
  46. }
  47. public void setResourceFile(Resource resourceFile) {
  48. this.resourceFile = resourceFile;
  49. }
  50. public Resource getTestUrl() {
  51. return testUrl;
  52. }
  53. public void setTestUrl(Resource testUrl) {
  54. this.testUrl = testUrl;
  55. }
  56. public String getFromAnotherBean() {
  57. return fromAnotherBean;
  58. }
  59. public void setFromAnotherBean(String fromAnotherBean) {
  60. this.fromAnotherBean = fromAnotherBean;
  61. }
  62. @Override
  63. public String toString() {
  64. return "Project{" +
  65. "normal='" + normal + '\'' +
  66. ", systemPropertiesName='" + systemPropertiesName + '\'' +
  67. ", randomNumber=" + randomNumber +
  68. ", fromAnotherBean='" + fromAnotherBean + '\'' +
  69. ", resourceFile=" + resourceFile +
  70. ", testUrl=" + testUrl +
  71. '}';
  72. }
  73. }
package com.example;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Component("beanInject")
public class BeanInject {

    @Value("z乘风")
    public String author;

    public String getAuthor() {
        return author;
    }

    public void setAuthor(String author) {
        this.author = author;
    }
}
package com.example;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class CustomConfig {

    @Bean
    public Project getProject(){
        return  new Project();
    }
}
package com.example;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class Test{
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context=new AnnotationConfigApplicationContext("com.example");
        Project project =(Project)context.getBean("getProject");
        System.out.println(project);
    }
}
执行结果:
Project{normal='normal', systemPropertiesName='Mac OS X', randomNumber=23.056497713387113, fromAnotherBean='z乘风', resourceFile=class path resource [text.txt], testUrl=URL [http://www.baidu.com]}

1.1.2 使用@PropertySource加载外部配置文件@Value读取外部配置文件动态注入Bean

Spring提供了@PropertySource注解用于加载外部配置文件并注入到Spring环境中,@PropertySource注解使用value属性(value属性是一个字符串数组)指定一个或多个配置文件的地址,可以是classpath地址也可以是磁盘地址。@Property注解@Value的结合可以实现读取外部配置文件并动态到注入Bean。

#此文件为外部配置文件,以供@PropertySource注解获取数据,该文件位于项目的src/main/resources/application.properties

user.name=z乘风
user.age=18
user.hobby=编程,音乐,黑丝美女
user.attrMap={name:"z乘风",address:"中国"}
package com.example;

import org.springframework.beans.factory.annotation.Value;

import java.util.List;
import java.util.Map;

public class UserInfo {
    @Value("${user.name}") // 使用@Value("${}")形式读取配置文件中的属性
    private String userName;

    @Value("${user.age}")
    public Integer age;

    @Value("#{'${user.hobby}'.split(',')}") // 读取配置文件的list格式并以,号分割转为字符串数组
    public List<String> hobby;

    @Value("#{${user.attrMap}}") // 读取配置文件的map格式
    public Map<String,String> attrMap;

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public List<String> getHobby() {
        return hobby;
    }

    public void setHobby(List<String> hobby) {
        this.hobby = hobby;
    }

    public Map<String, String> getAttrMap() {
        return attrMap;
    }

    public void setAttrMap(Map<String, String> attrMap) {
        this.attrMap = attrMap;
    }

    @Override
    public String toString() {
        return "UserInfo{" +
                "userName='" + userName + '\'' +
                ", age=" + age +
                ", hobby=" + hobby +
                ", attrMap=" + attrMap +
                '}';
    }
}
package com.example;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.context.annotation.PropertySources;

/**
 * 使用@PropertySource注解加载指定配置文件到Spring环境中,encoding为解码编码,
 * 解码编码若不指定正确则可能导致读取配置乱码。
 * @PropertySource等同于Spring配置文件中的
 <context:property-placeholder location="classpath:application.properties" />
 */
@PropertySource(value = {"classpath:application.properties"},encoding="utf8")
@Configuration
public class CustomConfig {

    @Bean
    public UserInfo getUserInfo(){
        return  new UserInfo();
    }
}
package com.example;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class Test {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext("com.example");
        UserInfo userInfo = (UserInfo) context.getBean("getUserInfo");
        System.out.println(userInfo);

        /**
         * 由于@PropertySouce将外部配置文件加载到Spring环境,除了结合@Value注解读取配置文件中            的数据外,也可以通过Spring上下文的getEnvironment方法获取Spring环境中所有环境变量。
         */
        ConfigurableEnvironment environment = context.getEnvironment();
        /**
         * 打印apple,apple是我本机的用户名,user.name环境变量是Spring内部提供的环境变量,
         * 优先级高于外部配置文件中的同名变量。
         */
        System.out.println(environment.getProperty("user.name"));
        System.out.println(environment.getProperty("user.age")); // 18
    }
}
执行结果为:
UserInfo{userName='apple', age=18, hobby=[编程, 音乐, 黑丝美女], attrMap={name=z乘风, address=中国}}
apple
18

在Spring4.0中,Spring提供了一个新的注解——@PropertySources,此注解与@PropertySource注解作用类似,都用于导入外部配置文件到Spring环境中,而@PropertySources是以@PropertySource的形式用于多文件导入。

package com.example;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.context.annotation.PropertySources;

/**
 * 使用@PropertySource注解加载指定配置文件到Spring环境中,encoding为解码编码,
 * 解码编码若不指定正确则可能导致读取配置乱码。
 * 
 * @PropertySources注解用于以@PropertySource注解的形式导入多个配置文件。
 */
@PropertySources({
  @PropertySource(value = {"classpath:application.properties"},encoding="utf8")
})
@Configuration
public class CustomConfig {

    @Bean
    public UserInfo getUserInfo(){
        return  new UserInfo();
    }
}

1.2 @ImportSouce

@ImportResource注解用于导入一个或多个spring配置文件.xml。Spring在启动时会默认加载classpath目录下的spring.xml作为配置文件,若要加载指定的spring.xml配置文件,则可以通过@ImportResource注解指定xml配置文件地址,@ImportResource注解可以引入一个或多个spring配置文件.xml,注意不能引入其他格式的配置文件,否则将抛出异常。

package com.haha;

public class User {
    private String userName;
    private Integer age;
    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "User{" +
                "userName='" + userName + '\'' +
                ", age=" + age +
                '}';
    }
}
<!-- 该文件为加载的spring配置文件,文件位于src/main/resources/application.xml -->
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:c="http://www.springframework.org/schema/c"
       xmlns:p="http://www.springframework.org/schema/p"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd">
     <bean id="user" class="com.haha.User">
        <property name="userName" value="z乘风"/>
        <property name="age" value="18"/>
     </bean>
</beans>
package com.haha;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ImportResource;

/**
 * Spring在启动时会默认加载classpath目录下的spring.xml作为配置文件,
 * 若要加载指定的spring.xml配置文件,则可以通过@ImportResource注解指定xml配置文件
 * 地址,@ImportResource注解可以引入一个或多个spring配置文件.xml,
 * 注意不能引入其他格式的配置文件,否则将抛出异常。
 */
@ImportResource(value = {"classpath:application.xml"})
@Configuration
public class CustomConfig {

}
package com.haha;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class Test {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context=
                new AnnotationConfigApplicationContext("com.haha");
        User user =(User)context.getBean("user");
        System.out.println(user.toString());// User{userName='z乘风', age=18}
    }
}

1.3 @Import

@Import注解提供与XML中等效的功能,允许去导入@Configuration类,ImportSelector 和 ImportBeanDefinitionRegistrar 的具体实现,以及常规组件类(例如@Configuration)。类似于 AnnotationConfigApplicationContext.register(java.lang.Class<?>…)注册Bean这种操作。
该注解仅限用于类上,如果需要导入XML或其他非@Configuration bean定义资源,请改用@ImportResource注释。下面列举了@Import注解的四种用法:

1.3.1 @Import注解将标记的类注册成Bean

package com.haha;
public class UserComponent {
    public void hello(){
        System.out.println("hello");
    }
}

package com.haha;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
/**
 * @Import注解可以将标注的类注入到Spring IOC容器。UserComponent.class无需使用@Component、
 * @Service等注解注入。
 */
@Import({UserComponent.class})
@Configuration
public class CustomConfig {

}


package com.haha;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class Test {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context=
                new AnnotationConfigApplicationContext("com.haha");
        UserComponent userComponent =context.getBean(UserComponent.class);
        userComponent.hello(); // hello
    }
}

1.3.2 @Import组合@Configuration类

当项目中的配置类比较繁杂时根据”大化小,小聚多”的思想可以对配置类进行拆分,然后通过@Import组合拆分的配置类。

package com.haha;
public class Config {
    private String name;
    public Config(String name) {this.name = name;}
    public String getName() {return name;}
    public void setName(String name) {this.name = name;}
}
package com.haha;

import org.springframework.context.annotation.Bean;
/**
 * 项目配置,由于使用@Import可以注入标识的类,所以@Configuration不是必须的。
 */
public class ProjectConfig {
    @Bean
    public Config initProjectConfig(){return new Config("ProjectConfig");}
}


package com.haha;
import org.springframework.context.annotation.Bean;

/**
 * 数据库配置类,由于使用@Import可以注入标识的类,所以@Configuration不是必须的。
 */
public class DBConfig {
   @Bean
   public Config initDBConfig(){
      return new Config("DBConfig");
   }
}


package com.haha;
import org.springframework.context.annotation.Bean;

/**
 * 自定义配置类,由于使用@Import可以注入标识的类,所以@Configuration不是必须的。
 */
public class CustomConfig {
    @Bean
    public Config initCustomConfig(){
        return new Config("CustomConfig");
    }
}
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;

/**
 * 使用@Import将配置类组合为一个配置类
 */
@Import({CustomConfig.class,ProjectConfig.class,DBConfig.class})
@Configuration
public class ComposeConfig {

}
package com.haha;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class Test {
    public static void main(String[] args) {

        /**
         * 由于ComposeConfig.class使用@Import导入了多个@Configuration配置类,
         * @Import会将标注的类注入到Spring容器,所以只要扫描ComposeConfig.class即可。
         */
        AnnotationConfigApplicationContext context=
                new AnnotationConfigApplicationContext(ComposeConfig.class);

        Config projectConfig =(Config)context.getBean("initProjectConfig");
        System.out.println(projectConfig.getName()); // ProjectConfig

        Config customConfig =(Config)context.getBean("initCustomConfig");
        System.out.println(customConfig.getName()); // CustomConfig

        Config dbConfig =(Config)context.getBean("initDBConfig");
        System.out.println(dbConfig.getName()); // DBConfig
    }
}
执行结果:
ProjectConfig
CustomConfig
DBConfig

1.3.3 @Import导入ImportBeanDefinitionRegistrar接口的实现类

Spring中的Bean大多都是由BeanFactory创建的,Spring中默认对扫描的路径下的类中带有@Configuration和@Componet、@Service、@Controller、@Repository的类创建并添加到Spring容器中。Spring也提供了ImportBeanDefinitionRegistrar接口,通过实现ImportBeanDefinitionRegistrar重写registerBeanDefinitions
�方法就能动态的注入Bean。
所有实现了ImportBeanDefinitionRegistrar接口的类的都会被ConfigurationClassPostProcessor处理,ConfigurationClassPostProcessor实现了BeanFactoryPostProcessor接口,所以ImportBeanDefinitionRegistrar中动态注册的bean是优先于依赖其的bean初始化,也能被aop、validator等机制处理。

package com.hehe;

import java.lang.annotation.*;

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Mapper {
}
package com.hehe;

import java.lang.annotation.*;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
public @interface CustomBean {
}
package com.hehe;

import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.annotation.ClassPathBeanDefinitionScanner;
import org.springframework.core.type.filter.AnnotationTypeFilter;

import java.util.Set;

/**
 * 继承ClassPathBeanDefinitionScanner类来扫描获取需要注册的Bean
 */
public class MapperBeanDefinitionScanner extends ClassPathBeanDefinitionScanner {
    public MapperBeanDefinitionScanner(BeanDefinitionRegistry registry,boolean useDefaultFilters) {
        super(registry,useDefaultFilters);
    }
    protected void registerFilters() {
        addIncludeFilter(new AnnotationTypeFilter(Mapper.class));
    }

    /**
     * 重写doScan方法,用于指定扫描范围
     * @param basePackages 包路径
     * @return
     */
    @Override
    protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
        return super.doScan(basePackages);
    }
}
package com.hehe;

import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.context.ResourceLoaderAware;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.filter.AnnotationTypeFilter;

public class AutoConfigureRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware {

    ResourceLoader resourceLoader;

    /**
     * 此方法用于注册Bean信息,我们常说的Bean在Spring中抽象成BeanDefinition接口,之后放入Spring容器中,
     * Spring IOC容器其实就是Beanfactory中的一个 Map,key是Bean的名称,
     * value是Bean对应的BeanDefinition,注册Bean的方法由BeanFactory子类实现。
     *
     * @param metadata 注解元信息,获取当前类上的注解元信息
     * @param registry Bean注册器,用于动态的注册Bean
     */
    @Override
    public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
        /**
         * 打印:[org.springframework.context.annotation.Import, org.springframework.context.annotation.Configuration]
         * 说明:由于AutoConfig.class通过@Import注解导入了AutoConfigureRegistrar.class(当前类),
         * metadata可以获取到@Import和@Configuration注解
         */
        System.out.println(metadata.getAnnotationTypes());

        /**
         * BeanDefinitionRegistry根据Bean的全限定类路径来注册Bean。首先通过containsBeanDefinition
         * 方法判断Spring容器中是否存在Bean Name为beanComponent的Bean,若不存在则初始化
         * 一个RootBeanDefinition实例,RootBeanDefinition类继承自beanDefinition,
         * 然后通过BeanDefinitionRegistry的registerBeanDefinition将RootBeanDefinition实例
         * 注册到Spring容器。registerBeanDefinition方法参数1为Bean的Name,参数2是一个需要注册到
         * Spring容器的BeanDefinition对象。
         */
        // 判断Spring容器中是否存在Bean Name beanComponent的Bean
        boolean bool=registry.containsBeanDefinition("beanComponent");
        if(!bool){
            /**
             * 初识化一个RootBeanDefinition,RootBeanDefinition继承自BeanDefinition接口,
             * RootBeanDefinition也提供了根据beanClassName获取RootBeanDefinition实例的构造函数,
             * 例如:new RootBeanDefinition("com.hehe.BeanComponent")
             */
            RootBeanDefinition beanDefinition=new RootBeanDefinition(BeanComponent.class);
            // 将beanDefinition注册到Spring容器
            registry.registerBeanDefinition("beanComponent",beanDefinition);
        }

        /**
         * MapperBeanDefinitionScanner继承自ClassPathBeanDefinitionScanner,
         * 借助MapperBeanDefinitionScanner实现类来扫描获取需要注册的Bean,由于MapperBeanDefinitionScanner类
         * 通过registerFilters方法已经添加注解类型过滤器,注解类型为Mapper.class,下面示例代码中又通过
         * MapperBeanDefinitionScanner实例添加了一个注解类型过滤器,注解类型为CustomBean.class,
         * 所以如果使用了@Mapper或@CustomBean注解的Bean都会被注入到SpringIoc容器。
         *
         * MapperBeanDefinitionScanner构造函数,参数一为Bean定义注册器,参数二是否使用默认过滤器处理Bean,
         * 由于下面示例中自定义了过滤器,所以不使用默认过滤器。
         */
        MapperBeanDefinitionScanner scanner=new MapperBeanDefinitionScanner(registry,false);
        // 设置资源处理器
        scanner.setResourceLoader(resourceLoader);
        // 注册过滤器
        scanner.registerFilters();
        /**
         * 添加要包含的过滤器,AnnotationTypeFilter是最常用的过滤器,根据指定注解进行过滤,
         * 除了AnnotationTypeFilter过滤器还有RegexPatternTypeFilter过滤器,根据正则表达式
         * 进行过滤Bean。
         */
        scanner.addIncludeFilter(new AnnotationTypeFilter(CustomBean.class));


        // 设置扫描包范围
        scanner.doScan("com.hehe");
    }

    /**
     * 通过实现ResourceLoaderAware接口重写setResourceLoader方法获取ResourceLoader对象
     * @param resourceLoader 资源处理器
     */
    @Override
    public void setResourceLoader(ResourceLoader resourceLoader) {
        this.resourceLoader=resourceLoader;
    }
}
package com.hehe;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;

// 将AutoConfigureRegistrar.class注入到Spring容器
@Import({AutoConfigureRegistrar.class})
@Configuration
public class AutoConfig {
}
package com.hehe;
@CustomBean
public class UserBean {
    public String getUser(){
        return "userBean";
    }
}


package com.hehe;
@Mapper
public class UserMapper {
    public String getUser(){
        return "{name:'z乘风',age:18}";
    }
}

package com.hehe;
public class BeanComponent {
    public String getBeanName(){
        return "beanComponent";
    }
}
package com.hehe;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class Test {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context=new
                AnnotationConfigApplicationContext(AutoConfig.class);
        UserMapper userMapper = context.getBean(UserMapper.class);
        System.out.println(userMapper.getUser()); // {name:'z乘风',age:18}

        UserBean bean = context.getBean(UserBean.class);
        System.out.println(bean.getUser()); // userBean

        BeanComponent beanComponent =(BeanComponent)context.getBean("beanComponent");
        System.out.println(beanComponent.getBeanName());
    }
}
执行结果:
[org.springframework.context.annotation.Import, org.springframework.context.annotation.Configuration]
{name:'z乘风',age:18}
userBean
beanComponent

1.3.4 @Import导入ImportSelector接口的实现类

ImportSelector接口用于注册指定Bean的Class名称的Bean。实现ImportSelector接口需要重写selectImports方法,该方法接收一个当前类注解元数据作为参数,返回一个需要注册为Bean的Class的字符串数组,可以在该数组指定需要注册为Bean的Class名称。

package com.aa;
public class Hard {
    public String getText(){return  "Hard";}
}

package com.aa;
public class Simple {
    public String getText(){return  "Simple";}
}
package com.aa;

import org.springframework.context.annotation.ImportSelector;
import org.springframework.core.type.AnnotationMetadata;

/**
 * ImportSelector接口用于注册指定Bean的Class名称的Bean。
 * 实现ImportSelector接口需要重写selectImports方法,该方法接收一个当前类注解元数据作为参数,
 * 返回一个需要注册为Bean的Class的字符串数组,可以在该数组指定需要注册为Bean的Class名称。
 */
public class AppConfigSelector implements ImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata annotationMetadata) {

        // 将Hard和Simple都注入到Spring容器
        return new String[]{
            Hard.class.getName(), // Bean Name为com.aa.Hard
            Simple.class.getName(), // Bean Name为com.aa.Simple
        };
    }
}
package com.aa;

import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;

@Import({AppConfigSelector.class})
@Configuration
public class AppConfig {
}
package com.aa;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class Test {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context=
                new AnnotationConfigApplicationContext(AppConfig.class);
        Hard hard=(Hard) context.getBean("com.aa.Hard");
        System.out.println(hard.getText()); // Hard

        Simple simple = context.getBean(Simple.class);
        System.out.println(simple.getText()); // Simple

    }
}
执行结果:
Hard
Simple

1.4 @ConfigurationProperties

@ConfigurationProperties这个注解由SpringBoot提供(使用时需要添加SpringBoot依赖),SpringBoot是Spring的超集,SpringBoot相较于Spring提供了更多封装与功能扩展。之所以要介绍@ConfigurationProperties注解是因为应对繁多且有规则的配置文件是非常便捷的,无需使用@Value注解一个一个标注属性,通过@ConfigurationProperties指定需要配置属性的前缀就能根据属性名注入到同名的属性中。

1.4.1 使用@ConfigurationProperties标注类并将读取的数据注入到类

# 该配置文件位于src/main/resources/application.properties。Spring启动时会默认加载此配置文件。
# 注意:读取配置文件中的数据出现乱码时,首先检查配置的编码格式,建议设置为utf-8
server.port=8888

project.version=1.0.0
project.name=dogAdmin
# 解析日期格式
project.lastUpdateTime=2021-01-01 09:01:00 
# 解析List格式
project.versionList[0]=1.0.0
project.versionList[1]=1.0.1
project.versionList[2]=1.0.2

# 解析Map格式
project.map.author=z乘风
project.map.email=2684837849@qq.com
project.map.mode=light
package com.fly.config;

import com.fasterxml.jackson.annotation.JsonFormat;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.format.annotation.DateTimeFormat;

import java.io.Serializable;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.Map;

@Configuration
@ConfigurationProperties(prefix = "project")
/**
 * 由于读取配置文件可能会乱码,这里使用@PropertySource注解指定配置文件地址并指定解码编码
 */
@PropertySource(value = {"classpath:application.properties"}, encoding = "utf8")
public class ProjectConfiguration implements  Serializable{

    private static final long serialVersionUID = -8357041624408744707L;
    private String version;
    private String name;

    /**
     * 将Date转换成String,一般后端向前端返回数据时使用。@JsonFormat注解的作用是
     * 完成json字符串到java对象的转换工作。
     * 注意:使用@JsonFormat注解时推荐类实现Serializable(序列化)接口,因为有时候会出现序列化失败。
     */
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm",timezone="GMT+8")
    /**
     * 将String转为Date,一般用于前端向后端传递参数时使用,@ConfigurationProperties注解读取的lastUpdateTime
     * 属性是字符串,故要转成Date类型。
     * 简单来说@DateTimeFormat是处理外部传递过来的参数,而@JsonFormat将内部数据处理后返回给外部。
     */
    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private Date lastUpdateTime;

    private List<String> versionList;

    private Map<String,String> map;

    public ProjectConfiguration(){}

    public ProjectConfiguration(String version, String name, Date lastUpdateTime) {
        this.version = version;
        this.name = name;
        this.lastUpdateTime = lastUpdateTime;
    }

    public String getVersion() {
        return version;
    }

    public void setVersion(String version) {
        this.version = version;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Date getLastUpdateTime() {
        return lastUpdateTime;
    }

    public void setLastUpdateTime(Date lastUpdateTime) {
        this.lastUpdateTime = lastUpdateTime;
    }

    public static long getSerialVersionUID() {
        return serialVersionUID;
    }

    public List<String> getVersionList() {
        return versionList;
    }

    public void setVersionList(List<String> versionList) {
        this.versionList = versionList;
    }

    public Map<String, String> getMap() {
        return map;
    }

    public void setMap(Map<String, String> map) {
        this.map = map;
    }

    @Override
    public String toString() {
        // 线程不安全
        SimpleDateFormat format=new SimpleDateFormat("yyyy-MM-dd");
        return "ProjectConfiguration{" +
                "version='" + version + '\'' +
                ", name='" + name + '\'' +
                ", lastUpdateTime=" + format.format(lastUpdateTime) +
                ", versionList=" + versionList +
                ", map=" + map +
                '}';
    }
}
package com.fly;

import com.fly.config.ProjectConfiguration;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.RestController;

@SpringBootApplication
@RestController
public class TestStartApp implements ApplicationRunner {

    @Autowired
    ProjectConfiguration projectConfiguration;

    public static void main(String[] args) throws InterruptedException {
         SpringApplication.run(TestStartApp.class);
    }

    @Override
    public void run(ApplicationArguments args) throws Exception {
        System.out.println(projectConfiguration.toString());
    }
}
执行结果:
ProjectConfiguration{version='1.0.0', name='dogAdmin', lastUpdateTime=2021-01-01, versionList=[1.0.0, 1.0.1, 1.0.2], map={author=z乘风, email=2684837849@qq.com, mode=light}}

1.4.2 将@ConfigurationProperties和@Bean标注在方法上

@Configuration
public class DataSourceConfig {

    @Primary
    @Bean(name = "primaryDataSource")
    // 将前缀为“spring.datasource.primary”的属性,赋值给DataSource对应的属性值
    @ConfigurationProperties(prefix="spring.datasource.primary")
    public DataSource primaryDataSource() {
        return DataSourceBuilder.create().build();
    }
}
#application.properties
spring.datasource.primary.name=root
spring.datasource.primary.password=123456

1.4.3 使用@ConfigurationProperties注解标注普通类,通过@EnableConfigurationProperties将其注入到IOC容器

@EnableConfigurationProperties用于将标记的一组类注入到Spring容器。

@ConfigurationProperties(prefix = "user1")  // User 并没有注入到IOC容器
public class User {
    private String name;
    // 省略getter/setter方法
}
@SpringBootApplication
 // 通过@EnableConfigurationProperties注解将User.class注入到IOC容器,注入后便会使用@ConfigurationProperties注解对属性进行匹配赋值
@EnableConfigurationProperties({User.class}) 
public class Application {
    public static void main(String[] args) throws Exception {
        SpringApplication.run(Application.class, args);
    }
}

2.SpEL表达式

Spring Expression Language(Spring表达式语言,简称SpEL)是一种强大的表达式语言,支持在运行时查询和操作对象图。语言语法类似于统一 EL,但提供了额外的功能,最显著的是方法调用和基本的字符串模板功能。
虽然还有其他几种可用的 Java 表达式语言——OGNL、MVEL 和 JBoss EL等等,但SpEL的设计目的是向Spring 社区提供一种单一的、支持良好的表达式语言。SpEL 基于与技术无关的 API,所以你也可以集成其他表达式语言。虽然SpEL作为Spring产品的基础,但是它不直接与 Spring 相关联,可以独立使用。SpEL表达式一般在注解中使用的比较多,例如Spring提供的@Value注解或自定义注解,基于SpEL的强大的表达能力能大大提供扩展能力。