1、自动注入
1.1 原理
当类上添加@SpringBootApplication注解时,就可以实现自动注入的功能,在第二章的末尾介绍它等价于@Configuration,@EnableAutoConfiguration,@ComponentScan注解。真正实现自动注入功能的是@EnableAutoConfiguration。
查看@EnableAutoConfiguration的源码,发现它又等价于@AutoConfigurationPackage,并且引入了AutoConfigurationImportSelector对象。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
/**
* Exclude specific auto-configuration classes such that they will never be applied.
* @return the classes to exclude
*/
Class<?>[] exclude() default {};
/**
* Exclude specific auto-configuration class names such that they will never be
* applied.
* @return the class names to exclude
* @since 1.3.0
*/
String[] excludeName() default {};
}
其中@AutoConfigurationPackage用于自动扫描相关的包。
AutoConfigurationImportSelector中有个方法getCandidateConfigurations,它的调用栈为getCandidateConfiguration---->SpringFactoriesLoader.loadFactoryNames--->loadSpringFactories,查看loadSpringFactories代码如下:
Enumeration<URL> urls = (classLoader != null ? ClassLoader.getResources(FACTORIES_RESOURCE_LOCATION) : ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
其中常量值为:
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
它会加载spring-boot-autoconfiguration-version.jar包,META-INF目录下的spring.factories文件,这个文件就是spring boot自动注入的配置。
1.2 禁用自动配置
当想要禁用某个自动配置项时,在启动类的SpringBootApplication注解上添加exclude属性,它指定禁用的自动配置项,多个时使用逗号分割。示例如下:
// 禁用了数据源的自动注入功能 @SpringBootApplication(exclude = {DataSourceAutoConfiguration.class}) public class DemoApplication { public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); } }
效果如下:
Exclusions: ----------- org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration
2、配置信息
在使用Spring时,有两种方式,XML方式,注解方式,它们都是在配置Bean,本质是将bean注入到IOC容器中。在spring boot有个很明显的变化,自动注入,它注入的这些bean虽然有默认值,但是遇到具体的需求,还是需要手动设置这些bean的值。
例如日志框架logback,之前是添加一份logback.xml,logback框架会根据这份配置文件创建一些对象,如Logger,Appender,这些对象并不在IOC容器中。Spring boot项目自动检测logback相关依赖,存在便自动注入bean,如ConsoleAppender,它有默认的pattern属性值,当不满足我们的需求时,就需要手动配置这些信息。
大部分的配置信息本质上都是给对象的属性赋值,我把它分为两类,
- 第一类为集成框架,框架核心对象的配置信息。
- 第二类为自定义的对象赋值。
少部分的配置信息是给容器上下文中添加全局变量。
2.1 定义配置信息
Spring boot获取配置信息的方式和顺序如下,如果找到则不会再继续往下查找。
- Devtools global settings properties:略
- @TestPropertySource:略
- @SpringBootTest:略
- Command line arguments:命令行的参数
- spring.application.json:变量spring.application.json,它的值是一个JSON字符串
- ServletConfig:ServletConfig对象中的初始化参数
- ServletContext:ServletContext对象中的初始化参数
- JNDI:略
- System.getProperties:JVM的系统变量
- OS environment:操作系统的环境变量
- RandomValuePropertySource:随机数。
- Profile-specific outside of the package jar:它是指当前项目中的application-profile配置文件
- Profile-specific inside of the package jar:它是指项目依赖的jar包下存在application-profile配置文件。
- Application outside of the package jar:当前项目中的applicaton配置文件。
- Application inside of the package jar:项目依赖的jar包下存在的application配置文件
- @PropertySource:使用注解引入的xx.properties文件中定义的变量
- SpringApplication.setDefaultProperties方法或SpringBootBuilder.properties方法。
Spring的变量确实比较复杂,所以在编写时规范一些,使用一种方式即可。例如都写在xx.properties文件中,然后引入。
2.1.1 命令行参数
参考CommandLineRunner接口
2.1.2 spring_application_json
它的值是JSON字符串,会将JSON中所有变量添加到容器中,例如'{"user":{"name":"jack","age":20}}'会把user.name,user.age添加到容器中
注:spring_application_json只有在命令行时才有效,在配置文件中无效
2.1.3 系统变量
系统变量有两种,操作系统的变量和JVM的系统变量,JVM也可以获取操作系统变量,调用System.getProperties方法即可,也可以向JVM中添加系统变量,调用System.setProperties。这些变量类似于全局变量,在程序的任何地方都可以获取,例如获取os.name系统变量,程序方式@Value("${os.name}"),配置文件中使用${os.name}。
2.1.4 RandomValuePropertySource
它的作用是为变量设置随机数(随机数不一定是数字,例如UUID),原著中的示例如下
my.secret=${random.value} my.number=${random.int} my.bignumber=${random.long} my.uuid=${random.uuid} my.number.less.than.ten=${random.int(10)} my.number.in.range=${random.int[1024,65536]}
2.1.5 配置文件
配置文件分为两类,一类是名称有profile,另一类是无profile。
查找无profile的配置文件分为两步:
- 第一步:首先需要找到配置文件存放的文件夹,它的默认查找步骤如下:
-
- 首先查找current directory下的config子目录,其中current directory为项目的根路径。
- 其次查找current directory目录。
- 之后查找classpath下的config子目录,一般这个文件夹为resources/config
- 最后查找classpath。
2.第二步:查找配置文件名称,它的默认查找步骤如下:
-
- 首先查找application.properties,
- 其次查找application.yml。
查找有profile的配置文件步骤不变,配置文件名变为application-{profile}.properties
可以通过设置spring.config.location指定文件夹的路径,设置spring.config.name指定配置文件的名称。
例如在resources下创建spring文件夹,配置文件名称为application-context.properties。
需要指定如下配置
--spring.config.location=classpath:/spring/ --spring.config.name=application-context
注:spring.config.name与spring.config.location写在配置文件中无效,可以设置为JVM变量或者是添加到命令行中
2.1.6 @PropertySource
是spring 框架的注解,功能是引入xx.properties文件,它有局限性,只在它标注的类上有效。例如多个@Configuration配置类上各自的@PropertySource只对自己有效。
2.1.7 API
调用SpringBuilder.setDefaultProperties或者SpringBootBuilder.properties方法。我感觉使用System.setProperties更好,JVM系统变量拥有更高的优先级。
2.2 对象的属性(无前缀)
当变量无前缀时,变量映射为属性遵循驼峰方式,例如userName属性,它会查找:
- user-name变量,
- user.name变量
- userName变量
注:变量名是不区分大小写的。
示例如下:
- 第一步定义变量,我在application.properties中定义变量app.name=learn spring boot
- 第二步在任何类中定义属性appName,在属性上添加@value注解
@Value("${app.name}") private String appName;
- 第三步验证,
******************** 项目名称是:learn spring boot
它与类无关,只与属性的名称有关,极大可能会引起重名。
2.3 对象的属性(有前缀)
当变量有前缀时,首先前缀映射到具体的类,变量名去除前缀的部分映射为属性,例如需要为User类属性赋值
- 第一步,定义User类,拥有三个属性
/** * * @Title: User.java * @Package learn.spring.boot.bean * @Description: 用户类 * @version V1.0 */ @Component @ConfigurationProperties(prefix = "test-user") public class User { // 用户姓名 private String name; // 用户地址 private String address; // 用户年龄 private Integer age; // get & set省略 }
2.第二步,定义变量,这里我单独定义了user.properties文件,并且通过@PropertySource引入,也可以直接定义在application.proerties文件中
# user name testUser.name=Jack # user age testUser.age=20 # user address testUser.address=some place
3.第三步:通过ApplicationContext获取User对象
// 获取用户对象 ApplicationContext ac = builder.context(); User user = ac.getBean("user",User.class); System.out.println("***********************"); System.out.println("user Name:"+user.getName()); System.out.println("user Age:"+user.getAge()); System.out.println("user Address:"+user.getAddress());
4.第四步:验证
*********************** user Name:Jack user Age:20 user Address:some place
注:
- 在设置对象属性时,不再使用@Value注解。
- 需要深刻变量名与属性名之间的映射关系
3、Profiles
Profile指针对不同场景加载不同的配置文件,此时配置文件的名称默认为application-profile.properties。
使用Profile有两个步骤,
- 创建application-{profile}.properties文件,本示例中创建application-test和application-production
- 激活profile。
激活profile的方式有三种
- 第一种设置spring.profiles.active变量
- 第二种方式在启动类上添加@ActiveProfiles注解
- 第三种方式调用SpringBootBuilder的profiles方法。