0%

SpringBoot笔记

SpringBoot,简化Spring开发。

介绍

SpringBoot

Spring Boot是由Pivotal团队提供的全新框架,其设计目的是用来简化新Spring应用的初始搭建以及开发过程。该框架使用了特定的方式来进行配置,从而使开发人员不再需要定义样板化的配置。通过这种方式,Spring Boot致力于在蓬勃发展的快速应用开发领域(rapid application development)成为领导者。摘自百度百科

springIOC

Ioc—Inversion of Control,即“控制反转”,不是什么技术,而是一种设计思想。在Java开发中,Ioc意味着将你设计好的对象交给容器控制,而不是传统的在你的对象内部直接控制。如何理解好Ioc呢?理解好Ioc的关键是要明确“谁控制谁,控制什么,为何是反转(有反转就应该有正转了),哪些方面反转了”,那我们来深入分析一下:

  • 谁控制谁,控制什么:传统Java SE程序设计,我们直接在对象内部通过new进行创建对象,是程序主动去创建依赖对象;而IoC是有专门一个容器来创建这些对象,即由Ioc容器来控制对 象的创建;谁控制谁?当然是IoC 容器控制了对象;控制什么?那就是主要控制了外部资源获取(不只是对象包括比如文件等)。
  • 为何是反转,哪些方面反转了:有反转就有正转,传统应用程序是由我们自己在对象中主动控制去直接获取依赖对象,也就是正转;而反转则是由容器来帮忙创建及注入依赖对象;为何是反转?因为由容器帮我们查找及注入依赖对象,对象只是被动的接受依赖对象,所以是反转;哪些方面反转了?依赖对象的获取被反转了。
  • 更详细介绍,见这篇博客

使用注解@Autowired,实现自动装配,不需要手动创建实例;但是也需要在IOC中有相应的对象,否则会报错。

使用注解@Mapper@Service@RestController(底层还是注解@Component)的类,spring会自动创建相应的实例(在IOC容器中)

创建项目

在IDEA中创建SpringBoot+mybatis项目。更多案例,见后边“整合mybatis”部分。

准备

可以对MavenJDK进行默认配置。不用每次创建项目时都需要再配置一次。

  • JDK版本配置,在Project Structure(项目结构)中。
    • 左上角“文件” - 设置 - 项目设置 - 项目 - 项目SDK
  • maven配置。在settings(设置)中。
    • 左上角“文件” - 项目结构 - “构建、执行、部署” - 构建工具 - Maven
  • 相关文件可在这里下载。

创建项目

使用开发工具IDEA创建SpringBoot项目,有两种方式:

  • 从一个普通maven项目,搭建SpringBoot项目。参考这篇博客
    1. 创建一个普通maven项目
    2. 编写SpringBoot配置文件
    3. 编写SpringBoot启动类
  • 一开始就搭建一个SpringBoot项目。见下边的案例

若已有项目(project)则在项目中新建模块(module)即可,否则创建一个新的项目。新建的项目中会默认创建一个模块。项目中可以包含多个模块。

左上角“文件 ”- 新建项目 - Spring Initializer - 选择jdk版本,使用默认配置。

一些基本信息:

  • 填写公司域名,作为Java程序包名。
  • 填写项目名(Artifact)。
  • 选择开发使用的jdk版本。

类型选择Spring Web。

Project name设置与前边的Artifact中设置的相同。project location,建议创建一个文件夹专门保存项目文件。

选择自动导入。

新建项目成功的状态:

项目结构

文件application.properties为springBoo的配置文件,详细配置参考这篇博客

代码编写

依赖

在pom.xml 中导入对应的Maven依赖:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<!--spring-boot-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!--mybatis-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.2.0</version>
</dependency>
<!--mysql数据库的jdbc驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>

配置

