SpringBoot

第一个SpringBoot程序

SpringBoot英文官方文档:Spring Boot Features

SpringBoot中文文档:SpringBoot中文文档

创建新项目选择Spring Initializr

如图设置Group,Artifact和Java Version

这里选择的是各个依赖和插件,可以过后补选

一直点到Finish后就会生成一个配置好类似如下的SpringBoot项目。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|-- undefined
|-- pom.xml
|-- SpringBoot.iml
|-- .idea
|-- .mvn
|-- src
|-- main
| |-- java
| | |-- com
| | |-- theo
| | |-- TheoApplication.java
| |-- resources
| |-- application.properties
| |-- static
| |-- templates
|-- test
|-- java
|-- com
|-- theo
|-- TheoApplicationTests.java

TheoApplication.java

这个就是该SpringBoot的项目启动文件,运行后就相当于启动了一个SpringBoot项目,不像SSM框架需要配置并启动Tomcat。

1
2
3
4
5
6
7
8
9
10
11
package com.theo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class TheoApplication {
public static void main(String[] args) {
SpringApplication.run(TheoApplication.class, args);
}
}

SpringApplication.run()就是启动SpringBoot组件,因为SpringBoot中集合了Tomcat,所以启动后也会启动Tomcat。

application.properties

这里就是SpringBoot的配置文件,不过用的都是application.yml。至于yml的优势以后讲解。

pom.xml

同样也是在这里配置依赖和插件,不过有点不同的是配置依赖将不需要配置版本号,因为不同版本的SpringBoot会配置好大量依赖的版本。如果要导入某一插件,只需要导入对应的启动器即可

1
2
3
4
5
6
7
8
9
10
11
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies>

至于每个依赖对应的启动器是什么,在spring-boot-autoconfigure-》Meta-INF-》spring.factories中能够查到,当然百度是个好东西。

创建Controller层

一定要注意Controller层乃至Dao、Service、pojo都需要和SpringBoot启动文件TheoApplication.java在同一级目录下!!!!!

创建Controller.HelloController.java

1
2
3
4
5
6
7
@RestController
public class HelloController {
@RequestMapping("/hello")
public String hello() {
return "hello";
}
}

运行启动文件,搜索localhost:8080/hello即可访问到控制器并获得返回值。

Application.yml

.yml语法格式和.properties有很大不同。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
key: value
#对象
student:
name: theo
age: 20
#另一种写法
student: {name: theo,age: 20}

#数组
pets:
- cat
- dog
- pig
#另一种写法
pets: [cat,dog,pig]
1
2
3
4
5
6
7
8
9
key=value
#对象
student.name=theo
student.age=20

#数组
pets[0]=cat
pets[1]=dog
pets[2]=pig

能发现yml在写数组、map、对象方面能省上很多力气。

通过yml实现JavaBean注入

创建pojo.User.java,并配置好构造器等.

1
2
3
4
5
6
public class User {
String name;
int age;
List<Integer> list;
Map<String,String> map;
}

如果按照Spring的配置Bean方法,应该注解如下:

1
2
3
4
5
6
7
8
9
10
11
@Component
public class User {
@Value("theo")
String name;
@Value("20")
int age;
@Value("[1,2,3]")
List<Integer> list;
@Value("{name: theo,age: 3}")
Map<String,String> map;
}

@Component的意思是将该JavaBean注册到Spring容器中,之后在Controller层通过@Autowired就可以直接从IOC中获取Bean了。

当然,我们不会这么用。倘若需要更改Bean的属性值就需要更改源代码,这样我们就需要了解yml配置JavaBean。

1
2
3
4
5
6
7
User:
name: theo
age: 20
list: [1,2,3]
map:
name: theo
age: 20

写好yml后,更改JavaBean

1
2
3
4
5
6
7
8
@Component
@ConfigurationProperties(prefix = "User")
public class User {
String name;
int age;
List<Integer> list;
Map<String,String> map;
}

这样就会从yml中找到User的配置并注入,再注册到IOC中。

注意初次使用@ConfiguraProperties会在上方报红:Spring Boot Configuring the Annotation Processor not Configured

这个并不影响程序正常运行,为了解决这个问题,可以在pom.xml中配置

1
2
3
4
5
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>

在yml配置中,普通字符串可以不用加单双引号。但如果字符串中含有转义字符如\n,加上双引号就会令转义字符生效即输出换行,但如果加上单引号就会将转义字符当作普通字串输出。

SpringBoot测试类

