本文介绍SpringBoot框架。
1. 概述
前面几篇文章介绍了经典的框架Spring、SpringMVC以及MyBatis。那么为什么还会出现SpringBoot呢,之前的框架有什么缺点,SpringBoot做了哪些提升呢?
前面Spring和SpringMVC框架有以下特点:
都有容器的概念,在创建对象以及其他配置时,有大量的配置文件。而且大型项目需要很多的对象,所以配置文件有很多的标签等等。所以,大量的配置文件以及大量的标签,使得我们在使用时容易出错。
另外,Spring在集成第三方框架时也会需要配置文件。所以在集成外部框架的时候,也需要手动创建多个配置文件;并且需要掌握该框架的具体用法。
还有,事务等等都需要配置文件。
总体上说,Spring框架随着项目的增大,配置文件越来越臃肿和复杂,集成其他框架也不是很流畅。因此,开发了SpringBoot框架,该框架可以使得我们少写配置文件,尽快进入开发业务功能阶段。他可以把大多数框架以及第三方资源已经写好的配置,直接集成到项目中使用。SpringBoot可以实现Spring和SpringMVC的功能,而不再写太多的配置文件。常用的配置文件以及第三方库已经由该框架设置好了。SpringBoot就相当于是不需要配置文件的Spring以及MVC。
2. Xml和JavaConfig
2.1 什么是JavaConfig
通过Spring发现,xml是作为配置文件的形式出现的。主要用于集成一些框架,以及声明对象。而SpringBoot主要采用注解来创建对象,而不再使用配置文件。JavaConfig则是基于注解的具体实现方式。
JavaConfig是Spring提供的使用Java类来配置容器,这是配置Spring IOC容器的纯Java方法,即采用Java类来配置容器,替代原来的xml文件;在Java类中可以创建对象,将该对象放到Spring容器中,即注入到容器中。具有以下优点:
- 可以使用面向对象的方式,一个配置类可以继承配置类,可以重写方法。
- 避免繁琐的xml配置。
两种方法简单对比案例如下所示:
- 以原始xml配置文件方式创建对象。创建maven项目,添加spring-context依赖以及junit依赖。之后,创建实体类,创建xml配置文件(Spring)并在其中用bean标签声明实体类对象。并在测试类中,手动创建容器对象,并获取实体类对象。
- 以JavaConfig方式创建对象。创建maven项目,添加spring-context依赖以及junit依赖。创建实体类,创建JavaConfig类(用@Configuration注解修饰),在类中创建方法,方法中和Java编程一样,创建要注入的对象,方法的返回值是该对象,在类上面用@Bean修饰。并在测试类中,手动创建容器对象,并获取实体类对象。
可以看到,其实两者没什么区别,只是以不同的形式体现而已。JavaConfig则是以Java方式来做的,xml则是以配置文件方式来做的。
2.2 Xml配置容器
要声明的对象类,如实体类如下所示:
1 | public class Student { |
xml配置文件中声明对象。
1 | <!-- 声明bean对象 --> |
测试类如下所示:
1 |
|
2.3 JavaConfig配置容器
注意,使用JavaConfig来配置容器,需要以下两个注解支持:
- @Configuration:放在类的上面,表示这个类作为配置文件使用;
- @Bean:用来声明对象,将该对象注入到容器中;
例子如下所示:
1 | // 这是一个类文件,可以有多个类 |
注意,手动创建容器对象的时候,不再采用原始的ClassPathXmlApplicationContext
方式,而是采用AnnotationConfigApplicationContext
方式,参数为具体方法的反射类型Class类型。测试类如下所示:
1 | /** |
2.4 @ImportResource注解
该注解是用来导入现有的xml配置,等同于xml文件的resources标签。比如想要使用JavaCofnig做容器配置,但是现在仍存在xml配置文件。后续只能创建一个容器,而创建容器的方法只能有一种,所以要么是JavaConfig,要么是xml。
如果采用JavaConfig,那么就需要在该文件中导入已有的xml。即采用@ImportResource注解。该注解用在JavaConfig类上面使用,有value和locations两个属性,都表明xml文件的位置,也就是类路径。此时,xml文件中的bean对象就被放入了容器中。
xml配置文件applicationContext.xml
中的bean标签如下所示:
1 | <bean id="myCat" class="org.hianian.entity.Cat"> |
JavaConfig中的注解如下所示:
1 |
|
2.5 @PropertyResource注解
除了xml配置文件,还有一些属性配置文件.properties
,如数据库url密码等等。读取这些文件用于创建bean对象时给属性赋值。此时就需要读取属性配置文件,获取属性值并赋值。
该注解的用法如下:
- 在JavaConfig类上面使用@PropertyResource注解引入properties文件的位置。(这部分操作是使得JavaConfig读取到属性配置文件)
- 使用@Component注解和@Value(value=”${key}”)的方式获取properties文件中的key的属性值。(这部分操作是在实体类中,即以注解形式创建实体类对象。注意,此时还没创建,需要在容器中创建)
- 在JavaConfig类中,告诉容器,提取以注解形式创建的对象。即xml中的组件扫描器标签,此时可用@ComponentScan()注解来扫描。即扫描要创建的类。
为了简单起见,这里设置简单的配置文件,(这种方式似乎只能采用注解的形式创建对象),以注解形式给实体类对象赋值。案例如下所示:
属性配置文件config.properties
如下所示:
1 | tigerName=dongbei |
实体类如下所示(因为采用注解方式创建对象,所以添加了注解):
1 |
|
JavaConfig如下所示:
1 |
|
测试类如下所示:
1 |
|
2.6 小节
实际上,原始xml配置文件创建对象有两种方式,一种是bean标签,一种是注解并在xml文件中使用组件扫描器;上面前2个例子中介绍的是如何将xml中标签形式创建的对象替换为JavaConfig形式创建,以及采用@ImportResource注解形式将xml以bean标签创建的对象引入到JavaConfig中。
那么以注解形式创建对象,是在Java类中操作的,因此JavaConfig不需要对其转换。对于组件扫描器扫描,xml中的组件扫描器是标签,在JavaConfig中对应的注解是@ComponentScan(basePackages = "org.hianian.entity")
。另外,xml中需要指明属性文件的位置,此时在JavaConfig中指明位置采用的是@PropertySource(value = "classpath:config.properties")
标签。
总体上说,JavaConfig提供了和xml配置文件相同的功能,即以类、方法和注解
的形式等同于xml以及其标签
。也就是说,JavaConfig仍然有两种方式创建对象:方法、注解。
3. SpringBoot入门
SpringBoot作为Spring全家桶的一员,可在官网Spring | Home中找到。有以下几个特点:
Create stand-alone Spring applications
创建独立的Spring应用。
Embed Tomcat, Jetty or Undertow directly (no need to deploy WAR files)
内嵌常用的服务器(默认是Tomcat)。
Provide opinionated ‘starter’ dependencies to simplify your build configuration
提供一个starter起步依赖,简化应用的配置。比如MyBatis等框架的依赖,这些基本的配置不再手动配置,只需要配置起步依赖即可。
Automatically configure Spring and 3rd party libraries whenever possible
尽可能地自动配置Spring和第三方库用到的对象。
Provide production-ready features such as metrics, health checks, and externalized configuration
提供了统计、健康检查、外部配置等功能。
Absolutely no code generation and no requirement for XML configuration
不用生成代码,不用使用xml配置文件。
即可以简化Spring、SpringMVC的使用,核心还是IOC容器。
SpringBoot的参考文档:Spring Boot Reference Documentation;API为:Overview (Spring Boot 2.6.7 API)
3.1 SpringBoot项目的创建方式
SpringBoot项目有两种创建方式,一种是SpringBoot提供的初始化器创建(需要联网),一种是使用Maven来创建。
3.1.1 使用SpringBoot的初始化器创建项目
在IDEA中新建Module,左侧选择类别,在maven上面有一个Spring Initializer,这就是初始化器。有两个需要设置,一个是JDK,一个是URL,这个URL默认就是Spring官方提供的网址,该网址会引导创建基于SpringBoot的项目。(也可采用国内的镜像网址:http://start.springboot.io,或者在浏览器中输入该网址,手动生成并下载压缩包,然后导入开发工具中。)
点击Next,此处设置模块的相关信息,如项目坐标。下面四个就是在pom文件中的描述信息。
配置好信息后,点击Next,此处设置项目所需的依赖项(以前都是手动在pom文件中写坐标),这步其实就是上面说的自动配置。左侧第一栏就是依赖的类别,比如开发工具、Web项目的、SQL的、NoSQL的等等。(由于这是案例,所以可以不选,这里简单选了Web中的Spring Web,也就是SpringMVC,简单看一下)
点击Next,此处即设置模块的位置。点击Finish,自动下载(注意,下面两个路径是本项目的存储路径,需要再设置一个子文件夹)。
创建好后的目录结构如下所示:
其中
.gitignore
文件是Git的一个文件;(不需要过多关注).iml
文件是IDEA的一个文件;(不需要过多关注)HELP.md
是一个描述帮助文件;(不需要过多关注).mvn
文件夹、mvnw
以及mvnw.cmd
是和maven有关的三个文件,相当于maven增强版的一个工具,和maven作用类似。pom.xml
文件则是负责maven依赖的文件。
其中3和4中的文件,实际上是不需要的,可以删掉。
接下来就是src目录
了,和Spring类似,也分为main和test子目录。
main
- Java:项目程序源码
- resources
- static:存放静态资源:css、js、图片等等;
- templates:存放模板文件,类似jsp,用于显示数据;
- application.properties:SpringBoot重要的配置文件;
test
测试
查看pom.xml文件,可以发现有如下配置:
1 |
|
查看maven依赖,可看到有相关的starter初始化器,并将常用的依赖spring等自动加入了。
3.1.2 使用maven创建项目
上面的方式需要使用网络,其实SpringBoot项目和普通的maven项目没什么区别。主要有以下两点区别:
SpringBoot必须有父项目
1
2
3
4
5
6<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.6.8</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>resources资源目录下有三个文件(夹):static、templates、application.properties。
因此,这两个区别我们可以手动实现。如果需要用到其他起步依赖,再手动添加即可,比如web依赖等等。
3.1.3 SpringBoot简单案例
这里使用SpringBoot开发web小案例。
首先采用上面任意一种方式创建SpringBoot项目。之后,在Java目录下创建Controller,代码如下所示:
1 |
|
(如果采用自带初始化器创建的项目会自带一个Application的Java类,该类由@SpringBootApplication注解修饰。如果采用maven创建,则需要手动创建一下该类。类名可自定义,但是无论是web项目还是其他项目,都是通过该类中的main方法来启动的。)在Application类文件中,运行main方法,启动本项目。代码如下所示:
1 |
|
启动界面如下所示:
在详细信息里面可看到:: Tomcat started on port(s): 8080 (http) with context path ''
,即该项目没有根路径,直接在浏览器中的端口后面输入上面控制器中的路径即可。如下所示:
可以看到,什么中央调度器啊、容器啥的都不再需要手动配置了,SpringBoot已经将这些内置好了。大大简化了项目的开发的前期准备过程,使得可以更加专注业务功能的实现。
3.1.4 @SpringBootApplication注解
通过案例可以看到,SpringBoot的核心似乎就是这个@SpringBootApplication注解,通过这个注解修饰的类中的主方法来启动整个项目。查看该注解的详细信息,如下所示:
1 |
|
上面的元注解的作用分别如下:
- @Target({ElementType.TYPE}):表明SpringBootApplication注解修饰的位置
- @Retention(RetentionPolicy.RUNTIME):表明该注解保存的位置
- @Documented:表明该注解可被Javadoc生成文档
- @Inherited:表明该注解可继承
其余的三个注解是本注解的核心元注解,本注解是一个复合注解,由这三个注解完成:
@SpringBootConfiguration
点击进去,可发现该注解有一个@Configuration元注解修饰,即本注解修饰的类可当做配置文件使用。(那么本注解修饰的子注解(@SpringBootApplication)修饰的类Application当然也可当做配置文件使用,和JavaConfig一样,可@Bean创建对象放入到容器中。)
@EnableAutoConfiguration
启用自动配置,即自动把常用的Java对象配置好,注入到Spring容器中。例如可以把MyBatis创建好放入到容器中。
@ComponentScan
组件扫描器,即找到注解,根据注解的功能创建对象(如@Component),给属性赋值(如@Value)等等。注意,仔细观察可发现,在2.5的例子中,用@Component创建对象需要用到组件扫描器;但是在3.1.3中,用@Controller创建对象却没有用组件扫描器。
其实组件扫描器有一个默认规则:自动扫描所在类的所在包以及其子包中所有的类。即在3.2.3中,因为@SpringBootApplication注解包含了组件扫描器,并且所在类是在项目的主包中,且@Controller修饰类在子包中。因此此处不需要显式设置组件扫描器也可,因此可找到@Controller并创建对象。
总之,使用@SpringBootApplication有很多个作用,首先当前类可作为配置文件使用;另外可启动组件扫描器扫描当前类所在包以及子包中的所有类(创建对象);启动自动配置,把常见的框架所需的对象都自动创建好,放入容器中。
3.2 SpringBoot核心配置文件
application.properties
文件是SpringBoot的核心配置文件。这种文件其实也可以是.yml
结尾的文件。这两种格式文件在内容上是一致的,仅仅是格式上有区别。最开始使用的是.properties
格式,现在主推的是.yml
格式。
注意,当两个格式的配置文件同时存在时,使用的是properties配置文件。注意,名称必须为Application。
可以在该文件中设置项目的根路径以及开放的端口号。
3.2.1 properties配置文件
该文件中的格式是(k=v),#
为注释。设置是上下文路径以及端口号如下所示:
1 | # 设置端口号 |
其实还可以设置其他属性,输入server.
之后可自动弹出很多个选项。
3.2.2 yml配置文件
yml是一种yaml格式的配置文件,主要采用一定的空格、换行等格式排版进行配置。yaml是一种直观的能够被计算机识别的数据序列化格式,容易被人类阅读,yaml类似于xml,但是语法比xml简洁很多,值与前面的冒号配置项必须要有一个空格,.yml
也可换成.yaml
。
该文件中的格式是(k: v)。注意,冒号后面有空格。这种格式的文件表达的内容比较清晰。和.properties
文件一样,在输入的时候,自动弹出并自动分级。
1 | server: |
3.2.3 多环境配置
在实际开发的过程中,我们的项目会经历很多的阶段(开发——>测试——>上线),每个阶段的配置也会不同,例如:端口、上下文路径、数据库url以及账号密码等等。那么这个时候为了方便在不同的环境之间切换,SpringBoot提供了多环境配置。
而SpringBoot的核心配置就是前面说的application.yml/properties
文件。因此可设置多个配置文件(每个配置文件对应一个环境),此时在不同的阶段使用不同的配置文件即可。这些配置文件的命名规则是:application-环境名称.yml/properties。如下所示:
1 | # 开发阶段的配置文件 application-dev.yml |
1 | # 测试阶段的配置文件 application-test.yml |
1 | # 线上运行阶段的配置文件 application-online.yml |
之后,再手动指定要读取的配置文件即可。因为默认会读取application.properties/yml中的内容,所以可在该文件中指定具体的配置文件。即使用spring.profiles.active=环境名称
,注意,这个环境名称就是上述配置文件中杠后面的内容。如下所示:
1 | # application.properties |
3.2.4 SpringBoot自定义配置
SpringBoot的核心配置文件中,除了使用内置的配置项之外,我们还可以在自定义变量,然后采用下面的注解来读取变量值。
3.2.4.1 @Value注解
正如2.5中例子所示,@Value注解可以读取.properties
文件中的key-value。因此@Value也可读取核心配置文件中的内容。application.properties文件中的内容如下所示:
1 | # 配置端口号以及项目路径 |
采用@Value
注解读取变量值。
1 |
|
3.2.4.2 @ConfigurationProperties注解
该注解用于将整个文件映射成一个Java对象,这时通过调用Java对象的属性即可获取该值。适用于自定义配置项比较多的情况。(因为如果仍然采用@Value方式的话,因为配置项比较多,就需要写多个@Value注解,容易出现错误。)
此时可设置一个Java类,类属性就是配置文件中想要读取的变量名,并设置set和get方法。在类上面加入@ConfigurationProperties注解,该注解有一个prefix前缀属性,表明本类中的属性用于匹配哪个前缀后面的子名。代码如下所示(配置文件中的内容仍然是上面的内容):
1 | // 设置前缀,即在配置文件中找company前缀的变量组,并依次赋值给同名属性 |
为了测试,这里采用@Component注解形式创建对象。在Controller中获取该对象,代码如下所示:
1 |
|
3.3 SpringBoot中使用JSP(了解)
其实使用SpringBoot框架的时候,不推荐使用JSP。该框架本身并不支持JSP,需要单独配置(底层SpringBoot使用的是内嵌的Tomcat,并不是原始的Tomcat)。框架提供了模板技术,即Thymeleaf模板作为视图使用。
使用JSP需要如下几个步骤:
加入一个处理jsp的依赖,负责编译jsp文件。
1
2
3
4<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
</dependency>可以不加版本,使用父依赖的版本。
如果需要使用servlet、jsp、jstl等其他功能,需要单独加入这些依赖。
创建一个用于存放jsp的目录,一般叫做webapp。在main目录下创建。
因为SpringBoot默认不支持JSP,所以创建的目录后,也不支持创建jsp文件。注意看下面目录中的图标:
此时需要将webapp目录设置为web资源目录。如下所示,选择file — > project structure,之后选择指定的模块,点击Web文件夹,在右侧中下方点击加号,选择添加的webapp目录即可。再次观看该目录图标,发生了变化:
在
pom.xml
文件中指定jsp文件的编译后的存放目录,为META-INF/resources
。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19<build>
<!-- 指定jsp编译后的存放目录 -->
<resources>
<resource>
<!-- jsp文件原来的目录 -->
<directory>src/main/webapp</directory>
<!-- 指定编译后的存放目录 -->
<targetPath>META-INF/resources</targetPath>
<!--指定要处理的文件,因为jsp文件不只存在于webapp下,还有可能存在于其子目录下-->
<!-- 即将该webapp目录下所有子文件都编译到resources下 -->
<includes>
<include>**/*.*</include>
</includes>
</resource>
</resources>
</build>创建Controller,访问JSP。
创建的JSP页面如下所示:
1
2
3
4
5
6
7
8
9<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h3>使用JSP显示Controller中的数据:${data}</h3>
</body>
</html>Controller如下所示:
1
2
3
4
5
6
7
8
9
10
11
12
13
public class JspController {
public String doJsp(HttpServletRequest request){
// 将数据放入到请求域中
request.setAttribute("data", "SpringBoot中使用Jsp");
// 返回视图的逻辑名称(底层会采用视图解析器来拼接的)
return "index";
}
}在
application.properties
文件中配置视图解析器。1
2
3
4# 配置视图解析器
# 此处的/表示 src/main/webapp
/ =
.jsp =
启动服务器,访问页面如下所示:
编译后的目录如下所示,可看到,确实在META-INF/resources下生成了JSP文件。
3.4 SpringBoot中使用ApplicationContext
SpringBoot也是基于容器的概念。在Spring以及SpringMVC中,创建容器有两种方式,一种是手动在Java程序中创建;一种是以标签形式,如监听器等等,在配置文件中创建。
那么在SpringBoot中怎么创建容器以及怎么使用容器呢?查看项目主程序,代码如下所示:
1 |
|
SpringApplication.run()
方法就是启动这个项目的代码,查看源码,run方法是SpringApplication类的静态方法,返回值是ConfigurableApplicationContext接口。如下所示:
1 | public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) { |
这个接口继承了ApplicationContext接口,也就是说,run方法返回值就是应用域对象,也就是容器。
1 | public interface ConfigurableApplicationContext extends ApplicationContext, Lifecycle, Closeable { |
可以看到,实际上项目启动的时候,就会自动创建容器。我们可以接收获取容器对象,也可以不获取。
3.5 CommandLineRunner接口
在开发中可能会有这样的场景,需要在容器启动后执行一些内容。比如读取配置文件、数据库连接之类的。SpringBoot给我们提供了两个接口来帮助我们实现这种需求。这两个接口分别为CommandLineRunner和ApplicationRunner。它们的执行时机为容器启动完成的时候,自动执行run方法。
这两个接口中有一个run方法,我们只需要实现这个方法即可。这两个接口的不同之处在于:ApplicationRunner中的run方法的参数为ApplicationArguments,而CommanLineRunner接口中run方法的参数为String数组。反编译后的源码如下所示:
1 | public interface CommandLineRunner { |
1 | public interface ApplicationRunner { |
这两个接口的效果是一样的,所以使用哪个接口都行,当我们需要在容器完成后执行一些自定义的操作时,可采用这两个接口来完成。
本质上说,该接口中的方法是被创建容器对象语句SpringApplication.run()
自动调用的,但是也是在创建容器对象之后调用的。所以说,本接口中的run方法是可以获取到容器中的对象的。代码如下所示:
1 | // 实现CommandLineRunner接口 |
结果如下所示,可以看到接口中的run方法中的内容,是比创建容器对象语句后的语句要先执行的。
4. SpringBoot和Web组件
上面只是讲解了SpringBoot的入门使用。因为SpringBoot是Spring和MVC的提升版,因此,可用于Web项目的开发。本节介绍SpringBoot中的Web组件使用。
4.1 SpringBoot中的拦截器
拦截器是SpringMVC中的一种对象,能拦截对Controller的请求。框架其实已经有了内置的拦截器,但是我们也可以自定义拦截器,实现对请求的预先处理。自定义拦截器有如下步骤:
- 自定义类实现SpringMVC框架中的HandlerInterceptor接口,有
preHandle
、postHandle
、afterCompletion
三个抽象方法。 - 将上述自定义类,创建对象,在SpringMVC的配置文件中声明该拦截器对象,以及声明要拦截的URL路径。
上面的步骤是SpringMVC中的步骤,其实在SpringBoot中也适用。只不过,SpringBoot将SpringMVC中的配置文件xml改为了WebMvcConfigurer接口,自定义配置类,实现该接口,并调用接口中的方法即可。(本质上还是SpringBoot用Java类文件来代替xml配置文件。)
WebMvcConfigurer接口的部分源码如下所示:
1 | public interface WebMvcConfigurer { |
SpringBoot中使用拦截器的步骤如下所示:
自定义拦截器类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20public class LoginInterceptor implements HandlerInterceptor {
// 实现该方法,即相当于拦截器
/**
*
* @param request
* @param response
* @param handler 被拦截的控制器对象
* @return 返回值是boolean类型,true表示能被Controller处理,即拦截器通过该请求。false表示拦截器将其过滤掉。
* @throws Exception
*/
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 为了简单起见,这里不进行业务处理,仅仅输出一句话,表示该方法执行了。
System.out.println("执行了LoginInterceptor的preHandle方法");
return true;
}
}创建拦截器对象,并设置其拦截路径以及不拦截路径等等。(此设置是在Java类配置文件中设置,即将其作为配置信息。注意,拦截器只能通过实现WebMvcConfigurer接口的配置类对象来添加到容器中)
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// 自定义类实现WebMvcConfigurer接口,相当于将拦截器添加到MVC容器中。
// 即原始SpringMVC中配置文件中的内容,均移动到了WebMvcConfigurer这个接口的方法中。
// 注意,这是类也是作为配置信息出现的,所以需要加Configuration注解,将本类作为配置文件,创建对象
public class MyAppConfig implements WebMvcConfigurer {
public void addInterceptors(InterceptorRegistry registry) {
// 创建拦截器对象
HandlerInterceptor handlerInterceptor = new LoginInterceptor();
// 将拦截器对象加入到注册器中,并声明要拦截的地址等等
InterceptorRegistration interceptorRegistration = registry.addInterceptor(handlerInterceptor);
String[] inter = {"/user/**"};
String[] exclude = {"/user/login"};
// 指定要拦截的地址
InterceptorRegistration interceptorRegistration1 = interceptorRegistration.addPathPatterns(inter);
// 指定不拦截的地址
InterceptorRegistration interceptorRegistration2 = interceptorRegistration1.excludePathPatterns(exclude);
}
}创建Controller,对上述拦截URL进行测试
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class BootController {
public String userAccount() {
return "访问user/**地址";
}
public String adminAccount() {
return "访问user/login地址";
}
}
其实,在SpringBoot中使用拦截器,和在SpringMVC中类似,只不过将拦截器声明到容器中的方式不同罢了。在SpringBoot中的思想就是以JavaConfig类文件代替配置文件。
4.2 SpringBoot中使用Servlet
本节讲解在SpringBoot框架中使用Servlet对象。步骤如下:
创建Servlet类,继承HttpServlet,实现业务方法
注册Servlet类对象,让框架能够找到Servlet。
注意,和拦截器一样,Servlet类对象也只能通过某个特殊的配置类对象添加到容器中。
代码如下所示:
自定义Servlet类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21public class MyServlet extends HttpServlet {
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doPost(req, resp);
}
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 通过response对象返回给浏览器一句话。
resp.setContentType("text/html;charset=utf-8");
PrintWriter writer = resp.getWriter();
writer.println("执行的是Servlet对象");
writer.flush();
writer.close();
}
}Servlet类对象配置类,通过该配置类文件,将Servlet类对象添加到容器中,并设置其映射的URL
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class MyConfig {
// 定义方法,用来注册Servlet对象。
// 该注解表示本方法的返回值对象可加入到容器中
public ServletRegistrationBean servletRegistrationBean(){
MyServlet ms = new MyServlet();
// 参数是Servlet对象,以及该Servlet处理的访问URL
// 第一种方式,在创建对象的时候,将参数直接传入
// ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(ms, "/myServlet");
// 第二种方式,无参创建对象,后续调用方法传入
ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean();
servletRegistrationBean.setServlet(ms);
servletRegistrationBean.addUrlMappings("/myServlet");
return servletRegistrationBean;
}
}
运行主程序,测试URL即可。
4.3 SpringBoot中使用Filter
本节介绍在SpringBoot中使用Filter。步骤和Servlet类似:
- 创建自定义过滤器类,实现Filter接口,实现业务方法
- 注册过滤器类对象,让框架能够找到过滤器。
代码如下所示:
1 | // 自定义过滤器类 |
配置类文件
1 |
|
Controller测试
1 |
|
4.4 字符集过滤器的使用
字符集过滤器CharacterEncodingFilter主要用于解决post请求中乱码的问题。这个过滤器是SpringMVC框架中提供的,所以说,字符集过滤器和上面的Filter是一样的操作步骤,在设置好字符集过滤器的参数后,采用FilterRegistration注册过滤器并设置其过滤的URL。
**除此之外,还需要在主配置文件applicaiton.properties中设置server.servlet.encoding.enabled=false
**,这是因为SpringBoot默认已经配置了CharacterEncodingFilter,而server.servlet.encoding.enabled
默认为真,表示使用的是框架内部的字符集过滤器。
过滤器代码如下所示:
1 |
|
主配置文件内容如下所示:
1 | # 设置为false,表示不使用框架内部的字符集过滤器。 |
4.5 在application.properties文件中设置过滤器
上一节提到,框架本身设置了字符集过滤器。因此,我们可直接修改字符编码方式,而不再自己设置字符集过滤器。
1 | true = |
5. ORM操作MySQL
前面讲解了SpringBoot集成Web组件,本节讲解SpringBoot集成MyBatis。ORM其实在Spring中提到过,是一个模块,用于操作数据库,ORM是Object Relation Mapping的简写,即Java对象和数据表的映射。
在SpringBoot中使用MyBatis框架操作数据,需要以下步骤:
- 在SpringBoot项目中加入MyBatis起步依赖:完成MyBatis对象的自动配置,并将对象放在容器中;
- 在
pom.xml
指定把src/main/java
目录中的xml等资源文件包含到classpath中(配置Resource插件); - 创建实体类Student;
- 创建Dao接口StudentDao,创建一个查询学生的方法;
- 创建Dao接口对应的Mapper文件、xml文件,写SQL语句;
- 创建Service层对象,创建StudentService接口和它的实现类。调用Dao对象的方法,完成数据库的操作;
- 创建Controller对象,访问Service;
- 写
application.properties
文件,配置数据库的连接信息。
5.1 创建SpringBoot项目
- 创建项目并选取MyBatis起步依赖
创建步骤的流程和前面类似,只不过在选择依赖的时候,需要将SQL中的MyBatis Framework、MySQL Driver选上。创建好后的项目依赖如下所示:
1 | <dependencies> |
具体Maven显示的依赖如下所示,可以看到起步依赖其实包含了很多必备的基础依赖,即起步依赖自动为我们添加了依赖:
在
pom.xml
文件中添加resources资源插件,将SQL语句xml文件编译到target目录的classes目录下。一定要做,否则找不到xml文件,不会创建代理对象,也就访问不到这个对象和方法。1
2
3
4
5
6
7
8
9
10
11
12
13<build>
<!-- 设置指定目录下的资源文件编译到指定target目录下的classes目录下 -->
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
<!-- 此处,最好是**/*.*,或者添加一个额外的resource配置,否则似乎会覆盖掉默认resources目录下文件编译后的位置信息。 -->
</includes>
</resource>
</resources>
</build>创建实体类User
注意,属性名要和数据表中的字段名一致。
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
49public class User {
private Integer no;
private String login;
private String password;
private String name;
public Integer getNo() {
return no;
}
public void setNo(Integer no) {
this.no = no;
}
public String getLogin() {
return login;
}
public void setLogin(String login) {
this.login = login;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String toString() {
return "user{" +
"no=" + no +
", login='" + login + '\'' +
", password='" + password + '\'' +
", name='" + name + '\'' +
'}';
}
}创建实体类的Dao接口,并创建操作方法
1
2
3
4
5
6
7
8
9
10
11/**
* 在SpringBoot中使用UserDao接口,需要在接口上面加Mapper注解,即告诉SpringBoot,这是接口,创建该接口的代理对象。
*
* 其实整合MyBatis有多种方式,Mapper只是其中一种。
*/
public interface UserDao {
// 简单类型作为参数,因此可采用MyBatis中的注解给参数命名
User selectByNo( Integer no);
}创建Dao接口的Mapper映射文件,xml形式
1
2
3
4
5
6
7
8
9
10
11
12
<mapper namespace="org.hianian.dao.UserDao">
<!-- 定义sql语句 -->
<select id="selectByNo" resultType="org.hianian.entity.User">
select no, login, password, name from t_user where no=#{userNo}
</select>
</mapper>创建service层接口以及实现类,用于调用Dao接口,实现业务功能
1
2
3
4public interface UserService {
User queryUser(Integer no);
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17// 采用注解声明这是业务层对象,自动创建该对象
public class UserServiceImpl implements UserService {
// 自动注入赋值
private UserDao userDao;
public User queryUser(Integer no) {
// 简单实现一下Service层调用Dao
User user = userDao.selectByNo(no);
return user;
}
}创建Controller对象,为用户URL请求调用不同的service业务对象
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class UserController {
// 采用自动注入,将该接口的实现类对象赋值给本属性。(该对象在实现类中已经采用@Service注解生成了)
private UserService userService;
public String queryUser(Integer no){
// 注意,参数no可以以URL中的参数携带过来。即localhost:8080/orm/userQuery?no=1
User user = userService.queryUser(no);
return user.toString();
}
}配置
application.properties
文件,配置数据库连接信息1
2
3
4
5
6
7
8
99001 =
/orm =
# 配置数据库
com.mysql.cj.jdbc.Driver =
jdbc:mysql://ip:端口/数据库?useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2B8 =
用户名 =
密码 =
测试结果如下所示:
5.2 @MapperScan
上述是SpringBoot使用MyBatis的基本步骤。其实和其他程序类似,只不过需要创建Mapper的代理对象。SpringBoot提供了以下方式创建Mapper代理对象。
@Mapper注解:放在Dao接口的上面,每个Dao接口都需要放置这个注解。缺点就是随着Dao接口数量的增加而注解增加。
作用就是:通过Mapper注解,找到Dao接口,然后找到该接口的xml文件,进而执行对应的SQL语句。
@MapperScan注解:扫描指定的Dao接口。放置在主启动类Application上面,并以basePackages属性形式提供Dao接口所在的包名。如下所示:
1
2
3
4
5
6
7
8
9
public class Springboot16Application {
public static void main(String[] args) {
SpringApplication.run(Springboot16Application.class, args);
}
}作用就是:通过@MapperScan注解,找到指定的包,并扫描该包中所有的Dao接口以及对应的xml文件,进而执行对应的SQL语句。
5.3 mapper文件和Java代码分开管理
仔细观察项目目录可以发现,Dao接口和其映射文件xml是放在一个目录下的。为了方便,一般情况下一个目录中存放的文件应该是同一类型的,因此可将mapper文件和Java代码分开存放。一般情况下,可将mapper映射文件放置到resources目录中自定的子目录下。此时因为java目录下没有了要编译的除.java外的文件,因此pom.xml就不需要设置resources插件了。
但是此时,因为Dao和Mapper映射文件不在同一个目录下,此时SpringBoot中的@Mapper注解和@MapperScan注解在找到Dao文件后,怎么找到映射文件呢?需要在application.properties文件中配置目录。
注意,resources目录中所有的内容都会在编译之后放置到target目录下的classes目录中。因此在application.properties文件中设置目录的时候,需要指定的是编译之后的目录(即类路径classpath+自定义目录
)。
此时,springboot通过两个注解,会找到Dao接口,然后通过主配置文件中的配置信息找到其映射文件,进而创建代理对象,执行指定的SQL语句方法。
1 | classpath:resources下自定义的目录/*.xml = |
目录如下所示,这样的话,目录还是比较清晰的:
5.4 事务支持
提到数据库,肯定要有事务的。本节讲解SpringBoot中使用数据库中的事务。先回顾一下Spring中如何使用事务的:
管理事务的对象:事务管理器(接口,接口中有很多的实现类,即负责不同数据库访问技术的具体事务管理)
如JDBC、MyBatis需要使用DataSourceTransactionManager。
声明式事务:和编程式事务不同,不需要在代码中编写事务代码;而是采用注解以及配置文件形式来声明事务的控制内容。
主要控制事务的隔离级别、传播行为以及超时时间等等。
事务的处理方式有两种:
- Spring框架中的@Tranactional
- AspectJ框架
那么在SpringBoot框架中怎么使用事务呢?(上面两种方式均可在SpringBoot中使用,下面逐个介绍)
第一种方式,采用Spring框架中的自带事务注解@Transactional:
- 在业务方法的上面加入@Transactional注解,加入注解后,该方法就有事务功能了。
- 最好加上本注解:在主启动类的上面加入@EnableTransactionManagement,表示启用事务管理器。
- 备注:@Transactional注解默认使用库的隔离级别,传播行为为REQUIRED,传播时间为-1。可以通过属性自己设置这些性质。如果修饰的方法中出现了异常,那么方法内部,异常前面已经“执行”的数据库操作就会回滚。即添加了事务。
Controller中的业务方法:
1 |
|
主启动类:
1 |
|
6. 接口架构风格——RESTful(了解)
注意,这里的接口指的并不是Java中的interface,而是应用程序接口,即API。也就是软件层面的接口,最直观的就是Java程序中各种库函数的调用,也可以是访问Servlet映射的URL,等等这些都是接口。从广义上说,API就是获取某种功能、服务的通道。
风格指的就是组织方式,即API的外在表现形式,比如Java语言中的API就是:类名/对象.方法名()
,通过这种形式来获取功能。而网络URL的传统风格就是:协议://IP地址:端口号/资源名?参数名=值&参数名=值
。而RESTful则是一种新型的风格。
6.1 认识REST
REST是Representational State Transfer的缩写,也被称为表现层状态转移。是一种互联网软件架构设计的风格,但它并不是标准,它只是提出了一组客户端和服务器交互时的架构理念和设计原则,基于这种理念和原则设计的接口可以更加简洁,更有层次。REST这个词,是Roy Thomas Fielding在他2000年的博士论文中提出的。
任何技术都可以实现这种理念,如果一个架构符合REST原则,就称它为RESTful架构。比如我们要访问一个http接口:http://localhost:8080/boot/order?id=1021&status=1
,采用RESTful风格的地址为:http://localhost:8080/boot/order/1021/1
。即REST风格是将URL中的额外参数以“/”分割。
REST中的要素:
- 用REST表示资源和对资源的操作。在互联网中,REST表示一个资源或者一个操作。
- 资源使用URL表示的,在互联网中,使用的图片,视频,文本,网页等等都是资源。
- 资源使用名词表示。
资源:
- 查询资源:观看资源,通过URL找到资源
- 创建资源:添加资源
- 更新资源:更新编辑
- 删除资源:去除
REST风格:资源使用URL表示,通过名词来表示资源。使用http中的动作(请求方式:GET、POST、PUT、DELETE)表示对资源的操作(分别对应:查、增、改、删)。但是浏览器不支持PUT和DELETE,所以需要其他的方式来实现(可以采用POST模拟)
6.2 RESTful的注解
SpringBoot已经支持了这种风格。因为REST风格和传统URL不同,参数的表现形式不一样,因此需要专门切分URL来获取请求的资源以及参数。
6.2.1 @PathVariable
从URL中获取数据
6.2.2 @PostMapping
支持的post请求方式,等同于@RequestMapping(method=RequestMethod.POST)
1 |
|
6.2.3 @DeleteMapping
支持的delete请求方式,等同于@RequestMapping(method=RequestMethod.DELETE)
6.2.4 @PutMapping
支持的put请求方式,等同于@RequestMapping(method=RequestMethod.PUT)
6.2.5 @GetMapping
支持的get请求方式,等同于@RequestMapping(method=RequestMethod.GET)
6.2.6 @RestController
复合注解,是@Controller和@ResponseBody的组合。在类上面加入此注解,表明该类中所有的方法都加入了@ResponseBody。
1 |
|
6.3 总结
简单地说,REST风格其实就是规定了请求资源以及对资源处理的一种新型的URL风格。针对这种URL风格,制定了后台从URL获取资源路径和参数的方法。其实就是类似Servlet中的url以及get、post等方式获取参数等等。代码案例就不再演示了。
另外,这几个注解似乎和RequestMapping类似,只不过取URL中的参数的方式不同。
7. SpringBoot集成Redis
本节讲解SpringBoot集成Redis,以小案例为基础。
Redis是一个NoSQL数据库,常作为缓存使用。通过Redis客户端可以在程序中访问Redis数据。Java语言中使用的客户端有Jedis、lettuce、Redission。SpringBoot以及Spring中使用RedisTemplate(StringRedisTemplate)模板类操作Redis数据,这个工具类是简化了客户端,和客户端的功能一样。
Redis是一个中间件,是一个独立的服务器,可直接调用,完成某个功能,不需要像框架那样需要编码才能使用。
案例需求如下:
完善根据学生id查询学生的功能,先从Redis缓存中查找,如果找不到,再从数据库中查找,然后放到Redis缓存中。之后,如果再查找类似的数据,可直接在Redis缓存中找到,提升效率。
7.0 RedisTemplate类
该类提供了若干个属性对象用于操作Redis,源码如下所示:
1 | public class RedisTemplate { |
另外,提供了方法来获取上述对象。
7.1 项目之前需要安装配置好Redis
安装好Redis,可以访问。
7.2 需求实现步骤
SpringBoot集成Redis需要以下步骤。首先新建一个SpringBoot项目,注意在选择依赖的时候,需要选择NoSQL中的Redis。
7.2.1 pom.xml
查看pom.xml文件,已经添加了Redis的起步依赖,如下:
1 | <!-- Redis的起步依赖,可直接在项目中使用工具类RedisTemplate(StringRedisTemplate) --> |
查看Maven中的依赖,可发现,这里Redis的起步依赖实际上使用的是lettuce客户端,也就是说,我们在程序中通过RedisTemplate(StringRedisTemplate)类访问Redis数据库,实际上调用的是lettuce客户端中的方法,即SpringBoot在客户端的层面上又封装了一层,使我们更加方便调用数据库。
7.2.2 核心配置文件application.properties
在核心配置文件中配置Redis数据库信息。
1 | 9003 = |
7.2.3 创建RedisConotroller
这里为了方便,直接在Controller中进行数据访问。代码如下所示:
1 |
|
7.2.4 启动Redis服务
7.2.5 执行SpringBoot Application启动类
7.2.6 使用Postman发送请求
7.2.7 启动浏览器访问Controller
7.2.8 Redis客户端中查看数据
可以看到,在key的前面部分出现了编码,其实在使用模板对象的时候,可采用StringRedisTemplate类型,对象名为stringRedisTemplate。
7.3 RedisTemplate和StringRedisTemplate的区别
- StringRedisTemplate把key和value都是作为String处理,使用的是String的序列化,可读性较好;(同时也意味着,这种类型只能存储字符串类型数据,如果存储对象,那是不可能的。)
- RedisTemplate把key和value是先经过了JDK序列化,key和value是序列化的内容,不能直接识别。
(Java)序列化:把对象转化为可传输的字节序列(二进制)过程称为序列化。
(Java)反序列化:把字节序列还原为对象的过程称为反序列化。
为什么需要序列化?
序列化的最终目的是为了对象可以跨平台存储,和进行网络传输。而我们进行跨平台和网络传输的方式就是IO,而我们的IO支持的数据格式就是字节数组。我们必须在把对象转成字节数组的时候就制定一种规则(序列化),那么我们从IO流里面读出数据的时候,再以这种规则把对象还原回来(反序列化)。
序列化只是一种拆装组装对象的规则,形式有多种多样,比如现在常见的序列化方式有:JDK(不支持跨语言,方便,性能最差)、JSON、XML、Kryo(不支持跨语言,支持Java,性能最好)、Thrift等等。JDK是Java自带的序列化,就是将对象转换为二进制数据,即byte[]。JSON就是那种key、value的形式。
7.3.1 修改默认的序列化方式
设置RedisTemplate序列化方式,可单独设置key,也可单独设置value,也可同时设置二者的序列化方式。在方法中,使用该对象的前面,直接调用方法即可。
1 | // 设置序列化方式 |
8. SpringBoot集成Dubbo
略。
9. SpringBoot打包
SpringBoot可以打包为war或者jar文件,以两种方式发布应用。
9.1 打包war包
以访问Controller,跳转到JSP页面这个简单案例为例,打包成war文件,并可以将该文件部署到独立的Tomcat中运行测试。
注意,SpringBoot中使用JSP需要加入处理JSP的依赖,见3.3。运行好没问题后,在pom.xml
文件中的<build>标签中指定打包后的文件名称。
1 | <!-- 指定打包后的文件名称 --> |
注意,在SpringBoot中开发的项目,使用的是SpringBoot内嵌的Tomcat。因此,如果我们想要将其打包,并且能够运行在独立的Tomcat中,需要将项目的主启动类继承SpringBootServletInitializer,并重写configure方法。
SpringBootServletInitializer类就是相当于原来的web.xml
文件的替代,使用了嵌入式的Servlet,默认是不支持JSP的。代码如下所示:
1 |
|
注意,因为是打包成war,所以还需要在pom.xml
文件中指定打包类型为war。
1 | <!-- 指定打包类型为war --> |
之后,采用maven工具,先clean一下,再package,可以发现在target目录中出现了打包后的myboot.war文件。将其放到Tomcat的webapp目录下即可(这里要注意,SpringBoot中的Tomcat插件版本和自己要部署的Tomcat的版本要一致,否则,有的目录可能不支持URL映射。)。
综上,打包war包,有如下步骤:
- 指定打包后的文件名称以及打包的文件类型
- 在主启动类中继承SpringBootServletInitializer类,并重写configure方法(目的是为了支持部署到外在的web服务器)
- maven clean、maven package
- 把war文件放到Tomcat等服务器的发布目录中,启动服务器即可。
9.2 打包jar包
打包jar包和war包类似,需要下面两步:
- 指定打包后的文件名称以及打包的文件类型
- 指定springboot-maven-plugin版本(1.4.2-RELEASE)
- maven clean、maven package
- 生成的jar包是一个可独立运行的springboot项目(程序),直接
java -jar 文件名.jar
即可。
9.3 war和jar区别
打包成war包,运行war包需要服务器支持才能运行,但是可以充分利用服务器的资源优势以及其他功能。
打包成jar包,可以独立运行,不依赖服务器,但是内嵌的服务器在功能上没有外部独立服务器丰富,所以在功能上可能有所欠缺。
10. SpringBoot集成Thymeleaf模板(了解)
10.1 概述
本质上说,JSP也是一种模板,因为它的内容框架是不变的,只是利用${变量值}
从后端动态获取数据。
Thymeleaf是一个流行的模板引擎,该模板引擎采用Java语言开发。模板引擎是一个技术名词,是跨领域跨平台的概念。在Java语言体系下有模板引擎,在C#、PHP语言体系下也有模板引擎,甚至在JavaScript中也会用到模板引擎技术,Java生态下的模板引擎有Thymeleaf、Freemaker、Velocity、Beetl(国产)等。
Thymeleaf对网络环境下不存在严格的要求,既能用于Web环境下,也能用于非Web环境下。在非Web环境下,他能直接显示模板上的静态数据;在Web环境下,它能像JSP一样从后台接收数据并替换掉模板上的静态数据。它是基于HTML的,以HTML标签为载体,Thymeleaf要寄托在HTML标签下实现。
SpringBoot集成了Thymeleaf模板技术,并且SpringBoot官方也推荐使用Thymeleaf来替代JSP技术。注意,Thymeleaf只是一种模板技术,本身并不属于SpringBoot。SpringBoot只是很好地集成这种模板技术,作为前端页面的数据显示,在过去的Java Web开发中,我们往往会选择使用JSP去完成页面的动态渲染,但是JSP需要编译成Java才能运行,效率较低。
Thymeleaf的官方网站:Thymeleaf
Thymeleaf的官方手册:Tutorial: Using Thymeleaf
10.2 案例入门
新建SpringBoot项目,注意,选择依赖的时候,除了选web依赖,还需要选择Thymeleaf模板引擎。
其实项目整体上没什么区别,和前面的类似,只不过Controller返回结果的时候,返回的视图不再是jsp,而是模板文件(一般情况下是html)。
代码如下所示:
1 |
|
注意,模板文件放在resources/templates
中。模板文件如下所示:
1 |
|
其中th:text="${data}"
会在服务端获取数据,并替换“显示数据”内容,如果有data则会替换,没有则不会替换,单独打开这个html也会正常显示。
配置文件中关于模板的常规设置:
1 | # 默认是true,即后续的请求模板会自动从缓存中读取。 |
模板引擎的依赖为:
1 | <!-- 模板引擎的起步依赖 --> |
10.3 表达式
10.3.1 标准变量表达式
语法格式为:${key}
,作用是获取request作用域中key的值。在模板文件(以html为例)中,在html的标签中使用th:text="表达式"
,相当于标签的属性。代码如下所示:
1 | <!-- 从服务端获取数据 --> |
10.3.2 选择变量表达式
语法格式为:*{key}
,作用是获取request作用域中key的值。注意,选择变量表达式不能单独用,需要和 th:object 这个属性一起使用。目的是简化获取对象的属性值。代码如下所示:
1 | <h3>选择变量表达式</h3> |
10.3.3 链接表达式(URL表达式)
语法为:@{URL}
,作用是用于链接、地址的显示。注意,比如加th
,意味着采用Thymeleaf引擎来处理。代码如下所示:
1 | <h3>链接表达式</h3> |
10.4 Thymeleaf属性
模板的属性就是html原有的属性,只不过前面加了一个th前缀。加了th的属性,表明需要经过模板引擎处理。不过加不加th,其标签作用是一样的,只不过加了th可以使用表达式,动态获取数据更新页面。
10.4.1 th:action
定义后台控制台的路径,类似form表单中的action,主要是结合URL表达式,获取动态变量,形成新的URL。
10.4.2 th:method
设置请求方式,如post、get等等。
10.4.3 th:href
定义超链接,主要是结合URL表达式,获取动态变量,形成新的URL。
10.4.4 th:src
用于外部资源引入,常与@{}表达式结合使用。
10.4.5 th:text
用于文本的显示,该属性显示的文本在标签体中,如果是文本框,数据会在文本框外显示。要想显示在文本框内,使用th:value
。
10.4.6 th:style
设置样式
10.4.7 th:each
这个属性非常常用,比如从后台传来一个对象集合,那么就可以使用该属性遍历输出结果。它与JSTL中的<c: forEach>类似,该属性既可以循环遍历List集合,也可以循环遍历Array数组以及Map。语法如下所示:
1 | <div th:each="集合循环成员, 循环的状态变量: ${key}"> |
其中,循环集合成员和循环的状态变量,两个名称都是自定义的。“循环的状态变量”这个名称可以不写,默认是“集合循环成员Stat”,该对象表示循环的信息,有多个属性,比如当前循环的索引下标、循环体的长度等等。代码如下所示:
1 |
|
1 | <h3>循环遍历List</h3> |
从结果可以看出,实际上循环显示的是整个<div>块,即each属性所在的标签。
遍历数组和遍历List一样。对于Map,因为是多个<k,v>,所以说,集合循环成员可以被看成是一个<k,v>对象,属性为key、value。那么Map中的对象属性值,也就是集合循环成员.value.属性名
。总体上和List大同小异,相当于外面又套了一层。
10.4.8 条件判断if
和Java语言中一样。语法为:th:if="布尔条件"
,条件为true,则显示标签体内容。还有一个类似的,th:unless="布尔条件"
,当条件为false则显示标签体内容。
1 | <div th:if="10 > 0"> 当条件为真时显示本内容 </div> |
注意,如果是表达式,则需要在花括号里面写布尔表达式。并不是先取值${sex},再判断,${sex} == ‘m’。
10.4.9 switch-case判断语句
类似Java中的switch-case。其中“*”表示以上都不匹配。
1 | <div th:switch="${sex}"> |
10.4.10 th:inline
内联,有三个取值:text、javascript、none。
内联text
可以让Thymeleaf表达式不依赖于html标签,直接使用
[[表达式]]
,即可获取动态数据,要求在父级标签上加th:inline="text"
属性,在使用表达式的时候,采用[[表达式]]
直接获取数据。因为之前获取动态数据,必须在设置标签属性为
th:text="表达式"
,必须设置text属性。可通过内联表达式,直接获取动态数据。1
2
3<div th:inline="text">
<p>显示数据:[[${sex}]]</p>
</div>备注:其实
th:inline="text"
可写可不写,直接[[表达式]]
就行。内联javascript
通过该属性,可以在JS脚本中,获取服务器后台中的数据。注意,是在脚本中。
1
2
3
4
5
6
7<script type="text/javascript" th:inline="javascript">
var name = [[${name}]];
var age = [[${age}]];
var sex = [[${sex}]];
alert("获取到的数据为:" + name + "===" + age + "===" + sex);
</script>
10.5 字面量
字面量和Java中的字面值是一回事,就是真实存在的数据。
文本字面量
用单引号包围的字符串为文本字面量。
数字字面量
就是数字。
boolean字面量
false,true
null字面量
null
10.6 字符串连接
字符串连接主要有两种方式,一种是加号,一种是双竖线。如下所示:
1 | <p th:text="'我是' + ${name} + ',我所在的城市是' + ${city}">显示数据</p> |
可以看到,第二种方式比较简单直观,不需要单引号,也不需要写加号。
10.7 运算符
运算符主要有以下几种:
算术运算
+,-,*,/,%
关系运算
>,<,>=,<=,==,!=,gt,lt,ge,le,eq,ne
三元运算符
布尔表达式 ? 值1 : 值2
10.8 Thymeleaf基本对象
模板引擎提供了一组内置的对象Tutorial: Using Thymeleaf,这些内置的对象可以直接在模板中使用,比较常用的内置对象有:
#request对象
表示HttpServletRequest对象
#session对象
表示HttpSession对象
session对象(是2的简写)
表示HttpSession对象。简化版的对象,以Map的<k,v>形式存储对象的属性名(方法名)和属性值(方法返回值)。
这些是内置对象,可以在文件中直接使用。即可以在模板文件中直接使用上述域对象,获取域中的数据。
假设已经在上述域中添加了数据。在html模板中直接获取对象并调用方法获取数据。
1 | <p>获取作用域中的数据</p> |
可以看到,实际上10.3中的表达式只能获取到的是request作用域中的数据,即默认采用内置对象HttpServletRequest。当然,不仅仅是getAttribute()方法,其实有很多种方法,如getRequestURL()、getRequestURI(),这里不再一一举例。
10.9 Thymeleaf内置工具类对象
模板引擎提供了一组工具类对象Tutorial: Using Thymeleaf,可以在模板中直接使用这些对象提供的功能方法。常见的功能类对象有如下:
- #dates:主要是对后台传过来的时间对象进行操作,比如格式化,取年月日等等
- #numbers:主要是操作数字,以不同的格式对数字进行处理,比如以货币形式、以高精度形式等等。
- #strings:主要是操作字符串,比如字符串切割、格式化等等。
- #lists:主要是处理list集合,比如计算元素个数,判断是否包含某元素等等。
- …
可直接在html模板文件中使用这些对象及其方法。
注意,有时候并不知道是否有数据。比如有一个Dog对象dog,属性有name。同时有一个Zoo对象zoo,里面有属性Dog。那么当我们在Thymeleaf模板文件中访问的时候,一般是这种形式:${zoo.dog.name}
。但是有时候,dog可能没有添加到zoo中,即该属性为null,此时访问就会报错。为了使得程序更加鲁棒,可采用问号的形式,即当为null的时候,就不访问:${zoo.dog?.name}
,也可都加问号${zoo?.dog?.name?}
。
10.10 内容复用(自定义模板)
自定义模板是复用的行为。可以把一些内容,当做模板,多次重复使用。语法为th:fragment="模板自定义名称"
,表示当前属性所在的标签就是模板,可重复多次引用该模板。此时在其他的地方可直接引用该模板。
1 | <!-- 文件名为test.html --> |
引用模板语法有以下两种:
- ~{模板所在文件名称 :: 模板自定义名称}
- 模板所在文件名称 :: 模板自定义名称
使用模板有两个:包含模板(th:include)、插入模板(th:insert)
1 | <!-- 以插入形式引用模板,插入形式就是将原模板全部插入到该属性所在标签的里面 --> |
如果想引用整个模板文件:
1 | <!-- test表示文件名,html表示全部文件,或者省略后面的html --> |
11. 总结
参考的教程,基本上就这些内容。感觉没什么新内容,SpringBoot只是以JavaConfig文件形式代替了xml文件(以及相应的注解),以及用起步依赖自动将相关依赖一起引入。
总体上,简化了对象的创建以及依赖的导入。下面复习一下用到的注解,这些注解有的是JDK中的,有的是Spring的,有的是SpringBoot的。
11.1 创建对象的注解
注解 | 描述 |
---|---|
@Controller | 放在类的上面,创建控制器对象,注入到容器中。 |
@RestController | 放在类的上面,创建控制器对象,注入到容器中。是@Controller和@ResponseBody的复合注解,表名该类的所有方法的返回值都是数据,即都被@ResponseBody修饰。 |
@Service | 放在业务层的实现类上面,创建Service对象,注入到容器中。 |
@Repository | 放在持久层Dao层的实现类上面,创建Dao上面,放入到容器中。(很少使用,因为使用了MyBatis框架,Dao对象会通过代理生成的。) |
@Component | 放在类的上面,创建该类对象,注入到容器中。(和三层架构没关系,就是创建对象。) |
11.2 赋值的注解
注解 | 描述 |
---|---|
@Value | 简单类型的复制,在属性上面使用,如@Value(“张三”)。或者获取属性文件中的变量值,如@Value(${server.port})。 |
@Autowired | 引用类型自动注入,支持byName、byType,默认是byName。用来放在属性和构造方法的上面。 |
@Qualifier | 给引用类型赋值,使用byName方式。 |
@Resource | (JDK中的)实现引用类型自动注入,支持byName、byType,默认是byName。用来放在属性之上。 |
11.3 其他注解
注解 | 描述 |
---|---|
@Configuration | 放在类的上面,表示这是一个配置类,相当于配置文件。 |
@Bean | 放在方法的上面,表示将该方法的返回值对象,注入到Spring容器中。 |
@ImportResource | 加载其他的xml配置文件,将文件中的对象注入到Spring容器中。 |
@PropertySource | 读取其他的properties属性配置文件 |
@ComponentScan | 组件扫描器,指定报名,扫描注解 |
@ResponseBody | 放在方法的上面,表示方法的返回值是数据,不是视图 |
@RequestBody | 把请求体中的数据,读取出来,转为Java对象使用 |
@ControllerAdvice | 用在类的上面,表示控制器增强,表示此类提供了方法,可以对Controller增强功能(如异常处理)。 |
@ExceptionHandler | 处理异常,放在方法的上面,表示该方法是处理异常的。 |
@Transactional | 处理事务,放在service实现类的public方法上面,表示此方法有事务。 |
@SpringBootApplication | 放在启动类上面,是一个复合注解(@SpringBootConfiguration、@EnableAutoConfiguration、@ComponentScan) |
@Mapper | 放在类的上面,让MyBatis找到接口,创建其代理对象。 |
@MapperScan | 放在启动类的上面,扫描指定的包,把这个包中的所有接口都创建代理对象,并将对象注入到容器中。 |
@Param | 放在dao接口的方法的形参上面,作为命名参数使用。 |
12. 备注
参考B站《动力节点》。