springBoot的配置文件application.properties中配置服务器地址和账户等信息。配置如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#服务端口
server.port=9191
#========mysql数据库配置信息==============
#数据库地址为qsdbl.site,数据库名为crm02,设置时区serverTimezone=UTC
spring.datasource.url=jdbc:mysql://qsdbl.site:3306/crm02?serverTimezone=UTC
#数据库用户
spring.datasource.username=jack
#密码
spring.datasource.password=12xxxdbl--
#驱动
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
#-----------mybatis-----------
#指定实体类所在路径
mybatis.type-aliases-package=com.hesj.crm.domain
#指定映射文件所在路径。(用于扫描mapper接口对应的xml文件)
mybatis.mapperLocations=classpath:mapper/*.xml

关于mybatis的参数配置,见后边“整合mybatis”部分。

DAO模块

定义Mapper模块(叫DAO模块也行,一个名称而已),对数据库进行增删改查等操作。

  • 接口使用注解@Mapper
  • 扩展:在SpringBoot启动类中使用注解@MapperScan,可自动扫描接口不用每一个mapper接口都添加注解@Mapper
  • 编写接口DepartmentMapper
    • 定义方法queryAll,查询所有的部门信息,SELECT * FROM department,查询表department
    • 方法使用注解@Select(根据SQL语句选择相应注解,@Insert、@Delete、@Update、@Select等)
    • tips:sql语句验证通过后再添加到注解中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@Mapper
public interface DepartmentMapper {
// 查询所有的部门信息
@Select("SELECT * FROM department")
public List<Department> queryAll();

//根据id删除一个部门信息
@Delete("DELETE FROM department WHERE id= #{id}")//el表达式
public void delect(Long id);

//添加一个部门信息
//#{name} ==> department.getName()
//#{sn} ==> department.getSn()
@Insert("INSERT INTO department(NAME,SN)VALUES(#{name},#{sn})")//el表达式
public void insert(Department department);

//根据id修改部门数据
//UPDATE department SET NAME='',SN='' WHERE ID=''
@Update("UPDATE department SET NAME=#{name},SN=#{sn} WHERE ID=#{id}")
public void update(Department department);
}

扩展

可编写一个类,专门用于拼接复杂的sql语句。

1
2
3
4
5
6
7
8
9
10
11
12
public class DepartmentMapperProvider {
//提供一个拼接SQL语句的方法
public String querySql(DepartmentQueryObject qo){
StringBuilder sql = new StringBuilder();
sql.append("SELECT * FROM department");
//拼接模糊查询的条件
if (qo.getKeyWord() != null && qo.getKeyWord().length() > 0){
sql.append(" WHERE name LIKE '%").append(qo.getKeyWord()).append("%' OR sn LIKE '%").append(qo.getKeyWord()).append("%'");
}
return sql.toString();
}
}

注解@Select改为@SelectProvider。指定类和类中的具体哪个方法用于拼接SQL。

1
2
@SelectProvider(value = DepartmentMapperProvider.class,method = "querySql")
public List<Department> queryAll(DepartmentQueryObject qo);

service模块

业务层。编写业务逻辑,调用DAO模块中实现的功能。

service接口:

  • 编写接口IDepartmentService,定义方法queryAll。public List<Department> queryAll();
  • 接口可以不写,视具体的开发规范而定

实现类:

  • 编写实现类 DepartmentServiceImpl,实现方法queryAll。

  • 调用DAO模块中DepartmentMapper接口的相应方法,实现对数据库的增、删、改、查操作。

  • 实现类,使用注解@Service

  • 变量departmentMapper,使用注解@Autowired,实现自动装配(不需要手动创建实例;但是也需要在IOC中有相应的对象,否则会报错。原因看这里

    1
    2
    3
    4
    //Mapper接口通过@Autowired注入。在代码编辑时,由于没有对应的实现类,IDEA会标红报错,无视即可。
    //项目是能正常运行的。(由于动态代理机制,会自动创建并实例化一个实现类)
    @Autowired//自动装配。不需要手动创建接口departmentMapper的实例
    private DepartmentMapper departmentMapper;

web模块

编写web层实现(Controller层),根据前端页面的请求调用(使用)业务层实现的功能。

编写类DepartmentController,作为web层组件

  • 使用注解@RestController,将类声明为web层组件

  • (调用)使用Service层实现的功能,使用注解Autowired实现自动装配:

    1
    2
    3
    4
    5
    6
    7
    8
    //调用Service模块中的 实现类
    @Autowired//自动装配。不new DepartmentServiceImpl()也可以
    private DepartmentServiceImpl departmentService;

    //或 调用Service模块中的 接口
    // @Autowired//自动装配
    // @Qualifier("departmentServiceImpl")//指定实现类(只有一个实现类时可以省略)
    // private IDepartmentService departmentService;
  • 使用注解@RequestMapping,配置请求路径

    • 例如:@RequestMapping("department/query") 或 @RequestMapping(value="/login",method=RequestMethod.POST)
    • 可在Controller上使用,将作为路径前缀
    • 可细分为@GetMapping@PostMapping@DeleteMapping@PatchMapping@PutMapping
  • 如果形参为一个对象,(前端页面)只需要传递创建对象需要的成员变量即可,springBoot会自动实例化对象

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    //新增 或 修改:
    //参数是一个自定义的Department部门对象,传递数据的时候,只需要传递对应的Department部门对象的属性即可
    //比如说:name --> department.setName(name)
    //sn --> department.setSn(sn)
    @RequestMapping("/department/saveOrUpdate")
    public Boolean saveOrUpdate(Department department){
    if (department.getName() =="" && department.getSn() == ""){
    return false;//没有name、sn这两个参数,则直接退出
    }
    //判断id是否为空,如果为空,就是新增操作,否则就是修改操作
    if (department.getId() == null){
    //新增
    departmentService.insert(department);
    }else {
    System.out.println("修改数据:id="+department.getId());
    //修改
    departmentService.update(department);
    }
    return true;
    }

跨域配置

两种配置方式:

  • 配置类
  • Controller层中允许跨域请求的类上使用注解“@CrossOrigin”
  • 详细配置,见这篇博客

高级查询

模糊查询

模糊查询: https://www.processon.com/view/link/5fcc400be401fd1e15ca02c1

1
2
3
4
模糊查询,%
SELECT * FROM department WHERE name LIKE '%部%' AND sn LIKE '%w%'

多个条件,关键字:ANDOR

分页显示数据

MySQL分页语句

1
2
SELECT * FROM department LIMIT 20,10;
第一个参数,索引(第一行数据索引为0)。第二个参数,分页大小(数据量)

使用插件实现

老师笔记:分页功能的实现

导入依赖

mybatis分页插件:传送门。Maven依赖:

1
2
3
4
5
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>1.3.0</version>
</dependency>

帮助文档:传送门

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
例三,使用PageInfo的用法:
//获取第1页,10条内容,默认查询总数count
PageHelper.startPage(1, 10);
List<User> list = userMapper.selectAll();
//用PageInfo对结果进行包装
PageInfo page = new PageInfo(list);
//测试PageInfo全部属性
//PageInfo包含了非常全面的分页属性
assertEquals(1, page.getPageNum());
assertEquals(10, page.getPageSize());
assertEquals(1, page.getStartRow());
assertEquals(10, page.getEndRow());
assertEquals(183, page.getTotal());
assertEquals(19, page.getPages());
assertEquals(1, page.getFirstPage());
assertEquals(8, page.getLastPage());
assertEquals(true, page.isFirstPage());
assertEquals(false, page.isLastPage());
assertEquals(false, page.isHasPreviousPage());
assertEquals(true, page.isHasNextPage());

代码修改

DAO层不需要修改(查询所有数据select * from 表名)。修改service层和web层。

PageHelper插件的分页是基于mybatis框架实现的。使用了mybatis的拦截器。在执行查询sql前根据分页信息,把分页参数拼接到sql后面再去查询。

参数:

添加插件需要的用到的参数:

查询条件DepartmentQueryObject类中定义一个分页处理插件的两个参数。pageSize、currentPage。(将前端页面发送请求的参数封装在类中。springboot会使用接收到的参数自动创建对象)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package com.qsdbl.demo.qo;
public class DepartmentQueryObject {

//部门编号或者名称
private String keyWord;

//每页显示的数据大小
private Integer pageSize = 10;//默认值
//查询的当前页
private Integer currentPage = 1;

public String getKeyWord() {
return keyWord;
}
...//省略get、set方法
public Integer getPageSize() {
return pageSize;
}
}

service模块

使用到插件的PageInfo类,对数据进行封装。

修改IDepartmentService(接口)的查询方法的返回值:

1
public PageInfo<Department> queryAll(DepartmentQueryObject qo);

修改DepartmentServiceImpl类(实现类)的查询方法:PageHelper查询指定的分页数据。PageInfo将DAO模块中查询出来的List数据进行封装,返回PageInfo类型数据。

1
2
3
4
5
6
7
8
9
10
11
@Override
public PageInfo<Department> queryAll(DepartmentQueryObject qo) {
//设置对应的分页参数
//第一个参数pageNum:需要查询的当前页。pageSize:需要显示的数据量大小
PageHelper.startPage(qo.getCurrentPage(),qo.getPageSize());
//查询数据
List<Department> list = departmentMapper.queryAll(qo);//mapper只需要查询所有数据即可(qo参数非必要)
//封装成分页数据
PageInfo pageInfo = new PageInfo(list);
return pageInfo;
}

方法queryAll的改变:

  • 修改前
    • 可以查询所有数据和模糊查询所有匹配的数据。根据是否有参数KeyWord来区分(不为null且长度大于0)
    • 见拼接SQL语句的类DepartmentMapperProvider
  • 修改后
    • 也是可以查询所有数据和模糊查询所有匹配的数据
    • 但是显示的数据为分页插件pagehelper处理后的数据,所以显示数据的数量由参数pageSize确定,本案例中默认为10
    • 其他分页设置参数见将参数封装的类DepartmentQueryObject

web模块

修改DepartmentController查询方法的返回值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//查询:
//部门查询(从数据库获取数据)
@RequestMapping("department/query")
public PageInfo<Department> queryAll(DepartmentQueryObject qo){//没有传递参数,则为模糊查询,见类DepartmentMapperProvider
// List<Department> departments = departmentService.queryAll(qo);
// //返回部门数据
// return departments;

//更改:
// 返回的集合封装在PageInfo中(第三方Mybatis插件),方法返回类型由List<Department>集合改为PageInfo<Department>对象(原数据封装在变量list中),Service、Web层都要进行相应的更改(数据类型有List改为PageInfo)
//departmentService.query() ==> 调用方法
//departmentService ==> DepartmentServiceImpl
PageInfo<Department> pageInfo = departmentService.queryAll(qo);
//返回部门数据
return pageInfo;
}

前端页面

运行结果

结果集映射

下边整合mybatis中的案例

单元测试

测试,需要注意两个地方:

  • 测试类上,使用注解@SpringBootTest
  • 测试方法上,使用注解@Test
  • 回顾:junit单元测试

案例:

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
package com.qsdbl.nazox_demo;

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import javax.sql.DataSource;

@SpringBootTest
class MyTest {

@Autowired
DataSource dataSource;

@Test
public void test(){
System.out.println(dataSource.getClass());
}
}


//运行结果:(SpringBoot默认使用的数据源为hikari)
...
class com.zaxxer.hikari.HikariDataSource

进程已结束,退出代码为 0

数据源

笔记来自狂神说博客

Spring Boot 2.0 以上默认使用 Hikari 数据源,Hikari 与 Druid 都是当前 Java Web 上最优秀的数据源。Druid 是阿里巴巴开源平台上一个数据库连接池实现,结合了 C3P0、DBCP 等 DB 池的优点,同时加入了日志监控。

集成Druid

1、添加maven依赖

1
2
3
4
5
6
7
<!--druid数据源-->
<!-- https://mvnrepository.com/artifact/com.alibaba/druid -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.21</version>
</dependency>

2、SpringBoot配置文件(application.properties)中,设置数据源为druid

1
2
#数据源使用 阿里巴巴的druid
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource

3、验证已切换数据源。参考上边“测试”中的案例,进行验证。

配置

注意:下边配置的参数都是druid的私有属性,后边还需要添加配置类绑定配置文件中配置的参数才能生效

同样是在SpringBoot配置文件(application.properties)中进行配置。druid配置说明:

配置示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#Spring Boot 默认是不注入这些属性值的,需要自己绑定
#druid 数据源专有配置
spring.datasource.initialSize=5
spring.datasource.minIdle=5
spring.datasource.maxActive=20
spring.datasource.maxWait=60000
spring.datasource.timeBetweenEvictionRunsMillis=60000
spring.datasource.minEvictableIdleTimeMillis=300000
spring.datasource.validationQuery=SELECT 1 FROM DUAL
spring.datasource.testWhileIdle=true
spring.datasource.testOnBorrow=false
spring.datasource.testOnReturn=false
spring.datasource.poolPreparedStatements=true
#配置监控统计拦截的filters:stat-监控统计、log4j-日志记录、wall-防御sql注入
#如果允许时报错 java.lang.ClassNotFoundException: org.apache.log4j.Priority
#则导入 log4j 依赖即可,Maven 地址:https://mvnrepository.com/artifact/log4j/log4j
spring.datasource.filters=stat,wall,log4j
spring.datasource.maxPoolPreparedStatementPerConnectionSize=20
spring.datasource.useGlobalDataSourceStat=true
spring.datasource.connectionProperties=druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500

绑定配置

为 DruidDataSource 绑定全局配置文件中的参数,添加到容器中(不使用 Spring Boot 自动生成的)。创建Druid配置类如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package com.qsdbl.nazox_demo.configuration;

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;

@Configuration
public class DruidConfig {
/**
* 将自定义的 Druid数据源添加到容器中,不再让 Spring Boot 自动创建
* 绑定全局配置文件中的 druid 数据源属性到 com.alibaba.druid.pool.DruidDataSource从而让它们生效
* @ConfigurationProperties(prefix = "spring.datasource"):作用就是将 全局配置文件中
* 前缀为 spring.datasource的属性值注入到 com.alibaba.druid.pool.DruidDataSource 的同名参数中
*/
@ConfigurationProperties(prefix = "spring.datasource")
@Bean
public DataSource druidDataSource() {
return new DruidDataSource();
}
}