java.com.theo.TheoApplicationTests是一个测试类,但要非常注意一点在SpringBoot测试类中@Test不是导的普通Junit包。而是需要导入依赖:

1
2
3
4
5
6
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-test</artifactId>
<version>2.4.5</version>
<scope>test</scope>
</dependency>

如果使用普通Junit将无法正常运行,在这次测试中就无法获取到User实例

我们编写测试类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package com.theo;

import com.theo.pojo.User;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
public class TheoApplicationTests {
@Autowired
User user;
@Test
public void contextLoads() {
System.out.println(user);
}
}

配置文件占位符

yml配置文件的值还能是动态值,即通过表达式生成值。

1
2
3
4
5
6
7
8
9
10
user:
name: theo${random.uuid} #生成随机uuid
age: 20${random.int} #生成随机int
list:
- 1
- 2
- 3
map:
name: tzq${user.name:DEFAULT} #类似三目运算符,如果存在user.name属性就取user.name的值,否则取后面的值。可以视作默认值
age: 20

通过properties实现注入

我们上面采用的yaml方法都是最简单的方式,开发中最常用的;也是springboot所推荐的!但当然properties也能实现注入。

注意一点:properties配置文件在写中文的时候,会有乱码 , 我们需要去IDEA中设置编码格式为UTF-8;

settings–>FileEncodings 中配置;

多环境配置

在实际开发中可能会有多个SpringBoot环境相互切换,比如测试环境和开发环境中用到的数据库会不同,那么如何在不同环境中自由切换呢?

配置文件的位置

file: ./config/application.yml classpath: ./config/application.yml

file: ./application.yml classpath: ./application.yml

file指的是项目根目录SpringBoot,可以在根目录下创建config再写配置文件也可以直接写配置文件。

classpath一般指的是resource目录,可以在其下创建config再写配置文件也可以直接写配置文件。

如果以上多个位置上都配置了文件,他们之间有如下优先级:
file:./config/ > file:./ > classpath:./config > classpath:./

多环境切换

如果在同一位置上有多套环境,可以手动切换环境。

多环境-多文件

创建配置文件application.yml、application-test.tml、application-dev.yml分别配置端口server.port: 8081、server.port: 8082、server.port: 8083

因为SpringBoot默认启动application.yml,因而在该配置文件切换环境。添加配置即可将环境切换成application-test.yml

1
2
3
spring:
profiles:
active: test

多环境-单文件

这种方式是.yml文件独有的,将多套环境同时写在一个配置文件中,环境之间用---分割

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
server:
port: 8081

spring:
profiles:
active: test
---
server:
port: 8082
spring:
profiles: test
---
server:
port: 8083
spring:
profiles: dev

这种方式已经被淘汰了,已经不推荐使用了

静态资源导入

默认静态资源路径

IDEA双击SHIFT,搜寻类WebMvcAutoConfiguration.class。找到方法addResourceHandlers(ResourceHandlerRegistry registry)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
protected void addResourceHandlers(ResourceHandlerRegistry registry) {
super.addResourceHandlers(registry);
if (!this.resourceProperties.isAddMappings()) {
logger.debug("Default resource handling disabled");
} else {
ServletContext servletContext = this.getServletContext();
this.addResourceHandler(registry, "/webjars/**", "classpath:/META-INF/resources/webjars/");
this.addResourceHandler(registry, this.mvcProperties.getStaticPathPattern(), (registration) -> {
registration.addResourceLocations(this.resourceProperties.getStaticLocations());
if (servletContext != null) {
registration.addResourceLocations(new Resource[]{new ServletContextResource(servletContext, "/")});
}

});
}
}

这个方法就会指导SpringBoot如何去寻找静态资源,下面逐步解析下这个方法:

  • 在第一个if判断,说明如果在配置文件配置了静态资源路径就会按照配置的路径寻找资源。比如:
