今天来学习spring boot,首先看一下课程体系.本文先负责前8章.前8章主要是学习spring boot的使用和内部的原理,后8章来学习它和一些高级场景的整合.
1. spring boot简介
简化spring应用开发的一个框架
对spring整个技术栈的一个整合
java ee开发的一站式解决方案
2. 微服务
微服务:它是一种架构风格,一个应用应该是一组小型服务,可以通过http的方式进行互通,
单体应用:all in one 所有的东西都放在一个应用程序内
优点:
开发测试简单
部署的话只需要把整个应用达成war包部署到服务器上即可
当系统负载过大时,直接可以把相同的应用进行水平复制扩展
缺点:
这种模式牵一发动全身,只要对项目进行修改,我们都需要把项目进行重新部署
这种模式无法满足日益复杂的业务需求
由此,诞生了微服务.
我们把每一个功能元素都独立放进一个独立的服务当中,然后根据需求将这些服务进行动态的组合
相比单体应用,我们只是对功能元素的复制,而没有进行整个应用的复制.
每一个服务都是可以替换,升级的软件单元.
环境约束
–jdk1.8:Spring Boot 推荐jdk1.7及以上;java version “1.8.0_112”,java -version;进行查看
–maven3.x:maven 3.3以上版本;Apache Maven 3.3.9, mvn -v : 查看版本号
–IntelliJIDEA2017:IntelliJ IDEA 2017.2.2 x64、STS
–SpringBoot 1.5.9.RELEASE:1.5.9;
统一环境;
1、MAVEN设置;
给maven 的settings.xml配置文件的profiles标签添加
<profile>
<id>jdk-1.8</id>
<activation>
<activeByDefault>true</activeByDefault>
<jdk>1.8</jdk>
</activation>
<properties>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<maven.compiler.compilerVersion>1.8</maven.compiler.compilerVersion>
</properties>
</profile>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
4、Spring Boot HelloWorld
浏览器发送hello请求,服务器接受请求并处理,响应Hello World字符串;
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.9.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
/**
* @SpringBootApplication 来标注一个主程序类,说明这是一个Spring Boot应用
*/
@SpringBootApplication
public class HelloWorldMainApplication {
public static void main(String[] args) {
// Spring应用启动起来
SpringApplication.run(HelloWorldMainApplication.class,args);
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
@Controller
public class HelloController {
@ResponseBody
@RequestMapping("/hello")
public String hello(){
return "Hello World!";
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
5.运行主程序进行测试
6.简化部署
<!-- 这个插件,可以将应用打包成一个可执行的jar包;-->
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
对于打包完成的项目生成的jar包,我们可以使用java -jar jar包 :在cmd下进行启动,同样可以在浏览器访问,英文springboot在打包的时候,都会给当前项目内Tomcat服务器.
helloword探究
1. pom文件
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.9.RELEASE</version>
</parent>
它的父项目还依赖一个父项目
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>1.5.9.RELEASE</version>
<relativePath>../../spring-boot-dependencies</relativePath>
</parent>
这个父工程中的properties属性,它定义了每一个依赖的版本号,这个父工程其实真正管理springboot工程中的所有的依赖的版本.
<properties>
<!-- Dependency versions -->
<activemq.version>5.14.5</activemq.version>
<antlr2.version>2.7.7</antlr2.version>
<appengine-sdk.version>1.9.59</appengine-sdk.version>
......
<properties>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
这个父工程就是spring boot的版本仲裁中心,我们以后在导入jar包时,默认是不用写版本的,(但是没有在dependencies里面管理的依赖,我们还是需要声明版本号的.)
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
- 1
- 2
- 3
- 4
项目中只导入了>spring-boot-starter-web.
spring-boot-stater:spring-boot场景启动器,帮我们导入了web模块正常运行所依赖的组件
springboot将所有的功能场景都抽取出来,做成一个个的starters(启动器),我们只需要在醒目中导入这些starter,相关场景的所有依赖都会被导入.而且版本由springboot的版本仲裁中心进行统一管理.要用什么场景就导入相应的场景启动器即可.
2.主程序类,主入口类
@SpringBootApplication
public class HelloworldApplication {
public static void main(String[] args) {
SpringApplication.run(HelloworldApplication.class,args);
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
@SpringBootApplication:spring boot 应用标注在某个类上,说明是这个类是springboot的主配置类.会运行这个类的main方法来起订spring boot应用.
我们来深入一下这个@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 {
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
@SpringBootConfiguration
@SpringBootConfiguration:spring boot的配置类,
它标注在某一个类上,表示这是springboot的一个配置类,点进去发现,它上边
标注着 >@Configuration:配置类上标注这个注解.
配置类的作用其实和我们在spring的时候写的xml的配置文件差不多,太多的配置文件太麻烦了,所以我们用配置类替换了.
继续点击会发现,@Configuration注解上面标注了@Component,这说明配置类也是容器中的组件
@EnableAutoConfiguration
开启自动配置功能,以前我们需要进行配置的内容,springboot会帮助我们自动配置,@EnableAutoConfiguration告诉springboot开启自动配置功能.这样自动配置才能生效
自动配置的原理??
@AutoConfigurationPackage
@Import(EnableAutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
- 1
- 2
- 3
@AutoConfigurationPackage:
它的作用就是将我们的主配置类(也就是@SpringBootApplication所标注的类,)所在的包及其所有的子包下的所有的组件扫描到spring容器中
自动配置包,他上面标注了
@Import(AutoConfigurationPackages.Registrar.class)
它使用了spring的底层的 @Import注解,它的作用就是给容器中导入组件,导入的组件由括号里面的内容决定,
本例中导入的就是AutoConfigurationPackages.Registrar.class这个组件
我们可以在@Import的括号中写逻辑判断语句并且返回,导入组件
深入Registrar这个类
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
@Override
//metadata,它值得是注解标注的一些源信息
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata,
BeanDefinitionRegistry registry) {
register(registry, new PackageImport(metadata).getPackageName());
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
最重要的就是 new PackageImport(metadata).getPackageName(),我们可以进行一个计算,如下图:
通过计算,这个方法的返回值真好就是我们这个主配置类所在的包名
所以说,我们这个@AutoConfigurationPackage注解的主要作用就是,对我们主配置类所在的包及其下面的所有的子包下的所有组件进行扫描到spring容器中
@Import(EnableAutoConfigurationImportSelector.class)
给容器中导入组件
EnableAutoConfigurationImportSelector:开启自动配置类的导包的选择器
它会把所有需要的组件以全类名的方式进行返回,然后添加到容器当中.
它最终会给容器中添加非常多的自动配置类(xxxAutoconfiguration),这些自动配置类会给容器中导入这些场景所需要的所有组件,并且配置好这些组件
有了自动配置类,就免去了我们手动编写配置,注入功能组件等工作.
那么容器是如何扫描到这么多了自动配置类呢?它是从哪得到的呢?
SpringFactoriesLoader.loadFactoryNames(EnableAutoConfiguration.class,classLoader);
- 1
从META-INF/spring.factories下获取EnableAutoConfiguration指定的值
从上图中可以看出,
Spring Boot在启动的时候从类路径下的META-INF/spring.factories中获取EnableAutoConfiguration指定的值,将这些值作为自动配置类导入到容器中,自动配置类就生效,帮我们进行自动配置工作;
以前我们需要自己配置的东西,自动配置类都帮我们;
J2EE的整体整合解决方案和自动配置都在spring-boot-autoconfigure-1.5.9.RELEASE.jar;
6.使用Spring Initializer快速创建spring boot应用.
IDE都支持使用Spring的项目创建向导快速创建一个Spring Boot项目;
选择我们需要的模块;向导会联网创建Spring Boot项目;
>@ResponseBody 注解加载类上,表示这个类的所有方法返回的数据直接写给浏览器(如果是对象转换为json)
@RestController = @Controller + @ResponseBody
默认生成的Spring Boot项目;
- 主程序已经生成好了,我们只需要我们自己的逻辑
- resources文件夹中目录结构
- static:保存所有的静态资源; js css images;
- templates:保存所有的模板页面;(Spring Boot默认jar包使用嵌入式的Tomcat,默认不支持JSP页面);可以使用模板引擎(freemarker、thymeleaf);
- application.properties:Spring Boot应用的配置文件;可以修改一些默认设置;
二 , Spring Boot 配置
1. 配置文件
spring boot默认会使用一个全局配置文件,(配置文件的名称是固定的,不能写错)
- application.properties
- application.yml
配置文件的作用就是用来修改springboot 自动配置的默认值
配置文件的作用:修改SpringBoot自动配置的默认值;SpringBoot在底层都给我们自动配置好;
YAML(YAML Ain’t Markup Language)
YAML A Markup Language:是一个标记语言
YAML isn’t Markup Language:不是一个标记语言;
标记语言:
以前的配置文件;大多都使用的是 xxxx.xml文件;
YAML:以数据为中心,比json、xml等更适合做配置文件;
YAML:配置例子
server:
port: 8088
- 1
- 2
<server>
<port>8088</port>
</server>
- 1
- 2
- 3
对比可以发现,xml的配置文件大量的数据都浪费在了标签的开辟上,而yml却非常的简介,所以说它是以数据为中心
2.YAML的语法
在yml的世界中,无论你的数据时哪种类型,k和v之间是一定要有一个空格的
它使用空格来控制层级关系,只要是左对齐的一列数据,都属于同一层级
属性和值都是大小写敏感的,严格区分大小写
2. yaml值的写法
k: v:字面直接来写;
字符串默认不用加上单引号或者双引号;
“”:双引号;不会转义字符串里面的特殊字符;特殊字符会作为本身想表示的意思
‘’:单引号;会转义特殊字符,特殊字符最终只是作为一个普通的字符串数据被解读
对象和map一样,都是使用键值对来表示.对象有两种表示形式.
1.第一种使用空格控制属性和值.
student:
name: zhangsan
age: 18
- 1
- 2
- 3
2.属性和值都写在同一行的大括号内,多个属性值之间使用逗号分隔
student: {name: zhangsan,age: 18}
- 1
数组也是有两种写法:
1,使用空格 -,来表示一个元素,注意使用空格控制层级关系
pets:
- dog
- cat
- pig
- 1
- 2
- 3
- 4
2.行内写法(使用中括号表示整个数组,每个元素有逗号分隔)
pets: [dog,cat,pig]
- 1
3.配置文件值注入
第一种 :从配置文件中进行属性值的注入
我们可以导入一个配置文件处理器,这样在编写配置文件就有提示的功能
<!--导入配置文件处理器,配置文件进行绑定就会有提示-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
- 1
- 2
- 3
- 4
- 5
- 6
总结:
将配置文件中的值,映射到这个组件中,
我们需要使用一个注解就是
@ConfigurationProperties:告诉springboot,本类中的所有属性需要和配置文件中
的相关配置进行绑定,它默认是从全局配置文件中获取值
prefix = “persion”,读取配置文件中的哪个前缀下的值和属性进行一一映射
@ConfigurationProperties必须和@Component一起使用才会生效,因为
只有容器中的组件才能使用容器提供的@ConfigurationProperties功能
/**
* 将配置文件中的值,映射到这个组件中,
* 我们需要使用一个注解就是
* @ConfigurationProperties:告诉springboot,本类中的所有属性需要和配置文件中
* 的相关配置进行绑定
* prefix = "persion",读取配置文件中的哪个前缀下的值和属性进行一一映射
* @ConfigurationProperties必须和@Component一起使用才会生效,因为
* 只有容器中的组件才能使用容器提供的@ConfigurationProperties功能
*/
@Component
@ConfigurationProperties(prefix = "persion")
public class Persion {
private String lastname;
private int age;
private boolean boss;
private Date birth;
private Map<String,Object> maps;
private List<Object> lists;
private Dog dog;
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
注意
idea默认是用的是utf-8编码,.properties默认使用的是ASCII编码,
如果我们从application.properties文件中注入属性,很可能出现中文乱码
修改方案如下图
第二种:我们使用 @Value注解来进行属性值注入
<bean class="Person">
<property name="lastName" value="字面量/${key}从环境变量、配置文件中获取值/#{SpEL}"></property>
<bean/>
- 1
- 2
- 3
- 4
其实
@Component注解就类似于xml中的标签,都是往容器中添加组件
@Value相当于,对组件的属性进行值注入
它支持写入的数据方式:
字面量,@Value(“字面量”)
$ {} :从环境变量,配置文件中获取值. @Value("${}")
#{spEL} :支持spring的EL表达式 @Value("#{}")
@Value("${persion.lastname}")
private String lastname;
@Value("#{11*2}")
private int age;
@Value("true")
private boolean boss;
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
@value获取值和@ConfigurationProperties获取值进行比较
@ConfigurationProperties | @Value | |
---|---|---|
功能 | 批量注入配置文件中的属性(只需要指定一个perfix,) | 一个个指定 |
松散绑定(松散语法) | 支持 | 不支持 |
SpEL | 不支持 | 支持 |
JSR303数据校验 | 支持(需要结合 @Validated使用) | 不支持 |
复杂类型封装 | 支持 | 不支持 |
那么我们到底如何选取二者为值注入?
如果说,我们只是在某个业务逻辑中需要获取一下配置文件中的某项值,使用@Value;
如果说,我们专门编写了一个javaBean来和配置文件进行映射,我们就直接使用@ConfigurationProperties;
多个值注入选用@ConfigurationProperties比较方便
松散绑定语法:
无论你写的是firstName,first-name,first_name,都可以对firstname属性进行值注入
3、配置文件注入值数据校验
@Component
@ConfigurationProperties(prefix = "person")
@Validated
public class Person {
/**
* <bean class="Person">
* <property name="lastName" value="字面量/${key}从环境变量、配置文件中获取值/#{SpEL}"></property>
* <bean/>
*/
//lastName必须是邮箱格式
@Email
private String lastName;
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
4.@PropertySource && @ImportResource
@PropertySource : 加载指定的配置文件,(value的值其实是一个数组,也就是可以加载多个配置文件)
@PropertySource(value = {"classpath:persion.properties"})
@Component
@ConfigurationProperties(prefix = "persion")
public class Persion {
- 1
- 2
- 3
- 4
@ImportResource:导入spring的配置文件,让其内容生效
spring boot中是没有spring的配置文件的,我们自己编写的配置文件也是不能被自动识别的.如果想让配置文件生效,加载到容器,我们需要把 @ImportResource标注在一个配置类上.
@ImportResource(locations = {“classpath:beans.xml”})
locations也是一个数组,可以加载多个配置文件的
刚才这种方式往容器中添加组件,十分麻烦,我们需要写一个配置文件,然后使用@ImportResource注解,springboot推荐的是下面这种方式
全注解的方式添加组件
1.编写一个配置类(它的功能就是替代我们之前写的spring的配置文件),标注 @Configuration能够是一个类称为配置类,
2.配置类中我们是用 @Bean来添加组件,(@Bean标注在方法上时,表示将该方法的返回值添加到容器中,被添加的这个组件的id就是该方法的方法名)
@Configuration
public class MyAppConfig {
//@Bean标注在方法上,表示的是将方法的返回值添加到容器中
//容器中这个组件的id就是该方法的方法名
@Bean
public HelloService helloService(){
System.out.println("@Bean 给容器中添加组件了!");
return new HelloService();
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
4.配置文件占位符
${random.value}、${random.int}、${random.long}
${random.int(10)}、${random.int[1024,65536]}
- 1
- 2
person.last-name=张三${random.uuid}
person.age=${random.int}
person.dog.name=${person.hello:hello}_dog
- 1
- 2
- 3
5. Profile
springboot提供了多环境的选择,为了满足开发,测试,生产等不同的环境,我们来说两种方式
我们为不同的环境编写多个Profile文件,文件的命名就是 application-{profile标识}.properties
默认使用的application.properties文件
我们可以全局配置文件(也就是application.properties)中通过 spring.profiles.active= profile标识来切换不同的环境
yml可以通过 — 来划分一个文档块.当我们指定的文档块被激活时,它的配置猜会生效
server:
port: 8081
spring:
profiles:
active: dev
---
server:
port: 8082
spring:
profiles: dev
---
server:
port: 8083
spring:
profiles: prod
---
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
1.在配置文件中指定 spring.profiles.active=dev
2.通过命令行: (命令行的优先级高于配置文件的)
–spring.profiles.active=dev,这个命令修改 Program arguments的值,这个是在测试的时候,不用打包
打过包之后,也可以 : java -jar spring-boot-02-config-0.0.1-SNAPSHOT.jar --spring.profiles.active=dev;
3.修改虚拟机参数:
-Dspring.profiles.active=dev,这个是固定写法.
6.配置文件的加载位置
springboot 启动会扫描以下位置的application.properties或者application.yml文件作为Spring boot的默认配置文件,下面就是springboot加载配置文件的位置,优先级由高到低,逐步降低,
–file:./config/ : 当前项目下的config文件夹下
–file:./ :当前项目下
–classpath:/config/ : 类路径(resource文件夹)下的config文件夹下
–classpath:/ 类路径下
上面就是springboot加载配置文件的位置,优先级由高到低,逐步降低,
相同配置内容同时存在时:高优先级别的配置文件会对低优先级的配置进行覆盖
而不同的配置则不会进行覆盖,并不是高优先级的加载之后就不加载低优先级的
SpringBoot会从这四个位置全部加载主配置文件;形成了 互补配置;
我们可以通过== spring.config.location 来改变默认的配置文件的位置==
我们直接在配置文件中这样写是不会生效的
该命令一般都是项目上线之后,在运维时使用的.我们使用命令行的方式运行
我们只需要重新写一份配置文件,只需要编写少量的配置,就可以和之前的配置共同起作用.特别适合运维.不需要更改整个配置然后打包发布
java -jar spring-boot-02-config-02-0.0.1-SNAPSHOT.jar --spring
.config.location=D:/application.properties
- 1
- 2
7.外部配置的加载顺序
我们对springboot的配置不仅仅是可以写在application.properties文件内,还可以从以下位置中加载配置.优先级从高到低,所有配置都支持的是互补配置.(总共是11个,官网是17个,我们只关心,1,6,7,8,9)
1.命令行参数
所有的配置都可以在命令行上进行指定
java -jar spring-boot-02-config-02-0.0.1-SNAPSHOT.jar --server.port=8087 --server.context-path=/abc 多个配置用空格分开; --配置项=值
2.来自java:comp/env的JNDI属性
3.Java系统属性(System.getProperties())
4.操作系统环境变量
5.RandomValuePropertySource配置的random.*属性值
由jar包外向jar包内进行寻找;
优先加载带profile的
6.jar包外部的application-{profile}.properties或application.yml(带spring.profile)配置文件
7.jar包内部的application-{profile}.properties或application.yml(带spring.profile)配置文件
再来加载不带profile的
8.jar包外部的application.properties或application.yml(不带spring.profile)配置文件
9.jar包内部的application.properties或application.yml(不带spring.profile)配置文件
10.@Configuration注解类上的@PropertySource
11.通过SpringApplication.setDefaultProperties指定的默认属性
8.自动配置原理
springboot的全局配置文件,到底如何写?能写哪些内容?
官方的属性参照
自动配置原理
它是springboot的精髓所在,理解了它就能随心所欲的进行配置.
1.springboot 在启动的时候,加载主配置类,并且通过主配置类上标注的 @EnableAutoConfiguration开启了自动配置功能,
2,@EnableAutoConfiguration作用:
- 利用 @Import(AutoConfigurationImportSelector.class)给容器中导入了一些组件,跟踪源码,会发现,
- public String[] selectImports(AnnotationMetadata annotationMetadata) {}方法中的
- List < String> configurations =getCandidateConfigurations(annotationMetadata,attributes); //它的作用就是获取候选的配置
- SpringFactoriesLoader.loadFactoryNames()
//它会扫描所有jar包类路径下 META-INF/spring.factoriesProperties properties = PropertiesLoaderUtils.loadProperties(resource);
- 1
//把扫描到的这些文件的内容包装成properties对象
for (Map.Entry<?, ?> entry : properties.entrySet()) { String factoryClassName = ((String) entry.getKey()).trim(); for (String factoryName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) { result.add(factoryClassName, factoryName.trim()); } ```
- 1
- 2
- 3
- 4
- 5
- //通过对properties 文件的遍历,从properties文件汇总获取到EnableAutoConfiguration.class类(类名)对应的值,然后把他们添加在容器中
3.每一个 自动配置类都会进行自动配置.
总结:
springboot在启动时,会将 类路径下 META-INF/spring.factories 里面配置的所有EnableAutoConfiguration的值加入到了容器中
下面只是列举了其中的一小部分
这里面的每一个XXXAutoConfiguration都是容器中的一个组件,都会加入到容器中红,他们的作用就是 用来做自动配置
# 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,\
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
4.举例 HttpEncodingAutoConfiguration解释自动配置原理
@Configuration:表示这是一个配置类,作用和之前编写的spring的配置文件类似,也可以向容器中添加组件
@EnableConfigurationProperties(HttpProperties.class) :开启指定类的ConfigurationProperties功能,然后把这个类加入到容器中.
@Configuration
//表示这个类是一个配置类
@EnableConfigurationProperties(HttpProperties.class)
//@EnableConfigurationProperties(HttpProperties.class) :开启指定类的ConfigurationProperties功能,然后把这个类加入到IOC容器中
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
//@ConditionalOn底层使用的是spring的@Conditional注解,根据我们的条件进行判断,如果满足我们的条件,整个配置类里面的配置才会生效,判断这是不是一个web应用,如果是,当前配置类生效
@ConditionalOnClass(CharacterEncodingFilter.class)
//判断当前项目中有没有CharacterEncodingFilter这个类,这个类是springmvc中用来解决乱码的过滤器
@ConditionalOnProperty(prefix = "spring.http.encoding", value = "enabled",
matchIfMissing = true)
//判断配置hi文件中是否含有某个配置, spring.http.encoding.enabled;
//matchIfMissing = true表示的是如果不存在这个配置,也是默认生效的
public class HttpEncodingAutoConfiguration {
//他已经和SpringBoot的配置文件映射了
private final HttpEncodingProperties properties;
//只有一个有参构造器的情况下,参数的值需要从容器中获取
public HttpEncodingAutoConfiguration(HttpProperties properties) {
this.properties = properties.getEncoding();
}
@Bean //给容器中添加一个组件,该组件中某些值需要从properties中获取
//容器中必须不包含某个类
@ConditionalOnMissingBean
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;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- @ConfigurationProperties(prefix = “spring.http”):表示从配置文件中获取指定的在值来和本类中的属性进行绑定,获取值的前缀时spring.http.将配置文件中对应的值和HttpEncodingProperties绑定起来
2.这也就是告诉我们,配置文件到底如何写,我们可以在全局配置文件(application.properties/yml)中,编写spring.http.x=y;例如:
spring.http.encoding.charset=UTF-8
spring.http.encoding.force=true
3.具体需要配置的x可以在propertis类中查看
@ConfigurationProperties(prefix = "spring.http")
public class HttpProperties {
- 1
- 2
5.所有在配置文件中能配置的属性都是在xxxxProperties类中封装者‘;配置文件能配置什么就可以参照某个功能对应的这个属性类
根据不同的条件进行判断,配置类是否生效 ?
1.一但这个配置类生效;这个配置类就会给容器中添加各种组件;这些组件的属性是从对应的properties类中获取的,这些类里面的每一个属性又是和配置文件绑定的;
2.我们到底能在配置文件中配置哪些内容,都是由xxxProperties类中的属性决定的
精髓:
1)、SpringBoot启动会加载大量的自动配置类
2)、我们看我们需要的功能有没有SpringBoot默认写好的自动配置类;
3)、判断这个自动配置类到底会不会生效,如果生效,我们再来看这个自动配置类中到底配置了哪些组件;(只要我们要用的组件有,我们就不需要再来配置了)
4)、给容器中自动配置类添加组件的时候,会从properties类中获取某些属性。我们就可以在配置文件中指定这些属性的值;
xxxxAutoConfigurartion:自动配置类;给容器中添加组件,组件的某些属性值会从和xxxxProperties映射的配置文件中读取
xxxxProperties:封装配置文件中相关属性;
2. 细节
1.@Conditional派生注解(Spring注解版原生的@Conditional作用)
作用:就是必须要使得@Conditional注解指定的条件成立,配置类内的配置才会生效,才会给容器中添加组件
@Conditional扩展注解 | 作用(判断是否满足当前指定条件) |
---|---|
@ConditionalOnJava | 系统的java版本是否符合要求 |
@ConditionalOnBean | 容器中存在指定Bean; |
@ConditionalOnMissingBean | 容器中不存在指定Bean; |
@ConditionalOnExpression | 满足SpEL表达式指定 |
@ConditionalOnClass | 系统中有指定的类 |
@ConditionalOnMissingClass | 系统中没有指定的类 |
@ConditionalOnSingleCandidate | 容器中只有一个指定的Bean,或者这个Bean是首选Bean |
@ConditionalOnProperty | 系统中指定的属性是否有指定的值 |
@ConditionalOnResource | 类路径下是否存在指定资源文件 |
@ConditionalOnWebApplication | 当前是web环境 |
@ConditionalOnNotWebApplication | 当前不是web环境 |
@ConditionalOnJndi | JNDI存在指定项 |
虽然springboot在启动时加载了很多自动配置类(默认好像是加载了96个),但是不是所有的都生效,自动配置类需要满足一定的条件才能生效,那么我们怎么知道到底哪些生效了,哪些没有生效呢?
1.如果不怕麻烦,你可以打开类路径下 /META-INF/spring.factories,找到,EnableAutoConfiguration,然后一个个的查看,到底哪个生效了.
2.debug=true,开启debug模式,在application,properties中写上这一句.启动应用就会在控制台进行输出,我们可以很方便的看出到底谁生效了,谁没有是生效.
三. springboot与日志
1.日志框架
市面上的日志框架;
JUL、JCL、Jboss-logging、logback、log4j、log4j2、slf4j…
日志门面 (日志的抽象层) | 日志实现 |
---|---|
Log4j JUL(java.util.logging) Log4j2 Logback |
左边选一个门面(日志的抽象层,类似JDBC)、右边来选一个实现;
日志门面: SLF4J;
日志实现:Logback;
slf4j和log4j和logback都是同一个人写的,但是log4j有性能问题,所有我们使用logback,log4j2其实是Apache的产品,只不过是借用了人家的名字.
springboot底层是spring,spring的日志框架是 jcl ,但是springboot的底层选用的是slf4j和logback
2. slf4j的使用
1.如何在系统使用slf4j
在开发的时候,日志方法的调用,不应该是直接调用日志的实现类,而是调用日志抽象层里面的方法
我们首先要给系统中导入slf4j的jar和logback实现的jar
示例代码:
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class HelloWorld {
public static void main(String[] args) {
//调用的的是抽象层LoggerFactory的getLogger方法
//getLogger(),获取记录器,记录器会记录指定类的信息
Logger logger = LoggerFactory.getLogger(HelloWorld.class);
//我们记录的信息可以打印到控制台,或者是输出到日志文件中
logger.info("Hello World");
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
如果想要用其他的日志实现,不用默认的logback呢?
先来看官方给的一个图:
解释上图:
上图给我们解释了该如何具体的使用slf4j,以及该导入那些包
- 使用logback: 导入slf4j-api.jar ,logback-classic.jar, logback-core.jar
- 使用log4j: 导入slf4j-api.jar ,log4j.jar,和适配层的 slf4j-log412.jar
- 使用JUL: 导入slf4j-api.jar,jul的jar.和适配层的slf4j-jdk14.jar
为设么要导入适配层的jar?
因为啊,这个log4j和JUL的出现比slf4j要早很多,人家的产品压根就没有考虑会出现这个抽象层的slf4j,
slf4j为了整合log4j和jul,推出了相应的适配层的jar,适配层包含实现了slf4j 的具体方法,还会调用log4j或者jul的api.
流程 : 应用 >>> 调用slf4j的api >>> 调用适配层的方法 >>> 调用log4j/jul 的api
每一个日志框架都有自己的配置文件,使用了sf4j之后,日志文件还是做成日志实现框架本身的配置文件,也就是说,我们的slf4j只是提供 了一个统一抽象层
如果使用的是logback,那么还是写logback的配置文件
如果使用的是log4j,那么就写log4j的配置 文件
2. 遗留问题
我们在实际的开发过程中,可能使用各种框架,他们都有自己的日志实现 ,例如,springboot( slf4j + logback ) ,hibernate (jbss-logging) , spring(jcl),…这个看上去进很乱.我们需要统一日志记录
统一日志记录
即使使用到别的框架,那么也要使用slf4j+logback,进行日志输出.
如何统一使用slf4j进行日志记录?
1. 首先要把其他日志框架进行排除
2. 用中间包替换掉原有的日志框架
3. 我们导入slf4j的其他的实现.
对上面的步骤进行解释:
1.为了统一使用slf4j进行日志记录,我们首先要把其他的日志框架进行排除,例如spring要排除jcl,hibernate 要排除jboss,还有要排除log4j,jul等
2.排除其他的日志框架之后,我们的其他框架肯定会报错,这时我们需要一个替换包来代替那些被替换掉的日志框架,替换包的功能就是替换其他的日志框架,然后还能支持抽象层的slf4j,例如 jcl-over-slf4j.jar, log4j-over-slf4j.jar, jul-to-slf4j.jar,分别是用来替换jcl,log4j,jul的
替换完成之后,我们 就可以使用你心仪的日志框架,如果你想用log4j,你需要导入适配层slf4j-log412.jar, 想用jul需要导入适配层 slf4j-jdk14.jar.
这样,我们只需要调用 抽象层slf4j的api, 抽象层会通过适配层调用具体的日志实现,这样我们就实现了日志的统一记录.
3.springboot的日志关系
每一个springboot应用找那个都包含spring-boot-start这儿依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
- 1
- 2
- 3
- 4
spring boot 使用它来做日志工程:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
<version>2.1.4.RELEASE</version>
<scope>compile</scope>
</dependency>
- 1
- 2
- 3
- 4
- 5
- 6
底层依赖关系:
总结:
1 SpringBoot底层也是使用slf4j+logback的方式进行日志记录
2 SpringBoot通过中间替换包也把其他的日志都替换成了slf4j;
4 从上图中,我们可以看出,springboot统一日志记录采用的就是slf4j官网上图片上的步骤,它在底层已经为我们完成了替换,我们如果引入了其他的框架,需要日志的话,一定要把引入的框架默认的日志依赖给移除掉.因为替换包中的日志的包名,类名字和默认日志框架中的包名,类名全部一样.如果不移除,jar吧就会发生冲突,肯定要报错的
springboot引入其他的框架时,在底层首先排除了人家默认的日志框架,例如spring使用的是commons-logging;springbot 就把commons-logging给移除了
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<exclusions>
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
日志核心总结:
springboot能够自动适配所有日志框架,并且在底层采用的都是slf4j+logback的方式记录日志.我们在引入其他的框架的时候,只需要把该该框架默认的日志框架给排除掉就行了
4.日志的使用
1 默认配置
spring boot默认已经帮我们做好了日志的配置,可以直接使用,示例如下:
public class SpringBoot03LoggingApplicationTests {
//记录器
Logger logger = LoggerFactory.getLogger(getClass());
@Test
public void contextLoads() {
//trace翻译就是跟踪轨迹的意思,它是最低级别的,但也是打印信息最详细的
//日志的级别: trace debug info warn error,级别是由低到高,默认级别是info
//我们可以调整日志级别来控制日志信息的输出,它就会打印本级别和更高级别的日志信息
logger.trace("这个是trace日志.....");
logger.debug("这个是debug日志.....");
//没有指定级别的,就使用springboot默认的root级别
logger.info("这个是info日志.....");
logger.warn("这个是warn日志.....");
logger.error("这个是error日志.....");
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
//trace翻译就是跟踪轨迹的意思,它是最低级别的,但也是打印信息最详细的
//日志的级别: trace debug info warn error,级别是由低到高,默认级别是info
//我们可以调整日志级别来控制日志信息的输出,它就会打印本级别和更高级别的日志信息
//没有指定级别的,就使用springboot默认的root级别
可以在application.properties文件中配置日志:
# 我们可以调整每一个包的日志级别
logging.level.com.atguigu=trace
# logging.path主要是指定,日志文件的目录,/表示的是当前磁盘的根目录
# 它会在当前磁盘根目录下创建spring/log文件夹,在该文件下存放日志文件,
# 文件的默认名是 spring.log
logging.path=/spring/log
# 不指定路径的话,它会在当前项目下生成spring-boot.log日志文件,也可以在文件前加上路径
# 指明配置文件的生成位置
logging.file=D:/spring-boot.log
# 指定在控制台输出的日志的格式
logging.pattern.console=%d{yyyy-MM-dd} [%thread] %-5level %logger{50} - %msg%n
# 指明日志文件中的输出格式
logging.pattern.file=%d{yyyy-MM-dd} === [%thread] === %-5level === %logger{50} ==== %msg%n
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
logging.file和loggingpath的区别
logging.file | logging.path | Example | Description |
---|---|---|---|
(none) | (none) | 只在控制台输出 | |
指定文件名 | (none) | my.log | 输出日志到my.log文件 |
(none) | 指定目录 | /var/log | 输出到指定目录的 spring.log 文件中 |
日志信息的语法如下:
日志输出格式:
%d表示日期时间,
%thread表示线程名,
%-5level:级别从左显示5个字符宽度
%logger{50} 表示logger名字最长50个字符,否则按照句点分割。
%msg:日志消息,
%n是换行符
-->
%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
2.指定配置
我们只能在application.yml配置文件修改一些特别日志的配置,
我们在application.properties中编写的日志配置,其实都会被保存在LoggingApplicationListener这个类中,而这个listener中配置的信息,最终会被封装到属性类LoggingSystemProperties中,它里面的apply方法会对包装过的配置进行解析.
阅读源码我们会发现
springboot已经配置好了日志,包括默认的日志文件是以追加的方式,默认大小10M,超过了10M,文件名会自增继续创建.
但是我们感觉这些还不够用,我们想要使用logback的一些高级功能?
例如要 异步日志 ,自动归档了等功能,这是我们就需要自己来写日志的配置文件
Logging System | Customization |
---|---|
Logback | logback-spring.xml , logback-spring.groovy , logback.xml or logback.groovy |
Log4j2 | log4j2-spring.xml or log4j2.xml |
JDK (Java Util Logging) | logging.properties |
如果你想用自己的日志配置文件,那么 给类路径下放上你要用的日志框架自己的配置文件即可;SpringBoot就不使用他默认配置的了,配置文件的命名如上表所示
推荐使用 logback-spring.xml组委自定义的日志配置文件
1.logback.xml : 它会绕过springboot被日志框架直接额加载
2.logback-spring.xml : 它不会被日志框架直接加载,而是由springboot加载,这样做的优点就是,我们可以在配置文件内使用< springProfile>标签,这个标签可以 把某段配置只在特定的激活环境中生效
3.如果你文件名是 logback.xml,但是你在文件中使用了< springProfile>标签,那么因公启动就会报错no applicable action for [springProfile]
<springProfile name="staging">
<!-- configuration to be enabled when the "staging" profile is active -->
可以指定某段配置只在某个环境下生效
</springProfile>
- 1
- 2
- 3
- 4
5 .切换日志框架
spring-boot-starter-logging和spring-boot-starter-log4j2,都是用来做日志的,springboot默认的是logging ,我们也可以切换为log4j2
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<artifactId>spring-boot-starter-logging</artifactId>
<groupId>org.springframework.boot</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
原理就是 把原来的logging排除,然后添加log4j2
spring-boot-starter-logging的底层使用的是 slf4j + logback的方式,我们也可以把logback替换掉,当然这样做是没有意义的,在这里只是学习一下原理:
1.把logback的依赖给排除
2.把之前logback 替换log4j的依赖给排除
3.添加log4j 的依赖
4.添加slf4j为log4j做的适配包
5.你同样可以为你的新的日志框架写一个自定义的配置文件,放在类路径下,名为为 xxx-spring.xml,推荐加上-spring,这样可以使用高级功能.
日志配置文件中配置异步日志
<appendername="ASYNC_ROLLING_FILE"class="ch.qos.logback.classic.AsyncAppender">
<appender-refref="ROLLING_FILE"/>
</appender>
<rootlevel="INFO">
<appender-refref="ASYNC_ROLLING_FILE"/>
</root>
- 1
- 2
- 3
- 4
- 5
- 6
四 ,spring boot与web开发
1.使用SpringBoot;
1)、创建SpringBoot应用,选中我们需要的模块;
2)、SpringBoot已经默认将这些场景配置好了,只需要在配置文件中指定少量配置就可以运行起来
3)、自己编写业务代码;
自动配置原理?
这个场景SpringBoot帮我们配置了什么?能不能修改?能修改哪些配置?能不能扩展?xxx
1.xxxxAutoConfiguration:帮我们给容器中自动配置组件;
2.xxxxProperties:配置类来封装配置文件的内容;它通过@ConfigurationProperties(prefix = “”)来完成配置类的属性和配置文件中之间的映射
他们两者之间通过@EnableConfigurationPropertie(),进行连接
2. spring boot对静态资源的映射规则
可以在配置文件中通过spring.resources设值和静态资源相关的参数,缓存时间等.
@ConfigurationProperties(prefix = "spring.resources", ignoreUnknownFields = false)
public class ResourceProperties {
- 1
- 2
springboot中对SpringMvc的所有配置都在 WebMvcAutoConfiguration这个类中,这个类中有个方法,作用是 addResourceHandlers 添加资源映射,代码如下:
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
if (!this.resourceProperties.isAddMappings()) {
logger.debug("Default resource handling disabled");
return;
}
Duration cachePeriod = this.resourceProperties.getCache().getPeriod();
CacheControl cacheControl = this.resourceProperties.getCache()
.getCachecontrol().toHttpCacheControl();
if (!registry.hasMappingForPattern("/webjars/**")) {
customizeResourceHandlerRegistration(registry
.addResourceHandler("/webjars/**")
.addResourceLocations("classpath:/META-INF/resources/webjars/")
.setCachePeriod(getSeconds(cachePeriod))
.setCacheControl(cacheControl));
}
String staticPathPattern = this.mvcProperties.getStaticPathPattern();
if (!registry.hasMappingForPattern(staticPathPattern)) {
customizeResourceHandlerRegistration(
registry.addResourceHandler(staticPathPattern)
.addResourceLocations(getResourceLocations(
this.resourceProperties.getStaticLocations()))
.setCachePeriod(getSeconds(cachePeriod))
.setCacheControl(cacheControl));
}
}
//配置欢迎页的映射
@Bean
public WelcomePageHandlerMapping welcomePageHandlerMapping(
ApplicationContext applicationContext) {
return new WelcomePageHandlerMapping(
new TemplateAvailabilityProviders(applicationContext),
applicationContext, getWelcomePage(),
this.mvcProperties.getStaticPathPattern());
}
//配置我们自己喜欢的图标
@Configuration
@ConditionalOnProperty(value = "spring.mvc.favicon.enabled",
matchIfMissing = true)
public static class FaviconConfiguration implements ResourceLoaderAware {
private final ResourceProperties resourceProperties;
private ResourceLoader resourceLoader;
public FaviconConfiguration(ResourceProperties resourceProperties) {
this.resourceProperties = resourceProperties;
}
@Override
public void setResourceLoader(ResourceLoader resourceLoader) {
this.resourceLoader = resourceLoader;
}
@Bean
public SimpleUrlHandlerMapping faviconHandlerMapping() {
SimpleUrlHandlerMapping mapping = new SimpleUrlHandlerMapping();
mapping.setOrder(Ordered.HIGHEST_PRECEDENCE + 1);
mapping.setUrlMap(Collections.singletonMap("**/favicon.ico",
faviconRequestHandler()));
return mapping;
}
@Bean
public ResourceHttpRequestHandler faviconRequestHandler() {
ResourceHttpRequestHandler requestHandler = new ResourceHttpRequestHandler();
requestHandler.setLocations(resolveFaviconLocations());
return requestHandler;
}
private List<Resource> resolveFaviconLocations() {
String[] staticLocations = getResourceLocations(
this.resourceProperties.getStaticLocations());
List<Resource> locations = new ArrayList<>(staticLocations.length + 1);
Arrays.stream(staticLocations).map(this.resourceLoader::getResource)
.forEach(locations::add);
locations.add(new ClassPathResource("/"));
return Collections.unmodifiableList(locations);
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
1./webjars/ : 请求:
请求都去classpath:/META-INF/resources/webjars/ ,下找资源,webjars:指的就是以jar包的方式引入资源,它把我们要用的jQuery,bootstrap等整合成了maven,详情请参考网站
测试案例:http://localhost:8080/webjars/jquery/3.3.1/jquery.js, 在访问时,我们只需要些要访问的资源就行
<!--引入jQuery的webjar-->
<dependency>
<groupId>org.webjars</groupId>
<artifactId>jquery</artifactId>
<version>3.3.1</version>
</dependency>
- 1
- 2
- 3
- 4
- 5
- 6
2. “/**” : 访问当前项目的任何资源(这几个都是用来存放项目的静态资源的文件夹,只要是请求没有处理,都会在以下文件夹下寻找资源)
1. “classpath:/META-INF/resources/”,
2. “classpath:/resources/”,
3. “classpath:/static/”,
4. “classpath:/public/”
5. “/” :当前项目的根路径
java和resources都是类路径的根路径
访问的规则:localhost:8080/abc : 如果没人处理,就会去以上几个文件下寻找abc
3 . 欢迎页 : 静态资源文件夹下的index.html页面,会被 ‘’ /** ''映射
访问localhost:8080/ 它就会找index.html
4.项目图标,所有的 **/favicon.ico,都会在静态资源文件夹下找
5.修改系统默认的静态资源文件夹
1.从类ResourcesProperty的源源码的标注得知,我们可以在application.properties使用spring.resource来修改和静态资源,缓存等有关的参数
2.spring.resources.static-locations = classpath:/hello,classpath:/aynu,修改的是默认的静态资源文件夹,这个locations是一个数组,也就是可以指定多个静态资源文件夹,多个元素之间使用逗号分隔
3.模板引擎
常见的模板引擎 : JSP Freemarker,Thymeleaf,等,他们的原来都是相通的
模板引擎的原理当我们把模板页面和数据都交给模板引擎时,它会把数据解析然后填充到模板页面中指定的位置,进行输出.只不过不同的引擎的语法可能不太一样
springboot推荐使用的是Thymeleaf,使用方法如下:
1.引入Thymeleaf
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
- 1
- 2
- 3
- 4
默认的版本太低了,我们要使用Thymeleaf3,我们可以在properties标签中覆盖要来的默认版本
切换你Thymeleaf版本
<properties>
<thymeleaf.version>3.0.9.RELEASE</thymeleaf.version>
<!-- 布局功能的支持程序 thymeleaf3主程序 layout2以上版本 -->
<!-- thymeleaf2 layout1-->
<thymeleaf-layout-dialect.version>2.2.2</thymeleaf-layout-dialect.version>
</properties>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
2.thymeleaf的语法和使用
根据自动配置原理,我们可以得知,可以在配置文件中编写的参数都保存在名为xxxProperties 的属性类中
@ConfigurationProperties(prefix = "spring.thymeleaf")
public class ThymeleafProperties {
private static final Charset DEFAULT_ENCODING = StandardCharsets.UTF_8;
public static final String DEFAULT_PREFIX = "classpath:/templates/";
public static final String DEFAULT_SUFFIX = ".html";
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
由此得知,我们只要把HTML页面(也就是目标页面)放在classpath:/templates/文件夹下,Thymeleaf模板引擎就可以自动帮我们渲染页面了
使用:
1.导入Thymeleaf的名称空间(这样就会有提示的功能)
<html lang="en" xmlns:th="http://www.thymeleaf.org">
- 1
2.使用Thymeleaf的语法
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>成功!</h1>
<!--th:text 它可以替换我们div中的文本,相当于模板引擎的数据填充-->
<div th:text="${hello}">这是我们要显示的内容</div>
</body>
</html>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
3.语法规则
- th:text : 改变当前元素的文本内容,我们可以使用 th:任意HTML属性 来替换原生属性的值 (属性值可以如下图)
2.能写哪些表达式?
``
Simple expressions:(表达式语法)
#{} 的语法功能如下:它是我们使用最多功能最强大的表达式
(1).
${}它主要是用来获取对象的属性,包括级联属性,调用方法,还能使用一些内置的基本对象
- ${person.father.name} : 获取对象的级联属性
- ${countriesByCode.ES} :从 map中读取属性值
- ${personsByName[‘Stephen Zucchini’].age} : 如果从map获取元素,map的key值中包含空格,需要按照这样写
- ${personsArray[0].name}:从数组中获取属性,如果想获取对象的属性,也可以获取
- ${person.createCompleteName()}
${person.createCompleteNameWithSeparator(’-’)},我们可以使用 ${}来调用对象的方法,以及给方法传参数
==(2) ${} 可以使用内置的基本对象
- #ctx: the context object. 当前上下文对象
- #vars: the context variables. 当前上下文中的变量值
- #locale : the context locale. 区域信息
- #request : (only in Web Contexts) the HttpServletRequest object.
- #response : (only in Web Contexts) the HttpServletResponse object.
- #session : (only in Web Contexts) the HttpSession object.
- #servletContext : (only in Web Contexts) the ServletContext object.
- 示例代码: < span th:text="${#locale.country}">US< /span>
- 获取 请求参数中的值
${param.foo} // Retrieves a String[] with the values of request parameter ‘foo’
${param.size()}
${param.isEmpty()}
${param.containsKey(‘foo’)}
(3) ${}还可以使用内置的工具对象
- #execInfo : information about the template being processed.
- #messages : methods for obtaining externalized messages inside variables expressions, in the same way as they would be obtained using #{…} syntax.
- #uris : methods for escaping parts of URLs/URIs
- #conversions : methods for executing the configured conversion service (if any).
- #dates : methods for java.util.Date objects: formatting, component extraction, etc.
- #calendars : analogous to #dates , but for java.util.Calendar objects.
- #numbers : methods for formatting numeric objects.
- #strings : methods for String objects: contains, startsWith, prepending/appending, etc.
- #objects : methods for objects in general.
- #bools : methods for boolean evaluation.
- #arrays : methods for arrays.
- #lists : methods for lists.
- #sets : methods for sets.
- #maps : methods for maps.
- #aggregates : methods for creating aggregates on arrays or collections.
- #ids : methods for dealing with id attributes that might be repeated (for example, as a result of an iteration).
*{}的功能:
*(1) {} 选择表达式
- 它和${}的功能是一样的,但是它还有一个补充功能,他可以配合 th:object来使用,代替当前div中的元素
div th:object="${session.user}"> <p>Name: < span th:text="*{firstName}">Sebastian< /span>.< /p> <p>Surname: < span th:text="*{lastName}">Pepper< /span>.< /p> <p>Nationality: < span th:text="*{nationality}">Saturn< /span>.< /p> </div>
- 1
- 2
- 3
- 4
- 5
- 上述代码中我们就可以在该div中使用*{}来代替${session.user},要配合th:object一起使用.
#{}的功能
它是用来获取国际化内容的
@{}的功能
他可以帮我们定义URL连接的
我们之前在jsp页面中的超链接中携带参数都是通过 链接?参数名参数值,现在使用Thymeleaf,使用th:href代替a:href,然后 ,所有需要在链接中携带的参数以key=value的方式,统一放到小括号内部,多个kv之间使用逗号分隔,其中value也可以使用${}的方式获取值,并且我们的超链接路径也可以简写,直接使用 / 来代替当前的项目路径例如 : 'http://localhost:8080/gtvg/order/details?orderId=3’替换之后写为
th:href="@{/order/details(orderId=${o.id})}"
<!-- Will produce 'http://localhost:8080/gtvg/order/details?orderId=3' (plus rewriting) -->
<a href="details.html"
th:href="@{http://localhost:8080/gtvg/order/details(orderId=${o.id})}">view</a>
<!-- Will produce '/gtvg/order/details?orderId=3' (plus rewriting) -->
<a href="details.html" th:href="@{/order/details(orderId=${o.id})}">view</a>
<!-- Will produce '/gtvg/order/3/details' (plus rewriting) -->
<a href="details.html" th:href="@{/order/{orderId}/details(orderId=${o.id})}">view</a>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
~{…} : 片段引用
Literals:字面量
Text literals: 'one text' , 'Another one!' ,…
Number literals: 0 , 34 , 3.0 , 12.3 ,…
Boolean literals: true , false
Null literal: null
Literal tokens: one , sometext , main ,…
Text operations:(文本操作)
String concatenation: +
Literal substitutions: |The name is ${name}|
Arithmetic operations:(数学运算)
Binary operators: + , - , * , / , %
Minus sign (unary operator): -
Boolean operations:(布尔运算)
Binary operators: and , or
Boolean negation (unary operator): ! , not
Comparisons and equality:(比较运算)
Comparators: > , < , >= , <= ( gt , lt , ge , le )
Equality operators: == , != ( eq , ne )
Conditional operators:(条件运算符,三元运算符)
If-then: (if) ? (then)
If-then-else: (if) ? (then) : (else)
Default: (value) ?: (defaultvalue)
Special tokens:三元运算如果成立或者不成立不想执行任何操作,写成_就行
Page 17 of 104No-Operation: _
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
1.高级写法,行内写法,[[]] ==== th:text(会转义特殊字符),[()] ===== th:utext (不会转义特殊字符)
2.th:text,写在那个标签上,那么每次遍历,都会生成一个该标签
4. SpringMVC的自动配置
Spring Boot 自动配置好了SpringMVC
以下是springboot对SpringMVC的默认配置:
- Inclusion of ContentNegotiatingViewResolver and BeanNameViewResolver beans.
- 自动配置了ViewResolver(作用:根据我们处理器方法的返回值,得到视图对象view,视图对象决定如何渲染页面,是转发?重定向)
- ContentNegotiatingViewResolver 组合所有的视图解析器,
- 如何定制ViewResolver?
- 我们可以给容器中添加一个ViewResolver,然后这个ContentNegotiatingViewResolver 就会自动的将它组合进来
- Support for serving static resources, including support for WebJars (covered later in this document)).静态资源文件夹李静,webjars
- Static index.html support. 静态首页访问
- Custom Favicon support (covered later in this document). favicon.ico,项目图标
- 自动注册了 Converter, GenericConverter, and Formatter beans.
- converter : 转换器,作用就是:类型转换使用(前台提交的是数据都是文本,那么在后台需要转换成int,string等类型),就需要使用converter
- Formatter : 格式化器,他可以帮我们把字符串按照我们指定的格式转换为日期类型,例如 : 2017-12-12 >>> Date类型
@Bean
@ConditionalOnProperty(prefix = "spring.mvc", name = "date-format")//在文件中配置日期格式化的规则
public Formatter<Date> dateFormatter() {
return new DateFormatter(this.mvcProperties.getDateFormat());//日期格式化组件
}
- 1
- 2
- 3
- 4
- 5
@Override
public void addFormatters(FormatterRegistry registry) {
for (Converter<?, ?> converter : getBeansOfType(Converter.class)) {
registry.addConverter(converter);
}
for (GenericConverter converter : getBeansOfType(GenericConverter.class)) {
registry.addConverter(converter);
}
for (Formatter<?> formatter : getBeansOfType(Formatter.class)) {
registry.addFormatter(formatter);
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
上面这个代码,告诉我们,如果我们想要添加自己的格式化器和转换器,那么,我们只需要将他们放入到容器中即可,它会自己把容器中所有的转换器和格式化器扫描并且添加.
- Support for HttpMessageConverters (covered later in this document).
- HttpMessageConverters: http消息转化器,它是SpringMVC用来转换请求和响应的,例如,我们其中一个处理器方法的返回值是user对象,我们想以json的格式写出去,那么我们就需要一个消息转换器.
- HttpMessageConverters 是从容器中确定值的,是获取容器中所有的HttpMessageConverters
- 所以,还是那句话,如果我们自己要写一个HttpMessageConverters ,我们只需要把它放入容器即可,你可以(@Bean,@Component等,将组件放入容器中)
- Automatic registration of MessageCodesResolver (covered later in this document).
- 定义错误代码生成规则的
- Automatic use of a ConfigurableWebBindingInitializer bean (covered later in this document).
- 它的作用就是初始化 webdatabinder 把请求体中携带的数据,绑定封装到javabean中,我们可以给容器中添加一个自定义的ConfigurableWebBindingInitializer ,来替换默认的
@Override
protected ConfigurableWebBindingInitializer getConfigurableWebBindingInitializer() {
//获取容器中的ConfigurableWebBindingInitializer
try {
return this.beanFactory.getBean(ConfigurableWebBindingInitializer.class);
}
catch (NoSuchBeanDefinitionException ex) {
//如果获取失败,会调用父类中的这个方法 ,它的功能就是初始化webdatabinder
return super.getConfigurableWebBindingInitializer();
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
If you want to keep Spring Boot MVC features and you want to add additional MVC configuration (interceptors, formatters, view controllers, and other features), you can add your own @Configuration class of type WebMvcConfigurer but without @EnableWebMvc. If you wish to provide custom instances of RequestMappingHandlerMapping, RequestMappingHandlerAdapter, or ExceptionHandlerExceptionResolver, you can declare a WebMvcRegistrationsAdapter instance to provide such components.
If you want to take complete control of Spring MVC, you can add your own @Configuration annotated with @EnableWebMvc
2 扩展springMVC
如果我们在实际开发过程中,springboot提供的默认的自动配置满足不了实际的开发需求,我们需要自己定义,或者是扩展自动配置,怎么办?
1.编写一个配置类(@Configuration),这个配置类的类型必须是WebMvcConfigurer ,并且不能标注@EnableWebMvc注解
特点:既保留了所有的自动配置,也能用我们的拓展配置
//使用webMvcConfigurer来扩展SpringMVC,它内部有很多的空方法,我们可以通过实现来扩展某个功能
@Configuration
public class MyMvcConfig implements WebMvcConfigurer {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
//发送 /atguigu 请求,能够访问success页面
registry.addViewController("/atguigu").setViewName("success");
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
5.如何来修改springboot的默认配置
模式:
1)、SpringBoot在自动配置很多组件的时候,先看容器中有没有用户自己配置的(@Bean、@Component)如果有就用用户配置的,如果没有,才自动配置;如果有些组件可以有多个(ViewResolver)将用户配置的和自己默认的组合起来;
2)、在SpringBoot中会有非常多的xxxConfigurer帮助我们进行扩展配置
3)、在SpringBoot中会有很多的xxxCustomizer帮助我们进行定制配置