测试

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
package com.qsdbl.nazox_demo;

import com.alibaba.druid.pool.DruidDataSource;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;

@SpringBootTest
class MyTest {

@Autowired
DataSource dataSource;

@Test
public void test() throws SQLException {
//看一下默认数据源
System.out.println(dataSource.getClass());
//获得连接
Connection connection = dataSource.getConnection();
System.out.println(connection);

DruidDataSource druidDataSource = (DruidDataSource) dataSource;
System.out.println("druidDataSource 数据源最大连接数:" + druidDataSource.getMaxActive());
System.out.println("druidDataSource 数据源初始化连接数:" + druidDataSource.getInitialSize());

//关闭连接
connection.close();
}
}

结果:可以看到,与我们的配置是一致的

1
2
3
4
5
6
class com.alibaba.druid.pool.DruidDataSource
com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl@424f02b8
druidDataSource 数据源最大连接数:20
druidDataSource 数据源初始化连接数:5

进程已结束,退出代码为 0

开启监控

Druid 数据源具有监控的功能,并提供了一个 web 界面方便用户查看,类似安装 路由器 时,人家提供一个默认的 web 页面。整合log4j后,也可以使用log4j的其他功能。

1、添加maven依赖

前边的配置中开启了log4j日志监控,需要在pom.xml文件中添加maven依赖:

