Day16 SpringBoot 3 全局配置文件如何生效 静态资源 thymeleaf简述
uwupu 啦啦啦啦啦

自动配置原理:全局配置文件如何生效

@Conditional派生注解

当@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存在指定项

自动装配

以WebMvc为例

当SpringBoot启动时,会扫描需要自动配置的类,然后加载其配置类,使用全局配置文件覆盖需要替换的配置。

  • 自动配置类:xxxAutoConfiguration
  • 对应的配置类:xxxProperties
  1. SpringBoot为多个类提供了自动配置类,名为xxxAutoConfiguration,比如:WebMvcAutoConfiguration;

  2. 自动配置类中的注解。

    1
    2
    3
    4
    5
    6
    7
    @AutoConfiguration(after = { DispatcherServletAutoConfiguration.class, TaskExecutionAutoConfiguration.class,
    ValidationAutoConfiguration.class })//@AutoConfiguration表示这是一个自动配置类,这个注解的下面还有@Configuration注解表示这是一个Spring配置类。
    @ConditionalOnWebApplication(type = Type.SERVLET)//Conditional注解用于表示注解是否需要加载,这里表示如果项目为WebApplication。
    @ConditionalOnClass({ Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class })//若存在以上几个类
    @ConditionalOnMissingBean(WebMvcConfigurationSupport.class)//容器中不存在指定Bean
    @AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)
    public class WebMvcAutoConfiguration {...}

    自动配置类中Conditional注解判断自动配置类是否需要加载。

    1
    @EnableConfigurationProperties({ WebMvcProperties.class, WebProperties.class })

    其中@EnableConfigurationProperties注解指向需要使用的配置类。这里是:WebMVCProperties和WebProperties。

  3. xxxProperties类中配置了自动配置类的默认值,并调用@ConfigurationProperties允许在全局配置文件中修改默认值。

    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
    //WebMvcProperties类
    @ConfigurationProperties(prefix = "spring.mvc")//可以在配置文件中使用spring.mvc前缀修改其中的值
    public class WebMvcProperties {

    /**
    * Formatting strategy for message codes. For instance, 'PREFIX_ERROR_CODE'.
    */
    private DefaultMessageCodesResolver.Format messageCodesResolverFormat;

    private final Format format = new Format();//下面测试这个对象的值
    ...
    }

    //Format类
    public static class Format {

    /**
    * Date format to use, for example 'dd/MM/yyyy'.
    */
    private String date;

    /**
    * Time format to use, for example 'HH:mm:ss'.
    */
    private String time;
    ...
    }

    比如像这样:

    1
    2
    3
    4
    5
    spring:
    mvc:
    format:
    date:
    time:

SpringBoot的调试

在配置文件中添加debug=true打开调试模式。

1
2
# 可以通过debug查看哪些自动配置类生效,哪些没有生效
debug: true

调试输出内容示例

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


============================
CONDITIONS EVALUATION REPORT
============================


Positive matches: //匹配并加载的类
-----------------
// 这里省略很多
WebMvcAutoConfiguration matched:
- @ConditionalOnClass found required classes 'javax.servlet.Servlet', 'org.springframework.web.servlet.DispatcherServlet', 'org.springframework.web.servlet.config.annotation.WebMvcConfigurer' (OnClassCondition)
- found 'session' scope (OnWebApplicationCondition)
- @ConditionalOnMissingBean (types: org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport; SearchStrategy: all) did not find any beans (OnBeanCondition)

WebMvcAutoConfiguration#formContentFilter matched:
- @ConditionalOnProperty (spring.mvc.formcontent.filter.enabled) matched (OnPropertyCondition)
- @ConditionalOnMissingBean (types: org.springframework.web.filter.FormContentFilter; SearchStrategy: all) did not find any beans (OnBeanCondition)

WebMvcAutoConfiguration.EnableWebMvcConfiguration#flashMapManager matched:
- @ConditionalOnMissingBean (names: flashMapManager; SearchStrategy: all) did not find any beans (OnBeanCondition)
// 这里省略很多

Negative matches: //不匹配不加载的类
-----------------
// 这里省略很多
WebSessionIdResolverAutoConfiguration:
Did not match:
- @ConditionalOnClass did not find required class 'reactor.core.publisher.Mono' (OnClassCondition)

