目录
1. 第一个SpringBoot项目(HelloWorld)
2. starter机制
3. YAML标记语言
4. 配置文件
5. 日志(记录运行情况、定位问题)
6. 静态资源映射
7. Thymeleaf模板引擎
8. 国际化
一款用于简化Spring项目搭建和开发的开源框架(使开发者更专注于业务逻辑)。
SpringBoot的特点
1. 以Spring为基础,使用更简单、功能更丰富、性能更稳定健壮。
2. 提供了大量开箱即用的依赖包(starter机制):自动管理依赖包中的依赖(简化了复杂的依赖包管理);提供了大量默认/自动配置(省去了大量的XML配置内容),不需要任何形式的配置即可实现Spring的所有配置(可以通过配置文件修改默认配置)。
3. 内嵌了Servlet容器(如:Tomcat、Jetty、Undertow等),应用无需打成WAR包 。
4. 可在终端执行java–jar xxx.jar命令来独立运行SpringBoot项目。
5. 可对正在运行的项目提供监控。
随着微服务技术的流行,SpringBoot也成了时下炙手可热的技术。
1. 第一个SpringBoot项目(HelloWorld)
===》1. 创建项目
方式1(创建Maven项目)
1. 修改pom.xml文件(添加SpringBoot依赖包)
org.springframework.boot
spring-boot-starter-parent
2.7.5
org.springframework.boot
spring-boot-starter-web
org.springframework.boot
spring-boot-starter-test
test
2. 创建HelloWorldApplication.java文件(在com.sst.cx包下)
package com.sst.cx;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class HelloWorldApplication {
public static void main( String[] args ) {
SpringApplication.run(HelloWorldApplication.class, args);
}
}
方式2(创建Spring项目)推荐
会自动在pom.xml添加SpringBoot依赖,并自动在com.sst.cx包下创建项目名+Application.java文件(内容同上)。
===》2. 创建HelloController.java文件(在com.sst.cx.controller包下)
package com.sst.cx.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class HelloController {
@ResponseBody // 将方法的返回内容作为页面内容。不使用该注解时,会使用该方法的返回值对应的同名html文件作为显示页面。
@RequestMapping("/hello") // 映射路径为/hello,在浏览器中使用http://localhost/hello可访问到。
public String hello() {
return "Hello World!";
}
}
===》3. 运行项目,在浏览器中输入http://127.0.0.1:8080/hello。
内置了Tomcat(不再需要部署到Tomcat),可以直接运行。
2. starter机制
Spring项目在创建后想要运行,需要:导入各种依赖jar包、添加许多xml配置、部署到Tomcat服务器中。
SpringBoot项目在创建后可以直接运行(不用编写任何代码、不用进行任何配置),这都要归功于starter机制。
SpringBoot将企业应用研发中的各种场景都抽取出来 做成一个个的starter依赖包(整合了该场景下所有可能用到的依赖,并提供了大量的默认/自动配置),开发者只需要在Maven的pom.xml中添加相应的starter即可(SpringBoot就能自动扫描到要加载的信息并启动相应的默认配置)。
1. SpringBoot官方提供的starter依赖包以spring-boot-starter-xxx方式命名。
spring-boot-starter-parent
spring-boot-starter-web
spring-boot-starter-test
spring-boot-starter-redis
spring-boot-starter-data-mongodb
spring-boot-starter-data-elasticsearch
2. 自定义的starter依赖包(第三方技术厂商提供 或 开发员自己创建)以xxx-spring-boot-starter方式命名。
druid-spring-boot-starter
mybatis-spring-boot-starter
- spring-boot-starter-parent
所有SpringBoot项目的父级依赖(即所有SpringBoot项目都需要添加该父依赖):统一管理项目内的部分常用依赖;统一管理其他starter的版本(该依赖又被称为SpringBoot的版本仲裁中心)。
该依赖包主要提供了以下特性:
1. 默认JDK版本(Java 8)
2. 默认字符集(UTF-8)
3. 依赖管理功能
4. 资源过滤
5. 默认插件配置
6. 识别 application.properties 和 application.yml 类型的配置文件
===》查看源码可知
其有一个父级依赖:spring-boot-dependencies。
org.springframework.boot
spring-boot-dependencies
2.4.5
查看spring-boot-dependencies的pom.xml内容:
4.0.0
org.springframework.boot
spring-boot-dependencies
2.4.5
pom
....
5.16.1
2.7.7
1.9.88
2.15.0
1.9.6
3.18.1
4.0.6
....
org.apache.activemq
activemq-amqp
${activemq.version}
org.apache.activemq
activemq-blueprint
${activemq.version}
...
org.codehaus.mojo
build-helper-maven-plugin
${build-helper-maven-plugin.version}
org.flywaydb
flyway-maven-plugin
${flyway.version}
...
- spring-boot-starter-web
为Web开发提供了所有依赖:
1. spring-boot-starter(核心启动器)
spring-boot
spring-boot-autoconfigure
spring-boot-starter-logging(log日志)
logback-classic
log4j-to-slf4j
jul-to-slf4j
jakarta.annotation-api
snakeyaml
2. spring-boot-starter-tomcat(Tomcat服务器)
3. spring-boot-starter-json(jackson)
4. spring-web(SpringFramework)
spring-beans
5. spring-webmvc
spring-aop
spring-context
spring-expression
为SpringMVC提供了大量默认配置
1. 引入了ContentNegotiatingViewResolver和BeanNameViewResolver(视图解析器)
2. 对包括WebJars在内的静态资源的支持
3. 自动注册Converter、GenericConverter、Formatter(转换器和格式化器)
4. 对HttpMessageConverters的支持(Spring MVC中用于转换HTTP请求和响应的消息转换器)
5. 自动注册 MessageCodesResolver(用于定义错误代码生成规则)
6. 支持对静态首页(index.html)的访问
7. 自动使用 ConfigurableWebBindingInitializer
使用(只需在pom.xml中添加依赖:spring-boot-starter-web):
org.springframework.boot
spring-boot-starter-parent
2.7.5
org.springframework.boot
spring-boot-starter-web
...
在终端执行如下命令,可查看项目的依赖树
mvn dependency:tree
自定义starter(命名规则:xxx-spring-boot-starter)
将独立于业务代码之外的功能模块封装成一个starter,便于复用。
步骤:
===》1. 创建一个SpringBoot项目,修改pom文件
添加spring-boot-autoconfigure依赖,可根据功能添加其他依赖。
org.springframework.boot
spring-boot-autoconfigure
===》2. 创建HelloProperties.java配置类
@ConfigurationProperties(prefix = "hello")
public class HelloProperties {
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
===》3. 创建HelloService.java
public class HelloService {
private String name;
private int age;
public HelloService(String name, int age) {
this.name = name;
this.age = age;
}
public void hello() {
System.out.println(name + " age:" + age);
}
}
===》4. 创建MyAutoConfiguration.java自动配置类
@Configuration
@EnableConfigurationProperties(HelloProperties.class)
public class MyAutoConfiguration {
@Autowired
private HelloProperties helloProperties;
@Bean
public HelloService helloService() {
return new HelloService(helloProperties.getName(), helloProperties.getAge());
}
}
===》5. 创建spring.factories文件(在resources/META-INF/目录下)
在该文件中配置上面创建的自动配置类。
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.sst.cx.config.MyAutoConfiguration
===》6. 在application.properties配置文件中可进行默认配置。
===》7. 打包
放置到测试项目的resources/lib目录下。
使用自定义的starter
===》1. 创建一个SpringBoot项目(勾选Web依赖),修改pom.xml文件
添加自定义的starter依赖
com.sst.cx
hello-spring-boot-starter
1.0-SNAPSHOT
system
${project.basedir}/src/main/resources/lib/hello-spring-boot-starter-0.0.1-SNAPSHOT.jar
===》2. 在appllication.properties/yml配置文件中,添加
hello.name=zhangsan
hello.age=12
===》3. 测试(TestController.java)
@Controller
public class TestController {
@Autowired
private HelloService helloService;
@ResponseBody
@RequestMapping("/hello")
public String hello() {
helloService.hello();
return "success";
}
}
在浏览器中访问http://127.0.0.1:8080/hello,控制台会输出:zhangsan age:12
3. YAML标记语言(以数据为中心,比xml、json更适合作为配置文件)
只需在SpringBoot项目中引入spring-boot-starter-web或spring-boot-starter(二者都集成了SnakeYAML库)就可以使用YAML(以 .yml 或 .yaml 结尾)作为配置文件。
语法规则
1. 使用缩进表示层级关系(不允许使用Tab键,只允许使用空格;空格数不重要,但同级元素必须左侧对齐)。
2. 大小写敏感。
3. 使用"key:[空格]value"形式(空格不能省略)表示一个键值对。
例: url: www.baidu.com
4. 支持3种数据结构(可任意组合嵌套):
1. 对象(键值对的集合:对象的每个属性对应一个键值对)
写法1. 普通写法(使用缩进表示:对象与属性的层级关系)
website:
name: 张三
url: www.baidu.com
写法2. 行内写法
website: {name: 张三,url: www.baidu.com}
2. 数组(一组按次序排列的值)
写法1. 普通写法(使用-表示:数组中的元素)
pets:
-dog
-cat
-pig
写法2. 行内写法
pets: [dog,cat,pig]
3. 字面量(单个的不可拆分的值,如:数字、字符串、布尔值、日期)
直接写在键值对的value中即可,默认情况下字符串是不需要使用单引号或双引号的。
若字符串使用了单引号,则会对字符串的特殊字符进行转义(如:"hello\nworld"则会输出hello\nworld)。
若字符串使用了双引号,则不会转义(如:"hello\nworld"则会进行换行)。
5. 文件组织结构
一个YAML文件由一个或多个相互独立的文档组成,文档之间使用“---”作为分隔符(只包含一个文档时可省略)。
例(hello.yaml)
spring:
profiles: dev
datasource:
url: jdbc:mysql://localhost:3306/Test
username: root
password: 12345678
driver-class-name: com.mysql.cj.jdbc.Driver
---
website:
name: bianchengbang
url: www.biancheng.net
---
name: "张三 \n 李四"
4. 配置文件
- 默认配置文件
SpringBoot项目启动时会将以下5个位置的application.properties或apllication.yml文件(文件名固定)作为默认配置文件,并读取配置内容。
1. file:./config/*/
2. file:./config/
3. file:./
4. classpath:/config/
5. classpath:/
说明:
1. 优先级依次降低,相同位置的application.properties的优先级高于application.yml。
2. file: 指项目的根目录;classpath: 指项目的类路径(即resources目录)。
3. 通常只使用第5种(在resources目录下创建配置文件,并添加内容来覆盖默认配置)。
4. Maven项目打包时,位于项目根目录下的配置文件无法被打包进项目的JAR包(即在jar包中失效)。
解决(3方式):
1. 在IDEA的运行配置(Run/Debug Configuration)中,添加虚拟机参数 -Dspring.config.additional-location=/my-application.yml,指定外部配置文件。
2. 在IDEA的运行配置(Run/Debug Configuration)中,添加程序运行参数 --spring.config.additional-location=/my-application.yml,指定外部配置文件。
3. 在主启动类中调用System.setProperty()方法添加系统属性spring.config.additional-location,指定外部配置文件。
例
===》创建项目,在项目根目录下、类路径的config目录下、类路径下分别创建一个配置文件(application.yml文件)。
1. 项目根路径下
#上下文路径为 /abc
server:
servlet:
context-path: /abc
2. 类路径的config目录下
#端口号为8084
#上下文路径为 /helloWorld
server:
port: 8084
servlet:
context-path: /helloworld
3. 类路径下
#默认配置
server:
port: 8080
===》MyController.java
package com.sst.cx.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class MyController {
@ResponseBody
@RequestMapping("/test")
public String hello() {
return "hello Spring Boot!";
}
}
===》根据优先级可知:服务器端口为8084;上下文路径为/abc
使用浏览器访问http://localhost:8084/abc/test
- 外部配置文件
除了默认配置文件,SpringBoot还可以加载项目外部的配置文件。
指定外部配置文件的路径(2种方式)
1. spring.config.location(将只加载外部配置文件,默认配置文件会失效)
使用命令:java -jar {JAR} --spring.config.location={外部配置文件全路径}
2. spring.config.additional-location(外部配置文件的优先级最高)
使用命令:java -jar {JAR} --spring.config.additional-location={外部配置文件全路径}
- 配置加载顺序
SpringBoot项目不仅可以通过配置文件进行配置,还可以通过环境变量、命令行参数等形式进行配置。
SpringBoot会加载以下所有形式的配置(优先级由高到低):
1. 命令行参数
SpringBoot中的所有配置都可以通过命令行参数进行指定。
命令格式:java -jar {Jar文件名} --{参数1}={参数值1} --{参数2}={参数值2}
2. 来自java:comp/env的JNDI 属性
3. Java系统属性(System.getProperties())
4. 操作系统的环境变量
5. RandomValuePropertySource配置的random.*属性值
6. 配置文件(.yml/.properties文件)
SpringBoot启动时,会自动加载JAR包内部及JAR包所在目录指定位置的配置文件(.yml/.properties文件)。
下图(配置文件的加载顺序)的说明:
1. /myBoot:表示JAR包所在目录
2. /childDir:表示JAR包所在目录下config目录的子目录
3. JAR:表示SpringBoot项目打包生成的JAR包;
4. 数字:表示该配置文件的优先级,数字越小 优先级越高 越先被加载(后加载的相同属性会被忽略)。
5. 同一位置下,Properties文件优先级高于YAML文件。
7. @Configuration注解修饰的配置类上的@PropertySource指定的配置文件
8. 通过SpringApplication.setDefaultProperties指定的默认属性
例(命令行参数)
java -jar hello-0.0.1-SNAPSHOT.jar --server.port=8081 --server.servlet.context-path=/hello
说明:
1. server.port:指定服务器端口
2. server.servlet.context:上下文路径(项目的访问路径)
- 配置绑定(把配置文件中的值绑定到JavaBean对象的属性中)
步骤:
1. 将配置信息存放在配置文件中。
如果将所有的配置都集中在application.properties/yml配置文件中,会十分臃肿、难以维护。通常会将与SpringBoot无关的自定义配置提取到一个单独的配置文件(如:resources/person.properties)中,然后给类添加@PropertySource注解指向该配置文件。
/*
例:
@Component
@ConfigurationProperties(prefix="person") // 将JavaBean属性和配置文件的值进行绑定
@PropertySource(value = "classpath:person.properties") // 指定配置文件
public class Person{}
*/
2. 在代码中给类添加注解进行绑定。
1. @ConfigurationProperties注解(修饰类:用于将配置文件的多个配置绑定到类的属性中)
支持松散绑定/松散语法(如:配置文件中的person.firstName、person.first-name、person.first_name、PERSON_FIRST_NAME都可以绑定到Person类的firstName属性)。
不支持SpEL表达式。
支持所有类型数据的封装(如: 基础数据类型、类、Map、List、Set)。
例:
@Component
@ConfigurationProperties(prefix="person")
public class Person{}
2. @Value注解(修饰属性,用于将配置文件中的某一个配置绑定到属性中)
不支持松散绑定。
支持SpEL表达式。
只支持基本数据类型(字符串、布尔值、整数)。
例:
@Component
public class Person {
@Value("${person.name}")
private String name;
}
例
===》在application.yml配置文件中(若为.properties文件则为:person.age=10形式)
person:
name: 张三
age: 10
boss: false
birth: 1949/10/1
maps: { k1: v1,k2: 12 }
lists:
‐ 张三
‐ 李四
dog:
name: 初一
age: 2
===》Person类
@Component // 类必须在Ioc容器中。
@ConfigurationProperties(prefix="person") // 将配置文件中以person前缀开头的配置绑定到类的属性中。
public class Person{
private String name;
private Integer age;
private Boolean boss;
private Date birth;
private Map maps;
private List
- 导入Spring配置(默认情况下,Spring的.xml配置文件不会被SpringBoot识别)
2种方式:
1. 使用@ImportResource注解加载Spring的xml配置文件。
首先在类路径(resources目录)下创建beans.xml,然后在主启动类中添加@ImportResource(locations = {"classpath:/beans.xml"})
2. 使用全注解方式加载Spring配置。
在使用@Configuration注解修饰的配置类中,使用@Bean注解向容器中注入JavaBean。
例:
@Configuration // 定义一个配置类(替代.xml配置文件)
public class MyAppConfig {
// @Bean修饰的方法会被AnnotationConfigApplicationContext或AnnotationConfigWebApplicationContext类扫描,构建Bean并注入到容器中。
// 等价于
@Bean // 等价于xml文件的Bean元素,id为方法名,class为返回值类型
public PersonService personService() {
return new PersonServiceImpl();
}
}
例(@ImportResource注解 方式加载)
===》beans.xml(resources目录下)
===》PersonService.java
public interface PersonService {
public Person getPersonInfo();
}
===》PersonServiceImpl.java
public class PersonServiceImpl implements PersonService {
@Autowired
private Person person;
@Override
public Person getPersonInfo() {
return person;
}
}
===》HelloworldApplicationTests.java(测试文件)
@SpringBootTest
class HelloworldApplicationTests {
@Autowired
Person person;
// IOC 容器
@Autowired
ApplicationContext ioc;
@Test
public void testHelloService() {
// 校验 IOC 容器中是否包含组件 personService
boolean b = ioc.containsBean("personService");
if (b) {
System.out.println("personService 已经添加到 IOC 容器中");
} else {
System.out.println("personService 没添加到 IOC 容器中");
}
}
@Test
void contextLoads() {
System.out.println(person);
}
}
===》HelloworldApplication.java(在主启动类中添加@ImportResource注解)
@ImportResource(locations = {"classpath:/beans.xml"}) // 将beans.xml加载到项目中
@SpringBootApplication
public class HelloworldApplication {
public static void main(String[] args) {
SpringApplication.run(HelloworldApplication.class, args);
}
}
- 配置Profile(多环境)
通常程序会运行在多套环境(开发、测试、生产)下,每套环境的配置不同(如:数据库地址、服务器端口、日志级别 等)。如果每次打包时靠手动修改代码来实现,繁琐且易错。可使用Profile功能来实现动态切换环境(2种方式):
方式1. yml多文档方式
在一个yml文件中每个分隔符隔开的文档对应一套环境。
方式2. 多profile文件方式
创建多个环境配置文件(每个环境配置文件对应一套环境)。
切换环境的方法:
1. 配置文件中手动切换
2. 虚拟机参数: 在VM options指定: -Dspring.profiles.active=dev
3. 命令行参数: java-jar xxx.jar --spring.profiles.active=dev
例(yml多文档方式)
在application.yml配置文件中,添加:
#默认配置
server:
port: 8080
#切换配置
spring:
profiles:
active: test #设置当前环境
---
#开发环境
server:
port: 8081
spring:
config:
activate:
on-profile: dev
---
#测试环境
server:
port: 8082
spring:
config:
activate:
on-profile: test
---
#生产环境
server:
port: 8083
spring:
config:
activate:
on-profile: prod
例(多profile文件方式)
===》创建application-dev.properties
server.port = 8080
===》创建application-test.properties
server.port = 8081
===》创建application-pro.properties
server.port = 8082
===》在application.properties配置文件中
spring.profiles.active = dev #设置当前环境,各环境配置文件的后缀。
- 自动配置原理
SpringBoot通过注解(而不是xml文件的方式)来实现自动/默认配置。SpringBoot在启动时从类路径下的META-INF/spring.factories中获取EnableAutoConfiguration指定的所有自动配置类,导入到容器中生效。
在SpringBoot项目的启动类中有一个@SpringBootApplication注解(核心注解,一个组合注解)。
===》@SpringBootApplication注解,定义如下
@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 {
...
}
===》@EnableAutoConfiguration注解,定义如下
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
...
}
SpringBoot通过@EnableAutoConfiguration注解开启自动配置(扫描jar包下的spring.factories文件,文件中包含了自动配置类)。
该注解导入了EnableAutoConfigurationImportSelector类的selectimports方法
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!this.isEnabled(annotationMetadata)) {
return NO_IMPORTS;
} else {
AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
}
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
if (!this.isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
} else {
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.getConfigurationClassFilter().filter(configurations);
this.fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
}
通过getCandidateConfiguration方法,获取配置文件列表。
protected List getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
List configurations = new ArrayList(SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader()));
ImportCandidates.load(AutoConfiguration.class, this.getBeanClassLoader()).forEach(configurations::add);
Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories nor in META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports. If you are using a custom packaging, make sure that file is correct.");
return configurations;
}
loadFactoryNames方法会加载所有META-INF下有spring.factories文件的jar包,并根据spring.factories文件中的配置,去加载相应的类。
public static List loadFactoryNames(Class> factoryType, @Nullable ClassLoader classLoader) {
ClassLoader classLoaderToUse = classLoader;
if (classLoader == null) {
classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
}
String factoryTypeName = factoryType.getName();
return (List)loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());
}
private static Map> loadSpringFactories(ClassLoader classLoader) {
Map> result = (Map)cache.get(classLoader);
if (result != null) {
return result;
} else {
Map> result = new HashMap();
try {
Enumeration urls = classLoader.getResources("META-INF/spring.factories");
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()) {
Map.Entry, ?> entry = (Map.Entry)var6.next();
String factoryTypeName = ((String)entry.getKey()).trim();
String[] factoryImplementationNames = StringUtils.commaDelimitedListToStringArray((String)entry.getValue());
String[] var10 = factoryImplementationNames;
int var11 = factoryImplementationNames.length;
for(int var12 = 0; var12 < var11; ++var12) {
String factoryImplementationName = var10[var12];
((List)result.computeIfAbsent(factoryTypeName, (key) -> {
return new ArrayList();
})).add(factoryImplementationName.trim());
}
}
}
result.replaceAll((factoryType, implementations) -> {
return (List)implementations.stream().distinct().collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList));
});
cache.put(classLoader, result);
return result;
} catch (IOException var14) {
throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var14);
}
}
}
===》@import
该注解导入了AutoConfigurationImportSelector类,这个类中有一个很重要的方法:selectImports(),它几乎涵盖了组件自动装配的所有处理逻辑,包括获得候选配置类、配置类去重、排除不需要的配置类、过滤等,最终返回符合条件的自动配置类的全限定名数组。
例(spring.factories文件)
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.sst.cx.config.MyAutoConfiguration,\
com.sst.cx.config.MyAutoConfiguration2
例:
当项目中需要使用依赖jar包中类的实例,只需创建一个该类类型的属性,并使用@Autowired或@Resource注解标注(会注入到容器中)。
5. 日志(记录运行情况、定位问题)
日志框架分为两类:
1. 日志抽象层(为日志功能提供了一套标准规范的JavaAPI)
1. JCL(Jakarta Commons Logging)
2. SLF4j(Simple Logging Facade for Java)目前最流行
可灵活使用占位符进行参数占位:简化代码、可读性更好。
3. jboss-logging
2. 日志实现
1. Log4j
2. JUL(java.util.logging)
3. Log4j2
4. Logback(SLF4j的原生实现框架)
和Log4j同一个作者,用来代替Log4j,拥有比Log4j更多的优点特性、更强的性能。
通常情况下,日志功能由一个日志抽象层和一个日志实现组合而成(SpringBoot选用的是:SLF4J+Logback)。
- 使用SLF4J
使用步骤:
1. 在项目中导入SLF4J框架和一个日志实现框架(如:Logback)。
2. 调用日志时,应调用日志抽象层的方法,而不是直接调用日志实现层的方法。
例:
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class HelloWorld {
public static void main(String[] args) {
Logger logger = LoggerFactory.getLogger(HelloWorld.class);
// 调用SLF4J的info()方法,而非直接调用logback的方法。
logger.info("Hello World");
}
}
从下图的SLF4J官方方案可以看出:
1. Logback作为Slf4j的原生实现框架。当项目使用SLF4J+Logback组合记录日志时,只需要引入SLF4J、Logback的Jar包即可;
2. Log4j虽然和Logback属于同一个作者,但Log4j的出现要早于SLF4J,因而Log4j没有直接实现SLF4J。当项目使用SLF4J+Log4j组合记录日志时,不但需要引入SLF4J、Log4j 的Jar包,还必须引入它们之间的适配层(承上启下:既要实现SLF4J的方法,还要有调用Log4j的方法):slf4j-log4j12.jar。
3. 当项目使用SLF4J+JUL组合记录日志时,与SLF4J+Log4j一样,不但需要引入SLF4J、JUL 的对应的Jar包,还要引入适配层:slf4j-jdk14.jar。
每一个日志实现框架都有自己的配置文件。使用SLF4J记录日志时,应该使用日志实现框架的配置文件。
- 统一各依赖包中的日志框架
通常,一个项目会依赖于各种框架,每个框架记录日志所使用的日志框架各不相同。如:Spring Boot(slf4j+logback)、Spring(commons-logging)、Hibernate(jboss-logging)。
因此,需要统一日志框架的使用:
1. 使用 各替换包 替换 项目中原来的全部日志实现框架(分为2步:去除原框架;引入相应的替换包)。
替换包包含了 被替换的日志框架中的所有类(保证应用不会报错),但使用的是SLF4J的API(达到统一日主框架的目的)。
如:log4j-over-slf4j替换Log4j、jul-to-slf4j.jar替换JUL
2. 导入SLF4J实现。
SpringBoot项目
spring-boot-starter(核心启动器)引入了spring-boot-starter-logging。spring-boot-starter-logging引入了 logback-classic(SLF4J的实现)、 log4j-to-slf4j(log4j的替换包)、jul-to-slf4j(JUL的替换包)。
所以引入spring-boot-starter就完成了:引入替换包和导入SLF4J实现 这2步。引入其他三方框架依赖时,只需删除其所依赖的日志框架,即可实现日志框架的统一。例:
org.apache.activemq
activemq-console
${activemq.version}
commons-logging
commons-logging
- 日志的配置(日志的级别、日志的输出格式)
- 默认配置
SpringBoot项目中引入spring-boot-starter(引入了SLF4J+Logback,提供了大量默认配置)就可以直接使用日志功能。
日志级别
日志的输出都是分级别的,当一条日志信息的级别大于或等于配置文件的级别时,才会对这条日志进行记录。
输出格式
可以通过日志参数对日志的输出格式进行修改。
序号 | 常见的日志级别(优先级依次升高) | 描述 |
---|---|---|
1 | trace | 追踪,指明程序运行轨迹。(很少使用) |
2 | debug | 调试。(实际项目中一般将其作为最低级别) |
3 | info | 输出重要的信息。(使用较多) |
4 | warn | 警告。(使用较多) |
5 | error | 错误信息。(使用较多) |
序号 | 常用的输出格式 | 描述 |
---|---|---|
1 | %d{yyyy-MM-dd HH:mm:ss, SSS} | 日志创建时间(年月日 时分秒 毫秒) |
2 | %-5level | 日志级别(-5表示:左对齐且固定输出5个字符,不足则右边补0) |
3 | %logger 或 %c | logger的名称 |
4 | %thread 或 %t | 当前线程的名称 |
5 | %p | 日志输出格式 |
6 | %message 或 %msg 或 %m | 日志内容。logger.info("message")中的message |
7 | %n | 换行符 |
8 | %class 或 %C | Java类名 |
9 | %file 或 %F | 文件名 |
10 | %L | 出错的行号 |
11 | %method 或 %M | 方法名 |
12 | %l | 语句所在的行数(包括类名、方法名、文件名、行数) |
13 | hostName | 本地机器名 |
14 | hostAddress | 本地ip地址 |
例(测试SpringBoot项目的日志默认级别)
package com.sst.cx;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class HelloApplication {
public static void main(String[] args) {
SpringApplication.run(HelloApplication.class, args);
Logger logger = LoggerFactory.getLogger(SpringBootMaven2Application.class);
logger.trace("trace 级别日志");
logger.debug("debug 级别日志");
logger.info("info 级别日志");
logger.warn("warn 级别日志");
logger.error("error 级别日志");
}
}
通过控制台输出结果可知:
1. SpringBoot项目的日志默认级别为:info。
2. 日志输出内容默认包含(占用一行,依次包含):
创建时间(yyyy-MM-dd HH:mm:ss.SSS)
日志级别(如:WARN)
进程 ID(如:945)
分隔符:---
线程名:方括号括起来(如:[ main])
logger的名称(如:com.sst.cx.HelloApplication)
日志内容(如:warn 级别日志)
例:
2015-10-29 11:37:21.940 INFO 945 --- [ main] com.sst.cx.HelloApplication : info 级别日志
- 修改默认配置
在resources目录下创建application.properties/yml,按需求添加如下内容来覆盖默认配置:
#日志级别
logging.level.net.biancheng.www=trace
#设置日志输出的位置-相对路径(会在项目根目录/my-log/myLog下自动生成spring.log文件)
#logging.file.path=my-log/myLog
#设置日志输出的位置-绝对路径(会在项目所在磁盘根目录/Users/cx/spring-boot/logging下自动生成spring.log文件)
logging.file.path=/Users/cx/spring-boot/logging
#控制台的日志输出格式
logging.pattern.console=%d{yyyy-MM-dd hh:mm:ss} [%thread] %-5level %logger{50} - %msg%n
#spring.log日志文件输出格式
logging.pattern.file=%d{yyyy-MM-dd} === [%thread] === %-5level === %logger{50} === - %msg%n
- 自定义日志配置
修改application.porperties/yml文件只能修改个别日志配置,若修改更多的配置或使用更高级的功能,则需要通过日志实现框架的配置文件(Spring官方提供,开发者只需将指定的配置文件放置到项目的类路径即resourecs目录下即可)来进行配置(按需修改)。
日志框架 | 配置文件 |
---|---|
Logback | logback-spring.xml、logback-spring.groovy、logback.xml、logback.groovy |
Log4j2 | log4j2-spring.xml、log4j2.xml |
JUL (Java Util Logging) | logging.properties |
日志框架的配置文件分为2类(在使用时大不相同):
1. 普通日志配置文件(即不带srping标识),如:logback.xml。
放置在项目的类路径下后,配置文件会跳过SpringBoot直接被日志框架加载。
2. 带有spring标识的日志配置文件(SpringBoot推荐该方式),如:logback-spring.xml、log4j2-spring.xml。
放置在项目的类路径下后,配置文件不会直接被日志框架加载,而是由SpringBoot对它们进行解析,这样就能使用SpringBoot的高级功能Profile(实现在不同的环境中使用不同的日志配置)。
例(logback.xml)普通日志配置文件
将logback.xml复制到SpringBoot项目的类路径下(resources目录下),该配置文件配置内容如下
%d{yyyy-MM-dd HH:mm:ss} [%thread]**************** %-5level %logger{50} - %msg%n
${LOG_HOME}/${appName}.log
${LOG_HOME}/${appName}-%d{yyyy-MM-dd}-%i.log
365
100MB
%d{yyyy-MM-dd HH:mm:ss} [ %thread ] ------------------ [ %-5level ] [ %logger{50} : %line ] -
%msg%n
例(logback-spring.xml)带有spring标识的日志配置文件
1. 将上例中的logback.xml文件名修改为:logback-spring.xml。修改控制台日志输出,通过Profile实现不同的环境使用不同的日志输出格式:
%d{yyyy-MM-dd HH:mm:ss.SSS} ----> [%thread] ---> %-5level %logger{50} - %msg%n
%d{yyyy-MM-dd HH:mm:ss.SSS} ==== [%thread] ==== %-5level %logger{50} - %msg%n
2. 在SpringBoot项目的application.yml中,激活开发环境(dev)的 Profile,配置内容如下:
#默认配置
server:
port: 8080
#在这里切换环境,dev、test、prod
spring:
profiles:
active: dev
---
#开发环境
server:
port: 8081
spring:
config:
activate:
on-profile: dev
---
#测试环境
server:
port: 8082
spring:
config:
activate:
on-profile: test
---
#生产环境
server:
port: 8083
spring:
config:
activate:
on-profile: prod
6. 静态资源映射
在Web项目中,为了让页面更美观、更好的用户体验,会使用到大量的静态资源(如: JS、CSS、HTML、jQuery、Bootstrap等)。
SpringMVC项目导入静态资源(将静态资源文件复制到webapp目录下)后需要配置静态资源的映射。
而SpringBoot项目则不需要,SpringBoot默认提供了3种静态资源映射规则:
1. WebJars映射(以Jar形式为Web项目提供资源文件)
SpringBoot项目是以Jar包的形式进行部署的,因此不存在webapp目录。
WebJars可以将Web前端资源(JS,CSS 等)打成一个个的Jar包,然后将这些Jar包部署到Maven中央仓库中进行统一管理。
只需在SpringBoot项目的pom.xml中引入依赖(访问WebJars官网,找到所需Web前端资源的pom依赖)。
例(在SpringBoot项目中引入jquery,只需在pom.xml中引入jquery依赖)
org.webjars
jquery
3.6.0
启动SpringBoot,在浏览器中访问“http://localhost:8080/webjars/jquery/3.6.0/jquery.js”访问 jquery.js
所有通过WebJars引入的前端资源都存放在当前项目类路径下的/META-INF/resources/webjars/目录中。
SpringBoot通过MVC的自动配置类WebMvcAutoConfiguration为这些WebJars前端资源提供了默认映射规则,部分源码如下:
public void addResourceHandlers(ResourceHandlerRegistry registry) {
if (!this.resourceProperties.isAddMappings()) {
logger.debug("Default resource handling disabled");
} else {
// WebJars 映射规则
this.addResourceHandler(registry, "/webjars/**", "classpath:/META-INF/resources/webjars/");
this.addResourceHandler(registry, this.mvcProperties.getStaticPathPattern(), (registration) -> {
registration.addResourceLocations(this.resourceProperties.getStaticLocations());
if (this.servletContext != null) {
ServletContextResource resource = new ServletContextResource(this.servletContext, "/");
registration.addResourceLocations(new Resource[]{resource});
}
});
}
}
通过源码可知,WebJars的映射路径为"/webjars/**"(即所有访问"/webjars/**"的请求,都会去“classpath:/META-INF/resources/webjars/”下查找WebJars前端资源)。
2. 默认资源映射
当访问项目中的任意资源(即"/**")时,SpringBoot会默认从以下路径(静态资源目录)中查找资源文件(优先级依次降低)classpath表示resources目录:
1. classpath:/META-INF/resources/
2. classpath:/resources/
3. classpath:/static/
4. classpath:/public/
3. 静态首页(欢迎页)映射
静态资源目录下的所有index.html被称为静态首页或欢迎页,会被"/**"映射(访问“/”或“/index.html”时会按优先级查找 并跳转到静态首页)。
7. Thymeleaf模板引擎(用于渲染xml/xhtml/html5内容的模板引擎,取代JSP)
Thymeleaf作为新一代Java模板引擎,相比传统Java模板引擎(JSP、Velocity、FreeMaker):
1. 支持HTML原型,其文件后缀为“.html”(因此可以直接被浏览器打开)。
2. 通过在html标签中增加额外的属性来达到“模板+数据”的展示方式。
特点:
1. 动静结合(最大的特点):未启动Web应用时,也能在浏览器中显示模板页面(此时展示的是静态内容;通过Web应用访问时会动态替换掉静态内容)。
2. 开箱即用:Thymeleaf 提供了 Spring 标准方言以及一个与 SpringMVC 完美集成的可选模块,可以快速的实现表单绑定、属性编辑器、国际化等功能。
3. 多方言支持:它提供了 Thymeleaf 标准和 Spring 标准两种方言,可以直接套用模板实现 JSTL、 OGNL 表达式;必要时,开发人员也可以扩展和创建自定义的方言。
4. 与SpringBoot完美整合:SpringBoot为Thymeleaf提供了默认配置,并且还为Thymeleaf设置了视图解析器。
例
Title
欢迎您访问静态页面 HTML
未启动Web应用时,直接使用浏览器打开显示:欢迎您访问静态页面HTML。
使用Web应用访问时,显示:迎您来到Thymeleaf。
使用(语法规则)
首先在html标签中声明thymeleaf空间(避免编辑器出现html验证错误,非必须):
语法规则:
1. 标准表达式语法
1. 变量表达式(使用${}包裹的表达式)
1. 获取对象的属性、方法
${person.lastName}
2. 获取内置的基本对象及其属性、方法
${#session.getAttribute('map')} 或 ${session.map}
内置的基本对象:
1. #ctx :上下文对象;
2. #vars :上下文变量;
3. #locale:上下文的语言环境;
4. #request:HttpServletRequest 对象(仅在Web应用中可用)。
5. #response:HttpServletResponse 对象(仅在Web应用中可用)。
6. #session:HttpSession对象(仅在Web应用中可用)。
7. #servletContext:ServletContext 对象(仅在Web应用中可用)。
3. 获取内置的工具对象
${#strings.equals('张三',name)}
内置的工具对象:
1. strings(字符串工具对象)
常用方法:equals、equalsIgnoreCase、length、trim、toUpperCase、toLowerCase、indexOf、substring、replace、startsWith、endsWith、contains、containsIgnoreCase 等;
2. numbers(数字工具对象)
常用方法:formatDecimal 等;
3. bools(布尔工具对象)
常用方法:isTrue、isFalse 等;
4. arrays(数组工具对象)
常用方法:toArray、length、isEmpty、contains、containsAll 等;
5. lists/sets(List/Set集合工具对象)
常用方法:toList、size、isEmpty、contains、containsAll、sort 等;
6. maps(Map 集合工具对象)
常用方法:size、isEmpty、containsKey、containsValue 等;
7. dates(日期工具对象)
常用方法:format、year、month、hour、createNow 等。
2. 选择变量表达式(使用*{}包裹的表达式)
在变量表达式的基础上增加了与th:object(用于存储一个临时变量,该变量只在该元素及其子元素中有效)的配合使用。
使用th:object存储一个对象后,可以在其子元素中使用选择变量表达式*{name}获取该对象(*代表该对象)中的属性。子元素中仍可使用${}变量表达式。
例:
firstname
3. 链接表达式(使用@{}包裹的表达式)
用于:静态资源引用、form表单请求(凡是链接都可以用链接表达式)。
1. 无参请求:@{/xxx}
2. 有参请求:@{/xxx(k1=v1,k2=v2)}
例:
4. 国际化表达式(使用#{}包裹的表达式)
用于国际化的场景。
th:text="#{msg}"
5. 片段引用表达式(使用~{}包裹的表达式)
用于在模板页面中引用其他的模板片段。
方式1. ~{templatename::fragmentname} 推荐
方式2. ~{templatename::#id}
说明:
1. templatename:模版名,Thymeleaf会根据模版名解析完整路径:/resources/templates/templatename.html,要注意文件的路径。
2. fragmentname:片段名,Thymeleaf通过th:fragment声明定义代码块(即:th:fragment="fragmentname")
3. id:html的id选择器,使用时要在前面加上#号,不支持class选择器。
2. th属性
可以直接在HTML标签中使用。
公共页面的抽取和引用
Web项目的页面中通常会存在一些重复代码(如:头部导航栏、侧边菜单栏、公共的js/css等)。可以把这些公共页面片段抽取出来存放在一个独立的页面中,然后再由其他页面根据需要进行引用(消除代码重复、使页面更简洁)。
1. 抽取公共页面
将公共页面片段抽取出来存放到一个独立的页面中,并使用th:fragment属性命名。
公共页面片段
2. 引用公共页面
可以通过以下3个属性,将公共页面片段引入到当前页面中。
1. th:insert:将代码块片段整个插入到使用了th:insert属性的html标签中。
2. th:replace:将代码块片段整个替换使用了th:replace属性的html标签中。
3. th:include:将代码块片段包含的内容插入到使用了th:include属性的html标签中。
属性值使用片段引用表达式引入引入页面片段(通常,~{}可以省略)
方式1. ~{templatename::#id}:模板名::选择器
方式2. ~{templatename::fragmentname}:模板名::片段名
行内写法为: [[~{...}]] 会转义特殊字符、[(~{...})] 不会转义特殊字符。
例:
3. 传递参数
引用公共页面片段时,通过以下2种方式将参数传入到被引用的页面片段中:
方式1(参数较多时使用该方式)明确指定参数名和参数值
模板名::选择器名或片段名(参数1=参数值1,参数2=参数值2)
方式2(参数较少时使用该方式)
模板名::选择器名或片段名(参数值1,参数值2)
例(查看th:insert、th:replace、th:include3者的区别)
1. 在页面 fragment.html 中引入 commons.html 中声明的页面片段
------------------------------------------------
------------------------------------------------
2. 启动 Spring Boot,使用浏览器访问fragment.html,右键查看页面源码:
公共页面片段
公共页面片段
------------------------------------------------
公共页面片段
公共页面片段
------------------------------------------------
公共页面片段
公共页面片段
例(传递参数)
------------------------------------------------
------------------------------------------------
在公共页面片段中使用:
...
SpringBoot项目中使用Thymeleaf
SpringBoot推荐使用Thymeleaf作为模板引擎(为其提供了大量默认配置)。
使用步骤
1. 引入Thymeleaf依赖(在项目的pom.xml中添加spring-boot-starter-thymeleaf依赖)
org.springframework.boot
spring-boot-starter-thymeleaf
2. 创建.html模板文件,并放置在项目类路径(resources目录)的templates目录下。
例(hello.html):
Title
3. 使用
创建HelloController.java,通过参数map传递数据到页面中:
package com.sst.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import java.util.Map;
@Controller
public class HelloController {
@RequestMapping("/hello")
public String hello(Map map) {
map.put("name", "hello world");
return "hello";
}
}
启动SpringBoot,在浏览器中访问:http://localhost:8080/hello
SpringBoot通过ThymeleafAutoConfiguration自动配置类为Thymeleaf提供了一整套的自动化配置方案。部分源码如下:
@Configuration(
proxyBeanMethods = false
)
@EnableConfigurationProperties({ThymeleafProperties.class})
@ConditionalOnClass({TemplateMode.class, SpringTemplateEngine.class})
@AutoConfigureAfter({WebMvcAutoConfiguration.class, WebFluxAutoConfiguration.class})
public class ThymeleafAutoConfiguration {
}
使用@EnableConfigurationProperties注解导入了ThymeleafProperties类(包含了和Thymeleaf相关的自动配置属性)。其部分源码如下:
@ConfigurationProperties(
prefix = "spring.thymeleaf"
)
public class ThymeleafProperties {
private static final Charset DEFAULT_ENCODING;
public static final String DEFAULT_PREFIX = "classpath:/templates/";
public static final String DEFAULT_SUFFIX = ".html";
private boolean checkTemplate = true;
private boolean checkTemplateLocation = true;
private String prefix = "classpath:/templates/";
private String suffix = ".html";
private String mode = "HTML";
private Charset encoding;
private boolean cache;
...
}
ThymeleafProperties通过@ConfigurationProperties注解将配置文件(application.properties/yml) 中前缀为spring.thymeleaf的配置和这个类中的属性绑定。
ThymeleafProperties还提供了以下静态变量:
1. DEFAULT_ENCODING:默认编码格式
2. DEFAULT_PREFIX:视图解析器的前缀
3. DEFAULT_SUFFIX:视图解析器的后缀
根据以上配置属性可知:
1. Thymeleaf模板的默认位置在resources/templates目录下,默认的后缀是html。
2. 只要将html页面放在该目录下,Thymeleaf就能自动进行渲染。
8. 国际化(为不同的国家/语言提供相应的页面和数据)
步骤:
1. 创建国际化资源文件(在resources目录的i18n目录下)
文件名格式:基本名_语言_国家.properties
例(IDEA会自动识别国际化资源文件并自动添加Resouce Bundle目录):
hello.properties:默认
hello_zh_CN.properties:中文时生效
hello_en_US.properties:英语时生效
打开任意一个国际化资源文件,切换为ResourceBundle模式(需要安装ResourceBundle插件),然后点击“+”号创建所需的国际化属性(需要进行国际化的字段)。
2. 使用ResourceBundleMessageSource管理国际化资源文件。
在application.porperties/yml配置文件中添加spring.messages.basename来覆盖默认值(当指定多个资源文件时,用逗号分隔)。
例:
spring.messages.basename=i18n.hello
3. 在页面中使用国际化表达式#{}来获取国际化内容。
SpringBoot通过MessageSourceAutoConfiguration类对ResourceBundleMessageSource提供了默认配置。
===》MessageSourceAutoConfiguration类的部分源码如下:
@Configuration(proxyBeanMethods = false)
@ConditionalOnMissingBean(name = AbstractApplicationContext.MESSAGE_SOURCE_BEAN_NAME, search = SearchStrategy.CURRENT)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@Conditional(org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration.ResourceBundleCondition.class)
@EnableConfigurationProperties
public class MessageSourceAutoConfiguration {
private static final Resource[] NO_RESOURCES = {};
// 将MessageSourceProperties以组件的形式添加到容器中,MessageSourceProperties下的每个属性都与以spring.messages开头的属性对应。
@Bean
@ConfigurationProperties(prefix = "spring.messages")
public MessageSourceProperties messageSourceProperties() {
return new MessageSourceProperties();
}
// Spring Boot会从容器中获取MessageSourceProperties,读取国际化资源文件的basename(基本名)、encoding(编码)等信息并封装到 ResourceBundleMessageSource中。
@Bean
public MessageSource messageSource(MessageSourceProperties properties) {
ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
// 读取国际化资源文件的basename (基本名),并封装到ResourceBundleMessageSource中
if (StringUtils.hasText(properties.getBasename())) {
messageSource.setBasenames(StringUtils
.commaDelimitedListToStringArray(StringUtils.trimAllWhitespace(properties.getBasename())));
}
// 读取国际化资源文件的encoding (编码),并封装到ResourceBundleMessageSource中
if (properties.getEncoding() != null) {
messageSource.setDefaultEncoding(properties.getEncoding().name());
}
messageSource.setFallbackToSystemLocale(properties.isFallbackToSystemLocale());
Duration cacheDuration = properties.getCacheDuration();
if (cacheDuration != null) {
messageSource.setCacheMillis(cacheDuration.toMillis());
}
messageSource.setAlwaysUseMessageFormat(properties.isAlwaysUseMessageFormat());
messageSource.setUseCodeAsDefaultMessage(properties.isUseCodeAsDefaultMessage());
return messageSource;
}
...
}
从源码可知:
1. SpringBoot将MessageSourceProperties以组件的形式添加到容器中。
2. MessageSourceProperties的属性与配置文件中以“spring.messages”开头的配置进行了绑定。
3. SpringBoot从容器中获取MessageSourceProperties组件,并从中读取国际化资源文件的basename(文件基本名)、encoding(编码)等信息,将它们封装到 ResourceBundleMessageSource中。
4. SpringBoot将ResourceBundleMessageSource以组件的形式添加到容器中,进而实现对国际化资源文件的管理。
===》MessageSourceProperties类的源码如下:
public class MessageSourceProperties {
private String basename = "messages";
private Charset encoding;
@DurationUnit(ChronoUnit.SECONDS)
private Duration cacheDuration;
private boolean fallbackToSystemLocale;
private boolean alwaysUseMessageFormat;
private boolean useCodeAsDefaultMessage;
public MessageSourceProperties() {
this.encoding = StandardCharsets.UTF_8;
this.fallbackToSystemLocale = true;
this.alwaysUseMessageFormat = false;
this.useCodeAsDefaultMessage = false;
}
...
}
从源码可知:
1. MessageSourceProperties为basename、encoding等属性提供了默认值。
2. basename表示国际化资源文件的基本名,其默认值为“message”(即SpringBoot默认会获取类路径下的message.properties以及message_XXX.properties作为国际化资源文件)。
3. 在application.porperties/yml配置文件中,使用配置参数“spring.messages.basename”即可重新指定国际化资源文件的基本名。
例
1. 在resources目录的i18n目录下创建
login.properties
login_en_US.properties
login_zh_CN.properties
2. 在application.porperties/yml配置文件中添加
application.porperties
spring.messages.basename=i18n.login
application.yml
spring:
messages:
basename: i18n/login
encoding: utf-8
3. 获取国际化内容
创建login.html,代码如下:
Login
手动切换语言
1. 区域信息解析器自动配置
可以通过以下两个对象对区域信息进行切换,继而切换语言。
1. Locale(区域信息对象)
2. LocaleResolver(区域信息解析器)容器中的组件,负责获取区域信息对象
2. 手动切换语言
1. 修改login.html中的切换语言链接,在请求中携带国际化区域信息,代码如下
中文|
English
2. 在com.sst.cx.component包下创建一个区域信息解析器MyLocalResolver,代码如下
package com.sst.cx.component;
import org.springframework.util.StringUtils;
import org.springframework.web.servlet.LocaleResolver;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Locale;
// 自定义区域信息解析器
public class MyLocalResolver implements LocaleResolver {
@Override
public Locale resolveLocale(HttpServletRequest request) {
// 获取请求中参数
String l = request.getParameter("l");
// 获取默认的区域信息解析器
Locale locale = Locale.getDefault();
// 根据请求中的参数重新构造区域信息对象
if (StringUtils.hasText(l)) {
String[] s = l.split("_");
locale = new Locale(s[0], s[1]);
}
return locale;
}
@Override
public void setLocale(HttpServletRequest request, HttpServletResponse response, Locale locale) {
}
}
3. 在com.sst.cx.config包下的MyMvcConfig中添加以下方法,将自定义的区域信息解析器以组件的形式添加到容器中,代码如下:
// 将自定义的区域信息解析器以组件的形式添加到容器中
@Bean
public LocaleResolver localeResolver(){
return new MyLocalResolver();
}
===》SpringBoot在WebMvcAutoConfiguration类中为区域信息解析器进行了自动配置,源码如下:
@Bean
@ConditionalOnMissingBean(name = DispatcherServlet.LOCALE_RESOLVER_BEAN_NAME)
@SuppressWarnings("deprecation")
public LocaleResolver localeResolver() {
if (this.webProperties.getLocaleResolver() == WebProperties.LocaleResolver.FIXED) {
return new FixedLocaleResolver(this.webProperties.getLocale());
}
if (this.mvcProperties.getLocaleResolver() == WebMvcProperties.LocaleResolver.FIXED) {
return new FixedLocaleResolver(this.mvcProperties.getLocale());
}
AcceptHeaderLocaleResolver localeResolver = new AcceptHeaderLocaleResolver();
Locale locale = (this.webProperties.getLocale() != null) ? this.webProperties.getLocale()
: this.mvcProperties.getLocale();
localeResolver.setDefaultLocale(locale);
return localeResolver;
}
从源码可知:
1. 该方法默认向容器中添加了一个LocaleResolver区域信息解析器组件,它会根据请求头中携带的“Accept-Language”参数,获取相应Locale区域信息对象。
2. 该方法上使用了@ConditionalOnMissingBean注解,其参数name的取值为 localeResolver(与该方法注入到容器中的组件名称一致),该注解的含义为:当容器中不存在名称为localResolver组件时,该方法才会生效。即手动向容器中添加一个名为“localeResolver”的组件时,SpringBoot自动配置的区域信息解析器会失效,自定义的区域信息解析器则会生效。