1
2
3
4
5
6
7
<!--日志监控:log4j-->
<!-- https://mvnrepository.com/artifact/log4j/log4j -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>

2021-12-20日,Apache Log4j连续曝光了三个漏洞。官方已发布 2.17.0 版本修复漏洞,注意不要使用比2.17.0低的版本。(受影响版本为2.0~2.14,此处使用的是1.2不受影响)

2、在Druid配置类中,配置 Druid 的后台管理页面,比如 登录账号、密码 等;DruidConfig中添加如下配置:

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
import com.alibaba.druid.support.http.StatViewServlet;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import java.util.HashMap;
import java.util.Map;


//配置 Druid 监控管理后台的Servlet:“/druid”进行访问
//内置 Servlet 容器时没有web.xml文件,所以使用 Spring Boot 的注册 Servlet 方式
@Bean
public ServletRegistrationBean statViewServlet() {
ServletRegistrationBean bean = new ServletRegistrationBean(new StatViewServlet(), "/druid/*");
// 这些参数可以在 com.alibaba.druid.support.http.StatViewServlet
// 的父类 com.alibaba.druid.support.http.ResourceServlet 中找到
Map<String, String> initParams = new HashMap<>();
initParams.put("loginUsername", "admin"); //后台管理界面的登录账号
initParams.put("loginPassword", "123456"); //后台管理界面的登录密码
//后台允许谁可以访问(ip,逗号分隔)
//initParams.put("allow", "localhost"):表示只有本机可以访问
//initParams.put("allow", ""):为空或者为null时,表示允许所有访问
initParams.put("allow", "");
//后台拒绝谁访问(ip,逗号分隔)
//initParams.put("deny", "192.168.1.20");表示禁止此ip访问
//设置初始化参数
bean.setInitParameters(initParams);
return bean;
}

