SpringBoot的简单使用
SpringBoot是一种更加方便使用Spring的框架,其通过自动配置和起步依赖两大核心功能解决了Spring的繁琐配置和Jar包依赖导入的问题,其本身相较于Spring并无新的功能。
起步依赖
所有SpringBoot工程必须继承spring-boot-starter-parent,该Jar包能提供Jar
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.1.RELEASE</version>
</parent>
配置文件
SpringBoot项目可以通过 application.properties, application.yml, application.yaml三种配置文件形式,其中 .yml 与 .yaml 属于同一种文件格式,相同路径下三种形式的配置优先级如下
.properties > .yml >.yaml
yml
yml配置文件对大小写敏感
-
Key:value格式:
key: value
-
对象格式:
person:
name: andrew
age: 23
- 数组格式:数组的元素以 - 分隔
list:
- name: andrew
id: 1
- name: aa
id: 2
- 行内格式:
person: {name: andrew,age: 23}//对象行内格式
list: [{name: andrew,id: 1,username: wenjin,password: 123},{name: aa,id: 2}]//数组行内格式
- 纯量:单引号包裹的内容不会被转义,双引号则相反
key1: 'this content /n wil be parsed' //此处的 /n 不会被解析
key2: "this content /n wont't be parsed" // 此处的 /n 会被解析成换行符
properties
由于properties是基于key:value结构的数据格式,故当一个对象配置多个属性时,需重复书写对象的结构 且不同的数据直接不容易看出属性的相对结构,所以properties配置文件仅适用于少量内容下的配置。
list.users[0].id=1
list.users[0].name=andrew
list.users[1].id=2
list.users[1].name=aa
配置文件的读取
配置文件里的内容可以分别通过:
- @Value("${key}"): 注解可以取出SpringContext内的键为 key 的值
@Value("${key1}")
private String key1; //key1会被注入SpringContext内键为key1的值
- Environment :对象包含SpringContext所有相关参数
@Autowired
private Environment environment;
environment.getProperty("person.name");//返回SpringContext内键为person.name的值
- @CoonfigurationProperty(prefix=“prefix”): 用于在类上或方法上,作用在类上,类中的字段会被自动注入相应键名的值(包含复杂对象)
@Component
@ConfigurationProperties(prefix = "list")
public class MyBean {
private List<User> users;//list.users会被自动注入
public List<User> getUsers() {
return users;
}
public void setUsers(List<User> users) {
this.users = users;
}
}
Profiles配置文件切换
SpringBoot支持多个配置文件动态切换,以实现开发,测试,部署之间的灵活改变和需求,Spring主要以以下两种方式实现多配置文件:
- 直接增加对应的application-***.yml文件
- 在同一配置文件下配置多份 每份之间以 - - - 分隔
---
server:
port: 8081
spring:
profiles: dev
---
server:
port: 8082
spring:
profiles: test
---
- 主配置文件切换:通过配置 spring.profiles.active 属性来设置外部配置文件的生效与否,配置文件必须以application-开头,且统一配置文件内可以通过${key}引用对应的属性
spring:
profiles:
active: dev //此处即将application-dev.yml配置文件生效
- 运行时切换:通过在虚拟机(VM)添加 -Dspring.profiles.active=*** 参数使对应的配置文件生效
- 命令行切换:通过给 命令行增加 --spring.profiles.active =*** 参数使得对应的配置文件生效
SpringBoot配置文件加载顺序
- file: /config/
- file: /
- classpath: /config/
- classpath: /
其中由1-4优先级依次降低(加载顺序依次升高)
classpath: 类路径:src下的根目录
file:项目根路径: 项目jar包所在的同级路径
SpringBoot自动配置原理
@Conditional
@Conditional的value属性用于指定 Class<? extends Condition>[] 对于condition接口有matchs方法需要实现,用于判断是否对@Conditional注解的Bean对象是否被加载进spring容器,自定义@Conditional如下:
public class ConditionOnRedis implements Condition {
@Override //matchs方法用于判断bean是否被加载的逻辑
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
//conditonContex对象用于获取SpringConetxt相关参数
//annotatedMetaData对象可获取@Conditional注解标记的对象所有注解相关参数
boolean flag = true;
try {
Class.forName("redis.clients.jedis.Jedis");
} catch (ClassNotFoundException e) {
flag=false;//当类加载失败返回false,实现了判断项目中是否有Jedis相关jar包
}
return flag;
}
}
@Configuration
public class AutoConfiguration {
@Bean
@Conditional(ConditionOnRedis.class)//此处的bean在满足ConditionOnClass对象的match方法后才会被创建
public Role getRole(){
Role role = new Role();
role.setTitle("nothing");
role.setDesc("role for autoConfig test");
return role;
}
}
Spring官方提供 bean 是否被加载的判断注解
- @ConditionOnBean: 有指定的bean 才加载对应的bean
- @ConditionOnClass: 有指定的Class 才加载对应的bean
- @ConditionOnResource: 有指定的资源文件(classpath下) 才加载对应的bean
…
@Enable***
SpringBoot中存在大量@Enable***注解,其作用是将指定相关功能所涉及的Bean对象加载进Spring容器,底层通过@Import将指定bean加载。
@SpringBootApplication
此注解用于标记SpringBoot的启动类,作为程序的入口
@Target(ElementType.TYPE)//元注解 标记此注解仅作用于类上
@Retention(RetentionPolicy.RUNTIME)//元注解 标记此注解在运行期生效(被读取)
@Documented//元注解 可生成JavaDoc
@Inherited//允许子类继承父类注解
@SpringBootConfiguration// 其实就是一个@Configuration注解
@EnableAutoConfiguration//SpringBoot自动加载 核心注解
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {....}
@EnableAutoConfiguration
SpringBoot自动配置核心注解,通过@Import可实现导入指定的bean
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)// 自动导入bean的顶级注解(注入AutoConfigurationImportSelector.class 即可实现大量的相关bean的注入)
public @interface EnableAutoConfiguration {...}
AutoConfigurationImportSelector
AutoConfigurationImportSelector通过实现 ImportSelector 接口的 selectImports(AnnotationMetadata annotationMetadata) 方法可以对指定的bean上的注解进行读取并判断bean的加载时机。
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);//此处利用注解medatData对象创建了AutoConfigurationEntry 对象
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
AnnotationAttributes attributes = getAttributes(annotationMetadata);
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);//此处通过getCandidateConfigurations 获得需要加载的类的全限定类名集合
configurations = removeDuplicates(configurations);
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = getConfigurationClassFilter().filter(configurations);
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
getBeanClassLoader());//此处可以看出SpringBoot是从META-INF/spring.factories文件读取加载指定bean的全限定类名
Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you "
+ "are using a custom packaging, make sure that file is correct.");
return configurations;
}
自动配置总结
springBoot通过spring.factories等配置文件在容器创建的时候读取相关bean的全限定名,并将其加载进容器,加载的bean大都是@Configuration注解的配置类其中包含有相关@Bean的方法,并通过@ConditionOn***注解判断是否加载该Bean;
spring.factories文件部分内容
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ #此key指定EnableAutoConfiguration,下面的都是它的value,会在SpringBoot初始化时候读取并加载进容器
org.springframework.boot.actuate.autoconfigure.amqp.RabbitHealthIndicatorAutoConfiguration,\
org.springframework.boot.actuate.autoconfigure.audit.AuditAutoConfiguration,\
org.springframework.boot.actuate.autoconfigure.audit.AuditEventsEndpointAutoConfiguration,\
org.springframework.boot.actuate.autoconfigure.beans.BeansEndpointAutoConfiguration,\
...
其中以第一个为例:
@Configuration //spring配置类,当前类被容器加载时,会扫描加载内部@Bean方法的bean
@ConditionalOnClass(RabbitTemplate.class)// 当前环境有RabbitTemplate类时才会加载
@ConditionalOnBean(RabbitTemplate.class)// 当前环境有RabbitTemplate bean 时才会加载
@ConditionalOnEnabledHealthIndicator("rabbit")
@AutoConfigureBefore(HealthIndicatorAutoConfiguration.class)
@AutoConfigureAfter(RabbitAutoConfiguration.class)
public class RabbitHealthIndicatorAutoConfiguration extends
CompositeHealthIndicatorConfiguration<RabbitHealthIndicator, RabbitTemplate> {
private final Map<String, RabbitTemplate> rabbitTemplates;
public RabbitHealthIndicatorAutoConfiguration(
Map<String, RabbitTemplate> rabbitTemplates) {
this.rabbitTemplates = rabbitTemplates;
}
@Bean
@ConditionalOnMissingBean(name = "rabbitHealthIndicator")// 当前环境不包含 name 为 rabbitHealthIndicator 的bean的时候才会加载
public HealthIndicator rabbitHealthIndicator() {
return createHealthIndicator(this.rabbitTemplates);
}
}
可以看出springBoot通过一个启动类@EnableAutoConfiguration和@Import注解作为入口加载了配置文件对应的很多bean并通过@ConditonOn***注解判断相关的bean的加载时机,从而实现自动配置,过程环环相扣。
自定义包实现自动加载
-
导入springBoot相关jar包
-
编写@EnableConfiguration***注解的配置类
-
使用@Import注解配置此注解需要加载的类(bean)
-
对需要加载的类添加@ConditionOn***配置对应bean的加载时机
-
在resource/META-INF/目录下创建spring.factories文件并配置:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=自己编写的@EnableAutoConfiguration所注解类的全限定类名
-
在另一个过程导入这个jar包,启动Springoot测试指定bean是否被加载
需要被加载的类
@Configuration
@ConditionalOnProperty(prefix = "test",havingValue = "enableUserAutoConfiguration",name = "enableUserAutoConfiguration")
public class UserConfig {
@Bean
@ConditionalOnMissingBean(User.class)
public User getUser(){
return new User();
}
}
spring.factories配置文件
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.andrew.config.UserConfig
User(id=null, name=null, birthDay=null)//通过测试可以发现user对象在容器启动后可以获取到
Spring事件监听
-
ApplicationContextInitializer
-
SpringApplicationRunListener
-
CommandLineRunner
-
ApplicationRunner
ApplicationRunner接口
直接实现ApplicationRunner接口并注入spring容器,则该监听器会在容器启动后执行对应的run方法,如下:
@Component
public class MyApplicationRunner implements ApplicationRunner {
@Override
public void run(ApplicationArguments args) throws Exception {
System.out.println("applicationRunner running...");
}
}
CommandLineRunner接口
与ApplicationRunner接口类似,通过实现该接口,接口的run方法会在spring容器创建后被执行
@Component
public class MyCommandLineRunner implements CommandLineRunner {
@Override
public void run(String... args) throws Exception {
System.out.println("CommandLine runner is running...");
}
}
ApplicationContextInitializer接口
实现此接口的bean需要在spring.factories文件注册才会被spring识别,其initialize方法在spring容器初始化完毕后执行
@Component
public class MyApplicationContextInitializer implements ApplicationContextInitializer {
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
System.out.println("myApplicationContextInitializer is running....");
}
}
SpringApplicationRunListener接口
实现此接口的bean需要在,spring.factories内配置:
org.springframework.boot.SpringApplicationRunListener=com.andrew.domain.listener.MySpringApplicationRunListener
public class MySpringApplicationRunListener implements SpringApplicationRunListener {
private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(MySpringApplicationRunListener.class);
public MySpringApplicationRunListener(SpringApplication application, String[] args) {
}
@Override
public void starting() {
log.info("springApplicationContext is starting...");
}
@Override
public void environmentPrepared(ConfigurableEnvironment environment) {
log.info("springApplicationContext 's environment is ready...");
}
@Override
public void contextPrepared(ConfigurableApplicationContext context) {
log.info("springApplicationContext is ready...");
}
@Override
public void contextLoaded(ConfigurableApplicationContext context) {
log.info("springApplicationContext loaded...");
}
@Override
public void started(ConfigurableApplicationContext context) {
log.info("springApplicationContext is started...");
}
@Override
public void running(ConfigurableApplicationContext context) {
log.info("springApplicationContext is running...");
}
@Override
public void failed(ConfigurableApplicationContext context, Throwable exception) {
log.info("springApplicationContext was failed...");
}
}
SpringBoot Web项目部署
- Jar包方式部署:需要将所有的静态资源放在Resource目录下的static文件加内。(官方推荐使用)
- War包部署:需要修改启动类
@SpringBootApplication
public class SpringbootDeployApplication extends SpringBootServletInitializer {
public static void main(String[] args) {
SpringApplication.run(SpringbootDeployApplication.class, args);
}
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
return builder.sources(SpringbootDeployApplication.class);
}
}