1
2
3
spring:
mvc:
static-path-pattern: classpath:/theo_static/**

这样就会去resources.theo_static找静态资源(.html等等),当然这样完全不推荐!!!因为SpringBoot会默认指定静态资源的路径

  • this.addResourceHandler(registry, "/webjars/**", "classpath:/META-INF/resources/webjars/");
    这段代码说明你可以通过Maven导入webjar静态资源,系统会去***.jar/META-INF/resources/webjars/下寻找静态资源。这种方法也不常用,一般用于导入JQuery.js等资源。
  • 再往下两行,registration.addResourceLocations(this.resourceProperties.getStaticLocations());则会指引SpringBoot去默认路径寻找静态资源。点开getStaticLocations(),逐渐追溯到变量String[] staticLocations。它由CLASSPATH_RESOURCE_LOCATIONS所定义,其内容为
    "classpath:/META-INF/resources/", "classpath:/resources/", "classpath:/static/", "classpath:/public/"
  • 这样我们就找到了系统默认的静态资源路径,即resouces下的resources、static、public文件夹以及添加Web框架后的META-INF/resources文件夹。

综上,我们以后添加静态资源的时候,就随着系统指定的路径添加即可。这样的话我们直接访问localhost:8080/xxx.html就能访问到资源了。

当然,如果三个文件夹中有同名的静态资源,其之间有访问优先级resources > static > public

classpath:指的是什么?

这里有篇博客比较详细说明了classpath的指代:(1条消息) java项目中的classpath到底指向的哪里_不仅仅是说说而已的博客-CSDN博客_classpath

classpath具体指的是什么,需要经过编译后到资源管理器中找到项目根目录中的.classpath文件,一般如下:

.classpath

这就很明白了,classpath指的就是(kind=”src”)所指定的文件,这里classpath: /resources/就是src/resources或者resource/resources

制定首页

访问localhost:8080/得到的内容就是首页内容,之前的默认静态资源路径没有localhost:8080/,这是因为SpringBoot寻找首页是通过另一个方法getWelcomePage()

1
2
3
4
5
6
7
8
9
10
11
private Resource getWelcomePage() {
String[] var1 = this.resourceProperties.getStaticLocations();
int var2 = var1.length;

for(int var3 = 0; var3 < var2; ++var3) {
String location = var1[var3];
Resource indexHtml = this.getIndexHtml(location);
if (indexHtml != null) {
return indexHtml;
}
}

这个方法也会先获取默认静态资源路径,然后将他和index.html拼接形成首页的完整访问路径。因此我们只需要在三个静态资源文件夹下创建index.html即可作为首页内容。

静态资源前缀

在默认情况下,访问静态资源可以直接输入localhost:8080/xxx,但如果涉及到拦截器需要过滤出一条指定路径下的静态资源,就可以加上静态资源前缀:

1
2
3
spring:
mvc:
static-path-pattern: /res/**

此时需要输入localhost:8080/res/xxx才能访问静态资源。

Thymeleaf模板引擎

注意到resources/templates,这个文件下的资源是无法被客户端直接访问的,有点类似于WEB-INF下的资源。如果要访问,就需要通过控制器跳转。在SpringMVC中,控制器可以直接return一个字符串,这是因为有视图解析器帮忙拼接路径。SpringBoot中也有一个类似的角色:Thymeleaf模板引擎

导入thymeleaf

按照教程安装插件EditStarters:IntelliJ IDEA中如何再次调出springboot的依赖窗口,随时可以根据喜好导入和移除插件

调出以来窗口后,选择Thymeleaf Engines后插入。

源码解析

双击SHIFT,搜索ThymeleafProperties,发现:

1
2
public static final String DEFAULT_PREFIX = "classpath:/templates/";
public static final String DEFAULT_SUFFIX = ".html";

这就是视图解析器里的前后缀,但注意只为拼接成.html,因此最好放在templates的文件都以html结尾。

Thymeleaf语法

底层注解

@Configuration

请配合视频食用

在Spring5中,@Configuratio的作用是利用JavaConfig将注册配置类装配到IOC中,同时该类下的方法通过@Bean就能将方法“装配”到IOC中。

1
2
3
4
5
6
7
@Configuration
public class SpringMVCConfig{
@Bean //方法名作为id,返回类型作为class来装配到IOC,return的new()则相当于Bean注入.
public User TZQ() {
return new User("谭志强");
}
}

在测试类中获取这个Bean

1
2
3
4
5
6
@Test
public void JavaConfigTest() {
ApplicationContext context = new AnnotationConfigApplicationContext(SpringMVCConfig.class);
User tzq = (User) context.getBean("TZQ");
System.out.println(tzq);
}

在SpringBoot2中,@Configuration新增了一个特性:@Configuration(proxyBeanMethods = true)—-组件依赖问题

如果在注册配置类中有多个组件注册方法,且都装配到了IOC中。这时有一个注册方法需要用到另一个注册方法(即需要用到后一个注册方法所对应的Bean),那么调用的Bean是否是IOC容器中的Bean呢?

proxyBeanMethods = true,则答案为是。因为这种情况下Bean的作用域就是单例模式,即IOC容器中一个id只对应唯一的Bean,无论调用多少次注册方法,装配的Bean都一样。那么这样有什么意义呢?前一个Bean会形成对后一个Bean的组件依赖

proxyBeanMethods = false,在这种情况下,每次调用都会产生不同的Bean并覆盖之前存在的Bean。前一个Bean调用另一个注册方法后得到的Bean将不同于后一个注册方法生成的Bean,只有后一个Bean才是真正IOC中的Bean。那么将不存在组件依赖

因为true的情况下,每一次调用注册方法都会询问IOC中是否已存在对应的Bean,这会影响项目启动速度。相比之下false的情况会跳过询问,项目启动速度大幅提升。

@Import

在注册配置类上通过@Import({User.class,DBHelper.class})可以自动装配这些类型的组件,组件id就是全类名

注意,使用的类都必须要有无参构造器!!!

@ConditionalOnBean

@ConditionalonBean(name = “User”)**可以使用在注册方法上,调用该方法时就会去判断IOC容器中是否存在id为User的组件,如果存在则执行该方法然后装配对应组件。相反的@ConditionalOnMissingBean(name = “User”)**则会判断是否不存在id为User的组件。

值得注意的是,如果注解用在了注册配置类上,一旦判断不通过,该注册配置类下的所有注册方法都将失效。

@ImportResource

@ImportResource(“classpath:beans.xml”) 使用在注册配置类上,它可以将classpath:beans.xml中的<bean>...</bean>都转移到注册配置类中,这样就能省取一一搬运到类中再用@Bean装配

Rest请求源码分析

在SpringMVC中我们提到过RestFul风格可以做到通过同一个URL,以不同请求方式就可访问到不通的控制器。

以表单发起请求为例:

1
2
3
4
5
6
<form action="/user" method="post">
<input value="post请求" type="submit"/>
</form>
<form action="/user" method="get">
<input value="get请求" type="submit"/>
</form>

注意表单只能以POST和GET方式发起请求,那就意味着最多设置两个控制器分别响应。可是控制器还能配置如PostMappping和DeleteMapping等,那么如何改变表单的请求发起方式呢?

1
2
3
4
<form action="/user" method="post">
<input name="_method" type="hidden" value="delete"> //通过隐藏域传递参数_method,值为DELETE
<input value="delete请求" type="submit"/>
</form>

那么这个参数是要传给谁呢?答案是HiddenHttpMethodFilter,由他来分析请求的具体方法。搜索HiddenHttpMethodFilter.class找到方法doFilterInternal

1
2
3
4
5
6
7
8
9
10
11
12
13
14
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
HttpServletRequest requestToUse = request;
if("POST".equals(request.getMethod()) && request.getAttribute("javax.servlet.error.exception") == null){
String paramValue = request.getParameter(this.methodParam);
if (StringUtils.hasLength(paramValue)) {
String method = paramValue.toUpperCase(Locale.ENGLISH);
if (ALLOWED_METHODS.contains(method)) {
requestToUse = new HiddenHttpMethodFilter.HttpMethodRequestWrapper(request, method);
}
}
}

filterChain.doFilter((ServletRequest)requestToUse, response);
}

在if判断中,会先判定是否是以POST方式发起的请求再判定请求是否有效。因此表单的method必须是POST而不是GET!!,满足后将隐藏域_method的值赋给paramValue,一律转为大写后得到method(值为DELETE)。这时查看DELETE是否被兼容了(ALLOWED_METHODS中有PUT、DELETE、PATCH),兼容就重新包装请求,请求方式就变为了DELETE。这个请求就会被DeleteMapping响应了。

了解原理之后,我们还需要开启Rest服务才能达到目的。查看WebMvcAutoConfigration.java,找到以下:

1
2
3
4
5
6
7
8
9
10
@Bean
@ConditionalOnMissingBean({HiddenHttpMethodFilter.class})
@ConditionalOnProperty(
prefix = "spring.mvc.hiddenmethod.filter",
name = {"enabled"},
matchIfMissing = false
)
public OrderedHiddenHttpMethodFilter hiddenHttpMethodFilter() {
return new OrderedHiddenHttpMethodFilter();
}

先追溯HiddenHttpMethodFilter.class发现没有配置@Bean,说明没有装配到IOC中,那么满足条件,执行其下语句。

注意@ConditionalOnProperty,这个是查看配置文件中是否存在指定属性,存在才会满足该注解,否则就取默认值matchIfMissing = false。显然配置文件中并没有配置spring.mvc.hiddenmethod.filter.enabled,那么Rest服务就不会开启。前往application.yml添加:

1
2
3
4
5
spring:
mvc:
hiddenmethod:
filter:
enabled: true

至此我们就可以做到访问同一个URL,却能分别对应到不同控制器了。

以上内容只是针对表单请求,如果其他软件(Postman)可以直接发送DELETE、PUT请求,就只需要开启Rest服务即可。

搭建一个自己的后台管理系统

我们可以在Bootstrap或Laiyu中下载后台模板或者组件,将静态资源文件夹如css、js、images、front复制到static文件夹之下。将html的一些基础页面放到template目录下。这样就能基本的套用这套模板了。详细可看视频:模板套用至SpringBoot,视频中所用模板下载:[AdminEx .rar](/download/AdminEx .rar)

打开网站可能会不如视频中的流畅,这是因为一些文件中通过链接引用了网络资源,这样每次加载就需要先去请求网络资源,要么先将该资源下载至本地,要么就将该链接引用删除。这里我们删去css/style.css中的第一行import

1
@import url(http://fonts.googleapis.com/css?family=Open+Sans:400%2C300%2C300italic%2C400italic%2C600%2C600italic%2C700%2C700italic%2C800%2C800italic);

公共页抽取

可以看到一个后台管理界面中,有一些固定部分如左侧导航栏,顶部信息栏,而其余部分是随着不同请求返回不同结果

对于这些公共内容,我们可以抽取到一个commom.html中,然后给每一组件冠以唯一id,这样在具体界面直接引用该组件就行。

我们利用元素审查,分别选中左侧导航栏与顶部信息栏,找到对应的代码段

ctrl+shift+C快速打开审查元素

通过这种方式我们就可以将这些公共代码段复制到common.html中,但注意除了这些显示的组件每个界面的样式也是一致的,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<!--common-->
<link href="css/style.css" rel="stylesheet">
<link href="css/style-responsive.css" rel="stylesheet">
<!-- HTML5 shim and Respond.js IE8 support of HTML5 elements and media queries -->
<!--[if lt IE 9]>
<script src="js/html5shiv.js"></script>
<script src="js/respond.min.js"></script>
<![endif]-->

<!-- Placed js at the end of the document so the pages load faster -->
<script src="js/jquery-1.10.2.min.js"></script>
<script src="js/jquery-ui-1.9.2.custom.min.js"></script>
<script src="js/jquery-migrate-1.2.1.min.js"></script>
<script src="js/bootstrap.min.js"></script>
<script src="js/modernizr.min.js"></script>
<script src="js/jquery.nicescroll.js"></script>

<!--common scripts for all pages-->
<script src="js/scripts.js"></script>

为了以后调试方便,我们把所有href和src都替换成Thymeleaf样式,如<link th:href="@{/css/style.css}" rel="stylesheet">

但注意要提前添加xml约束xmlns:th="http://www.thymeleaf.org"。最终完成情况:common.html

可能注意到既然是以组件的形式插入到其他页面中去,我们就要把一些标签以<div>...</div>的闭合标签框起来,比如:

1
2
3
4
5
6
7
8
9
10
<div id="commonscript">
<script th:src="@{/js/jquery-1.10.2.min.js}"></script>
<script th:src="@{/js/jquery-ui-1.9.2.custom.min.js}"></script>
<script th:src="@{/js/jquery-migrate-1.2.1.min.js}"></script>
<script th:src="@{/js/bootstrap.min.js}"></script>
<script th:src="@{/js/modernizr.min.js}"></script>
<script th:src="@{/js/jquery.nicescroll.js}"></script>
<!--common scripts for all pages-->
<script th:src="@{/js/scripts.js}"></script>
</div>

这样我们就相当于把这些js文件打包成了一个组件,在index.html中需要添加组件时只需要在合适位置使用以下之一即可。

1
2
#1   <div th:replace = "common :: #commonscript"></div> 
#2 <div th:include="common :: #commonscript"></div>

第一种方式会将<div th:replace = "common :: #commonscript"></div> 全部删去,然后把整个组件加上(包括组件的div闭合标签);然而第二种方式将会保留<div th:include="common :: #commonscript"></div>的div标签,标签的包含内容用组件内容填充(不包括div闭合标签)。

这部分知识还有部分没有涉及,如id=“commonscript”可以替换成th:fragment=”commonscript”,那么引用组件方式也会有些许不同。

拦截器

拦截器与过滤器类似,我们在SSM整合的时候曾配置过过滤器用来解决乱码问题,但他是直接在配置文件中配置的。

1
2
3
4
5
6
7
8
9
10
11
12
<filter>
<filter-name>encodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>utf-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

在Spring中我们提到,除了配置文件直接配置,我们还能通过JavaConfig配置组件到IOC容器中,这种方式将会在SpringBoot中广泛使用,因为一个JavaConfig类继承了WebMvcConfigurer接口,就能一致通过addXXX()方法来配置你想要的组件了。

以这种方式,我们创建:Config/AdminWebConfig.java:

1
2
3
4
5
6
7
8
9
10
@Configuration
public class AdminWebConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LoginInterceptor()) //添加的拦截器
.addPathPatterns("/**") //拦截器拦截路径,即所有资源路径都要经过拦截器
.excludePathPatterns("/", "/login", "/css/**", "/fonts/**", "/images/**", "/js/**");
//拦截器的放行路径,这里需要放行登录请求和所有静态资源,避免登入页缺失渲染
}
}

覆写WebMvcConfigurer中的默认方法可以不用在方法名上添加@Bean,如果你需要添加独有的组件就必须添加注解@Bean,因为只有被IOC接管的组件才会在项目中生效。

以上只是写了拦截器的拦截路径和放行路径,具体拦截的处理还没有定义。我们写一个Interceptor/LoginInterceptor.java保证未登入的用户会被强制登入:

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
@Slf4j
public class LoginInterceptor implements HandlerInterceptor {
/*方法执行前*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
log.info("preHandler拦截了{}",request.getRequestURI()); //控制台打印信息
HttpSession session = request.getSession();
Object loginUser = session.getAttribute("loginUser");
if (null == loginUser) { //查询到用户之前没有登陆过
session.setAttribute("loginError","请先登入"); //将提示信息发送到Session,在前端就可以展示了
response.sendRedirect("/"); //重定向到登入页
return false;
}else{
return true;
}
}

/*方法执行后*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {}

/*页面渲染后*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {}
}

数据访问

导入JDBC场景

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

数据库驱动是需要额外导入的,因为SpringBoot不知道你会连接哪个数据库,同时也要注意数据库版本要和数据驱动的版本对应,这里以MySQL为例

1
2
3
4
5
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>

ctrl+左击访问版本仲裁,知道了SpringBoot托管的mysql驱动版本为8.0.23,查询本机Mysql版本为5.7.24。官方文档说明:Connector/J 8.0 provides compatibility with all the functionality of MySQL 5.5, 5.6, 5.7, and 8.0,也就是说8.0的驱动可以兼容所有版本的MySQL数据库。

接下来需要在pom.xml中配置数据库信息

1
2
3
4
5
6
spring:
datasource:
url: jdbc:mysql://localhost:3306/matlab
username: root
password: tzq1635886602
driver-class-name: com.mysql.cj.jdbc.Driver //8.0+的数据库驱动需要加上cj

配置版Mybatis

创建com.theo.Mapper.AccountMapper.java:

1
2
3
4
@Mapper
public interface AccountMapper {
Account queryAccount(String username);
}

@Mapper注解表示将该接口类+配置类注入到IOC容器中,也就是说服务层如果要取用Mapper将不需要利用事先的MybatisUtils.getsession()以及sqlsession.getMapper(Account.class)来获取接口类的实例了,因为SpringBoot自动做了这一步,然后我们只需要通过

1
2
@Autowired
AccountMapper accountMapper;

即可直接从容器中获取,accountMapper就可直接调用所有接口方法。

这种方式可能IDEA会报错:Could not autowire. No beans of ‘AccountMapper’ type found.

这是编译器的问题,不用管。

接下来创建resources.Mybatis.Mapper.AccountMapper.xml:

1
2
3
4
5
6
7
8
9
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.theo.Mapper.AccountMapper">
<select id="queryAccount" resultType="Account">
SELECT * FROM matlab.patient where username = #{username};
</select>
</mapper>

对应的映射AccountMapper中的接口方法。此后我们在application.yml中配置Mybatis的配置信息,以此代替mybatis-config.xml:

1
2
3
4
5
mybatis:
mapper-locations: classpath:Mybatis/Mapper/*.xml
typeAliasesPackage: com.theo.pojo
configuration:
map-underscore-to-camel-case: true

当然你也可以存在mybatis-config.xml,其中填上别名,mapper映射地址等等,但同时要加上配置config-location: classpath:Mybatis/mybatis-config.xml,这样的话就一定要删除configuration这一配置!!!!两者不能共存!!!!!!!!

Shiro


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!