3、添加log4j配置文件

完成上边两步配置后,Druid 监控管理后台就可以正常访问了。但是log4j的日志功能还需要进行相关配置(在resources文件夹下创建log4j.properties文件),简单配置如下:

1
2
3
4
log4j.rootLogger=DEBUG, stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n

log4j的使用和properties文件的配置,见这篇博客。建议整合上边druid中的配置一起使用。

测试

启动SpringBoot项目后,访问路径/druid,输入上边配置的用户名密码后进入Druid 监控管理后台。进行一些数据库crud操作后即可看到Druid的监控数据:

过滤器

配置 Druid web 监控 filter 过滤器,过滤我们不需要Druid进行监控的资源。(了解即可)

示例:

Druid配置类DruidConfig中添加如下配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import com.alibaba.druid.support.http.WebStatFilter;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

//配置 Druid 监控 之 web 监控的 filter
// WebStatFilter:用于配置Web和Druid数据源之间的管理关联监控统计
@Bean
public FilterRegistrationBean webStatFilter() {
FilterRegistrationBean bean = new FilterRegistrationBean();
bean.setFilter(new WebStatFilter());
//exclusions:设置哪些请求进行过滤排除掉,从而不进行统计
Map<String, String> initParams = new HashMap<>();
initParams.put("exclusions", "*.js,*.css,/druid/*,/jdbc/*");
bean.setInitParameters(initParams);
//"/*" 表示过滤所有请求
bean.setUrlPatterns(Arrays.asList("/*"));
return bean;
}

事务管理

笔记摘自CSDN

