IDEA-SpringBoot学习笔记
- 手动创建一个maven SpringBoot应用
- 使用Spring Initializr搭建Springboot应用(常用方式)
- 通过@ConfigurationProperties注解将yaml文件中的配置自动注入到组件
- Controller层的一系列注解
- 视图解析——Thymeleaf
- SpringBoot使用拦截器
- 文件上传
- SpringBoot配置原生Servlet组件
- SpringBoot整合数据源
- 使用Druid的简便方式
- 整合Mybatis
- MybatisPlus
- Thymleaf的常见应用
个人学习笔记,如有错误欢迎指正
手动创建一个maven SpringBoot应用
1.pom.xml文件的编写:
当工程创建后会根据工程创建的选项自动生成gav坐标,除此之外,作为springboot工程,我们还必须要添加的有:
1.parent
2.dependencies
parent:
parent标签中需要写的是springboot应用的父工程,夫工程将会为我们完成项目版本的管理,几乎所有的依赖版本都已在夫工程中做了默认设置
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.0.RELEASE</version>
</parent>
dependencies:
这里以web开发场景为例,denpendencies中需要加入相应场景需要的“启动器”,springboot可以适用于许多场景,不仅仅是web开发,当选定了自己需要的应用场景后,只需要在denpendencies中加入对应的“启动器”依赖即可,而不同场景的“启动器”的gav坐标也有许多相似之处,很多都是artifactId标签中最后一个单词不同而已
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
上面的代码中加入了soringboot在web场景下的“启动器”依赖,除此之外还加入了lombok和devtools,lombok暂时先不展开,在说到domin的时候再展开,而devtools只是一个使用idea开发springboot应用时一个方便开发的工具而已,与项目本省无关,这里大致介绍一下:idea(eclipse中可定也可以用,但是操作不一定相同)开发springboot应用时,使用devTools可以在当代码进行修改后,按下Ctrl+F9即可重启启动中的项目,并非真正的热更新
关于将应用打包成jar:
首先再pom中添加如下代码:
<packaging>jar</packaging><!--也可以是war,指的是打包的类型-->
<build>
<plugins>
<plugin><!--该插件就是用于应用打包的插件-->
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
当要进行打包时:
先双击clean(可以清除之前打过的包),然后双击package,运行成功后就可以在target目录下找到jar包了
2.启动类编写:
在自定义的根目录下可以创建一个带有主方法的Java类作为启动类,示例如下
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
//加上该注解即标志该类为启动类
@SpringBootApplication
public class MainApplication {
public static void main(String[] args) {
SpringApplication.run(MainApplication.class, args);
}
}
@SpringBootApplication是默认形式,除此之外还有一种替代写法,可用于手动指定包扫描的目录
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
//@SpringBootApplication //以下三个注解可替代该注解
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan("com.csq.boot") //手动指定注解扫描的目录
public class MainApplication {
public static void main(String[] args) {
SpringApplication.run(MainApplication.class, args);
}
}
启动类主方法中的这段代码的返回值即是spring的ioc容器
//返回ioc容器
ConfigurableApplicationContext run = SpringApplication.run(MainApplication.class,args);
//查看所有组件的名字
String[] beanDefinitionNames = run.getBeanDefinitionNames();
//根据beanid和类型获取容器中的bean
User user1 = run.getBean("user1", User.class);
3.domin中lombok的使用
domin中放的即是用于存放数据的java类,有多种叫法,个人习惯叫domin,这里以User类为例介绍lombok的使用:
首先需要给idea安装lombok插件(eclipse也有),还需要再pom文件中加入lombok依赖,接下来只需要在java类中加上一些注解即可,使用lombok可以避免编写get、set等方法的繁琐操作
package com.csq.boot.domine;
import lombok.*;
@Data //提供get、set方法
@AllArgsConstructor //提供全属性构造方法
@NoArgsConstructor //提供无参构造方法
@ToString //提供toString方法
@EqualsAndHashCode //提供equals、hashCode方法
public class User {
private String name;
private Integer age;
}
4.配置类(重要)
该知识点非常重要,对于源码的理解也会有一定的帮助,有助于理解springboot一系列默认配置
首先以一个简单的配置类为例:
package com.csq.boot.config;
import com.ibm.boot.domine.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration(proxyBeanMethods = true) //加上该标签等同于这个类是一个配置文件
public class MyConfig {
@Bean //加上该标签,会向ioc容器中添加以方法名为id,返回值为类型的组件
public User user01(){
return new User("jack",18);
}
}
加上@Configuration注解即表示该类为一个配置类,可以理解为它就是speing配置文件的一部分,而每一个@Bean注解就是一个配置文件中的注解。@Configuration中的proxyBeanMethods = true时每当需要创建bean加入到容器中时就会检查容器中时否已有该组件,如果有,则不会重复创建
接下来围绕配置类介绍一些注解:
1.@ImportResource(“xxx.xml”)注解:
该注解用于引入原生的spring配置文件,例如当想要引入resources文件加下的application.xml文件时,可以在配置类上加上该注解
application.xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="ConfigUser" class="com.csq.boot.domine.User">
<property name="name" value="jack"/>
<property name="age" value="21"/>
</bean>
</beans>
配置类类名上注解的书写:
@ImportResource("classpath:application.xml")
2.@ConditionalOnBean(name = “xxx”)和@ConditionalOnMissingBean(name = “xxx”)注解:
这两个注解即可用在类上,又可以用在@Bean标注的方法上,表示创建一下组件时需要检查容器中时否已经存在id为xxx的组件,若存在(不存在)才会创建,一下以举例
用在方法上:
@ConditionalOnBean(name = "ConfigPet")
@Bean("ConfigUser")
public User createUser(){
return new User("jack",21);
}
用在类上写法相同,相当于作用与类中的所有方法。至于@ConditionalOnMissingBean的用法与@ConditionalOnBean完全相同,只是作用相反而已
3.@EnableConfigurationProperties和@ConfigurationProperties(prefix = “xxx”)注解:
首先说说@ConfigurationProperties(prefix = “xxx”)注解,该注解适用于domin中的java类上,使用这个注解需要保证该组件在容器中,因此需要配合@Component注解一起使用;另一种使用方法是不适用@Component注解,而是在配置类中使用@EnableConfigurationProperties注解。作用:将使用了@ConfigurationProperties注解的组件添加到容器,同时使用配置文件中(如spring boot的yaml配置文件)指定前缀(prefix = “xxx”)的键值对对组件属性进行赋值,下面以@EnableConfigurationProperties和@ConfigurationProperties(prefix = “xxx”)的使用为例:
在配置文件中需要的配置:
#这里使用了mycar这个前缀
mycar.brand=BYD
mycar.price=1000000.0
Car.java:
package com.csq.boot.domine;
import lombok.*;
import org.springframework.boot.context.properties.ConfigurationProperties;
//顺便用一下lombok
@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
@EqualsAndHashCode
@ConfigurationProperties(prefix = "mycar") //前缀需要使用小写
public class Car {
private String brand;
private double price;
}
配置类上需要加上@EnableConfigurationProperties注解:
@EnableConfigurationProperties(Car.class)
这样应用启动后容器中就会有“car”这个组件,并且自动绑定了配置文件中的属性
4.@Import注解:
该注解也可以向容器中加入组件,不同之处在于它后面的参数是一个数组,因此可以一次加入多个组件。这个注解应该是需要用在配置类上方的。
@Import({User.class,Pet.class})
使用Spring Initializr搭建Springboot应用(常用方式)
1.点击file > New > project… 弹出如下窗口:
点击Spring Initializr,选择Project SDK,点击next进入如下窗口
选择输入项目选项后点击next进入如下窗口
在这里可以选择可能会用到的场景依赖,选择完成点击next会自动在pom文件中添加算选的场景依赖。此处选择完成后点击next进入如下窗口
输入项目名和存储位置后点击finish即可完成项目的创建(如果是第一次时间会略长,耐心你等待)
通过@ConfigurationProperties注解将yaml文件中的配置自动注入到组件
之前有讲过@ConfigurationProperties注解和@EnableConfigurationProperties注解配合使用完成组件自动注入的知识点,接下来针对包含复杂属性组件进行研究,重点在于yaml文件中的一些语法
1.加注解
回顾之前的知识点,要完成组件的自动注入,需要使用@ConfigurationProperties注解,同时需要指定其prefix属性,即属性值在配置文件中的前缀,之后还需要保证该组件是存在于容器之中的,这里就有两种选择,一种是在配置类中使用@EnableConfigurationProperties注解;另一种方式是在组件java类上添加@Component注解。这里将采用后一种方式
组件java类:
package com.csq.boot.domin;
import lombok.Data;
import lombok.ToString;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Set;
@Data
@ToString
@Component
@ConfigurationProperties(prefix = "person")
public class Person {
private String userName;
private Boolean boss;
private Date birth;
private Integer age;
private Pet pet;
private String[] interests;
private List<String> animal;
private Map<String, Object> score;
private Set<Double> salarys;
private Map<String, List<Pet>> allPets;
}
可以看到这个组件类中包含了基本数据类型(包装类暂且认为也是基本数据类型)以及自定义类、数组、集合,接下来介绍yaml语法中这些东西的书写方式:
首先需要明白yaml语法中通过缩进控制层级关系(缩进不要求缩进的空格数,只要对其就好,不能使用Tab缩进,但是idea中使用Tab似乎也是可以的),这里不明白可以继续往下看,看到最终地例子就会明白了
①普通属性值
#主义“:”后的空格,不能丢
userName: jack
②Date类型
birth: 1999/09/05
③数组和List、Set
这三种类型在yaml中可以认为都是线性表,写法有以下两种
interests: [篮球,足球]
animal:
- jerry
- tom
salarys:
- 99999.90
- 99999.98
④对象和Map
在yaml语法中Map和对象的下发一致
score:
english: 80
math: 90
Person类
综合以上语法书写Person类的配置信息
person:
userName: jack
#当遇到字符串时可以不加引号,当使用""表示不对字符进行转义(即字符串中出现/n会换行),而是用''则会进行自动转义(即字符串中存在/n会将其输出为//n)
boss: true
birth: 1999/09/05
age: 22
#数组和List、Set的表示方法相同,因此以下两种方式他们都能用
interests: [篮球,足球]
animal:
- jerry
- tom
#Map是键值对的形式,写法与对象类似(第一种写法“{}”中“:”后不需要加空格,因为里面是json的写法)
# score: {english:80,math:90}
score:
english: 80
math: 90
salarys:
- 99999.90
- 99999.98
# pet: {name:tom,weight:20.9}
pet:
name: tom
weight: 20.9
allPets:
sick:
- {name: tome,weight: 20.9}
# - {name:jerry,weight:5.9}
- name: jerry
weight: 5.9
health: [{name: 小花,weight: 15.5}]
# - {name:小花,weight:15.5}
特别说明allPets这个属性,它本身是一个Map,但是每一个元素(键值对)的value又是一个List,List中的每一个元素又是一个Pet对象(数组或List、Set中的对象用“{}”框住,使用“,”隔开),因此需要结合上述讲到的多个知识点
Controller层的一系列注解
1.Rest风格请求
rest风格请求希望在怎对数据进行的操作分别对应GET、POST、PUT、DELETE四种请求,而浏览器通常只支持get、post两种,一下操作可以完成springboot进行rest风格请求
①启用rest过滤器
#enabled: true:启用rest过滤器
spring:
mvc:
hiddenmethod:
filter:
enabled: true
②前端表单
表单要发送GET、POST请求只需将form的method属性改为get或post即可,而要发送PUT、DELETE请求需要在表单中添加一条name="_m" value="put / delete"的input,而这个inpute不需要展示出来,因此将type设置为hidden即可,以发送PUT请求为例:
<form action="put" method="post">
<!--这里的name="_m"是固定写法,通过自定义配置类可以改变-->
<input type="hidden" name="_m" value="put">
<input type="submit" value="提交">
</form>
③Controller中的方法
不同于普通请求通过@RequestMapping注解,针对不同的请求需要使用@GetMapping、@PostMapping、@PutMapping和@DeleteMapping注解来接收,对应上面的例子,这里仍以PUT请求为例:
//类上使用了@RestController(相当于同时使用了@Controller和@ResponseBody),因此这里返回String不会被视图解析器解析为页面
@PutMapping("/put")
public String putMethod(){
return "PUT";
}
2.参数绑定的注解
先把例子中相关的的页面代码贴出来,接下来的部分会逐一研究
<h1>参数绑定注解的测试</h1>
<a href="/car/1/owner/zhangsan?age=18&inters=basketball&inters=game">/car/1/owner/zhangsan</a>
<br>
<form action="/save" method="post">
username:<input type="text" name="username"><br>
password:<input type="password" name="password"><br>
<input type="submit" value="submit">
</form>
①@PathVariable注解
该注解适用于获取get请求url中的参数,观察上面例子中的url
href="/car/1/owner/zhangsan?age=18&inters=basketball&inters=game"
若想接收href中“car/”后的“1”和“1/”后面的“zhangsan”就可以使用这个注解,Controller写法(这一节的Controller中均使用了@RestController):
@GetMapping("/car/{id}/owner/{username}")
public String getCar(@PathVariable("id") Integer id,
@PathVariable("username") String name){
return id.toString() + ":" + name;
}
该注解含可以一次将多个url中的参数封装的Map<String,String>中
@GetMapping("/car/{id}/owner/{username}")
public void getCar(@PathVariable Map<String,String> pv){
pv.forEach((key,value) -> {
System.out.println(key + " = " + value);
});
}
②使用@RequestHeader注解获取请求头信息
首先,获取单个请求头参数
@GetMapping("/car/{id}/owner/{username}")
public void getCar(@RequestHeader("User-Agent") String agent){
System.out.println("User-Agent:" + agent); //User-Agent是用户浏览器信息
}
使用Map<String,String>获取所有请求头参数
@GetMapping("/car/{id}/owner/{username}")
public void getCar(@RequestHeader Map<String,String> headers){
headers.forEach((key,value) -> {
System.out.println(key + " = " + value);
});
}
③使用@RequestParam注解获取请求参数
再来看一下“<a>”中的href(这里仅以get请求为例)
href="/car/1/owner/zhangsan?age=18&inters=basketball&inters=game"
当要获取age、inters属性时
@GetMapping("/car/{id}/owner/{username}")
public void getCar(@RequestParam("age") Integer age,
//当一个参数有多个值时也可以使用List获取
@RequestParam("inters") List<String> inters){
System.out.println("age:" + age);
headers.forEach(item -> System.out.println(key + " = " + value));
}
该注解同样可以使用Map<String,String>获取多个参数
@GetMapping("/car/{id}/owner/{username}")
public void getCar(@RequestParam Map<String,String> params){
params.forEach((key,value -> {
System.out.println(key + " = " + value);
}));
}
④使用@CookieValue注解获取cookie
@GetMapping("/car/{id}/owner/{username}")
public void getCar(//使用如下注解可获取cookie(“Idea-1514c524”是本次测试时idea自带的测试cookie属性的key)
@CookieValue("Idea-1514c524") String cookie1,
@CookieValue("Idea-1514c524") Cookie cookie2){
System.out.println("Idea-1514c524:" + cookie1);
System.out.println(cookie2.getName() + "=" + cookie2.getValue());
}
⑤@RequestBody获取请求体数据
不同于get方式,post请求有请求体,可以通过使用@RequestBody获取表单中的数据,先回顾一下页面上的表单
<form action="/save" method="post">
username:<input type="text" name="username"><br>
password:<input type="password" name="password"><br>
<input type="submit" value="submit">
</form>
Controller代码
@PostMapping("/save")
public void saveUser(@RequestBody String content){
System.out.println(content);
}
输出如下字符串
username=jack&password=123
⑥使用@RequestAttribute获取HttpServletRequest对象中的数据
@GetMapping("/goto")
public String gotoPage(HttpServletRequest request){
HttpSession session = request.getSession();
session.setAttribute("message","success!");
session.setAttribute("code","200");
//这种写法表示将请求转发到 /success 请求,但好像不是普适的
return "forward:/success";
}
@GetMapping("/success")
@ResponseBody
public String successHandler(@RequestAttribute String message,
@RequestAttribute Integer code){
return message + ":" + code;
}
gotoPage方法中向HttpServletRequest 中放入数据,之后将请求转发到successHandler方法处理,在successHandler中即可使用@RequestAttribute 注解将参数取出,但是该注解并没有提供之前集中注解中使用Map<String,String>将多个参数一次获取的功能
@MatrixVariable注解及矩阵变量
首先,矩阵变量的使用是建立在路径变量的基础上,以下是一个矩阵变量的例子:
<a href="/cars/sell;low=34;brand=byd,audi,wuLingHongGuang">矩阵变量</a><br>
这里的sell是路径变量,“;”之后,下一个“/”之前是矩阵变量,当一个路径变量下有多个矩阵变量时使用“;”隔开;当一个矩阵变量有多个值时使用“,”隔开。上图中sell路径变量下有两个矩阵变量,分别是low和brand,而brand又含有多个值(brand=byd、audi和wlhg)
接下来就是后台如何接收矩阵变量了,以下是后台代码示例
@GetMapping("/cars/{path}")
public @ResponseBody Map carsSell(@MatrixVariable("low") Integer low,
@MatrixVariable("brand") List<String> brand,
@PathVariable("path") String path){
Map<String,Object> map = new HashMap<>();
map.put("low",low);
map.put("brand",brand);
map.put("path",path);
System.out.println(low);
System.out.println(brand);
System.out.println(path);
return map;
}
输出的值
34
[byd, audi, wuLingHongGuang]
sell
springBoot默认是禁用了矩阵变量的功能,需要我们手动开启矩阵变量的功能
springBoot默认是禁用了矩阵变量的功能,需要我们手动开启矩阵变量的功能。首先我们需要明白矩阵变量为何会被禁用?原因即在于springBoot的自动配置(默认配置),我们要做的就是使用自定义的配置,“覆盖”springBoot默认的配置,这就是之前讲到的配置类(所以说配置类很重要),针对这里的问题,我们先清楚问题所在:springBoot中默认对WebMvcConfigurer(web相关的很多配置都在其中)接口的实现中有一个名为ConfigurePathMatch方法,它有一个参数PathMatchConfigurer,PathMatchConfigurer中又有一个属性为UrlPathHelper,默认将UrlPathHelper属性removeSemicolonContent默认设置成了true这就会导致传递到后端带有矩阵变量的url的矩阵变量部分会被“砍掉”(这部分涉及到源码,不明白也不影响操作)。以下介绍两种开启矩阵变量功能的办法:
方法一,使用实现WebMvcConfigurer接口的自定义配置类代替默认配置类
该方法需要我们重写ConfigurePathMatch方法,配置类代码如下
package com.csq.springboot.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.PathMatchConfigurer;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.util.UrlPathHelper;
@Configuration(proxyBeanMethods = false)
//由于springBoot推荐使用jdk1.8以上版本,接口有默认实现,所以我们无需对其他接口进行实现
public class MyConfig implements WebMvcConfigurer {
/**
* 手动开启矩阵变量:new一个新的UrlPathHelper
* 并将removeSemicolonContent设置为false
* 代替PathMatchConfigurer中默认的UrlPathHelper
* @param configurer
*/
@Override
public void configurePathMatch(PathMatchConfigurer configurer) {
UrlPathHelper pathHelper = new UrlPathHelper();
pathHelper.setRemoveSemicolonContent(false);
configurer.setUrlPathHelper(pathHelper);
}
}
方法二,在配置类中使用@Bean注入自定义WebMvcConfigurer实现类
代码如下:
package com.csq.springboot.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.PathMatchConfigurer;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.util.UrlPathHelper;
/**
* 手动开启矩阵变量的第二种方式:不需要实现接口,直接通过@Bean注入
* @return
*/
@Bean
public WebMvcConfigurer getWebMvcConfigurer(){
//匿名内部类返回只主动实现了configurePathMatch方法的WebMvcConfigurer对象
return new WebMvcConfigurer() {
@Override
public void configurePathMatch(PathMatchConfigurer configurer) {
UrlPathHelper pathHelper = new UrlPathHelper();
pathHelper.setRemoveSemicolonContent(false);
configurer.setUrlPathHelper(pathHelper);
}
};
}
}
当仅有一个路径变量时,只需要使用@MatrixVariable注解,并将value值设置成想要获取的矩阵变量的变量名即可。可以看到,当使用@PathVariable时获取到的正式sell,因此证明了sell是一个路径变量
以上为只有一个路径变量,那么当有多个路径变量,甚至多个路径变量中还存在变量名相同的矩阵变量又该如何获取,以下为示例
<a href="/cars/1;age=34/2;age=23">获取两个矩阵变量的同名属性</a>
后台代码
@GetMapping("/cars/{boosId}/{empId}")
public @ResponseBody Map boosAndEmp(@MatrixVariable(value = "age",pathVar = "boosId") Integer boosAge,
@MatrixVariable(value = "age",pathVar = "empId") Integer empAge){
Map<String,Object> map = new HashMap<>();
map.put("boosAge",boosAge);
map.put("empAge",empAge);
return map;
}
这种含有多个路径变量,且其下含有同名矩阵变量的情况,只需要多使用一个pathVar指定需要获取矩阵变量的路径变量即可
小结
相信和多人有和我一开始一样的疑惑:@RequestAttribute和@RequestParam注解的区别是什么,这里不妨参考一下别人的文章:https://blog.csdn.net/weixin_45614626/article/details/108510486
一些小知识点
1.webjars
webjars可以使例如jQuery、Vue等的js文件通过jar包的形式被我们引用
首先我们需要引入依赖,这里以引入jQuery.js为例
<dependency>
<groupId>org.webjars</groupId>
<artifactId>jquery</artifactId>
<version>3.5.1</version>
</dependency>
随后我们只需要按照特定的路径就可以访问到js文件,就像以往的方式一样,只不过这里的路径需要去找:
点开Project中的External Libraries
从中找到jQuery
点开后打开META-INF > 找resources.webjars开头的文件夹
可以看到有jquery.js因此前端引入就可以这样写:
<script type="text/javascript" src="/webjars/jquery/3.5.1/jquery.js"></script>
也就是META-INF/resources之后的路径
视图解析——Thymeleaf
Thymeleaf基本语法:
功能 | |
---|---|
${…} | 获取request、session、对象等 |
*{…} | 获取上下文对象值 |
#{…} | 获取国际化等值 |
@{…} | 生成链接 (生成的链接会被自动拼接上项目名) |
~{…} | 引入公共页面 |
示例:
${…}:
从request中取出一个名为name的字符串,作为a的text:
<a href="/select" th:text="${name}">name</a>
从session中取出一个User类型名为user的对象的name属性到input的value中:
<input type="text" th:value="${session.user.name}">
@{…}:
假设项目根路径为http://localhost:8080/project,首页路径未http://localhost:8080/index.html,现在要求首页超链接点击后发送http://localhost:8080/project/login的请求:
<a th:href="@{/login}">登录</a>
~{…}:
当页面common.html中有公共的页面左侧菜单,菜单被包含在一div中,需要在页面a.html中引入:
<!--common.html中的部分代码-->
<div th:fragment="header" id="headerId">
...
</div>
<!--a.html中引入herder的三种方法及对应效果-->
1.<div th:insert:="common :: fragment名/选择器选择" id="target"></div>:
<div id="target">
<div id="headerId">
...
</div>
</div>
2.<div th:replace="common :: herder" id="target"></div>:
<div id="headerId">
...
</div>
3.<div th:include="common :: #headerId" id="target"></div>:
<div id="target">
...
</div>
使用Thymeleaf的示例
1.引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
2.页面开发
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www/thymeleaf.org"><!--加上xmlns使用thymeleaf时会有相应的高亮显示-->
<head>
<meta charset="UTF-8">
<title>success</title>
</head>
<body>
<h1 th:text="${message}">default text</h1>
<a href="http://www.XXX.com" th:href="${link}">csdn</a><br>
<!--使用@{link}会将"link"作为路径,这里是一个相对路径,因此这里的超链接指向的路径即是当前路径下的link(http://localhost:8080/link)
如果换成"/link"就会是根路径下的link(由于没有配置项目前置路径,这里两种方式结果相同)-->
<a href="http://www.XXX.com" th:href="@{link}">CSDN</a>
</body>
</html>
3*.修改项目前置路径
直接在配置文件中进行配置即可
server:
servlet:
context-path: /root
进行如上配置2.中使用“th:href="@{link}"”时相对路径(@{link})和绝对路径(@{/link})的结果就会不同了
4.后台开发
@GetMapping("/gotoSuccess")
public String thymeleafTest(Model model){ //model中的数据会被自动放到请求域中
model.addAttribute("message","hello");
model.addAttribute("link","https://mp.csdn.net/");
return "success";
}
SpringBoot使用拦截器
1.编写拦截器类:
编写一个拦截器类首先需要实现HandlerInterceptor接口,该接口中有三个方法:
preHandle:请求处理前,即请求方法执行前
postHandle:请求处理后,即请求方法执行结束后
afterCompletion:页面渲染后
以下为验证用户是否登录的拦截器示例:
package com.csq.web_admin.interceptor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
@Slf4j
public class LoginInterceptor implements HandlerInterceptor {
/**
* 方法执行前
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
log.info("intercepted path:" + request.getRequestURI());
//登录检查:从session中获取user对象,若存在认为用户已登录,则请求放行
HttpSession session = request.getSession();
if(null != session.getAttribute("user")){
return true; //返回true即表示放行
}
//跳转到登录页
request.setAttribute("message","您还未登录..."); //在request域中存放提示信息
request.getRequestDispatcher("/").forward(request,response); //转发
return false;
}
/**
* 方法执行完成后
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
log.info("method is completed:",modelAndView);
}
/**
* 页面渲染完成后
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
log.info("page is rendered:",ex);
}
}
2.使用实现WebMvcConfigurer接口的配置类配置拦截器:
package com.csq.web_admin.config;
import com.csq.web_admin.interceptor.LoginInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
* 有关web方面的配置都在WebMvcConfigurer接口中,而拦截器的配置又是通过其中的addInterceptors来完成的
*/
@Configuration
public class AdminWebConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LoginInterceptor())
.addPathPatterns("/**") //拦截器将会拦截的路径/**表示所有路径
.excludePathPatterns("/","/login","/css/**","/fonts/**","/images/**","/js/**"); //拦截器不会拦截的路径
}
}
文件上传
文件上传表单编写
需要注意,文件上传的表单method必须是post,且需要enctype=“multipart/form-data”
<form role="form" th:action="@{/upload}" method="post" enctype="multipart/form-data">
<!--单文件上传-->
头像:<input type="file" name="headPortrait">
<!--多文件上传 需要加上multiple-->
相册:<input type="file" name="photos" multiple>
<button type="submit">Submit</button>
</form>
文件上传controller编写
/**
* MultipartFile会自动封装传递过来的文件
*/
@PostMapping("/upload")
public String upload(@RequestParam("email") String email,
@RequestParam("name") String name,
//单文件接收
@RequestPart("headPortrait") MultipartFile headPortrait,
//多文件接收
@RequestPart("photos") MultipartFile[] photos) throws IOException {
//单文件保存
if (!headPortrait.isEmpty()){
//将文件以原文件名保存到本地磁盘
headPortrait.transferTo(new File("C:\\java\\picture\\" + headPortrait.getOriginalFilename()));
}
//多文件保存
if (photos.length > 0){
for (MultipartFile photo : photos) {
if(!photo.isEmpty()){
photo.transferTo(new File("C:\\java\\picture\\" + photo.getOriginalFilename()));
}
}
}
//保存结束返回根目录下main.html
return "main";
}
}
SpringBoot配置原生Servlet组件
方法一
配置Servlet
编写servlet
需要在自定义的Servlet上添加@WebServlet注解,urlPatterns属性用于指定该Servlet处理的请求路径
package com.csq.web_admin.servlet;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* 注入自定义的Servlet组件,Filter、Listener同理
*/
@WebServlet(urlPatterns = "/myServlet")
public class MyServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.getWriter().print("my servlet...");
}
}
启动类上加注解
在启动类上添加原生Servlet组件扫描注解,basePackages 属性用于指定扫描的包路径,配置Filter和Listener也需要该注解
@ServletComponentScan(basePackages = "com.csq.web_admin")
配置Filter
编写Filter
需要在自定义的Filter上添加@WebFilter注解,urlPatterns属性用于指定该Filter会拦截的请求路径,编写完成Filter同样需要在启动类上添加@ServletComponentScan注解
package com.csq.web_admin.filter;
import lombok.extern.slf4j.Slf4j;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;
@Slf4j
/** 单“*”是Servlet的写法,而“**”是Spring的写法*/
@WebFilter(urlPatterns = {"/css/*","/js/*"})
public class MyFilter implements Filter {
/**
*过滤器初始化执行
*/
@Override
public void init(FilterConfig filterConfig) throws ServletException {
log.info("MyFilter init...");
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
log.info("my filter...");
chain.doFilter(request,response);
}
/**
*过滤器销毁时执行
*/
@Override
public void destroy() {
log.info("MyFilter destroy...");
}
}
配置Listener
编写Listener
以下Liscener会在系统启动和注销时打印日志,Listener上需要加@WebListener注解,同时启动类上同样需要使用@ServletComponentScan注解
package com.csq.web_admin.listener;
import lombok.extern.slf4j.Slf4j;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;
@Slf4j
@WebListener
public class MyListener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent sce) {
log.info("system init...");
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
log.info("system destroyed...");
}
}
方法二
除了在自定义的Servlet组件上添加注解,还可以使用配置类对Servlet组件进行配置
编写Servlet组件
编写还与上一种方式一样,但是不需要在组件类上添加注解,启动类上的@ServletComponentScan注解同样也不需要
配置类的编写
以下的配置类的三个不同的方法分别将之前编写的三个Servlet组件放入到了容器中
package com.csq.web_admin.config;
import com.csq.web_admin.filter.MyFilter;
import com.csq.web_admin.listener.MyListener;
import com.csq.web_admin.servlet.MyServlet;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.boot.web.servlet.ServletListenerRegistrationBean;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.Arrays;
@Configuration
public class MyRegistConfig {
@Bean
public ServletRegistrationBean myServlet(){
MyServlet myServlet = new MyServlet();
return new ServletRegistrationBean(myServlet,"/myServlet");
}
@Bean
public FilterRegistrationBean myFilter(){
MyFilter myFilter = new MyFilter();
//方法1:这里的Filter将会拦截上面注入的这个Servlet的请求
//return new FilterRegistrationBean(myFilter, myServlet());
//方法2:
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(myFilter);
filterRegistrationBean.setUrlPatterns(Arrays.asList("/myServlet","/css/*"));
return filterRegistrationBean;
}
@Bean
public ServletListenerRegistrationBean myListener(){
MyListener myListener = new MyListener();
return new ServletListenerRegistrationBean(myListener);
}
}
SpringBoot整合数据源
SpringBoot整合自带的HikariPool数据源
1.导入依赖:
需要导入jdbc的starter,以及连接对应数据库的驱动(这里选用mysql)
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
2.配置数据源
个人使用的是较老版本的MySQL(大概5.5版本),MySQL8以上的url书写会有所不同
spring:
datasource:
url: jdbc:mysql://localhost:3306/databaseName?useUnicode=true&characterEncoding=utf8
#这里的引号,建议加上,不加可能会不好用
username: "root"
password: "123456"
driver-class-name: com.mysql.jdbc.Driver
3.使用JdbcTemplate操作数据库
这里使用测试类进行测试,JdbcTemplate是springBoot自带的操作数据源的小组件,它提供了一些简单的数据库操作方法
package com.csq.web_admin;
import com.csq.web_admin.domin.Admin;
import com.csq.web_admin.mapper.MybatisPlusMapper;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import javax.sql.DataSource;
import java.util.List;
@Slf4j
@SpringBootTest
class WebAdminApplicationTests {
@Autowired
JdbcTemplate template;
@Autowired
DataSource dataSource;
/**
* 当不配置其他第三方数据源时,springBoot会使用自带的HikariPool数据库连接池
*/
@Test
public void druidDataSorcesTest(){
//测试数据源
log.info("dataSource class:{}",dataSource.getClass());
List<Admin> adminList = template.query("select * from t_admin where adminid=1", new Object[]{}, new BeanPropertyRowMapper<Admin>(Admin.class));
if(null != adminList && adminList.size() > 0){
Admin admin = adminList.get(0);
log.info("admin:{}",admin);
}
}
}
使用Druid数据库连接池作为数据源
1.导入依赖
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.17</version>
</dependency>
2.启用Druid
这里将使用配置类通过@Bean的方式将Druid数据源对象加入到容器
package com.csq.web_admin.config;
import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.sql.DataSource;
import java.sql.SQLException;
import java.util.Arrays;
@Configuration
public class MyDataSourceConfig {
/**
* 自定义向容器中添加Druid数据源
* 使用读取配置文件的形式代替手动配置Druid数据源
* 将这个方法注释,Spring会使用默认的HikariPool
* @return
*/
@Bean
//通过下面的注解可以直接将之前写在配置文件中有关连接数据库的配置信息应用到Druid中
@ConfigurationProperties(prefix = "spring.datasource")
public DataSource getDataSource(){
DruidDataSource dataSource = new DruidDataSource();
return dataSource;
}
}
此时仍然执行之前测试类中的方法,日志打印DataSources的类型已经变成了Druid中的类型了
开启Druid其余功能
Druid提供非常丰富的数据源功能,这里仅以开启监控页的web监控和防火墙功能为例
仍然是在之前的配置类中进行配置:
1.开启监控页功能
监控页功能即Druid提供的一系列页面,我们可以通过浏览器访问:项目根目录/druid,进入监控页页面,要开启这一功能,需要用到一个Servlet(StatViewServlet),回顾之前通过配置类中使用@Bean像容器中注入Servlet余生组件,我们可以这样编写配置类中的方法:
@Bean
public ServletRegistrationBean servletRegistrationBean(){
StatViewServlet statViewServlet = new StatViewServlet();
ServletRegistrationBean<StatViewServlet> registrationBean =
new ServletRegistrationBean<>(statViewServlet, "/druid/*");
//通过添加初始化参数(initParameter)还可以配置访问白名单和监控页登录的用户名和密码
//以下两行代码是用于设置进入监控页的用户名和密码
registrationBean.addInitParameter("loginUsername","admin");
registrationBean.addInitParameter("loginPassword","123");
return registrationBean;
}
接下来即可尝试访问监控页:
配置进行到这里就可以通过 http://localhost:8080/druid 访问到监控页,但是其中的各项功能并不能正常使用,接下来介绍如何开启页面菜单上的其余功能
2.开启web监控功能
开启这两个功能需要向容器中加入一个Filter(webStatFilter),并对数据源进行简单的设置(使用相应的Set方法即可)。注入Filter,仍然是在配置类中回顾之前的方式
/**
* 配置Druid用于监控web应用的过滤器,过滤路径为"/*",但是一些静态资源不需要进行拦截
* @return
*/
@Bean
public FilterRegistrationBean filterRegistrationBean(){
WebStatFilter webStatFilter = new WebStatFilter();
FilterRegistrationBean registrationBean =
new FilterRegistrationBean<WebStatFilter>(webStatFilter);
/**
* 作用等同于web.xml中的
* <filter>
* <filter-name>DruidWebStatFilter</filter-name>
* <filter-class>com.alibaba.druid.support.http.WebStatFilter</filter-class>
* <initParameter>
* <param-name>exclusions</param-name>
* <param-value>*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*</param-value>
* </initParameter>
* </filter>
* <filter-mapping>
* <filter-name>DruidWebStatFilter</filter-name>
* <url-pattern>/*</url-pattern>
* </filter-mapping>
*/
//配置过滤器拦截路径
registrationBean.setUrlPatterns(Arrays.asList("/*"));
//配置过滤器不拦截的路径
registrationBean.addInitParameter("exclusions","*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*");
return registrationBean;
}
显然,从前在web.xml中可以进行的操作在springBoot中使用配置配仍然可以完成。然后就是在之前开启监控页的Servlte中进行配置:
try {
//开启Druid监控页监控功能,防火墙功能
dataSource.setFilters("stat,wall");
//限制最大活跃数
dataSource.setMaxActive(10);
} catch (SQLException e) {
e.printStackTrace();
}
接下来访问监控页,并对数据库进行访问都会配监控页检测到
关于Druid配置的配置类完整展示(无package、import、注释):
public class MyDataSourceConfig {
@Bean
@ConfigurationProperties(prefix = "spring.datasource")
public DataSource getDataSource(){
DruidDataSource dataSource = new DruidDataSource();
try {
dataSource.setFilters("stat,wall");
dataSource.setMaxActive(10);
} catch (SQLException e) {
e.printStackTrace();
}
return dataSource;
}
@Bean
public ServletRegistrationBean servletRegistrationBean(){
StatViewServlet statViewServlet = new StatViewServlet();
ServletRegistrationBean<StatViewServlet> registrationBean =
new ServletRegistrationBean<>(statViewServlet, "/druid/*");
registrationBean.addInitParameter("loginUsername","admin");
registrationBean.addInitParameter("loginPassword","123");
return registrationBean;
}
@Bean
public FilterRegistrationBean filterRegistrationBean(){
WebStatFilter webStatFilter = new WebStatFilter();
FilterRegistrationBean registrationBean =
new FilterRegistrationBean<WebStatFilter>(webStatFilter);
registrationBean.setUrlPatterns(Arrays.asList("/*"));
registrationBean.addInitParameter("exclusions","*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*");
return registrationBean;
}
}
使用Druid的简便方式
以上方式使用Druid显然不够方便,这明显不是SpringBoot的风格,接下来介绍通过Starter及纯配置文件的形式完成与上述相同的操作
加入依赖
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.17</version>
</dependency>
进行配置
进行配置之前记得删除之前的数据源配置类以及原生Druid的依赖
spring:
datasource:
url: jdbc:mysql://localhost:3306/databaseName?useUnicode=true&characterEncoding=utf8
username: "root"
password: "123456"
driver-class-name: com.mysql.jdbc.Driver
druid:
#监控页功能
stat-view-servlet:
enabled: true
login-username: admin
login-password: 123456
#禁用重置按钮
reset-enable: false
#web监控功能
web-stat-filter:
enabled: true
url-pattern: /*
exclusions: '*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*'
aop-patterns: com.csq.web_admin.*
#防火墙等功能
filters: stat,wall,slf4j
#对filters中的详细配置
filter:
stat:
#配置慢查询及相应日志记录
slow-sql-millis: 1000
log-slow-sql: true
enabled: true
wall:
enabled: true
config:
#可以配置哪些类型的sql会被拦截
update-allow: false
整合Mybatis
1.加入依赖
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.3</version>
</dependency>
2.进行配置
在配置文件中指定Mybatis全局配置文件及映射文件的位置:
mybatis:
#指定mybatis全局配置文件的位置
config-location: classpath:mybatis/mybatis-config.xml
#指定mybatis的sql映射文件
mapper-locations: classpath:mybatis/mapper/*.xml
当然,我们需要根据配置在相应位置创建好对应的文件
以下为各配置文件的基本格式示例:
全局配置文件:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
...
</configuration>
映射文件:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.csq.web_admin.mapper.AdminMapper">
...
</mapper>
示例
接下来从会展示一个从Controller层直到Dao层使用Mybatis的一个完整示例
Controller(仅展示用到的方法)
/**
*根据id从数据库中查出对应admin的信息
*/
@GetMapping("/mybatis")
public @ResponseBody Admin mybatisTest(@RequestParam("adminid") Integer adminid){
return myService.selectById(adminid);
}
Service(仅展示实现类中用到的方法)
@Override
public Admin selectById(Integer adminid) {
return adminMapper.selectById(adminid);
}
Dao
1.接口
@Mapper
public interface AdminMapper {
public Admin selectById(Integer adminid);
}
2.映射文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--namespace会指定对应的Dao层接口-->
<mapper namespace="com.csq.web_admin.mapper.AdminMapper">
<select id="selectById" resultType="com.csq.web_admin.domin.Admin">
select * from t_admin where adminid = #{adminid}
</select>
</mapper>
当Mybatis全局配置文件中没有什么配置时(这里的数据库连接配置已经写在了Spring Boot配置文件中),就可以将其删去(反正配置在SpringBoot配置文件中同样可以完成),同时还要将SpringBoot配置文件中指定全局配置文件位置的配置信息删去(关于Mybatis的一些配置在SpringBoot配置及全局配置文件配置中只能使用一个)。
除此之外,映射文件同样也可以没有,通过在Dao层接口上使用@Select、@Insert、@Delete和@Update注解也可以完成,当然,这两种方式也可以混合出现。
下面是一个Dao层接口及对应映射文件示例,展示使用纯注解和配置文件两种方式并存
接口:
package com.csq.web_admin.mapper;
import com.csq.web_admin.domin.Admin;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
@Mapper
public interface AdminMapper_onlyUseAnnotation {
@Select("select * from t_admin where adminid = #{adminid}")
Admin getById(Integer adminid);
void insertAdmin(Admin admin);
}
映射文件:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.csq.web_admin.mapper.AdminMapper_onlyUseAnnotation">
<!--useGeneratedKeys="true" keyProperty="adminid" 可以省去自增的“adminid”
同样也可以在dao接口中使用注解实现-->
<insert id="insertAdmin" useGeneratedKeys="true" keyProperty="adminid">
insert into t_admin(username,password)
values(#{username},#{password})
</insert>
</mapper>
MybatisPlus
MybatisPlus相比较Mybatis具有更加丰富的功能,可以使开发者避免编写一些简单的增删改查sql,并且还有根据Domin生成数据库表等功能
接下来介绍如何使用MybatisPlus
1.引入依赖
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.1</version>
</dependency>
需要注意的是需要把之前Mybatis Starter和jdbc Starter两个依赖,同样是因为MybatisPlus中均已引入
2.Domin java类上添加注解
顺便一提,配置仍然沿用之前Mybatis中的配置即可
package com.csq.web_admin.domin;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.ToString;
/**
* mybatisPlus默认会从数据库中寻找与类名相同的表
* 可以通过使用@TableName注解指定表名
* */
@TableName("t_admin")
@Data
@ToString
@AllArgsConstructor
public class Admin {
@TableId /*表明该属性在表中为主键*/
private Integer adminid;
private String username;
private String password;
/*
* mybatisPlus默认需要Domin对象中的属性在数据库中都有对应的列
* 但是可以:
* @TableField(exist = false)
* private String other;
* 声明该字段不存在于数据库表中
* */
}
3.Dao层接口的编写
MybatisPlus提供了许多数据库操作的方法,要想使用这些方法,只需要继承BaseMapper并指定要操作的类即可
package com.csq.web_admin.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.csq.web_admin.domin.Admin;
import org.apache.ibatis.annotations.Mapper;
/**
* 通过继承BaseMapper,并指定泛型,即可通过BaseMapper得到基本的增删改查方法
*/
@Mapper
public interface MybatisPlusMapper extends BaseMapper<Admin> {
//不需要编写任何方法即可调用BaseMapper中自带的方法
}
4.Service层的编写
①Service接口:
Service层接口只需要继承IService并指定要操作的类即可
package com.csq.web_admin.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.csq.web_admin.domin.Admin;
/**
* 继承IService(mybatisPlus提供的service层顶级接口)
* IService中有更加丰富的方法
*/
public interface AdminTableService extends IService<Admin> {
//Service接口中同样不需要编写人方法即可直接使用IService中丰富的方法
}
②Service实现类:
package com.csq.web_admin.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.csq.web_admin.domin.Admin;
import com.csq.web_admin.mapper.MybatisPlusMapper;
import com.csq.web_admin.service.AdminTableService;
import org.springframework.stereotype.Service;
/**
* extends ServiceImpl<MybatisPlusMapper,Admin>
* 通过继承ServiceImpl,我们无需对service层接口继承的IService接口中的方法进行实现
* 在这里只需要指定<dao层mapper,结果集对象>即可调用各种操作数据库的方法
*/
@Service
public class AdminTableServiceImpl extends ServiceImpl<MybatisPlusMapper,Admin> implements AdminTableService {
//...
}
Controller调用
这里举的例子使一个涉及到thymeleaf分页组件操作的Controller,关于thymeleaf分页组件的使用之后会介绍
package com.csq.web_admin.controller;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.csq.web_admin.domin.Admin;
import com.csq.web_admin.domin.User;
import com.csq.web_admin.service.AdminTableService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
import java.util.Arrays;
import java.util.List;
@Controller
public class TableController {
@Autowired
private AdminTableService adminTableService;
@GetMapping("/delete/{adminid}")
public String deleteAdmin(@PathVariable("adminid") Integer adminid,
@RequestParam(value = "current",defaultValue = "1") Integer current,
RedirectAttributes attributes){
adminTableService.removeById(adminid);
attributes.addAttribute("pageInteger",current);
return "redirect:/dynamic_table";
}
}
至此,使用MybatisPlus进行操作数据整个流程介绍完毕,这里暂时不介绍前台页面,因为其中有涉及到分页组件的部分,仍然留到后续研究
Thymleaf的常见应用
Thymleaf循环
这一应用被用于循环遍历后台传递的集合等数据结构中的数据
假设后端向session中存放一个名为users的ArrayList<User>集合,以下为后端代码
ArrayList<Admin> admins = new ArrayList<>();
admins.add(new Admin(1,"jack","123"));
admins.add(new Admin(2,"rose","456"));
httpSession.serAttribute("users",admins);
前端取值
<table>
<thead>
<tr>
<th>No</th>
<th>userid</th>
<th>username</th>
<th>password</th>
</tr>
</thead>
<tbody>
<tr th:each="admin,stats:${session.users}"><!--遍历Session中的List-->
<!--stats可以用来计数等操作-->
<td th:text="${stats.count}"></td>
<td th:text="${admin.userid}">
<td th:text="${admin.username}">username</td>
<!--行内写法-->
<td>[[${admin.password}]]</td>
</tr>
</tbody>
<tfoot>
<tr>
<th>No</th>
<th>userid</th>
<th>username</th>
<th>password</th>
</tr>
</tfoot>
</table>
循环加分页查询
后端代码:
@GetMapping("/dynamic_table")
public String dynamicTable(@RequestParam(value = "pageInteger",defaultValue = "1")
Integer pageInteger,
Model model){
//实现分页查询,参数:当前页码,每页显示条数(MybatisPlus)
Page<Admin> page = new Page<>(pageInteger, 2);
//参数:分页对象,查询条件对象
Page<Admin> pageResult = adminTableService.page(page, null);
//pageResult中封装了分页查询到的所有信息
model.addAttribute("pageResult",pageResult);
System.out.println("当前第几页:" + pageResult.getCurrent());
System.out.println("总页数:" + pageResult.getPages());
System.out.println("总条数:" + pageResult.getTotal());
System.out.println("所有记录的集合:" + pageResult.getRecords());
return "tables/dynamic_table";
}
/**
*在分页中删除后回到页面仍然保持在原来的页
*/
@GetMapping("/delete/{adminid}")
public String deleteAdmin(@PathVariable("adminid") Integer adminid,
@RequestParam(value = "current",defaultValue = "1") Integer current,
RedirectAttributes attributes){
adminTableService.removeById(adminid);
attributes.addAttribute("pageInteger",current);
return "redirect:/dynamic_table";
}
前端代码:
前端代码包括了分页查询及下方的切页组件
<table>
<thead>
<tr>
<th>#</th>
<th>username</th>
<th>password</th>
<th>operate</th>
</tr>
</thead>
<tbody>
<tr class="gradeX" th:each="admin,stats:${pageResult.records}">
<td th:text="${stats.count}">#</td>
<td th:text="${admin.username}">username</td>
<td>[[${admin.password}]]</td>
<td>
<a th:href="@{/delete/{adminid}(adminid=${admin.adminid},current=${pageResult.current})}" class="btn btn-danger" type="button">delete</a>
</td>
</tr>
</tbody>
<tfoot>
<tr>
<th>#</th>
<th>username</th>
<th>password</th>
<th>operate</th>
</tr>
</tfoot>
</table>
<!--分页组件:通过数据库进行分页查询-->
<div>
<div>
<div>
current page:[[${pageResult.current}]]
pages count:[[${pageResult.pages}]]
records count:[[${pageResult.total}]]
</div>
</div>
<div>
<div>
<ul>
<!--使用thymeleaf的numbers对象生成从from到to的数字序列,用来表示分页查询页码-->
<!--th:class="${num==pageResult.current?'active':''}":class为active时页码高亮,这里使用三元运算符判定是否为当前页,是,则高亮显示-->
<li th:class="${num==pageResult.current?'active':''}" th:each="num:${#numbers.sequence(1,pageResult.pages)}">
<!--为链接动态加参数实现点击切页-->
<a href="#" th:href="@{/dynamic_table(pageInteger=${num})}">[[${num}]]</a>
</li>
</ul>
</div>
</div>
</div>
未完待续…