WebSocketMessagingAutoConfiguration:
Did not match:
- @ConditionalOnClass did not find required class 'org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer' (OnClassCondition)

WebSocketReactiveAutoConfiguration:
Did not match:
- @ConditionalOnWebApplication did not find reactive web application classes (OnWebApplicationCondition)
// 这里省略很多

Exclusions: //排除的类
-----------

None


Unconditional classes: //不满足条件的类
----------------------

org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration

org.springframework.boot.autoconfigure.context.LifecycleAutoConfiguration

org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration

org.springframework.boot.autoconfigure.availability.ApplicationAvailabilityAutoConfiguration

org.springframework.boot.autoconfigure.info.ProjectInfoAutoConfiguration


SpringBoot Web开发 资源路径

静态资源

静态资源放在哪里

静态资源可以放在以下几个目录:

  1. 自定义的优先级最高
  2. classpath:/META-INF/resources/
  3. classpath:/resources/
  4. classpath:/static/ (默认)
  5. classpath:/public/

在templates下的页面只能通过controller来跳转。

这几个目录会直接引用到页面的根目录。优先级依据上面的顺序执行。

(很少使用webjars)/webjars/**指向由webjars依赖引入的静态文件,如:http://127.0.0.1:8080/webjars/jquery/3.4.1/jquery.js

自定义静态资源位置

1
2
3
4
5
6
7
8
9
spring:
mvc:
static-path-pattern: /res/**
# 映射路径,静态资源url访问时使用的路径
web:
resources:
static-locations:
[ classpath:/yn/]
# 静态资源放哪里

静态资源访问url:使用spring.mvc.static-path-pattern来配置,如:/res/**,访问时使用http://127.0.0.1:8080/res/1.js

静态资源放在哪里:使用spring.web.resources.static-locations来配置,这里要写数组。如:[ classpath:/yn/],资源要放在以下位置。

image

源码解释

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public void addResourceHandlers(ResourceHandlerRegistry registry) {
if (!this.resourceProperties.isAddMappings()) {
logger.debug("Default resource handling disabled");
return;
}
addResourceHandler(registry, "/webjars/**", "classpath:/META-INF/resources/webjars/");//webjars 通过依赖添加jquery
addResourceHandler(registry, this.mvcProperties.getStaticPathPattern(), (registration) -> {
registration.addResourceLocations(this.resourceProperties.getStaticLocations());
if (this.servletContext != null) {
ServletContextResource resource = new ServletContextResource(this.servletContext, SERVLET_LOCATION);
registration.addResourceLocations(resource);
}
});
}

webjars

很少使用webjars

使用依赖可以加入一些js文件。

image

1
addResourceHandler(registry, "/webjars/**", "classpath:/META-INF/resources/webjars/");

代码里指向的就是webjars依赖的位置。这里添加的是jquery.js。

首页配置

默认位于资源目录下的index.html。

可以使用controller自定义。

源码解释

1
2
3
4
5
6
7
8
9
10
11
private Resource getIndexHtml(Resource location) {
try {
Resource resource = location.createRelative("index.html");
if (resource.exists() && (resource.getURL() != null)) {
return resource;
}
}
catch (Exception ex) {
}
return null;
}

通过这个方法得知,项目的首页默认指向为static下的index.html。

图标配置

放在静态资源目录下就可。默认:/favicon.ico

thymeleaf模板引擎

thymeleaf是Java模板引擎之一。

依赖

SpringBoot里使用依赖spring-boot-starter-thymeleaf。

1
2
3
4
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>

thymeleaf官方的依赖使用

Module Group ID Artifact ID
Core library org.thymeleaf thymeleaf
Spring 3 integration org.thymeleaf thymeleaf-spring3
Spring 4 integration org.thymeleaf thymeleaf-spring4
Spring 5 integration org.thymeleaf thymeleaf-spring5
Testing library org.thymeleaf thymeleaf-testing
Spring Security 3 integration org.thymeleaf.extras thymeleaf-extras-springsecurity3
Spring Security 4 integration org.thymeleaf.extras thymeleaf-extras-springsecurity4
Java 8 Time API compatibility org.thymeleaf.extras thymeleaf-extras-java8time
Tiles 2 integration org.thymeleaf.extras thymeleaf-extras-tiles2
IE Conditional Comments support org.thymeleaf.extras thymeleaf-extras-conditionalcomments

使用

第一个程序

  1. 在templates创建html文件;

    image

  2. 在controller中返回字符串为html文件的文件名,不包含后缀。

1
2
3
4
5
6
7
8
9
@Controller
public class IndexController {

@RequestMapping("/test")
public String index(){
return "test";
}

}
  1. 测试运行

image

html中使用

  1. 需要头<html xmlns:th="http://www.thymeleaf.org">

  2. 在页面中使用表达式。表达式格式为th:abc="xxx",abc表示内容类型,xxx表示内容。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    <!DOCTYPE html>
    <html lang="en" xmlns:th="http://www.thymeleaf.org">
    <head>
    <meta charset="UTF-8">
    <title>一个Templates页面</title>
    </head>
    <body>
    一个Templates页面
    <!--所有的html元素可以被thymeleaf替换接管: th:元素名-->
    <div th:text="${msg}"></div>
    </body>
    </html>

    text为内容,${msg}为引入的值。

  3. 在Controller中设置msg的值。

1
2
3
4
5
@RequestMapping("/test")
public String index(Model model){
model.addAttribute("msg","这是一个msg。");
return "test";
}
  1. 测试运行

    image

作用

image

可以使用表达式在页面中显示内容。

源码

1
2
3
4
5
6
7
8
9
10
@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. 在配置中使用spring.thymeleaf前缀;
  2. 默认模板前缀:classpath:/templates/
  3. 默认模板后缀:.html

thymeleaf模板引擎语法

官方文档:https://www.thymeleaf.org/doc/tutorials/3.0/

Springboot中的一些配置

1
2
# 关闭模板引擎的缓存
spring.thymeleaf.cache=false

一些表达式

  • Simple expressions:
    • Variable Expressions: ${...} //普通变量
    • Selection Variable Expressions: *{...}
    • Message Expressions: #{...} //国际化消息表达式
    • Link URL Expressions: @{...} //表示URL
    • Fragment Expressions: ~{...} //片段表达式,可以提取网页片段作为一个模块,然后在另一个网页插入。
  • Literals
    • Text literals: 'one text', 'Another one!',… //文本使用单引号,不能使用双引导
    • Number literals: 0, 34, 3.0, 12.3,… //数字直接写
    • Boolean literals: true, false //布尔值直接写
    • Null literal: null //null直接写
    • Literal tokens: one, sometext, main,… //字符串文本,如果内容没有一些影响语法的符号(空格,符号),就可以直接写。
      • 比如<div th:class="'content'">...</div>可以用<div th:class="content">...</div>表达。
  • 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) //用这个代替前端的if-else
    • Default: (value) ?: (defaultvalue)
  • Special tokens:
    • No-Operation: _

一些属性

这是一些thymeleaf的属性,优先级从上到下依次降低。

Order Feature Attributes 功能
1 Fragment inclusion th:insert th:replace 包含,类似include
2 Fragment iteration th:each 循环
3 Conditional evaluation th:if th:unless th:switch th:case 判断
4 Local variable definition th:object th:with
5 General attribute modification th:attr th:attrprepend th:attrappend
6 Specific attribute modification th:value th:href th:src ... 属性
7 Text (tag body modification) th:text th:utext th:text 不转义字符
th:utext转义字符
8 Fragment specification th:fragment 提取公共页面
9 Fragment removal th:remove

运算符的使用

1
<button th:href="@{/emp/delete/}+${emp.getId()}">删除</button>

一些方法的使用

dates.format

可以格式化Date对象的格式。

使用

1
<td th:text="${#dates.format(emp.getBirth(),'yyyy-MM-dd HH:mm:ss')}">1,001</td>

用”#”修饰,然后参数输入Date对象和格式’yyyy-MM-dd HH:mm:ss’。

一些属性的使用

th:text,th:utext

th:text下的字符不会进行html转义,th:utext下的字符会进行html转义。

th:text有另一种写法(这个写法一般不用)

1
<div>[[ ${msg} ]]</div><!-- 这个写法一般不用 -->

代码

1
2
3
<div th:text="${msg}"></div>
<div>[[ ${msg} ]]</div><!-- 这个写法一般不用 -->
<div th:utext="${msg}"></div>
1
2
3
4
5
@RequestMapping("/test")
public String index(Model model){
model.addAttribute("msg","<h1>这是一个msg。</h1>");
return "test";
}

运行

image

th:each

可以用来遍历一个数组,使用方法:

  1. 在属性中添加th:each="user:{$users}"

    1
    <h3 th:each="user:${users}"></h3>

    遍历users,类似for(User user : users){}

  2. 通过另一个属性值使用这个值th:text="${user}"

    1
    <h3 th:each="user:${users}" th:text="${user}"></h3>
  3. 会出现多个元素,分别对应遍历的一个效果,表现如下:

    1
    2
    3
    <h3>张三</h3>
    <h3>李四</h3>
    <h3>王五</h3>

代码

Controller中

1
2
3
4
5
6
7
8
@Controller
public class thyleaf_for_each {
@RequestMapping("/test2")
public String test2(Model model){
model.addAttribute("users", Arrays.asList("zhangsan","lisi","wangwu"));
return "test2";
}
}

html中

1
<h3 th:each="user:${users}" th:text="${user}"></h3>

运行结果

image

th:href 和 @{/js/jquery.js}

  • th:href修改html元素中的href属性值

  • 可以根据server.servlet.context-path的配置自动在链接前添加内容。

  • @{/js/jquery.js}一般用在表示链接

如果链接是根目录(/qwe.js)

1
2
<link th:href="@{/css/bootstrap.min.css}" rel="stylesheet">
<link th:href="@{/css/signin.css}" rel="stylesheet">

在springBoot配置中添加servlet目录

1
server.servlet.context-path=/yn

在HTML中的表现

1
2
<link href="/yn/css/bootstrap.min.css" rel="stylesheet">
<link href="/yn/css/signin.css" rel="stylesheet">

如果链接是相对目录(qwe.js)

1
2
<link th:href="@{css/bootstrap.min.css}" rel="stylesheet">
<link th:href="@{css/signin.css}" rel="stylesheet">

表现效果:

1
2
<link href="css/bootstrap.min.css" rel="stylesheet">
<link href="css/signin.css" rel="stylesheet">

若设置server.servlet.context-path后,这个表现效果不会改变。

th:fragment,th:insert,th:replace

分别代表:声明当前区域 插入 替换

th:fragment

th:fragment放在html元素属性中,用于为当前元素声明一个名字。

1
2
<!-- common.html -->
<nav th:fragment="header">asd</nav>

声明时不需要~{…}表达式括住。

th:insert

th:insert也放在元素属性中,会将指定的fragment插入到元素中。

1
2
<!-- qwe.html -->
<div th:insert="~{common::header}"></div>

引用时需要使用~{…}表达式括住。内容为“模板名::元素声明的名字”

表现效果

1
2
3
<div>
<nav>asd</nav>
</div>

th:replace

th:replace将使用指定的fragment替代原来的元素。

1
2
<!-- asd.html -->
<div th:replace="~{common::header}"></div>

表现效果

1
<nav>asd</nav><!-- 原本的div被替代了 -->

传递参数

在insert和replace后面,可以直接加括号传参。

格式:(参数名=’值’)

使用:

对于th:fragment

1
2
3
4
5
<!-- commons.html 公共页面 -->
<div th:fragment="sidebar"><!-- 侧边栏 -->
<a th:class="${action=='index'?'active':'inactive'}">主页</a>
<a th:class="${action=='list'?'active':'inactive'}">员工管理页面</a>
</div>

对于th:replace

1
2
3
4
5
6
7
8
<!-- list.html 假定:员工管理页面 -->
<div th:replace="~{commons::sidebar(action='list')}"></div>

<!-- 表现效果 -->
<div>
<a th:class="inactive">主页</a>
<a th:class="active">员工管理页面</a>
</div>
1
2
3
4
5
6
7
8
<!-- list.html 假定:主页 -->
<div th:replace="~{commons::sidebar(action='index')}"></div>

<!-- 表现效果 -->
<div>
<a th:class="active">主页</a>
<a th:class="inactive">员工管理页面</a>
</div>

使用场景

这个一般用于多个页面有公共部分时,可以将公共部分放在同一个页面中,然后在别的页面只需要引用就可以了。

image

 评论