https://blog.csdn.net/u011870547/article/details/80975623
给maven 的settings.xml配置文件的profiles标签添加:让Maven使用JDK1.8进行编译
jdk‐1.8
true
1.8
1.8
1.8
1.8
org.springframework.boot
spring‐boot‐starter‐parent
1.5.9.RELEASE
org.springframework.boot
spring‐boot‐starter‐web
/**
* @SpringBootApplication 来标注一个主程序类,说明这是一个Spring Boot应用
*/
@SpringBootApplication
public class HelloWorldMainApplication {
public static void main(String[] args) {
// Spring应用启动起来
SpringApplication.run(HelloWorldMainApplication.class,args);
}
}
@Controller
public class HelloController {
@ResponseBody
@RequestMapping("/hello")
public String hello(){
return "Hello World!";
}
}
4.5运行主程序测试
4.6简化部署
org.springframework.boot
spring‐boot‐maven‐plugin
将这个应用打成jar包,直接使用java -jar的命令进行执行(java -jar HelloWorldMainApplication.jar
org.springframework.boot
spring-boot-starter-parent
2.0.4.RELEASE
SpringBoot的版本仲裁中心.
org.springframework.boot
spring-boot-starter-web
spring-boot-starter-web:
spring-boot-starter:spring-boot场景启动器;帮我们导入了web模块正常运行所依赖的组件;
Spring Boot将所有的功能场景都抽取出来,做成一个个的starters(启动器),只需要在项目里面引入这些starter
相关场景的所有依赖都会导入进来。要用什么功能就导入什么场景的启动器
/**
* @SpringBootApplication 来标注一个主程序类,说明这是一个Spring Boot应用
*/
@SpringBootApplication
public class HelloWorldMainApplication {
public static void main(String[] args) {
// Spring应用启动起来
SpringApplication.run(HelloWorldMainApplication.class,args);
}
}
@SpringBootApplication: SpringBoot应用标注在某个类上说明这个类是SpringBoot的主配置类,SpringBoot就应该运行这个类的main方法来启动SpringBoot应用;
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(
excludeFilters = {@Filter(
type = FilterType.CUSTOM,
classes = {TypeExcludeFilter.class}
), @Filter(
type = FilterType.CUSTOM,
classes = {AutoConfigurationExcludeFilter.class}
)}
)
public @interface SpringBootApplication {
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {
}
@SpringBootConfiguration:Spring Boot的配置类; 标注在某个类上,表示这是一个Spring Boot的配置类;
@Configuration:配置类上来标注这个注解;表示该类是spring的配置类。
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration {
通过这个源码可以发现配置类也是spring容器中的一个主件——@Component
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import({Registrar.class})
public @interface AutoConfigurationPackage {
}
@Import:spring的底层注解;作用是:用于给spring容器导入主件。
通过查看Registrar类的源码可以发现以下代码:
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
//该句代码就是获取元注解所在的包,从而就可以发现(new AutoConfigurationPackages.PackageImport(metadata)).getPackageName()的值为@SpringBootApplication注解所在包下的包名
AutoConfigurationPackages.register(registry, (new AutoConfigurationPackages.PackageImport(metadata)).getPackageName());
}
那么就有一个重要的结论:
@AutoConfigurationPackage注解最主要的作用就是:将主配置类(@SpringBootApplication标注的类)的所在包及下面所有子包里面的所有组件扫描到Spring容器;
@Import({AutoConfigurationImportSelector.class})的作用:给容器导入一些主件。那么导入哪些主件呢?
就由AutoConfigurationImportSelector类来选择:将所有需要导入的主件以全类名的方式返回;
通过测试发现:会给容器中导入非常多的自动配置类(xxxAutoConfiguration);就是给容器中导入这个场景需要的所有组件, 并配置好这些组件;有了自动配置类,免去了我们手动编写配置注入功能组件等的工作;
SpringFactoriesLoader.loadFactoryNames(EnableAutoConfiguration.class,classLoader);
Spring Boot在启动的时候从类路径下的META-INF/spring.factories中获取EnableAutoConfiguration指定的值,将这些值作为自动配置类导入到容器中,自动配置类就生效,帮我们进行自动配置工作;以前我们需要自己配置的东西,自动配置类都帮我们配置好了;
J2EE的整体整合解决方案和自动配置都在spring-boot-autoconfigure-1.5.9.RELEASE.jar;
SpringBoot使用一个全局的配置文件,配置文件名是固定的:application.properties或者application.yml
配置文件的作用: 修改SpringBoot自动配置的默认值;(SpringBoot在底层都给我们自动配置好了)。
YAML(YAML Ain’t Markup Language):
yaml 的区别:
YAML:以数据为中心,比json、xml等更适合做配置文件;
yaml例子:
server:
port: 8081
xml例子:
<server>
<port>8081port>
server>
server:
port: 8081
path: /hello
使用"k: v"来写。(字符串默认不用加上单引号或者双引号;)
那么,如果加双引号和单引号又有什么含义呢?
##通过双引号括起来的字符串有特殊字符就会转意其本身的含义:例如\n为换行符,那么输出的结果就会进行换行##
name: "zhangsan\nlisi"
##输出结果会把\n当成普通字符输出##
name: "zhangsan\nlisi"
对象也是使用k: v的方式来写。在下一行来写对象的属性和值的关系;注意缩进。
举个例子:假设有一个friends对象。用yaml的写法有以下两种:
方式1:换行写法
friends:
lastName: zhangsan
age: 18
方式2:行内写法
friends: {lastName: zhangsan,age: 18}
用 ”- “值表示数组中的一个元素。
例子:假设有一个pets数组。用yam的写法有两种:
方式1:换行写法
pets:
- cat
- dog
- pig
方式2:行内写法
pets: [cat,dog,pig]
我们下面就通过在配置文件中写一个person的配置,并把它一一映射到Person对象中做一个演示。
配置文件:
person:
lastName: 掌声
age: 18
boss: false
salary: 7000.8
birthday: 2018/12/12
maps: {k1: v1,k2: 12}
lists: [k1,lisi,18,2.3]
dog:
name: xiaogou
age: 2
bean对象:
/**
* 将配置文件中配置的每一个属性值,映射到组件中
* @ConfigurationProperties:告诉springboot将本类中的所有属性和配置文件中的相关配置进行绑定
* prefix: 配置文件中哪个配置下的属性进行一一对应
* 只有这个组件是容器中的组件,才能容器提供@ConfigurationProperties功能
*/
@ConfigurationProperties(prefix = "person")
@Component
public class Person {
private String lastName;
private Integer age;
private Boolean boss;
private Double salary;
private Date birthday;
private Map<String,Object> maps;
private List<Object> lists;
private Dog dog;
主意:
<!‐‐导入配置文件处理器,配置文件进行绑定就会有提示‐‐>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring‐boot‐configuration‐processorartifactId>
<optional>trueoptional>
dependency>
@Value和@ConfigurationProperties 都是把配置文件中的配置和对象的属性进行绑定
1 | @ConfigurationProperties | @Value |
---|---|---|
功能 | 批量注入配置文件中的属性 | 一个个指定 |
松散绑定 | 支持 | 不支持 |
SpEL | 不支持 | 支持 |
JSR303数据校验 | 支持 | 不支持 |
复杂类型封装 | 支持 | 不支持 |
@PropertySource(value = {"classpath:person.properties"})
@ConfigurationProperties(prefix = "person")
@Component
public class Person {
private String lastName;
private Integer age;
private Boolean boss;
private Double salary;
private Date birthday;
private Map<String,Object> maps;
private List<Object> lists;
private Dog dog;
例子:我们创建一个自定义的bean.xml文件,并在bean.xml文件中添加一个bean到spring容器中。并让该bean.xml文件有效。
自定义spring配置文件bean.xml
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema‐instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring‐beans.xsd">
<bean id="helloService" class="com.atguigu.springboot.service.HelloService">bean>
beans>
为了让该配置文件生效,我们需要在配置类上添加一个@ImportResource注解来让该配置文件生效:那就在springboot主程序中添加。
@ImportResource(locations = {"classpath:bean.xml"})//让classpath目录下的bean.xml文件生效
@SpringBootApplication
public class SpringBootDemo04ConfigApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootDemo04ConfigApplication.class, args);
}
}
注意:在springboot不推荐使用配置文件的方式给spring容器添加组件,建议使用全组件的方式给spring容器添加组件
例子:使用@Bean的方式给spring容器添加组件。
/**
* @Configuration:指明当前类是一个配置类;就是来替代之前的Spring配置文件
* 在配置文件中用 标签添加组件
*/
@Configuration
public class MyAppConfig {
//将方法的返回值添加到容器中;容器中这个组件默认的id就是方法名
@Bean
public HelloService helloService02(){
System.out.println("配置类@Bean给容器中添加组件了...");
return new HelloService();
}
}
SpringBoot为我们在配置配置文件的时候提供了一种生成随机数方法、占位符。
例子:
person.lastName=张三${random.uuid} #使用${random.uuid}生成一个随机的uuid值
person.age = ${random.int} #使用${random.int}生成一个随机的整数
person.dog.name = ${person.lastName}的dog #通过${person.lastName}作为占位符
person.dog.age = ${dong.age:${random.int}} #通过${dong.age:${random.int}}指定占位符,如果占位符没有值就通过${random.int}获取一个随机的整数
Profile是Spring对不同环境提供不同配置功能的支持,可以通过激活、指定参数等方式快速切换环境。
我们在主配置文件编写的时候,文件名可以是 application-{profile}.properties/yml。在启动的时候没有指定激活哪个文件,springboot启动默认使用aplication.properties/yml。
每个properties代表自己独立的属性配置,在启动的时候可以指定让哪个属性文件生效。然而yml可以把不同的环境配置到同一个yml文件中,并在启动的时候指定让哪一个配置生效。原因是:yml文件中可以分为几个文档块,每个文档块就相当于独立的一个文件一样。这样在每次启动springboot项目的时候可以指定让哪个文档块生效。
例子:下面就给出一个yml文件中写两个文档块,并在第一个文档块中指定让哪个文档块生效。如果不指定,就默认第一个文档块生效
server:
port: 8080
spring:
profiles:
active: prod #指定激活的文档块
--- # 划分文档块
server:
port: 8081
spring:
profiles: dev #指定文档的名称
---
server:
port: 8082
spring:
profiles: prod
例子:我们在默认application.properties文件中对端口不进行修改默认为8080,我们再创建一个application-dev.properties文件。在默认文件启动的时候指定让哪个文件生效。
application.properties
spring.profiles.active= dev
application-dev.properties
server.port=8081
这样启动springboot项目后,使用的端口就是8081而不是8080.
首先对整个工程打成一个可执行的jar包。然后运行一下命令进行启动:
java -jar spring-boot-demo04-config-0.0.1-SNAPSHOT.jar -spring.profiles.active=prod
命令是通过 -spring.profiles.active=需要激活的主配置文件的profile名称 来传入命令行参数判断激活哪个主配置文件。
springboot启动的时候会扫描以下位置的application.properties/yml文件作为spring的默认配置文件。以下是按照优先级从高到低的顺序进行加载。所有位置的application.properties/yml都会被加载。如果内容相同,高优先级配置内容会覆盖低优先级配置的内容。如果内容不同,就会形成互补配置。当然:我们也可以通过配置sprig.config.location来改变默认配置
项目打包后:我们可以使用命令行参数的形式,启动项目的时候来指定配置文件的新位置;指定的配置文件和默认加载的哪些配置文件共同起作用形成互补配置。
--spring.config.location=指定配置文件的路径 #命令行参数
注意:通过命令行参数指定的配置文件的优先级级别最高。
springboot 支持多种外部配置方式。springboot可以从以下外部位置加载配。优先级从高到低;高优先级的配置覆盖低优先级的配置,所有的配置会形成互补配置
java -jar spring-boot-02-config-02-0.0.1-SNAPSHOT.jar –server.port=8087 –server.context-path=/abc #通过命令行参数指定
由jar包外向jar包内进行寻找;优先加载带profile:
配置文件到底能写什么?怎么写?关于自动配置怎么写就得说起自动配置原理啦!那么我接下来就来解释以下自动配置原理
自动配置原理:
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!this.isEnabled(annotationMetadata)) {
return NO_IMPORTS;
} else {
AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);
AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
List configurations = this.getCandidateConfigurations(annotationMetadata, attributes);//获取候选的配置
configurations = this.removeDuplicates(configurations);
Set exclusions = this.getExclusions(annotationMetadata, attributes);
this.checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = this.filter(configurations, autoConfigurationMetadata);
this.fireAutoConfigurationImportEvents(configurations, exclusions);
return StringUtils.toStringArray(configurations);
}
}
通过selectImports方法可以发现,该方法最终返回了一个configurations的一个list集合:
List configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
我们再进一步查看getCandidateConfigurations方法:
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader());
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;
}
通过这段代码可以发现configurations的list集合是通SpringFactoriesLoader.loadFactoryNames返回的。那么loadFactoryNames方法的作用又是什么呢? 接下来我们再进一步的查看loadFactoryNames方法的源码:
public static List loadFactoryNames(Class> factoryClass, @Nullable ClassLoader classLoader) {
String factoryClassName = factoryClass.getName();
return (List)loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
}
private static Map> loadSpringFactories(@Nullable ClassLoader classLoader) {
MultiValueMap result = (MultiValueMap)cache.get(classLoader);
if (result != null) {
return result;
} else {
try {
Enumeration urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories");
LinkedMultiValueMap result = new LinkedMultiValueMap();
while(urls.hasMoreElements()) {
URL url = (URL)urls.nextElement();
UrlResource resource = new UrlResource(url);
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
Iterator var6 = properties.entrySet().iterator();
while(var6.hasNext()) {
Entry, ?> entry = (Entry)var6.next();
List factoryClassNames = Arrays.asList(StringUtils.commaDelimitedListToStringArray((String)entry.getValue()));
result.addAll((String)entry.getKey(), factoryClassNames);
}
}
cache.put(classLoader, result);
return result;
} catch (IOException var9) {
throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var9);
}
}
}
通过上面这段代码我们不难发现loadFactoryNames()方法的作用是扫描META-INF/spring.factories文件。并把扫描到的内容包装成properties对象。从properties中获取到EnableAutoConfiguration.class类(类名)对应的值,然后把他们添加在容器中。
将 类路径下 META-INF/spring.factories 里面配置的所有EnableAutoConfiguration的值加入到了容器中;
我们就给出一部分properties中的内容:
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\
org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\
org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration,\
org.springframework.boot.autoconfigure.cloud.CloudAutoConfiguration,\
每一个这样的 xxxAutoConfiguration类都是容器中的一个组件,都加入到容器中;然后再用他们来做自动配置
那么我们可以确认每一个 xxxAutoConfiguration类可以进行自动配置功能。
那么接下来:我们就以一个org.springframework.boot.autoconfigure.web.servlet.HttpEncodingAutoConfiguration的自动配置类来讲解是如何进行自动配置的。
HttpEncodingAutoConfiguration:
@Configuration //表示是一个配置类
@EnableConfigurationProperties(HttpEncodingProperties.class) //启用ConfigurationProperties功能;并将配置文件中的值和HttpEncodingProperties绑定起来。并把HttpEncodingProperties加入到ioc容器中。
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)//spring底层有一个@Conditional注解。它的作用:根据不同的条件、如果满足指定的条件,该配置类才能生效。@ConditionalOnWebApplication的作用:就是判断当前应用是否为web应用,如果是,当前配置类生效。
@ConditionalOnClass(CharacterEncodingFilter.class)//判断当前项目有没有CharacterEncodingFilter(springmvc进行乱码解决的过滤器类)类。如果有当前配置类就生效。
//判断配置文件中是否存在某个配置(存在spring.http.encoding.enabled)
//通过matchIfMissing属性可以发现就算配置文件中不存在spring.http.encoding.enabled也默认也是默认生效的
@ConditionalOnProperty(prefix = "spring.http.encoding", value = "enabled", matchIfMissing = true)
public class HttpEncodingAutoConfiguration {
private final HttpEncodingProperties properties;
//只有一个有参构造器的情况下,参数的值从spring容器中获取
public HttpEncodingAutoConfiguration(HttpEncodingProperties properties) {
this.properties = properties;
}
@Bean //给容器中添加一个主件(CharacterEncodingFilter),这个组件中的某些值要从properties中获取。
@ConditionalOnMissingBean//容器中配有没有该组件,@Bean就生效
public CharacterEncodingFilter characterEncodingFilter() {
CharacterEncodingFilter filter = new OrderedCharacterEncodingFilter();
filter.setEncoding(this.properties.getCharset().name());
filter.setForceRequestEncoding(this.properties.shouldForce(Type.REQUEST));
filter.setForceResponseEncoding(this.properties.shouldForce(Type.RESPONSE));
return filter;
}
根据当前不同的条件判断当前配置类是否生效.一旦,这个配置类生效,就会给spring容器中添加各种组件。而这写组件中的属性是从对应properties类中获取。而这个properties类中的属性又是和配置文件进行绑定哒。
HttpEncodingProperties:
@ConfigurationProperties(prefix = "spring.http.encoding") //从配置文件中获取指定的值来和bean进行绑定对应的bean(也就是HttpEncodingProperties)
public class HttpEncodingProperties {
public static final Charset DEFAULT_CHARSET = StandardCharsets.UTF_8;
private Charset charset = DEFAULT_CHARSET;
private Boolean force;
private Boolean forceRequest;
private Boolean forceResponse;
private Map mapping;
通过这段代码可以发现:所有在配置文件中配置的属性都是在xxxProperties类中进行封装着;配置文件中能配置什么就可以参考某个功能对应的这个属性类。
springboot自动配置总结:
1)SpringBoot启动会加载大量的自动配置类
2)我们看我们需要的功能有没有SpringBoot默认写好的自动配置类;(如果没有,我们就自己写一个配置类)
3)我们再来看这个自动配置类中到底配置了哪些组件;(只要我们要用的组件有,我们就不需要再来配置了)
4)给容器中自动配置类添加组件的时候,会从properties类中获取某些属性。我们就可以在配置文件中指定这 些属性的值;
xxxxAutoConfigurartion:自动配置类; 给容器中添加组件
xxxxProperties:封装配置文件中相关属性;
@Conditional注解是spring中定义的原生注解。作用:必须是@Conditional指定的条件成立,才给容器中添加组件,配置里面的所有内容才生效;
springboot对@Conditional注解进行了扩展:
@Conditional扩展注解 作用(判断是否满足当前指定条件)
@ConditionalOnJava 系统的java版本是否符合要求
@ConditionalOnBean 容器中存在指定Bean;
@ConditionalOnMissingBean 容器中不存在指定Bean;
@ConditionalOnExpression 满足SpEL表达式指定
@ConditionalOnClass 系统中有指定的类
@ConditionalOnMissingClass 系统中没有指定的类
@ConditionalOnSingleCandidate 容器中只有一个指定的Bean,或者这个Bean是首选Bean
@ConditionalOnProperty 系统中指定的属性是否有指定的值
@ConditionalOnResource 类路径下是否存在指定资源文件
@ConditionalOnWebApplication 当前是web环境
@ConditionalOnNotWebApplication 当前不是web环境
@ConditionalOnJndi JNDI存在指定项
自动配置类必须在一定的条件下才能生效; 我们怎么知道哪些自动配置类生效?
答案:我们可以通过启用 debug=true属性;来让控制台打印自动配置报告,这样我们就可以很方便的知道哪些自动配置类生效;
注意:上面我们所提到的配合文件都是springboot的主配置文件既:application.properties/yml