在SpringBoot中开启事务管理,只需要在方法上添加注解@Transactional即可。(复习:在spring中开启声明式事务管理

介绍

事务管理是应用系统开发中必不可少的一部分。Spring 为事务管理提供了丰富的功能支持。Spring 事务管理分为编程式和声明式的两种方式。编程式事务指的是通过编码方式实现事务;声明式事务基于 AOP,将具体业务逻辑与事务处理解耦。声明式事务管理使业务代码逻辑不受污染, 因此在实际使用中声明式事务用的比较多。声明式事务有两种方式,一种是在配置文件(xml)中做相关的事务规则声明,另一种是基于 @Transactional 注解的方式。

注意:

mybatis + springboot,springboot 会自动配置一个 DataSourceTransactionManager,我们只需在方法(或者类)加上 @Transactional 注解,就自动纳入 Spring 的事务管理了。

实现机制

​ 在应用系统调用声明了 @Transactional 的目标方法时,Spring Framework 默认使用 AOP 代理,在代码运行时生成一个代理对象,根据 @Transactional 的属性配置信息,这个代理对象决定该声明 @Transactional 的目标方法是否由拦截器 TransactionInterceptor 来使用拦截,在 TransactionInterceptor 拦截时,会在目标方法开始执行之前创建并加入事务,并执行目标方法的逻辑, 最后根据执行情况是否出现异常,利用抽象事务管理器 AbstractPlatformTransactionManager 操作数据源 DataSource 提交或回滚事务。

​ Spring AOP 代理有 CglibAopProxy 和 JdkDynamicAopProxy 两种,以 CglibAopProxy 为例,对于 CglibAopProxy,需要调用其内部类的 DynamicAdvisedInterceptor 的 intercept 方法。对于 JdkDynamicAopProxy,需要调用其 invoke 方法。

​ 正如上文提到的,事务管理的框架是由抽象事务管理器 AbstractPlatformTransactionManager 来提供的,而具体的底层事务处理实现,由 PlatformTransactionManager 的具体实现类来实现,如事务管理器 DataSourceTransactionManager。不同的事务管理器管理不同的数据资源 DataSource,比如 DataSourceTransactionManager 管理 JDBC 的 Connection。

多线程

访问这篇博客

注解总结

记录使用过的注解:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@PostConstruct
是Java自带的注解,在方法上加该注解会在项目启动的时候执行该方法,也可以理解为在spring容器初始化的时候执行该方法。可作为一些数据的常规化加载。

扩展:
实现CommandLineRunner接口,在项目启动完之后加载。
SpringBoot提供的一种简单的实现方案,实现CommandLineRunner接口,实现功能的代码放在实现的run方法中加载,并且如果多个类需要设置加载顺序,则实现类上使用@Order注解,且value值越小则优先级越高。

执行顺序:Constructor >> @Autowired >> @PostConstruct >> 静态方法 >> 实现CommandLineRunner接口



@RequiredArgsConstructor
写controller、Service层的时候,若觉得自动注入@AutoWired注解太多了可以使用此注解代替。
这是lombok提供了一个注解,写在类上代替@AutoWired注解实现自动注入,要自动注入的变量使用 final 修饰

个性化配置

启动banner

配置SpringBoot项目启动动画(控制台打印的字符)。

在resources文件夹下,新建一个banner.txt文件。在其中添加自定义的字符即可。

例如:

1
2
3
4
 _ __
| '_ \ ___ ___
| .__/ / _ \ (_-<
|_|__ \___/ /__/_

Spring Boot自定义启动Banner在线生成工具

配置文件

一些常见配置:

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
#服务端口
server.port=8001

#mybatis配置:
#实体类所在的包
mybatis.type-aliases-package=com.qsdbl.springcloud.entity
#mapper接口对应的 xml配置文件
mybatis.mapper-locations=classpath:mybatis/mapper/*.xml
#mybatis配置文件
mybatis.config-location=classpath:mybatis/mybatis-config.xml

#spring配置:
#程序名
spring.application.name=springcloud-provider
#数据源(druid)
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
#mysql驱动
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
#mysql数据库
spring.datasource.url=jdbc:mysql://localhost:3306/cashier_system?useUnicode=true&characterEncoding=utf-8
#mysql数据库 账号
spring.datasource.username=root
#mysql数据库 密码
spring.datasource.password=123456
#激活的配置文件(默认激活 开发环境)
#dev - 开发环境
#prod - 部署(生产)环境
spring.profiles.active=dev

自定义配置

将配置保存在springBoot配置文件中,在class中通过@Value注解获取。

在springBoot配置文件application.properties中,添加配置参数:

1
2
3
4
5
#JWT配置
#私钥
jwt.SECRET_KEY=cashier_system_BGYkcYQLROAYgZKu
#过期时间(单位毫秒,这里设置为1天)
jwt.EXPIRATION_TIME=86400000

class中,通过@Value注解获取:

1
2
3
4
5
6
7
//私钥
@Value("${jwt.SECRET_KEY}")//从springBoot配置文件获取配置
private String SECRET_KEY;

//过期时间 单位毫秒
@Value("${jwt.EXPIRATION_TIME}")
private long EXPIRATION_TIME;

注意:@Value取不到值的原因

  • 使用static或final修饰了变量
  • 类没有加上@Component(或者其他将类注册到spring容器的注解)
  • 类被new新建了实例,而不是使用@Autowired

实体类中,自动注入配置文件中配置的属性值

1
2
3
4
5
6
7
8
//使用注解ConfigurationProperties
@ConfigurationProperties(prefix = "jwt")
public class RsaConf{
//配置文件中的“jwt.SECRET_KEY”将会自动注入此成员变量中
private String SECRET_KEY;
private long EXPIRATION_TIME;
。。。
}

多个配置文件

笔记摘自:传送门

项目在开发环境生产环境下的配置是不一样的。我们可以使用多个配置文件,在不同的环境下使用不同的配置文件即可。

起名

springboot中,可以根据如下格式给配置文件起名:application-{profile}.properties,其中profile可以看作是该配置文件的标识。比如:

1
2
3
4
5
6
7
8
9
10
11
开发环境:
文件名 application-dev.properties
标识 dev

测试环境:
文件名 application-test.properties
标识 test

生产环境:
文件名 application-prod.properties
标识 prod

激活

application.properyies中通过spring.profiles.active来具体激活一个或者多个配置文件(逗号隔开)。比如:

1
2
3
4
5
6
7
8
9
10
#application.properyies中进行配置

#配置当前的环境
#dev - 开发环境
#test - 测试环境
#prod - 部署环境
spring.profiles.active=dev

#或
spring.profiles.active=dev,test

扩展:还可以使用spring.profiles.include来启用多个配置文件(逗号隔开)

命令行方式可在使用命令行启动程序时,指定激活哪个配置文件。

1
java -jar xxx.jar --spring.profiles.active=dev

连续的两个减号就是对application.properties中的属性值进行赋值的标识。若要屏蔽通过命令行修改配置,需要在启动类中添加配置:SpringApplication.setAddCommandLineProperties(false)

高级应用

在类上添加注解Profile,只有当激活的配置文件与注解Profile中的相同时,才会实例化该类。

1
2
3
4
5
//在类上添加注解Profile
@Profile("dev") //处于开发环境时才实例化
public class demo{
。。。
}

总结

application.properties中配置通用内容,并设置spring.profiles.active默认激活开发环境。在运行项目时可以根据需要,通过命令行方式去激活不同的环境配置。

配置类

springboot整合其他框架,一般需要编写配置类。例如上边整合Druid的例子。

1
2
3
4
5
6
7
8
#配置类上需要添加此注解
@Configuration

#方法上使用下边注解
# 可将配置文件application.properties中的配置(前缀为 spring.datasource的属性值)注入方法返回的bean中
# 同理,在实体类中使用注解@ConfigurationProperties可将配置注入实体类的成员变量中
@ConfigurationProperties(prefix = "spring.datasource")
@Bean

整合

MyBatis

SpringBoot项目中,整合mybatis框架。上边的“创建项目”部分,就记录了使用SpringBoot+mybatis来创建一个web项目的过程。

步骤:

  1. 添加Maven依赖
  2. 在SpringBoot配置文件application.properties中配置mybatis、数据库
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#-----------mybatis-----------
#指定实体类所在路径(包)
mybatis.type-aliases-package=com.qsdbl.xxx.entity
#指定映射文件所在路径。(或mapper-locations。用于扫描mapper接口对应的xml文件)
mybatis.mapperLocations=classpath:mapper/*.xml
#mybatis配置文件
mybatis.config-location=classpath:mybatis/mybatis-config.xml
#在数据源durid中集成log4j,此处不需要配置
#mybatis.configuration.log-impl=org.apache.ibatis.logging.log4j.Log4jImpl

#-----------数据库(mysql)相关-----------
#数据源(阿里巴巴的druid,druid配置见上边的“数据源”部分)
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
#数据库驱动
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
#数据连接,allowMultiQueries=true是支持批量执行SQL语句,语句之前是";"隔开
spring.datasource.url=jdbc:mysql://localhost:3306/jxstar_cloud?serverTimezone=UTC&allowMultiQueries=true&useUnicode=true&characterEncoding=UTF-8
#数据库登录账号、密码
spring.datasource.username=root
spring.datasource.password=123456

注意:仔细检查一下xml文件实际所在的路径与配置文件中的是否一致。

若xml文件与mapper接口在同一个包下,则不需要在配置中指定路径。resource文件夹下的文件(包括文件夹)在编译后会保存到”classpath:“下,所以根据上边的mybatis.mapperLocations的配置信息,xml文件应当保存在resource文件夹下的mapper文件夹内。

案例2

springBoot+MyBatis+pagehelper,案例见这篇博客

案例1

使用结果集映射,实现多表查询。(属于“案例2”中的“结果嵌套查询”)

有用户表、角色表、用户-角色表三张表。用户可能有多个角色,所以需要使用结果集映射,将用户角色映射到用户实体类中的一个集合中进行保存。

用户实体类

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
package com.qsdbl.cashier_system.entity;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
import java.util.List;

//针对不同的数据,设计不同的bean类
//该类对应数据库中的account表的一行数据(一个account对象),用于存储从数据库中返回的数据
@Data//生成无参构造器、get、set、toString、hashcode、equals等
@AllArgsConstructor//生成有参构造器(会删掉@Data生成的无参构造器)
@NoArgsConstructor//生成无参构造器
public class Account implements Serializable{
private int id;
private String user;//用户名
private String passwd;
private String sex;
private String birthday;
private String tel;
private String mail;
private String state;//状态(0-已注销,1-正常使用)
private String register;//注册时间
private String icon;//头像
private List<String> roleList;//角色列表
public Account(String user){
this.user = user;
}
}

mapper接口

1
2
3
4
5
6
7
8
9
10
11
12
package com.qsdbl.cashier_system.mapper;
import com.qsdbl.cashier_system.entity.Account;
import org.apache.ibatis.annotations.*;
import java.util.List;

@Mapper
public interface AccountMapper {
//通过 用户名 查找用户信息
public Account queryByName(Account account);
//查询所有数据
public List<Account> queryAll();
}
xml文件

在resources文件夹下,新建包mappers,添加mapper接口对应的xml配置文件,配置如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
<?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开始就是自己定义的了-->
<mapper namespace="com.qsdbl.cashier_system.mapper.AccountMapper"><!--对应的mapper类的包路径-->

<resultMap id="queryByNameMap" type="com.qsdbl.cashier_system.entity.Account"><!--返回查询结果对应的实体类-->
<result column="id" property="id"/>
<result column="user" property="user"/>
<result column="passwd" property="passwd"/>
<result column="sex" property="sex"/>
<result column="birthday" property="birthday"/>
<result column="tel" property="tel"/>
<result column="mail" property="mail"/>
<result column="state" property="state"/>
<result column="register" property="register"/>
<result column="icon" property="icon"/>
<collection property="roleList" ofType="java.lang.String" javaType="java.util.List">
<result column="roles"/>
</collection>
</resultMap>
<!--传参是对象的话parameterType就写那个对象的全限定类名-->
<select id="queryByName" resultMap="queryByNameMap" parameterType="com.qsdbl.cashier_system.entity.Account">
SELECT DISTINCT a.*,b.name as roles
from account a,role_table b,user_role_table c
where 1=1
and a.id = c.user_id
and b.id = c.role_id
and a.user = #{user}
</select>

<resultMap id="queryAllMap" type="com.qsdbl.cashier_system.entity.Account">
<result column="id" property="id"/>
<result column="user" property="user"/>
<result column="passwd" property="passwd"/>
<result column="sex" property="sex"/>
<result column="birthday" property="birthday"/>
<result column="tel" property="tel"/>
<result column="mail" property="mail"/>
<result column="state" property="state"/>
<result column="register" property="register"/>
<result column="icon" property="icon"/>
<collection property="roleList" ofType="java.lang.String" javaType="java.util.List">
<result column="roles"/>
</collection>
</resultMap>
<select id="queryAll" resultMap="queryAllMap">
SELECT DISTINCT
a.*,b.name as roles
from account a,role_table b,user_role_table c
where 1=1
and a.id = c.user_id
and b.id = c.role_id
</select>

</mapper>

扩展:若不想每一个mapper接口都添加一个@Mapper注解注册Bean到SpringBoot框架,可以在SpringBoot启动类中使用注解@MapperScan,扫描mapper包下的接口自动注册Bean。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package com.qsdbl.cashier_system;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
@MapperScan(basePackages = "com.qsdbl.cashier_system.mapper")//mapper接口所在的包
public class CashierSystemApplication {

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

}

SpringBoot配置文件application.properties

1
2
3
4
5
#指定实体类所在路径
mybatis.type-aliases-package=com.qsdbl.cashier_system.entity
#指定映射文件所在路径。(用于扫描mapper接口对应的xml文件)
#classpath:mappers/*.xml -> resources文件夹下的mappers内的xml文件。
mybatis.mapperLocations=classpath:mappers/*.xml

结果

查询所有数据,queryAll:

1
2
3
4
5
[
Account(id=6, user=pete, passwd=123456, sex=男, birthday=2005-10-11 00:00:00.0, tel=1235436xx23, mail=1135637451@qq.com, state=正常, register=2021-05-04 18:11:32.0, icon=../../../../images/markdown_img/2020/20210502162745.jpg, roleList=[role_common])
,
Account(id=7, user=lily, passwd=123456, sex=女, birthday=2000-09-03 00:00:00.0, tel=1235436xx23, mail=1135637451@qq.com, state=正常, register=2021-05-04 18:11:32.0, icon=../../../../images/markdown_img/2020/20210502162745.jpg, roleList=[role_admin, role_common])
]

实体类Account中的变量roleList,可以正常保存数据([role_admin, role_common]

部署前端项目

将打包好的前端项目,放在static目录下即可。springBoot程序(.jar文件)运行后,访问所设置的端口即可访问前端项目(index.html文件要在static下)。

打包

打开maven面板,双击执行package命令即可。打包后的jar包在target文件夹内,使用命令java -jar xxx.jar启动项目。

1
2
3
4
5
6
7
8
...
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 22.061 s
[INFO] Finished at: 2021-09-01T16:56:30+08:00
[INFO] ------------------------------------------------------------------------

进程已结束,退出代码为 0

启动项目时可以修改SpringBoot的配置,例如:

java -jar xxx.jar --server.port=8090 --spring.datasource.username=root --spring.datasource.password=123456

设置端口为8090,数据库账号为root,数据库root账号的密码为123456

若图片不能正常显示,请在浏览器中打开

欢迎关注我的其它发布渠道