Spring6
Spring 是轻量级的开源的JavaEE框架,解决企业应用开发的复杂性。它有两个核心模块:IOC和AOP
官网:https://spring.io/
Spring6特点:
要求JDK17、Maven3.6、Idea工具2022
IOC
控制反转,把创建对象过程交给Spring进行管理,作用:降低耦合
底层:XML解析、工厂模式、反射
提供IOC容器实现两种方式
- BeanFactory 是Spring内部的使用的接口,加载配置文件不会创建对象
- ApplicationContext BeanFactory 接口的子qq接口,一般开发人员使用,加载配置文件会把配置文件对象进行创建
IOC操作Bean二种方式
-
基于XML方式
// XML文件配置 (set注入) // autowire=“byName” 是根据属性名称自动装配 , 可放在class=“”后面 <bean id="id属性" class=“包的全路径” > <property name="属性名" value="属性值"></property> </bean> // 创建对象 ApplicationContext ac = new ClassPathXmlApplicationContext("XML文件.xml"); IAccountService as = (IAccountService) ac.getBean("id属性");
-
基于注解方式
// 创建Bean实例 @Component @Service @Controller @Repository // 属性注入 @AutoWired 根据属性类型自动装配 @Qualifier 根据属性名称进行注入 @Resource 根据类型注入,可以根据名称注入 @Value 注入普通类型 // 创建配置类,替换XML文件 @Configuration @ComponentScan(basePackages={"com.包名"}) // 扫描包 public class 类{ ... } // 创建对象 ApplicationContext ac = new AnnotationConfigApplicationContext(类.class); IAccountService as = (IAccountService) ac.getBean("id属性");
AOP
面向切面,不修改原代码进行增强操作
底层:动态代理
动态代理两种方式
- JDK动态代理 有接口情况,使用Proxy类实现
- CGLIB动态代理 没有接口情况
常用术语
- 连接点:类里的哪些方法可以被加强
- 切入点:实际被真正增强的方法
- 通知:实际增强的逻辑就是通知,分为:前置通知、后置通知、环绕通知、异常通知、最终通知
- 切面:把通知应用到切入点的过程
切入点表达式
语法结构:execution([权限修饰符][返回类型][类的全路径][方法名称]([参数列表]))
例子:对com.abc.dao包下里所有类的全部方法进行增强
execution(* com.abc.dao.*.*(..))
例子:对com.abc.dao.A类的add方法进行增强
execution(* com.abc.dao.A.add(..))
例子:对com.abc.dao.A类,所有的方法进行增强
execution(* com.abc.dao.A.* (..))
案例:
# 1.引包
<!--aop依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>6.0.2</version>
</dependency>
<!--aspects依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>6.0.2</version>
</dependency>
# 2.AspectJ注解
@Aspect // 这个注解就是生成代理对象
@Component
@Order(1) // 设置多个类同一个方法增强,设置优先级,值越少越先执行
public class LogAdvice {
// 切入点注解 (相同的切入点 - 提取)
@Pointcut("execution(* com.abc.dao.A.add(..)")
private void check() {
}
// 前置通知 [check] 就是使用相同的切入点方法名
@Before("check()")
public void before(JoinPoint joinPoint){
System.out.println("before,代理逻辑前执行");
String name = joinPoint.getSignature().getName();
System.out.println(" 该方法名称: "+name);
Object[] args = joinPoint.getArgs();
for (Object arg : args) {
System.out.println(" 参数: "+arg);
}
}
// 后置通知 - 也可以是最终通知,遇到异常也会执行
@After("execution(* com.abc.dao.A.add(..)")
public void after(){
System.out.println("after 代理逻辑后执行");
}
// 前置通知 - 返回通知,有异常就不执行, res 返回值
@AfterReturning(value = "check()", returning = "res")
public void afterReturning(JoinPoint joinPoint, Object res){
System.out.println("after 代理逻辑后执行,有异常就不执行,比有@After先执行");
}
// 异常通知
@AfterThrowing(value = "check()",throwing = "ex")
public void afterThrowing(JoinPoint joinPoint, Throwable ex){
System.out.println("after 代理逻辑后执行,有异常才执行,会执行这里" + ex.toString());
}
// 环绕通知
@Around("execution(* com.abc.dao.A.add(..)")
public void around(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("环绕通知之 前执行");
joinPoint.proceed(); // 执行增强逻辑
System.out.println("环绕通知之 后执行");
}
}
Log4j2
是一个开源的日志记录组件
<!--log4j2的依赖-->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.19.0</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j2-impl</artifactId>
<version>2.19.0</version>
</dependency>
配置log4j2.xml
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<loggers>
<!-- level指定日志级别,从低到高的优先级:-->
<root level="DEBUG">
<appender-ref ref="spring6log"/>
<appender-ref ref="RollingFile"/>
<appender-ref ref="log"/>
</root>
</loggers>
<appenders>
<!--输出日志信息到控制台-->
<console name="spring6log" target="SYSTEM_OUT">
<!--控制日志输出的格式-->
<PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss SSS} [%t] %-3level %logger{1024} - %msg%n"/>
</console>
<!--文件会打印出所有信息,这个log每次运行程序会自动清空,由append属性决定,适合临时测试用-->
<File name="log" fileName="F:/log/xxx/test.log" append="false">
<PatternLayout pattern="%d{HH:mm:ss.SSS} %-5level %class{36} %L %M - %msg%xEx%n"/>
</File>
</appenders>
</configuration>
使用:
private Logger logger = LoggerFactory.getLogger(TestUser.class);
logger.info("ok");
事务
事务是数据库操作最基本的单元。
特性:(ACID)
- 原子性:要么都成功,要么都失败
- 一致性:操作前和操作后,总量是不变的
- 隔离性:每个事务是互相隔离的
- 持久性:当提交后,数据就会发生变化
事务操作:
注解:@Transactional
传播行为:(有7个,常用)
- Propagation.REQUIRED 如果有事务运行,在当前方法事务内运行,否则就创建新的事务;
- Propagation.REQUIRES_NEW 当前方法在自己的事务内运行
- Propagation.SUPPORTS 如果有事务运行,当前方法在这个事务内运行,否则可以不运行事务中
隔离级别:
产生的问题:
- 脏读 :一个未提交的事务读到了另一个事务读到
- 不可重复读:一个未提交事务读到了另一个事务修改的数据
- 幻读:一个未提交事务读到了另一个事务添加的数据
解决问题:
- 读未提交 Isolation.READ_UNCOMMITTED
- 读已提交 isolation = Isolation.READ_COMMITTED
- 可重复读 Isolation.REPEATABLE_REA
- 串行化 Isolation.SERIALIZABLE
- 数据库默认的 isolation = Isolation.DEFAULT
例子:
@Transactional(
timeout = 20, // 超时时间: 默认是-1,单位:秒
propagation = Propagation.SUPPORTS, // 传播行为
isolation = Isolation.READ_COMMITTED, // 隔离级别
readOnly = true, // 是否只读,默认 false ,true代表只能查询
rollbackFor = Exception.class, // 设置出现哪些异常进行回滚
noRollbackFor = Exception.class // 设置哪些异常不进行回滚
)
开启事务注解:@EnableTransactionManagement
数据校验
Validation
案例:
- 创建 效验器
@Service
@Validated
public class MyService {
// 使用 @Valid 注解标记了 User 参数,表示对该参数进行校验、
// 使用 @NotNull 注解标记了 User 参数,表示该参数不能为空。
public String testParams(@NotNull @Valid User user) {
return user.toString();
}
}
- 创建实体
/**
* 数据校验:Validation - 基于方法演示
* 2. 创建实体类
* 常用注解:
* @NotNull: 检查字段值不为null。
* @NotEmpty: 检查字符串、集合或数组的值不为空。
* @NotBlank: 检查字符串的值不为空或不只包含空格字符。
* @Min(value): 检查字段值大于或等于指定的最小值。
* @Max(value): 检查字段值小于或等于指定的最大值。
* @Size(max, min): 检查字段值的大小在指定范围内。
* @Email: 检查字段值符合电子邮件的格式要求。
* @Pattern(regex): 检查字段值符合指定的正则表达式。
*/
@Data
public class User {
@NotNull
private String name;
@Min(0)
@Max(120)
private int age;
@Pattern(regexp = "^1(3|4|5|7|8)\\d{9}$",message = "手机号码格式错误")
@NotBlank(message = "手机号码不能为空")
private String phone;
}
- 演示
// 如果校验不通过,将抛出 ConstraintViolationException 异常、如果校验通过,执行其他业务逻辑
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(ValidationConfig.class);
MyService myService = context.getBean(MyService.class);
User user = new User();
user.setAge(-1);
// 验证方法 ,执行方法
myService.testParams(user);
}
自定义效验
- 创建自定义校验注解
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
// @Constraint 标记该注解为校验注解,并指定对应的校验器类 -- 指定规则的逻辑
@Constraint(validatedBy = {CannotBlankValidator.class})
public @interface CannotBlank {
//默认错误消息
String message() default "不能包含空格";
//分组 - 可以指定校验器在哪个分组生效
Class<?>[] groups() default {};
//负载 - Payload 可在自定义校验器中使用
Class<? extends Payload>[] payload() default {};
//指定多个时使用
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.TYPE_USE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@interface List {
CannotBlank[] value();
}
}
- 编写校验类 - 效验规则
public class CannotBlankValidator implements ConstraintValidator<CannotBlank, String> {
// initialize() 方法用于初始化校验器
@Override
public void initialize(CannotBlank constraintAnnotation) {
}
// isValid() 方法是校验的核心逻辑,用于判断被校验的值是否符合自定义的校验规则
@Override
public boolean isValid(String value, ConstraintValidatorContext context) {
//null时不进行校验
if (value != null && value.contains(" ")) {
//获取默认提示信息
String defaultConstraintMessageTemplate = context.getDefaultConstraintMessageTemplate();
System.out.println("default message :" + defaultConstraintMessageTemplate);
//禁用默认提示信息
context.disableDefaultConstraintViolation();
//设置提示语
context.buildConstraintViolationWithTemplate("can not contains blank").addConstraintViolation();
return false;
}
return true;
}
}
- 使用,演示与上同步
AOT
提前编译,运行时编译。可以把源码直接转换机器码,启动快,内存占用低。需要安装。
使用前需要安装Native Image插件,在这里没有详细安装步骤。
SpringBoot
介绍
简化Spring应用开发的一个框架,整个Spring技术栈的一个大整合。
官方网站:https://spring.io/projects
优点:
- 快速创建Spring项目,使用嵌入式的服务,内置了Tomcat
- starters(启动器)自动依赖与版本控制
- 无需配置XML,开箱即用
系统要求:
Java 8、Maven 3.3+
maven配置:
给maven 的settings.xml配置文件的profiles标签添加
<mirrors>
<mirror>
<id>nexus-aliyun</id>
<mirrorOf>central</mirrorOf>
<name>Nexus aliyun</name>
<url>http://maven.aliyun.com/nexus/content/groups/public</url>
</mirror>
</mirrors>
<profiles>
<profile>
<id>jdk-1.8</id>
<activation>
<activeByDefault>true</activeByDefault>
<jdk>1.8</jdk>
</activation>
<properties>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<maven.compiler.compilerVersion>1.8</maven.compiler.compilerVersion>
</properties>
</profile>
</profiles>
特点:
-
依赖管理
父项目做依赖管理、开发导入starter场景启动类、无需关注版本号
-
自动配置
自动配置了tomcat、配置SpringMVC、Web常见功能
常用注解:
@Configuration //告诉SpringBoot这是一个配置类 == 配置文件 @Bean //给容器中添加组件。 @ComponentScan //包扫描 @Import({User.class, User2.class}) // 给容器中自动创建出这两个类型的组件 @Conditional //条件装配 ,注入 @ImportResource //原生配置文件引入 @Component //实现Bean的注入 @ConfigurationProperties //配置绑定 取到properties文件中的内容,封装到Bean中 配合@Component注解 @EnableConfigurationProperties 同上,就不需要加 @Component
Spring自动配置:
启动类
@SpringBootApplication (引导类)
@SpringBootConfiguration
@Configuration 定义为配置类 - 等价于配置文件
@Component 添加到Spring容器中,是个组件
@EnableAutoConfiguration
@AutoConfigurationPackage 将引导包所在子包下面的组件,添加Spring容器中
@Import
会给容器导入非常多的自动 配置类,并配置好。(这样省去手写配置注入Spring容器)
@ComponentScan 这个注解被标识,会被Spring容器进行管理
总结:
SpringBoot先加载所有的自动配置类,每个自动动,按配置类条件去生效,生效后就给容器配置很多的组件,容器有这些组件,就有了这些功能。
如果我们定制配置,可以直接@Bean替换底层默认的组件。
核心功能
yaml配置文件
如果出现乱码,可在IDEA工具搜索-( File Encodings) 配置utf-8格式
# yaml表示以上对象
person:
userName: zhangsan
birth: 2019/12/12 20:12:33
# 数组 List<String> arr (代表arr里有2个元素)
arr:
- jpg
- png
# map Map<String, List<Pet>> allPets;
allPets:
sick:
- {name: tom}
- {name: jerry,weight: 47}
health: [{name: mario,weight: 47}]
@ConfigurationProperties(prefix = "person")
@Component
public class Person {
private String userName;
...
}
日志配置
SpringBoot默认采用SLF4j +logback
打印日志:
// 测试 - 日志 (级别顺序)
Logger log = LoggerFactory.getLogger(getClass());
log.trace("这是 跟踪运行信息");
log.debug("这是 跟调试信息");
log.info("这是 跟info自定义信息");
log.warn("这是 跟警告信息");
log.error("这是 记录错误信息");
// 输出日志 【时间 日志级别 进程 [线程] 完整包名 信息】
基本日志配置:
# 日志配置
logging:
level:
# 指定指定包的日志级别 com.xx.boot包
com:
xx:
boot: debug
# 修改springboot的root 默认级别
root: info
file:
# 日志输出位置
name: G:/log/abd.log
pattern:
# 控制台输出格式:'时间 日志级别 [线程] 日志前缀包名50限时长度是50 日志信息 %n是换行'
console: '%d{yyyy-mm-dd} %-5level [%thread] %logger{50} - %msg%n'
# 日志文件输出格式
file: '%d{yyyy-MMM-dd HH:mm:ss.SSS} %-5level [%thread] %logger{15} - %msg%n'
使用自定义日志:Log4j2
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<!-- 除去默认的日志包 -->
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- 引入新的日志包 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>
简化开发
-
lombok
<dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.10</version> </dependency>
-
dev-tools
# 快捷键 Ctrl + F9 ,自动加载编译 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <optional>true</optional> </dependency>
静态资源访问
web相关jar包:https://www.webjars.org/
静态目录:/static /public /resources /META-INF/resources
改变默认的静态资源路径
spring:
# 设置静态资源访问前缀,默认无前缀,访问:localhost:8080/res/xxx.css
mvc:
static-path-pattern: /res/**
# 指定静态资源的文件目录
resources:
static-locations: [classpath:/st1/]
# static-locations: [classpath:/st1/,/st2/,/st3/]
# 禁用所有静态资源规则
add-mappings: false
RESTful
@RequestMapping(value = "/user",method = RequestMethod.GET) ---- @GetMapping()
@RequestMapping(value = "/user",method = RequestMethod.POST) ---- @POSTMapping()
@RequestMapping(value = "/user",method = RequestMethod.PUT) ---- @PUTMapping()
@RequestMapping(value = "/user",method = RequestMethod.DELETE) ---- @DELETEMapping()
# yml文件开启页面表单的Rest功能
spring:
mvc:
hiddenmethod:
filter:
enabled: true
常用注解
@PathVariable 获取路径变量
@RequestHeader 获取消息头信息
@RequestParam 请求消息参数
@CookieValue 获取Cookie值
@RequestBody 请求体信息 - 只有post请求需要
@RequestAttrbute 获取请求域中的值
@ModelAttribute 矩阵变量,默认禁用
例子:
@RestController
public class TestController {
// car/2
@GetMapping("/car/{id}")
public String getCar(@PathVariable("id") Integer id,
@RequestHeader("User-Agent") String userAgent,
// 全部的消息头信息
@RequestHeader Map<String,String> header,
@RequestParam("age") Integer age,
// xxx?age=8&arrs=n1&arrs=n2&arrs=n3
@RequestParam("inters") List<String> arrs,
@CookieValue("_ga") String _ga,
// 得到Cookie对象
@CookieValue("_ga") Cookie cookie){
return null;
// return "forward:/pageA"; // 转到到pageA页
}
// @RequestBody 请求体信息
@PostMapping("/save")
public Map postMethod(@RequestBody String content){
Map<String,Object> map = new HashMap<>();
map.put("content",content);
return map;
}
}
复杂参数:
Map、Model(放在request的请求域 request.setAttribute)、Errors/BindingResult、RedirectAttributes( 重定向携带数据)、ServletResponse(response)、SessionStatus、UriComponentsBuilder、ServletUriComponentsBuilder
内容协商:
根据客户端接收能力不同,返回不同媒体类型的数据。
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
</dependency>
# yml文件开启请求参数内容协商模式
spring:
contentnegotiation:
favor-parameter: true #开启请求参数内容协商模式
只需要改变请求头中Accept字段。Http协议中规定的,告诉服务器本客户端可以接收的数据类型。
模板引擎
Thymeleaf使用:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
基本语法:
表达式名字 | 语法 | 用途 |
---|---|---|
变量取值 | ${…} | 获取请求域、session域、对象等值 |
选择变量 | *{…} | 获取上下文对象值 |
消息 | #{…} | 获取国际化等值 |
链接 | @{…} | 生成链接 |
片段表达式 | ~{…} | jsp:include 作用,引入公共页面片段 |
静态页面全部放在resources/templates目录下
拦截器
1.继承HandlerInterceptor 接口
package com.good.yan.config.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 {
String requestURI = request.getRequestURI();
log.info("preHandle拦截的请求路径是{}",requestURI);
//登录检查逻辑
HttpSession session = request.getSession();
Object loginUser = session.getAttribute("LoginState");
if(loginUser != null){
//放行
return true;
}
//拦截住。未登录。跳转到登录页
request.setAttribute("msg","请先登录");
request.getRequestDispatcher("/login").forward(request,response);
return false;
}
/**
* 目标方法执行完成以后
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
log.info("postHandle执行{}",modelAndView);
}
/**
* 页面渲染以后
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
log.info("afterCompletion执行异常{}",ex);
}
}
2.配置拦截器
package com.good.yan.config.interceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
* 1、编写一个拦截器实现HandlerInterceptor接口
* 2、拦截器注册到容器中(实现WebMvcConfigurer的addInterceptors)
* 3、指定拦截规则【如果是拦截所有,静态资源也会被拦截】
*/
@Configuration
public class AdminWebConfig implements WebMvcConfigurer {
/** 默认显示首页 */
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/").setViewName("home/list");
registry.addViewController("/index.html").setViewName("home/list");
registry.addViewController("/index").setViewName("home/list");
}
/** 拦截器 */
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LoginInterceptor())
.addPathPatterns("/**") //所有请求都被拦截包括静态资源
.excludePathPatterns("/login","/doLogin","/asserts/**"); //放行的请求
}
}
文件上传
/**
* MultipartFile 自动封装上传过来的文件
* @param email
* @param username
* @param headerImg
* @param photos
* @return
*/
@PostMapping("/upload")
public String upload(@RequestPart("headerImg") MultipartFile headerImg,
@RequestPart("photos") MultipartFile[] photos) throws IOException {
if(!headerImg.isEmpty()){
//保存到文件服务器,OSS服务器
headerImg.transferTo(new File("H:\\cache\\"+headerImg.getOriginalFilename()));
}
if(photos.length > 0){
for (MultipartFile photo : photos) {
if(!photo.isEmpty()){
photo.transferTo(new File("H:\\cache\\"+photo.getOriginalFilename()));
}
}
}
return "上传ok";
}
异常处理
自定义错误页:在templates目录下寻找error目录下的404.html 、5xx.html的页面。
定义全局异常:
@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {
/**
* 获取其它异常。包括500
*/
@ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR)
// @ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR,reason = "用户访问太多")
@ExceptionHandler(value = Exception.class)
public Map<String, Object> defaultErrorHandler(Exception e) {
log.error("Exception", e);
Map<String, Object> map = new HashMap<String, Object>();
map.put("code", FIVE);
map.put("message", e.getMessage());
return map;
}
}
数据访问
SpringBoot连接Mysql
# 1.引入包
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
# 2.修改yml配置文件
spring:
datasource:
username: 用户名
password: 密码
# mysql8版本以上的驱动包,需要指定以下时区
url: jdbc:mysql://127.0.0.1:3306/数据库?useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2B8
driver-class-name: com.mysql.cj.jdbc.Driver
使用Druid数据源
官网地址:https://github.com/alibaba/druid
# 1.引入包
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.17</version>
</dependency>
# 2.修改yml配置文件
spring:
datasource:
url: jdbc:mysql://localhost:3306/db
username: root
password: 123456
driver-class-name: com.mysql.jdbc.Driver
druid:
initial-size: 5
min-idle: 5
max-active: 20
max-wait: 60000
#...
# 监控哪些包
aop-patterns: com.good.yan.modules.*
# 底层开启功能,stat(sql监控),wall(防火墙)
filters: stat,wall
stat-view-servlet: # 配置监控页功能
enabled: true # 开启维护功能
login-username: admin # 访问的用户名和密码
login-password: admin
resetEnable: false # 是否重置按钮启用
web-stat-filter: # 监控web
enabled: true
urlPattern: /*
exclusions: '*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*'
filter:
stat: # 对上面filters里面的stat的详细配置
slow-sql-millis: 1000
logSlowSql: true
enabled: true
wall:
enabled: true
config:
drop-table-allow: false
详情配置地址:https://github.com/alibaba/druid/tree/master/druid-spring-boot-starter
整合MyBatis操作
官网地址:https://github.com/mybatis
# 1.引入包
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.4</version>
</dependency>
# 2.修改yml配置文件
mybatis:
# 映射配置文件路径
mapper-locations: classpath:mybatis/mapper/*.xml
# 核心配置文件路径
# config-location: classpath:mybatis/mybatis-config.xml
# mybatis的全局配置
configuration:
map-underscore-to-camel-case: true
配置文件配置:https://mybatis.org/mybatis-3/zh/configuration.html
单元测试
JUnit5常用注解
// 测试方法的名称
@DisplayName("A类的功能测试")
@SpringBootTest
public class test4 {
@DisplayName("测试方法名称1")
// 测试类 - 也可以加事务注解,(测试完自动回滚)
@Transactional(isolation = Isolation.DEFAULT)
@Test
void test1() {
System.out.println("test1测试");
}
// 重复测试 5 次
@RepeatedTest(5)
// 超时时间 (如果超时,会自动报出超时异常)
@Timeout(value = 500, unit = TimeUnit.MILLISECONDS)
// 表示这个注解不执行
@Disabled
@Test
void test2() {
System.out.println("test2测试");
}
// 每个测试方法 - 都需要执行
@BeforeEach()
void befor() {
System.out.println("测试方法之前执行。。");
}
@AfterEach()
void after() {
System.out.println("测试方法之后执行。。");
}
// 只会调用一次,所以一般加 static 修饰
@BeforeAll()
static void beforAll() {
System.out.println("所有的测试方法之前执行。。只执行1一次");
}
@AfterAll()
static void afterAll() {
System.out.println("所有的测试方法之后执行。。只执行1一次");
}
}
断言:
来检查业务逻辑返回的数据是否合理,所有运行的结果,会有测试报告;
// 需要导入这个包
import static org.junit.jupiter.api.Assertions.*;
// 断言:测试方法需要满足的条件进行验证,检查方法是否合理
@DisplayName("测试断言1 - 普通断言")
@Test
void testdy1() {
// assertEquals 是否相等 参数1 期望值 ,参数2 实际值
assertEquals(1, 1, "测试断言1?");
// 例子: assertEquals(期望值, 实际值, "测试断言1?");
// assertSame 是否同同一个对象
// assertSame(new User(), new User(), "测试断言2?");
// assertArrayEquals 是否同数组相同
assertArrayEquals(new int[]{1, 2}, new int[]{1, 2}, "测试断言3?");
// assertThrows 异常断言,如果出异常代表正常
assertThrows(ArithmeticException.class, () -> {
System.out.println("执行业务代码逻辑。。。");
int i = 10 / 0; // 会出现 ArithmeticException 异常
}, "测试断言4?");
// assertTimeout 超时测试,如果超过设置的时间(参数1),就会出现异常
// assertTimeout(Duration.ofMillis(1000), () -> {
// System.out.println("执行业务代码逻辑--超时。。。");
// Thread.sleep(1200);
// }, "测试断言5?");
// fail 快速失败 (满足条件就触发)
if (1 == 1) {
fail("测试断言6?");
}
}
前置条件
// 前置条件:测试方法需要满足的条件进行验证,前置条件使测试方法的执行终止
@DisplayName("测试前置条件1 - 假设")
@Test
void testjs1() {
Assumptions.assumeTrue(false,"结果不是true,才打印这个");
System.out.println("jjj"); // 如果不满足条件,后面的逻辑不会执行,jjj也不会打印
}
参数化测试
不同的参数,多次运行
// 参数化测试:可设置不同的参数多次运行某个测试方法
@DisplayName("参数化测试1 - 参数化")
@ParameterizedTest
// 这个注解就是多个参数值
@ValueSource(ints = {1,3,4,5})
void testcs1(int i) {
System.out.println(i);
// 该方法 执行 4次,参数是:1,3,4,5
}
@DisplayName("参数化测试2 - 参数化")
@ParameterizedTest
// 参数化 指定具体方法名,自定义参数对象
@MethodSource("stringStream")
void testcs2(String str) {
// 该方法 执行 3次,参数是:"参数1","参数2","参数3"
System.out.println(str);
}
// 方法名 stringStream ,代表有哪些参数,参数是String类型
static Stream<String> stringStream(){
return Stream.of("参数1","参数2","参数3");
}
指标监控
每一个微服务在云上部署以后,我们都需要对其进行监控、追踪、审计、控制等。
常用监控:health 健康端点、metrics 运行时的指标 、loggers 日志记录
# 1.引入依赖包
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
# 2.修改配置文件
management:
endpoints:
enabled-by-default: true #暴露所有端点信息
web:
exposure:
include: '*' #以web方式暴露,暴露所有
# 3.访问测试
http://localhost:8080/actuator/beans
http://localhost:8080/actuator/configprops
http://localhost:8080/actuator/metrics
http://localhost:8080/actuator/metrics/jvm.gc.pause
http://localhost:8080/actuator/endpointName/detailPath
可视化
地址: https://github.com/codecentric/spring-boot-admin
详细配置:https://www.yuque.com/atguigu/springboot/sgpvgn
快速开始:https://codecentric.github.io/spring-boot-admin/2.5.1/#getting-started
# 1.服务端配置-引入依赖包
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-starter-server</artifactId>
<version>2.3.1</version>
</dependency>
# 2.服务端配置-启动类增加 @EnableAdminServer注解
# 3.客户端配置-引入包
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-starter-client</artifactId>
<version>2.3.1</version>
</dependency>
# 4.客户端配置-更改配置文件
spring:
boot:
admin:
client:
url: http://localhost:8888 # 服务端的地址
instance:
prefer-ip: true # 使用ip进行注册
# 5.访问服务端,可以看到可视化界面
配置完启动 - 服务端
访问地址:http://localhost:8888/applications
在导航栏-应用墙 可以看到客户端信息;可以看到:
-
健康信息 : 所有服务都健康 - 当前服务是健康,包含:db、redis等;
-
性能监听
-
日志配置:可以设置日志级别
-
JVM:可观看线程、内存信息
邮箱配置
配置邮箱服务器:
步骤1:
步骤2:点了生成授权码,发送短信,得到密钥。
点击什么是IMAP,它又是如何设置?
在客户端设置 - 发送邮件服务器:smtp.qq.com
发送邮件相关案例:
# 引入相关包
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
# 修改配置文件
# 配置邮箱信息
# spring.mail.username=xxx@qq.com
# spring.mail.password=秘密
# 发送邮件服务器
# spring.mail.host=smtp.qq.com
相关代码
@Autowired(required = false)
JavaMailSenderImpl javaMailSender;
@Test
void test3() {
// 发送简单的邮件
SimpleMailMessage message = new SimpleMailMessage();
message.setSubject("标题");
message.setText("邮件内容");
message.setFrom("发件人邮箱");
message.setTo("收件人邮箱");
javaMailSender.send(message);
System.out.println("发送成功");
}
Mybatis
是一个基于Java的持久层框架。
下载地址:https://github.com/mybatis/mybatis-3
关于环境搭建在我的码云SpringBoot案例有:
详细配置:https://mybatis.org/mybatis-3/zh/configuration.html
核心包
<!-- Mybatis核心 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.7</version>
</dependency>
<!-- junit测试 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<!-- MySQL驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.18</version>
</dependency>
<!-- log4j日志 -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
获取参数两种方式
#{} 与 ${}区别:
#{} 占位符;${}本质就是字符串拼接
// 动态设置表名,使用${}
<select id="getUserByTable" resultType="cn.good.yan.pojo.User">
select * from ${table}
</select>
默认的类型别名
我们在resultType=“别名”,这里写别名,映射对应的类型
alias(别名) | mapped type |
---|---|
_byte | byte |
_long | long |
_short | short |
_int | int |
_integer | int |
_double | double |
_boolean | boolean |
string | String |
byte | Byte |
long | Long |
short | Short |
int | Integer |
integer | Integer |
double | Double |
float | Float |
boolean | Boolean |
date | Date |
decimal | BigDecimal |
digdecimal | BigDecimal |
object | Object |
map | Map |
hashmap | HashMap |
list | List |
arraylist | ArrayList |
collection | Collection |
iterator | iterator |
增删改查案例
-
添加
<insert id="insertUser"> insert into t_user values(null,'admin','123456') </insert>
-
删除
<delete id="deleteUser"> delete from t_user where id = 6 </delete>
-
修改
<update id="updateUser"> update t_user set username = '张三' where id = 5 </update>
-
查询一个实体类对象
<select id="getUserById" resultType="com.xxx.User"> select * from t_user where id = 2 </select>
-
模糊查询
<select id="getUserByLike" resultType="com.xxx.User"> <!--select * from t_user where username like concat('%',#{mohu},'%') escape '/' --> select * from t_user where username like "%"#{mohu}"%" </select>
-
自定义映射resultMap
<!-- 一对一 --> <resultMap id="BillResultMap" type="BillOne"> <!-- 配置主键 property 属性名 column 数据库字段名--> <id property="bid" column="bid"></id> <result property="billCode" column="bill_code"></result> <result property="user.realName" column="real_name"></result> <result property="user.username" column="username"></result> </resultMap> <!-- 一对多 ,集合使用collection --> <resultMap id="ContentResultMap" type="Content"> <id property="id" column="id"></id> <result property="content" column="content"></result> <collection property="bills" ofType="Bill"> <id property="bid" column="bid"></id> <result property="billCode" column="bill_code"></result> <result property="billName" column="bill_name"></result> </collection> </resultMap> <select id="findAll" resultMap="ContentResultMap" > select * from content c left join bill b on c.id = b.pid </select> <!-- 多对一 --> <resultMap id="BillResultMap" type="Bill"> <id property="bid" column="bid"></id> <result property="billCode" column="bill_code"></result> <result property="billName" column="bill_name"></result> <association property="content" javaType="Content"> <id property="id" column="id"></id> <result property="content" column="content"></result> </association> </resultMap> <select id="findBillAll" resultMap="BillResultMap"> select * from bill b left join content c on b.pid = c.id </select>
-
动态SQL
<!-- if where 标签 --> <select id="getEmp" resultType="Emp"> select * from t_emp <where> <if test="empName != null and empName !=''"> emp_name = #{empName} </if> </where> </select> <!-- trim 标签 prefix:前缀添加内容 suffix:后缀添加内容 prefixOverrides:前缀要去掉内容 suffixOverrides:后缀要去掉内容 --> <select id="getEmp" resultType="Emp"> select * from content <trim prefix="where" suffixOverrides="and|or"> <if test="nodeCode != null and nodeCode != ''"> node_Code = #{nodeCode} and </if> </trim> </select> <!-- choose、when、otherwise 标签 --> <select id="findAllChoose" resultMap="CResultMap" > select * from content c left join bill b on c.id = b.pid <where> <choose> <when test="bill.billCode != null and bill.billCode != ''"> b.bill_code like concat('%',#{bill.billCode} ,'%') escape '/' </when> <when test="bill.billName != null and bill.billName != ''"> b.bill_name like concat('%',#{bill.billName} ,'%') escape '/' </when> <!-- 都不满足,执行这里逻辑 --> <otherwise> b.pay = 1 </otherwise> </choose> </where> </select> <!-- foreach 标签 collection:设置要循环的数组或者集合 item:当前每个数据 separator:循环之间的分隔符 open:循环前的开始符 close:循环后的结束符 --> <delete id="deleteByAll"> delete from content where id in <foreach collection="ids" item="id" separator="," open="(" close=")"> #{id} </foreach> </delete> <!-- sql 标签 , include 使用 --> <sql id="Table_Name"> bill </sql> <include refid="Table_Name"/>
注解版的增删改查
// useGeneratedKeys是否使用自增主键,keyProperty 指定主键值
@Options(useGeneratedKeys = true, keyProperty = "id")
@Insert("INSERT INTO bill (id,code,name) VALUES (null,#{code},#{name}) ")
int add(Bill bill);
@Update("update bill set bill_Name=#{billName} where bid=#{bid}")
int update(Bill bill);
@Select("select * from bill where bill_code=#{billCode} ")
Bill findByBillCode(String billCode);
@Select(" select * from bill where bid = #{bid} ")
List<Bill> findByBid(Long bid);
// 批量查询 ,注意:参数是字符串 1,3,4
@Select(" select * from bill where bid in (${bids}) ")
List<Bill> findByBids(String bids);
@Delete("delete from user where uid=#{uid}")
int deletePByPid(Long uid);
延迟加载
# 1. 配置文件:mybatis-config.xml
<settings>
<!--开启延迟加载-->
<setting name="lazyLoadingEnabled" value="true"/>
</settings>
# 2. 此时就可以实现按需加载,获取的数据是什么,就只会执行相应的sql。
# association和collection中的fetchType属性进行设置,fetchType="lazy(延迟加载)|eager(立即加载)"
<resultMap id="BillResultMOne" type="Bill">
<id property="bid" column="bid"></id>
<result property="billCode" column="bill_code"></result>
<result property="billName" column="bill_name"></result>
<!-- 方式3 分步查询
fetchType="lazy(延迟加载)|eager(立即加载)
property 实体的字段名 select 对应的Mapper文件
column 关联字段名 fetchType 延迟加载
-->
<association property="content"
select="com.xxx.dao.ContentMapper.getContent"
column="id"
fetchType="lazy"></association>
</resultMap>
MyBatis的缓存
一级缓存
一级缓存是SqlSession级别的,通过同一个SqlSession查询的数据会被缓存,默认是开启的。
失效情况:
- 不同的SqlSession下有不同的一级缓存
- 查询条件不同
- 同一个SqlSession两次查询期间执行了任何一次增删改操作
- 同一个SqlSession两次查询期间手动清空了缓存
二级缓存
二级缓存是SqlSessionFactory级别。
开启条件:
- 在核心配置文件中,设置全局配置属性cacheEnabled=“true”,默认为true,不需要设置
- 在映射文件中设置标签
- 二级缓存必须在SqlSession关闭或提交之后有效
- 查询的数据所转换的实体类类型必须实现序列化的接口
相关配置:
-
在mapper配置文件中添加的cache标签可以设置一些属性
-
eviction属性:缓存回收策略
-
LRU 最近最少使用的:移除最长时间不被使用的对象,默认的。
FIFO 先进先出:按对象进入缓存的顺序来移除它们。
SOFT 软引用:移除基于垃圾回收器状态和软引用规则的对象。
WEAK 弱引用:更积极地移除基于垃圾收集器状态和弱引用规则的对象。
-
flushInterval属性:刷新间隔,单位毫秒
-
默认情况是不设置,也就是没有刷新间隔,缓存仅仅调用语句(增删改)时刷新
-
size属性:引用数目,正整数,代表缓存最多可以存储多少个对象,太大容易导致内存溢出
-
readOnly属性:只读,true/false
true:只读缓存;会给所有调用者返回缓存对象的相同实例。因此这些对象不能被修改。这提供了很重要的性能优势。
false:读写缓存;会返回缓存对象的拷贝(通过序列化)。这会慢一些,但是安全,因此默认是false
MyBatis缓存查询顺序
- 先查询二级缓存,如果有之间返回,没有下一步
- 再查询一级缓存,如果有也返回,没有就进行查询数据库
- SqlSession关闭,会把当前一级缓存写入到二级缓存
缓存EHCache
# 1.引入包
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache</artifactId>
</dependency>
# 2.创建ehcache.xml配置文件,必须以这个为命名
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../config/ehcache.xsd">
<diskStore path="java.io.tmpdir"/>
<!--defaultCache:chcache的默认缓存策略 -->
<defaultCache
maxElementsInMemory="10000"
eternal="false"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
maxElementsOnDisk="10000000"
diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU">
<persistence strategy="localTempSwap"/>
</defaultCache>
<cache name="Bill"
maxElementsInMemory="10000"
eternal="false"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
maxElementsOnDisk="10000000"
diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU">
<persistence strategy="localTempSwap"/>
</cache>
</ehcache>
# 3.配置文件添加这行
spring:
# ehcache 缓存配置
cache:
ehcache:
config: classpath:/ehcache.xml
type: ehcache
# 4.启动类添加这个注解
// 开启缓存
@EnableCaching
# 5.实体类实现可序列化接口Serializable
# 6. 使用
// 定义缓存名字
@CacheConfig(cacheNames = "Bill")
// 进行添加缓存
@Cacheable(value = "Bill",key = "'bill:'+#bid")
public List<Bill> findAll() {
return billMapper.findAll();
}
// 修改,刷新到缓存中
@CachePut(value = "Bill", key = "#bill.bid")
public Bill updateById(Bill bill) {
billMapper.updateByBid(bill.getBid());
return bill;
}
// 删除缓存
@CacheEvict(value="Bill", allEntries=true)
public void save(Bill bill) {
//...
}
逆向工程
-
新建一个springboot 工程
-
修改pom.xml
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.mybatis.generator</groupId> <artifactId>mybatis-generator-core</artifactId> <version>1.3.7</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> <!-- mybatis generator 自动生成代码插件 --> <plugin> <groupId>org.mybatis.generator</groupId> <artifactId>mybatis-generator-maven-plugin</artifactId> <version>1.3.7</version> <configuration> <!-- 在控制台打印执行日志 --> <verbose>true</verbose> <!-- 重复生成时会覆盖之前的文件--> <overwrite>true</overwrite <configurationFile> src/main/resources/generatorConfig.xml </configurationFile> </configuration> <!-- 数据库连接选择8.0以上的,因为用的mysql8.0--> <dependencies> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.16</version> </dependency> </dependencies> </plugin> </plugins> <resources> <resource> <directory>src/main/java</directory> <includes> <include>**/*.xml</include> </includes> </resource> </resources> </build>
-
创建generator.xml配置文件
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE generatorConfiguration PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN" "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd"> <generatorConfiguration> <!-- context 是逆向工程的主要配置信息 --> <!-- id:起个名字 --> <!-- targetRuntime:设置生成的文件适用于那个 mybatis 版本 MyBatis3 奢华版 MyBatis3Simple 简洁版 --> <context id="default" targetRuntime="MyBatis3Simple"> <!--optional,指在创建class时,对注释进行控制--> <commentGenerator> <property name="suppressDate" value="true"/> <!-- 是否去除自动生成的注释 true:是 : false:否 --> <property name="suppressAllComments" value="true"/> </commentGenerator> <!--jdbc的数据库连接 wg_insert 为数据库名字--> <jdbcConnection driverClass="com.mysql.cj.jdbc.Driver" connectionURL="jdbc:mysql://10.238.233.180:3306/b?useUnicode=true&characeterEncoding=utf-8&serverTimezone=UTC" userId="root" password="xxx"></jdbcConnection> <!--非必须,类型处理器,在数据库类型和java类型之间的转换控制--> <javaTypeResolver> <!-- 默认情况下数据库中的 decimal,bigInt 在 Java 对应是 sql 下的 BigDecimal 类 --> <!-- 不是 double 和 long 类型 --> <!-- 使用常用的基本类型代替 sql 包下的引用类型 --> <property name="forceBigDecimals" value="false"/> </javaTypeResolver> <!-- targetPackage:生成的实体类所在的包 --> <!-- targetProject:生成的实体类所在的硬盘位置 --> <javaModelGenerator targetPackage="com.good.yan.modules.entities" targetProject="src/main/java"> <!-- 是否允许子包 --> <property name="enableSubPackages" value="false"/> <!-- 是否对modal添加构造函数 --> <property name="constructorBased" value="true"/> <!-- 是否清理从数据库中查询出的字符串左右两边的空白字符 --> <property name="trimStrings" value="true"/> <!-- 建立modal对象是否不可改变 即生成的modal对象不会有setter方法,只有构造方法 --> <property name="immutable" value="false"/> </javaModelGenerator> <!-- targetPackage 和 targetProject:生成的 mapper 文件的包和位置 --> <sqlMapGenerator targetPackage="com.good.yan.modules.mapper" targetProject="src/main/java"> <!-- 针对数据库的一个配置,是否把 schema 作为字包名 --> <property name="enableSubPackages" value="false"/> </sqlMapGenerator> <!-- targetPackage 和 targetProject:生成的 interface 文件的包和位置 --> <javaClientGenerator type="XMLMAPPER" targetPackage="com.good.yan.modules.dao" targetProject="src/main/java"> <!-- 针对 oracle 数据库的一个配置,是否把 schema 作为字包名 --> <property name="enableSubPackages" value="false"/> </javaClientGenerator> <!-- tableName是数据库中的表名,domainObjectName是生成的JAVA模型名,后面的参数不用改,要生成更多的表就在下面继续加table标签 --> <!--<table tableName="student" domainObjectName="Student" enableCountByExample="false" enableUpdateByExample="false" enableDeleteByExample="false" enableSelectByExample="false" selectByExampleQueryId="false"></table>--> <table tableName="t_md_org" domainObjectName="Org" enableCountByExample="false" enableUpdateByExample="false" enableDeleteByExample="false" enableSelectByExample="false" selectByExampleQueryId="false"></table> </context> </generatorConfiguration>
-
运行,点击maven中的generate进行运行。
分页插件
# 1.引包
<!-- https://mvnrepository.com/artifact/com.github.pagehelper/pagehelper -->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.2.0</version>
</dependency>
# 2.配置分页插件
<plugins>
<plugin interceptor="com.github.pagehelper.PageInterceptor">
<!--分页参数合理化 -->
<property name="reasonable" value="true"/>
</plugin>
</plugins>
# 3.案例
public void testPageHelper() throws IOException {
PageHelper.startPage(1, 2);
List<Bill> all = billMapper.findAll();
// 参数:数据 、 页码 尽量是奇数 1,2,3,4,5
PageInfo<Bill> pageInfo = new PageInfo<>(all,5);
}
常用数据:
- pageNum:当前页的页码
- pageSize:每页显示的条数
- size:当前页显示的真实条数
- total:总记录数
- pages:总页数
- prePage:上一页的页码
- nextPage:下一页的页码
- isFirstPage/isLastPage:是否为第一页/最后一页
- hasPreviousPage/hasNextPage:是否存在上一页/下一页
- navigatePages:导航分页的页码数
- navigatepageNums:导航分页的页码,[1,2,3,4,5]