什么是Spring?
Spring是一个轻量级的Java开发框架,于2003年兴起,作者:Rod Johnson
Spring为应用开发提供平台。它是为了解决企业应用开发的复杂性而创建的。Spring框架的主要优势之一是分层架构,分层架构允许使用者选择使用哪一个组件,同时为J2EE应用程序开发提供继承的框架
Spring的核心是控制反转(IoC)和面向切面编程(AOP)
Spring是如何简化Java开发的?
存在的缺点:
Spring Boot提供了一种快速使用并简化Spring的应用开发,基于约定大于配置的思想,去繁从简,可以让开发不必在配置与业务逻辑之间进行思维的切换,全身心的投入到业务逻辑的编码中,大大提高开发效率!
SpringBoot并不是对Spring功能上的增强,而是提供了一种快速使用Spring的方式,它默认配置了很多框架的使用方式,就像Maven整合了所有的jar包,SpringBoot整合了许多的框架
SpringBoot主要优点:
SpringBoot是由Pivotal团队提供的全新框架,其设计目的是用来简化Spring应用的初始搭建以及开发过程
创建SpringBoot入门程序
创建新模块,选择Spring Initializr,并配置模块相关基础信息
选择当前模块需要使用的技术集
开发控制器类
// Rest模式
@RestController
@RequestMapping("/hello")
public class BookController {
@GetMapping
public String hello() {
System.out.println("Springboot is running...");
return "Hello Springboot";
}
}
运行自动生成的Application类
注:基于idea开发SpringBoot程序需要确保联网且能够加载到程序框架结构
starter
parent
在实际开发中,使用任意坐标时,仅书写GAV中的G和A,V由SpringBoot提供,除非SpringBoot未提供对应版本V
主启动类(xxxApplication)
Boot内置了三款服务器分别是
tomcat(默认):apache出品,粉丝多,应用面广,负载了若干较重的组件
jetty:更轻量级,负载性能远不及tomcat
undertow:负载性能勉强跑赢tomcat
内嵌Tomcat工作原理是将Tomcat服务器作为对象运行,并将该对象交给Spring容器管理
SpringBoot默认配置文件application.properties
,通过键值对配置对应属性
属性配置
# 服务器端口配置
server.port=80
# 关闭运行日志图标(banner)
spring.main.banner-mode=off
# 设置日志相关
logging.level.root=error
SpringBoot内置属性查询
配置文档
SpringBoot提供了3种配置文件的格式
# application.properties
server.port=80
# application.yml
server:
port: 81
# application.yaml
server:
port: 82
SpringBoot配置文件加载优先级
不同配置文件中相同配置按照加载优先级相互覆盖,不同配置文件中不同配置全部保留
在SpringBoot项目当中除了以上3种配置文件外,SpringBoot为了增强程序的扩展性,除了支持配置文件的配置方式以外,还支持另外两种常见的配置方式:
JVM系统属性配置 (格式: -Dkey=value)
-Dserver.port=10010
命令行参数 (格式:–key=value)
--server.port=10011
优先级: 命令行参数 > 系统属性参数 > properties参数 > yml参数 > yaml参数
YAML
YAML文件扩展名
核心规则:数据前面要加空格与冒号隔开
定义数据
country: China
user:
my-name: xiaoZhang
hobbies:
- music
- girl
- swim
# 对象数组缩略格式
userList: [ { name: liming,age: 22 },{ name: xiaolin,age: 26 } ]
使用@Value注解读取单个数据,属性名引用方式: ${一级属性名.二级属性名....}
// 读取yml数据中的单一数据 使用SPEL表达式取值
@Value(value = "${country}")
private String country;
// 烤肉串模式获取属性
@Value(value = "${user.my-name}")
private String name;
@Value(value = "${user.hobbies[1]}")
private String hobbyTwo;
// 驼峰方式获取属性
@Value("${userList[1].age}")
private Integer age;
SpringBoot把全部数据封装到Environment对象中,可以直接进行获取
// 使用自动装配将所有的数据封装到一个对象的Environment中
@Autowired
private Environment environment;
@Test
void contextLoads() {
System.out.println(environment.getProperty("user.hobbies[2]")); // swim
System.out.println(environment.getProperty("userList[0].name")); // liming
}
缺陷:把全部数据都封装了,有时只需要一部分数据
自定义对象封装数据
person:
name: "xiaozhao\nxiaobai"
age: 22
is-marry: false
birth: 2003-05-21
hobby: [ boy,music,housework ]
map: { k1: v1,k2: v2 }
love-fruit:
- "苹果"
- "香蕉"
- "甜橙"
// 1.定义数据模型封装yml文件中对应的数据 yml和实体类属性一一对应
// 2.提供get/set方法 并定义为Spring管控的bean
@Data
@Component
@ConfigurationProperties(prefix = "person") // 将本类中的所有属性和配置文件中的相关配置进行绑定
@Validated // 开启JDR303校验
public class Person {
private String name;
@Max(value = 130,message = "年龄不能大于130岁")
@Min(value = 0,message = "年龄不能小于0岁")
private Integer age;
private boolean isMarry;
@DateTimeFormat(pattern = "yyyy-MM-dd")
private Date birth;
private String[] hobby;
private Map<String, Object> map;
private List<String> loveFruit;
}
添加spring-boot-configuration-processor
坐标,解决警告问题!
注:
""
引起来的字符会转义,单引号则不会转义,原样输出特性 | @ConfigurationProperties | @Value |
---|---|---|
批量注入配置文件中的属性 | 只能单个注入 | |
松散语法绑定 | 支持 | 不支持 |
JSR303数据校验 | 支持 | 不支持 |
复杂数据类型封装 | 支持 | 不支持(无法进行获取) |
SPEL表达式 | 不支持 | 支持 |
resources
文件夹中目录结构
private static final String[] CLASSPATH_RESOURCE_LOCATIONS = new String[]{"classpath:/META-INF/resources/", "classpath:/resources/", "classpath:/static/", "classpath:/public/"};
RandomValuePropertySource:配置文件中可以使用随机数,占位符获取配置的值,如果没有的话可以使用:
之后指定默认值!
animal:
name: 哈士奇${random.uuid}
age: ${random.int(0,10)}
master: ${user.uname:小昭}_dog
@Data
@Component
@ConfigurationProperties(prefix = "animal")
public class Animal {
private String name;
private Integer age;
private String master;
}
Profile是Spring对不同环境提供不同配置功能的支持,可以通过激活、指定参数等方式快速切换指定环境
在编写主配置文件的时候,文件名可以是:application-{profile}.properties/yml
默认使用application.properties
的配置,此文件优先级最高
开发环境:application-dev.yml
server:
port: 80
生产环境:application-pro.yml
server:
port: 90
测试环境:application-test.yml
server:
port: 100
激活方式
配置文件
spring:
profiles:
active: pro
命令行:java -jar springboot_quickstart-0.0.1-SNAPSHOT.jar --spring.profiles.active=test
jvm系统属性:java -jar -Dspring.profiles.active=dev springboot_quickstart-0.0.1-SNAPSHOT.jar
SpringBoot启动会扫描以下位置的application.properties
或者application.yml
文件作为当前项目的默认配置文件
以上是按照优先级从高到低的顺序,所有位置的文件都会被加载(互补配置),高优先级配置内容会覆盖低优先级配置的内容
也可通过配置spring.config.location
来改变其默认的配置
默认情况下,SpringBoot项目在启动的时候会自动的创建IOC容器(也称为Spring容器),并且在启动的过程当中会自动的将bean对象都创建好,存放在IOC容器当中。应用程序在运行时需要依赖什么bean对象,直接进行依赖注入就可以了
3种常用方式获取Bean:
调用getBean()
的重载方法获取即可
@Autowired
private ApplicationContext applicationContext; // 注入IOC容器对象
@Test
public void testGetBean() {
// 根据名称获取
Person p1 = (Person) applicationContext.getBean("person");
// 根据类型获取
Person p2 = applicationContext.getBean(Person.class);
// 根据bean的名称和类型获取
Person p3 = applicationContext.getBean("person", Person.class);
System.out.println("p1 = " + p1);
System.out.println("p2 = " + p2);
System.out.println("p3 = " + p3);
System.out.println(p1 == p2 && p2 == p3); // true
}
注:默认情况下,IOC容器中的bean对象是单例的!
默认bean对象是单例模式(只有一个实例对象),那么如何设置bean 对象为非单例呢?需要设置bean的作用域
在Spring中支持五种作用域,后三种在web环境才生效:
作用域 | 说明 |
---|---|
singleton | 容器内同名称的bean只有一个实例(单例)(默认) |
prototype | 每次使用该bean时会创建新的实例(非单例) |
request | 每个请求范围内会创建新的实例(web环境中) |
session | 同上 |
application | 同上 |
可以借助Spring中的@Scope注解来进行配置作用域
注:实际开发当中,绝大部分的Bean是单例的,也就是说绝大部分Bean是不需要配置scope属性的!
之前所配置的bean,像controller、service,dao三层体系下编写的类,这些类都是我们在项目当中自己定义的类(自定义类),当要声明这些bean,也非常简单,只需要在类上加上 @Component以及它的这三个衍生注解(@Controller、@Service、@Repository)
但是开发中有时会使用大量的第三方Bean,这些第三方提供的Bean是只读的,我们无法在上面进行修改或添加@Component注解或衍生注解
如果要管理的bean对象来自于第三方(不是自定义的),是无法用@Component 及衍生注解声明 bean的,那么此时就需要用到@Bean注解
如果需要定义第三方Bean时, 通常会单独定义一个配置类
@Configuration // 配置类 (在配置类当中对第三方bean进行集中的配置管理)
public class OtherBeanConfig {
@Bean
@ConfigurationProperties(prefix = "spring.datasource")
public DataSource dataSource() {
return new DruidDataSource();
}
}
配置文件
spring:
datasource:
url: jdbc:mysql://localhost:3306/boot
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: root
type: com.alibaba.druid.pool.DruidDataSource
# Spring Boot 默认是不注入这些属性值的,需要自己绑定
# druid 数据源专有配置
# 初始化时建立物理连接的个数
initialSize: 5
# 最小连接池数量
minIdle: 5
# 最大连接池数量 maxIdle已经不再使用
maxActive: 20
# 获取连接时最大等待时间,单位毫秒
maxWait: 60000
# 申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效
timeBetweenEvictionRunsMillis: 60000
# 设置连接空闲时间的阈值
minEvictableIdleTimeMillis: 300000
# 用来检测连接是否有效的sql 必须是一个查询语句
# mysql中为 select 'x'
# oracle中为 select 1 from dual
validationQuery: select 'x'
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
poolPreparedStatements: true
测试
@Test
public void testOtherBean() {
System.out.println(dataSource.getClass());
}
注:
对于数据访问层,无论是SQL还是NOSQL,Spring Boot默认采用整合Spring Data的方式进行统一处理,添加大量自动配置,屏蔽很多设置,引入各种xxxTemplate、xxxRepository来简化对数据访问层的操作
创建新模块,选择Spring初始化,并配置模块相关基础信息
选择当前模块需要使用的技术集(MyBatis、MySQL)
配置数据源参数
# 配置数据库相关信息
spring:
datasource:
username: root
password: root
url: jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai
driver-class-name: com.mysql.cj.jdbc.Driver
# mybatis相关配置
mybatis:
# 指定 mapper.xml 的位置
mapper-locations: classpath:mapper/*.xml
# 扫描实体类的位置,在此处指明扫描实体类的包,在 mapper.xml 中就可以不写实体类的全路径名
type-aliases-package: com.zhang.pojo
configuration:
# 标准日志输出
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
# 开启驼峰映射
map-underscore-to-camel-case: true
编写实体类
定义数据层接口与映射配置
@Mapper // 自动生成该接口的实现类对象(代理对象),并把该对象交由IOC容器进行管理,如果mapper文件较多,可在主启动类上添加 @MapperScan 注解扫描指定包
public interface BookMapper {
List<Book> queryAllBook();
Book queryBookById(@Param("id") Integer id);
Integer addBook(Book book);
Integer delBook(@Param("id") Integer id);
Integer modifyBook(Book book);
}
编写mapper.xm
l文件
DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.zhang.mapper.BookMapper">
<select id="queryAllBook" resultType="Book">
select id, type, name, description
from mybatis.tbl_book
select>
<select id="queryBookById" resultType="Book">
select id, type, name, description
from mybatis.tbl_book
<where>
<if test="id!=null">id=#{id}if>
where>
select>
<insert id="addBook" parameterType="Book">
insert into mybatis.tbl_book(id, type, name, description)
values (#{id}, #{type}, #{name}, #{description})
insert>
<delete id="delBook" parameterType="integer">
delete from mybatis.tbl_book
<where>
<if test="id!=null">id=#{id}if>
where>
delete>
<update id="modifyBook" parameterType="Book">
update mybatis.tbl_book set type=#{type},name=#{name},description=#{description}
<where>
<if test="id!=null">id=#{id}if>
where>
update>
mapper>
测试类中注入mapper接口,测试功能
@SpringBootTest
class Springboot05MybatisApplicationTests {
@Autowired
private BookMapper bookMapper;
@Test
void contextLoads() {
List<Book> books = bookMapper.queryAllBook();
books.forEach(System.out::println);
}
}
1.手动添加SpringBoot整合Mybatis-Plus的坐标,可通过maven仓库获取
<dependency>
<groupId>com.baomidougroupId>
<artifactId>mybatis-plus-boot-starterartifactId>
<version>3.4.2version>
dependency>
编写实体类并定义数据层接口,并继承BaseMapper
@Mapper
public interface UserMapper extends BaseMapper<User> {
// 编写业务扩展方法
}
继承完BaseMapper
接口之后,无需编写mapper.xml
文件,就拥有大部分操作数据库的方法了
测试类中注入mapper接口,测试功能
@SpringBootTest
class MybatisPlusApplicationTests {
@Autowired
private UserMapper userMapper;
@Test
void contextLoads() {
System.out.println(userMapper.selectById(1L));
}
}
定义业务层接口,继承IService
public interface UserService extends IService<User> {
// 编写业务扩展方法
}
定义业务层实现类,并继承ServiceImpl
,实现其自定义的业务层接口
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
}
注:如果mapper接口较多时,可以在启动类上添加@MapperScan(value = "com.zhang.mapper")
SpringBoot 2.x 默认使用HikariCP作为数据源,数据源的相关配置都在DataSourceProperties
里面
可参考DataSourceConfiguration
,根据配置创建数据源,可以使用spring.datasource.type
指定自定义的数据源类型
HikariCP 是目前市面上性能最好的数据源产品,但在实际的开发过程中,企业往往更青睐于另一款数据源产品:Druid,它是目前国内使用范围最广的数据源产品
Druid 是阿里巴巴推出的一款开源的高性能数据源产品,Druid 支持所有 JDBC 兼容的数据库,包括 Oracle、MySQL、SQL Server 和 H2 等等。Druid 不仅结合了 C3P0、DBCP 和 PROXOOL 等数据源产品的优点,同时还加入了强大的监控功能。通过 Druid 的监控功能,可以实时观察数据库连接池和 SQL 的运行情况,帮助用户及时排查出系统中存在的问题
1.导入Druid
对应的starter
<dependency>
<groupId>com.alibabagroupId>
<artifactId>druid-spring-boot-starterartifactId>
<version>1.2.16version>
dependency>
2.在 Spring Boot 配置文件中配置以下内容
###### JDBC通用配置 ######
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/boot?useSSL=true&useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai
username: root
password: root
type: com.alibaba.druid.pool.DruidDataSource
druid:
###### Druid连接池的配置 ######
initial-size: 5 # 初始化连接大小
min-idle: 5 # 最小连接池数量
max-active: 20 # 最大连接池数量
max-wait: 60000 # 获取连接时最大等待时间,单位毫秒
time-between-eviction-runs-millis: 60000 # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
min-evictable-idle-time-millis: 300000 # 配置一个连接在池中最小生存的时间,单位是毫秒
validation-query: select 'x' # 测试连接
test-while-idle: true # 申请连接的时候检测,建议配置为true,不影响性能,并且保证安全性
test-on-borrow: false # 获取连接时执行检测,建议关闭,影响性能
test-on-return: false # 归还连接时执行检测,建议关闭,影响性能
pool-prepared-statements: false # 是否开启PSCache,PSCache对支持游标的数据库性能提升巨大,oracle建议开启,mysql下建议关闭
max-pool-prepared-statement-per-connection-size: 20 # 开启poolPreparedStatements后生效
filters: stat,wall # 配置扩展插件,常用的插件有=>stat:监控统计 wall:防御sql注入
connection-properties: 'druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000' # 通过connectProperties属性来打开mergeSql功能;慢SQL记录
###### Druid监控配置信息 ######
# StatViewServlet配置
stat-view-servlet:
enabled: true # 是否开启内置监控页面,默认值为 false
url-pattern: '/druid/*' # StatViewServlet 的映射路径,即内置监控页面的访问地址
reset-enable: true # 是否启用重置按钮
login-username: admin # 内置监控页面的登录页用户名 username
login-password: admin # 内置监控页面的登录页密码 password
# WebStatFilter配置
web-stat-filter:
enabled: true # 是否开启内置监控中的 Web-jdbc 关联监控的数据
url-pattern: '/*' # 匹配路径
exclusions: '*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*' # 排除路径
session-stat-enable: true # 是否监控session
filter:
# 配置StatFilter (SQL监控配置)
stat:
enabled: true # 开启 SQL 监控
slow-sql-millis: 1000 # 慢查询
log-slow-sql: true # 记录慢查询 SQL
# 配置WallFilter (防火墙配置)
wall:
enabled: true # 开启防火墙
config:
update-allow: true # 允许更新操作
drop-table-allow: false # 禁止删表操作
insert-allow: true # 允许插入操作
delete-allow: true # 允许删除操作
通过整合以上三种技术,得出经验,在整合任何第三方技术时需
- 导入对应的starter
- 配置对应的设置或采用默认配置
市面上存在很多的日志框架,比如:
SpringBoot在框架内部使用JCL,spring-boot-starter-logging采用了slf4j+logback的形式
SpringBoot也能自动适配(JUL、log4j2、logback)并简化配置
日志门面(接口) | 日志实现 |
---|---|
Log4j、Log4j2、Logback |
SLF4J官方文档
SpringBoot能自动适配所有的日志,而且底层使用slf4j+logback的方式记录日志,引入其他框架的时候,只需要把这个框架依赖的日志框架排除掉即可!
SpringBoot默认帮我们配置好了日志,日志级别,由低到高为:
@SpringBootTest
public class LoggingApplicationTests {
// 日志记录器
private Logger log = LoggerFactory.getLogger(getClass());
@Test
void contextLoads() {
// 日志的级别,由低到高:trace < debug < info < warn < error
// 可以调整输出的日志级别;日志就只会在这个级别以后的高级别生效
// 不要导错包:org.slf4j.LoggerFactory
// SpringBoot默认级别为:info,可通过配置进行调整
log.trace("trace");
log.debug("debug");
log.info("info");
log.warn("warn");
log.error("error");
}
}
修改日志的默认配置
logging:
level:
# 为对应包设置日志级别
com.zhang: debug
file:
# 指定日志生成的目录名称,日志文件名默认为:spring.log
path: E:/log
# 当前项目下生成 myBoot.log 日志文件
# name: myBoot.log
# 文件大小限制
max-size: 6MB
pattern:
# 指定文件中日志输出的格式
file: "%d{yyyy-MM-dd} --- [%thread] --- %-5level --- %logger{50} ---- %msg%n"
# 在控制台输出的日志格式
console: "%d{yyyy-MM-dd} [%thread] %-5level %logger{50} - %msg%n"
参考官方文档
给类路径下放上每个日志框架自己的配置文件即可;SpringBoot就不再使用它默认的配置
logback.xml
:直接会被日志框架识别logback-spring.xml
:日志框架就不直接加载日志的配置项,由SpringBoot解析日志配置,可以使用SpringBoot的高级Profile功能<appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">
<layout class="ch.qos.logback.classic.PatternLayout">
<springProfile name="dev">
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} ===> [%thread] ===> %-5level %logger{50} - %msg%npattern>
springProfile>
<springProfile name="!dev">
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} ==== [%thread] ==== %-5level %logger{50} - %msg%npattern>
springProfile>
layout>
appender>
配置原理总结:导入场景starter—>xxxAutoConfiguration—>导入xxx组件—>绑定xxxProperties—>绑定配置文件项
创建新模块,选择Spring初始化,并配置模块相关基础信息
选择当前模块需要使用的技术集(SpringWeb、MySQL)
手动导入SpringBoot中不包含的坐标
<dependency>
<groupId>com.baomidougroupId>
<artifactId>mybatis-plus-boot-starterartifactId>
<version>3.4.2version>
dependency>
<dependency>
<groupId>com.alibabagroupId>
<artifactId>druid-spring-boot-starterartifactId>
<version>1.2.8version>
dependency>
修改配置文件为yml格式
设置端口为80方便访问
# 配置端口
server:
port: 80
创建数据库表并添加数据
create database if not exists `ssMp`;
use ssMp;
create table if not exists `tb_book`
(
`id` INT(10) auto_increment NOT NULL COMMENT '图书id',
`type` VARCHAR(20) NOT NULL COMMENT '图书分类',
`name` VARCHAR(50) NOT NULL COMMENT '图书名称',
`description` VARCHAR(100) NOT NULL COMMENT '图书描述',
PRIMARY KEY (`id`)
) ENGINE = INNODB
DEFAULT CHARSET = utf8
# --------------------
INSERT INTO `tb_book` VALUES (1, '市场营销', '直播带货:淘宝、天猫直播从新手到高手', '一本教你如何玩转直播的书');
INSERT INTO `tb_book` VALUES (2, '企业管理', '领导力', '高绩效团队管理法则不懂带团队你就自己累');
INSERT INTO `tb_book` VALUES (3, '企业管理', '高情商管理', '三分管人 七分做人');
INSERT INTO `tb_book` VALUES (4, '企业管理', '管理三要', '能识人 会用人 懂管人');
INSERT INTO `tb_book` VALUES (5, '经济学', '每个人的经济学', '做复杂世界的明白人');
INSERT INTO `tb_book` VALUES (6, '政治经济学', '政治经济学的任务', '揭示经济规律经济规律');
编写实体类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Book implements Serializable{
private Integer id;
private String type;
private String name;
private String description;
}
配置yml
文件
server:
port: 80
# 配置druid数据源(druid 专用配置)
spring:
datasource:
druid:
username: root
password: root
url: jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC
driver-class-name: com.mysql.cj.jdbc.Driver
# 表名前缀
mybatis-plus:
global-config:
db-config:
table-prefix: tb_
# 设置主键生成策略为数据库自增策略
id-type: auto
# 开启MP日志(标准日志输出)
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
注:使用数据库自增策略必须给id
设置为自动增长,否则报错
定义数据层接口与映射配置,继承BaseMapper并指定泛型
@Mapper
public interface BookMapper extends BaseMapper<Book> {
//编写扩展方法
}
创建测试类并注入mapper接口,测试结果
@SpringBootTest
public class BookMapperTestCase {
@Autowired
private BookMapper bookMapper;
@Test
public void testBookById() {
Book book = bookMapper.selectById(6L);
System.out.println(book);
}
}
添加分页功能
分页需要设定分页对象Ipage,其中封装了分页操作中的所有数据
@Test
void testPageBook() {
//查询第二页,查询五条数据
IPage<Book> page = new Page<>(2, 5);
bookMapper.selectPage(page, null);
System.out.println("当前页:" + page.getCurrent());
System.out.println("每页数据总量:" + page.getSize());
System.out.println("总记录数:" + page.getTotal());
// 多少页 = 总记录数 除以 每页数据总量
System.out.println("共多少页:" + page.getPages());
// 查询得到的数据
List<Book> records = page.getRecords();
records.forEach(System.out::println);
}
分页操作是在MP的常规操作基础上增强得到(依赖MP分页拦截器),**其内部是动态的拼写SQL语句,**因此需要增强对应的功能,需要添加MP拦截器实现
@Configuration
public class MPConfig {
// 配置分页插件
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
// 1.定义MP拦截器
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
// 2.添加具体的拦截器
interceptor.addInnerInterceptor(new PaginationInnerInterceptor());
return interceptor;
}
}
按照条件查询
使用QueryWrapper对象封装查询条件,**推荐使用LambdaQueryWrapper对象,**所有查询操作封装成方法调用
@Test
void queryGetByCondition() {
String name = "经济";
LambdaQueryWrapper<Book> lqw = new LambdaQueryWrapper<>();
// 查询包含spring的数据
//if (name != null) lqw.like(Book::getName, name);
// 动态条件拼接
lqw.like(!Strings.isNullOrEmpty(name), Book::getName, name);
bookMapper.selectList(lqw);
}
定义Service
接口
public interface BookService {
// 添加图书
Boolean save(Book book);
// 修改图书
Boolean modify(Book book);
// 删除图书
Boolean delete(Integer id);
// 根据id查询图书
Book queryById(Integer id);
// 查询全部图书
List<Book> queryAll();
// 分页查询
IPage<Book> queryPage(int currentPage, int pageSize);
}
定义service
对应实现类
// 定义为业务层的bean
@Service
public class BookServiceImpl implements BookService {
@Autowired
private BookMapper bookMapper;
@Override
public Boolean save(Book book) {
return bookMapper.insert(book) > 0;
}
@Override
public Boolean modify(Book book) {
return bookMapper.updateById(book) > 0;
}
@Override
public Boolean delete(Integer id) {
return bookMapper.deleteById(id) > 0;
}
@Override
public Book queryById(Integer id) {
return bookMapper.selectById(id);
}
@Override
public List<Book> queryAll() {
return bookMapper.selectList(null);
}
@Override
public IPage<Book> queryPage(int currentPage, int pageSize) {
IPage<Book> page = new Page<>(currentPage, pageSize);
return bookMapper.selectPage(page, null);
}
}
定义测试类测试相关功能
@SpringBootTest
public class BookServiceTestCase {
@Autowired
private BookService bookService;
@Test
void testQueryById() {
System.out.println(bookService.queryById(6));
}
@Test
void testPageBook() {
//查询第一页,查询五条数据
IPage<Book> page = bookService.queryPage(1, 5);
// 查询得到的数据
List<Book> records = page.getRecords();
records.forEach(System.out::println);
}
}
注:**Service接口名称尽量定义成业务名称,**并与Mapper接口名称进行区分
简化通用业务层代码开发
MP提供有业务层通用接口ISerivce
与业务层通用实现类 ServiceImpl
如果其中提供的方法不满足需要时,可以在通用类的基础上做功能重载或者功能追加(自定义需要的功能)
定义service
接口
public interface IBookService extends IService<Book> {
}
定义service
对应实现类
@Service
public class BookServiceImpl extends ServiceImpl<BookMapper, Book> implements IBookService {
@Autowired
private BookMapper bookMapper;
@Override
public boolean addBook(Book book) {
return bookMapper.insert(book) > 0;
}
@Override
public boolean modifyBook(Book book) {
return bookMapper.updateById(book) > 0;
}
@Override
public IPage<Book> queryPage(int currentPage, int pageSize) {
IPage<Book> page = new Page<>(currentPage, pageSize);
bookMapper.selectPage(page, null);
return page;
}
}
定义测试类测试相关功能
@SpringBootTest
public class BookServiceTest {
@Autowired
private IBookService bookService;
@Test
void testQueryAllBook() {
List<Book> books = bookService.list();
books.forEach(System.out::println);
}
}
注:重载时不要覆盖原始操作,避免原始提供的功能丢失
表现层数据一致性处理
设计表现层返回结果的模型类,用于后端与前端进行数据格式统一,也称为前后端数据协议
@Data
@NoArgsConstructor
public class R {
private Boolean flag;
private Object data;
public R(Boolean flag) {
this.flag = flag;
}
public R(Boolean flag, Object data) {
this.flag = flag;
this.data = data;
}
}
表现层接口统一返回值类型结果
@RestController
@RequestMapping("/books")
public class BookControllerTwo {
@Autowired
private IBookService bookService;
@GetMapping
public R getAll() {
return new R(true, bookService.list());
}
// @RequestBody 接收JSON格式的实体数据
@PostMapping
public R save(@RequestBody Book book) {
//R r = new R();
//boolean data = bookService.save(book);
//r.setData(data);
return new R(bookService.addBook(book));
}
@PutMapping
public R modify(@RequestBody Book book) {
return new R(bookService.modifyBook(book));
}
// http:localhost/books/5
@DeleteMapping("/{id}")
public R delete(@PathVariable Integer id) {
return new R(bookService.removeById(id));
}
// @PathVariable 路径变量
@GetMapping("/{id}")
public R bookById(@PathVariable Integer id) {
return new R(true, bookService.getById(id));
}
@GetMapping("/{currentPage}/{pageSize}")
public R queryPage(@PathVariable int currentPage, @PathVariable int pageSize) {
return new R(true, bookService.queryPage(currentPage, pageSize));
}
}
目的:设计统一的返回值结果类型便于前端开发读取数据
在前后端分离结构设计中页面归属于前端服务器
单体工程中页面应放置在resources
目录下的static
目录中
前端发送异步请求,调用后端接口(Retrieve)
// 列表
getAll() {
// 使用axios发送异步请求
axios.get("/books").then((res) => {
// console.log(res.data);
this.dataList = res.data.data;
});
}
添加操作(Add)
//弹出添加窗口
handleCreate() {
this.dialogFormVisible = true;
this.resetForm();
},
//重置表单
resetForm() {
this.formData = {};
},
//添加
handleAdd() {
axios.post("/books", this.formData).then((res) => {
// 判断当前操作是否成功
if (res.data.flag) {
//1、关闭弹层
this.dialogFormVisible = false;
this.$message.success("添加成功");
} else {
this.$message.error("添加失败");
}
}).finally(() => {
//2、重新加载数据
this.getAll();
})
},
删除操作(Delete)
// 删除
handleDelete(row) {
// console.log(row);
//1、弹出提示框
this.$confirm("此操作将永久删除当前信息,是否删除?", "温馨提示", {type: "info"}).then(() => {
//2、删除操作
axios.delete("/books/" + row.id).then((res) => {
if (res.data.flag) {
this.$message.success("删除成功");
} else {
this.$message.error("删除失败");
}
}).finally(() => {
//重新加载数据
this.getAll();
});
}).catch(() => {
//3、取消删除
this.$message.info("取消删除操作");
});
},
修改操作(Modify)
//弹出编辑窗口
handleUpdate(row) {
axios.get("/books/" + row.id).then((res) => {
if (res.data.flag && res.data.data != null) {
this.dialogFormVisible4Edit = true;
this.formData = res.data.data;
} else {
this.$message.error("数据同步失败,自动刷新");
}
}).finally(() => {
// 重新加载数据
this.getAll();
});
},
消息一致性处理
// 定义SpringMvc异常处理器处理异常
@RestControllerAdvice
public class ProjectExceptionAdvice {
// 拦截所有的异常信息
@ExceptionHandler(Exception.class)
public R doException(Exception ex) {
// 记录日志
// 通知运维
// 通知开发
ex.printStackTrace();
return new R("服务器故障,请稍后再试!");
}
}
修改表现层返回结果的模型类,封装出现异常后对应的信息
private String msg; // 要显示的消息
public R(String msg) {
this.flag = false;
this.msg = msg;
}
分页功能(Page)
替换查询全部功能为分页功能
// 分页查询
getPage() {
// 发送异步请求
axios.get("/books/" + this.pagination.currentPage + "/" + this.pagination.pageSize).then((res) => {
// 加载分页数据
let data = res.data.data;
this.pagination.currentPage = data.current;
this.pagination.pageSize = data.size;
this.pagination.total = data.total;
this.dataList = data.records;
});
},
分页页码值切换
// 切换页码
handleCurrentChange(currentPage) {
// 修改页码值为当前的页码值
this.pagination.currentPage = currentPage;
// 执行查询
this.getPage();
},
条件查询(Condition)
查询条件数据封装
pagination: {// 分页相关模型数据
currentPage: 1,// 当前页码
pageSize: 10,// 每页显示的记录数
total: 0,// 总记录数
type: "",
name: "",
description: ""
}
页面数据模型绑定(v-model进行绑定)
组织数据成为get请求发送的数据
// 分页查询
getAll() {
// 组织参数,拼接url请求地址
// console.log(this.pagination.type)
let params = "?type=" + this.pagination.type + "&name=" + this.pagination.name + "&description=" + this.pagination.description;
console.log(params);
// /books/1/10?type=???&name=???&description=???
// 发送异步请求
axios.get("/books/" + this.pagination.currentPage + "/" + this.pagination.pageSize + params).then((res) => {
let data = res.data.data;
this.dataList = data.records;
});
},
controller接收参数
@GetMapping("{currentPage}/{pageSize}")
public R queryPage(@PathVariable int currentPage, @PathVariable int pageSize, Book book) {
System.out.println("参数==>" + book);
IPage<Book> page = bookService.queryPage(currentPage, pageSize,book);
return new R(true, page);
}
业务层接口功能开发
IPage<Book> queryPage(int currentPage, int pageSize, Book book);
@Service
public class BookServiceImpl extends ServiceImpl<BookMapper, Book> implements IBookService {
@Override
public IPage<Book> queryPage(int currentPage, int pageSize, Book book) {
IPage<Book> page = new Page<>(currentPage, pageSize);
LambdaQueryWrapper<Book> lqw = new LambdaQueryWrapper<>();
lqw.like(Strings.isNotEmpty(book.getType()), Book::getType, book.getType());
lqw.like(Strings.isNotEmpty(book.getName()), Book::getName, book.getName());
lqw.like(Strings.isNotEmpty(book.getDescription()), Book::getDescription, book.getDescription());
bookMapper.selectPage(page, lqw);
return page;
}
}
至此,整合完毕,收工!