目录
SpringBoot背景及特点
SpringBoot的设计目是抛弃之前Spring、SpringMVC繁杂的配置过程,简化开发过程。之前的Spring框架需要大量的手动配置,包括XML配置文件或Java配置类,配置过程繁琐且易出错。此外,Spring应用程序需要部署到外部的Web服务器,并需要额外的步骤来启动和运行。SpringBoot通过自动配置、默认配置可以简化配置过程;通过嵌入的服务器可以使得应用程序独立运行,无需部署到外部服务器。SpringBoot有如下特点:
1、自动配置、默认配置:SpringBoot提供自动配置功能,根据项目的依赖和环境自动设置Spring应用程序,减少了手动配置的复杂度
2、starter启动器:SpringBoot提供各类启动器,如 spring-boot-starter-web
,简化了项目的依赖管理。
3、嵌入服务器:SpringBoot支持嵌入式服务器,如Tomcat
、Jetty等,
使得应用程序可以独立运行,无需外部Web服务器。
4、健康检查、监控:SpringBoot提供健康检查、应用监控、日志管理等。Actuator 模块可以轻松监控和管理应用程序。
SpringBoot整合Mybatis Plus
整合目标如下:
1、整合mybatis plus,提供分页功能(基于mybatis plus内部自带的分页插PaginationInterceptor
、MybatisPlusConfig)
2、前台controller统一的的返回值封装(GenericWebResult)
3、统一的异常处理(GenericExceptionHandler、BusinessExceptionCode、BusinessException)
4、增加请求记录时间(LogInterceptor、WebConfig)
5、Hikari数据源
代码工程目录如下:
整合步骤如下:
1、增加依赖【pom.xml】
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.6</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.gingko</groupId>
<artifactId>springboot</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>springboot</name>
<description>Demo project for Spring Boot</description>
<dependencies>
<!-- 依赖spring-web-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- springboot默认数据源(HikariCP)-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<!-- mysql驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.16</version>
</dependency>
<!-- 整合MyBatis Plus-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.2</version>
</dependency>
<!-- SpringBoot 的AOP实现 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<!-- junit 依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<!-- SpringBoot 健康检查 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!-- lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.0</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>${maven.compiler.source}</source>
<target>${maven.compiler.target}</target>
<encoding>${project.build.sourceEncoding}</encoding>
</configuration>
</plugin>
</plugins>
</build>
</project>
2、 配置springboot【application.yml】
3、配置类MybatisPlusConfig、WebConfig
package com.gingko.config;
import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* MybatisPlusConfig 配置类,配置分页插件
*/
@Configuration
public class MybatisPlusConfig {
@Bean
public PaginationInterceptor paginationInterceptor() {
return new PaginationInterceptor();
}
}
package com.gingko.config;
import com.gingko.interceptor.LogInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import javax.annotation.Resource;
/**
* web配置类,配置拦截器
*/
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Resource
LogInterceptor logInterceptor;//日志拦截器
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(logInterceptor)
.addPathPatterns("/**")
.excludePathPatterns("/test/*");//测试相关的不用日志拦截
}
}
4、实体、dto、Mapper接口、mapper.xml
package com.gingko.entity;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
@TableName("student")
public class Student {
@TableId(value = "id")
private String id;
private String name;
private int age;
private String courseId;//课程id
}
package com.gingko.dto;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class StudentDto {
private String id;
private String name;
private int age;
private String courseId;//课程id
private String courseName;//课程名称
}
package com.gingko.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.gingko.dto.StudentDto;
import com.gingko.entity.Student;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Repository;
@Repository
@Mapper
public interface StudentMapper extends BaseMapper<Student> {
IPage<StudentDto> selectStudentMap(IPage page, @Param("minAge") Integer minAge);
}
<?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.gingko.mapper.StudentMapper">
<select id="selectStudentMap" parameterType="Integer" resultType="com.gingko.dto.StudentDto">
select s.* ,c.name as courseName from student s left join course c on s.course_id = c.id where s.age >= #{minAge}
</select>
</mapper>
5、服务层接口及实现
package com.gingko.service;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.gingko.dto.StudentDto;
import com.gingko.entity.Student;
import com.gingko.exception.BusinessException;
public interface StudentService {
void batchInsert() throws BusinessException;
IPage<Student> getByPage(long pageNum, long pageSize) throws BusinessException;
IPage<StudentDto> getStudentInfoByPage(long pageNum, long pageSize, int minAge) throws BusinessException;
}
package com.gingko.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.gingko.dto.StudentDto;
import com.gingko.entity.Student;
import com.gingko.exception.BusinessException;
import com.gingko.exception.BusinessExceptionCode;
import com.gingko.mapper.StudentMapper;
import com.gingko.service.StudentService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
@Transactional(rollbackFor = Exception.class)
@Service
public class StudentServiceImpl implements StudentService {
@Resource
private StudentMapper studentMapper;
@Override
public void batchInsert() throws BusinessException {
for(int i=10;i<20;i++) {
if(i==13) {
throw new BusinessException(BusinessExceptionCode.SIMULATE_EXCEPTION);
}
Student student = new Student(i+"","张三"+i,i,"1");
studentMapper.insert(student);
}
}
@Override
public IPage<Student> getByPage(long pageNum, long pageSize) throws BusinessException {
IPage<Student> page = new Page<>(pageNum,pageSize);
QueryWrapper<Student> queryWrapper = new QueryWrapper<>();
//queryWrapper.gt("age",10);
page = this.studentMapper.selectPage(page, queryWrapper);
return page;
}
@Override
public IPage<StudentDto> getStudentInfoByPage(long pageNum, long pageSize, int minAge) throws BusinessException {
IPage<StudentDto> page = new Page<>(pageNum,pageSize);
page = this.studentMapper.selectStudentMap(page,minAge);
return page;
}
public StudentMapper getStudentMapper() {
return studentMapper;
}
public void setStudentMapper(StudentMapper studentMapper) {
this.studentMapper = studentMapper;
}
}
6、http接口层
package com.gingko.controller;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.gingko.common.GenericWebResult;
import com.gingko.dto.StudentDto;
import com.gingko.service.StudentService;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
@RestController
@RequestMapping("student")
public class StudentController {
@Resource
private StudentService studentService;
@PostMapping("/batchInsert")
public GenericWebResult batchInsert() {
this.studentService.batchInsert();
return GenericWebResult.ok("批量新增成功");
}
@GetMapping("/getByPage")
public GenericWebResult getByPage(@RequestParam long pageNum, @RequestParam long pageSize,
@RequestParam int minAge) {
IPage<StudentDto> studentPage = this.studentService.getStudentInfoByPage(pageNum, pageSize,minAge);
return GenericWebResult.ok("分页查询成功",studentPage);
}
}
7、统一web返回结果GenericWebResult
package com.gingko.common;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
/**
* 向前台返回的统一格式的结果
*/
@Data
@ToString
@AllArgsConstructor
@NoArgsConstructor
public class GenericWebResult {
/**
* 是否成功
*/
private boolean successFlag;
/**
* 成功或失败返回的信息
*/
private String msg;
/**
/**
* 返回列表或树形数据,用户可以自由添加
*/
private Object property;
/**
* 数据总条数,分页情况下使用
*/
private Long total;
public static GenericWebResult ok() {
return new GenericWebResult(true,null,null,null);
}
public static GenericWebResult ok(String msg) {
return new GenericWebResult(true,msg,null,null);
}
public static GenericWebResult ok(Object property) {
return new GenericWebResult(true,null,property,null);
}
public static GenericWebResult ok(String msg, Object property) {
return new GenericWebResult(true,msg,property,null);
}
public static GenericWebResult ok(Object property, Long total) {
return new GenericWebResult(true,null,property,total);
}
public static GenericWebResult ok(String msg, Object property, Long total) {
return new GenericWebResult(true,msg,property,total);
}
public static GenericWebResult error(String msg) {
return new GenericWebResult(false,msg,null,null);
}
public static GenericWebResult error(String msg, Object data) {
return new GenericWebResult(false,msg,data,null);
}
}
8、 统一异常
package com.gingko.exception;
/**
* 业务异常代码
*/
public enum BusinessExceptionCode {
//Common
SIMULATE_EXCEPTION("模拟异常"),
RECORD_NOT_EXISTED("系统中不存在此条记录");
private String desc;//异常描述
BusinessExceptionCode(String desc) {
this.desc = desc;
}
public String getDesc() {
return desc;
}
public void setDesc(String desc) {
this.desc = desc;
}
}
package com.gingko.exception;
/**
* 通用业务异常类
*/
public class BusinessException extends RuntimeException{
private BusinessExceptionCode code;
public BusinessException (BusinessExceptionCode code) {
super(code.getDesc());
this.code = code;
}
public BusinessExceptionCode getCode() {
return code;
}
public void setCode(BusinessExceptionCode code) {
this.code = code;
}
@Override
public Throwable fillInStackTrace() {
return this;
}
}
package com.gingko.exception;
import com.gingko.common.GenericWebResult;
import lombok.extern.slf4j.Slf4j;
import org.springframework.validation.BindException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
/**
* 统一异常处理、数据预处理等
*/
@Slf4j
@ControllerAdvice // AOP所有controller
public class GenericExceptionHandler {
/**
* 校验异常统一处理
* @return
*/
@ExceptionHandler(value = BindException.class)
@ResponseBody
public GenericWebResult validExceptionHandler(BindException e) {
log.error("参数校验失败:{}", e.getBindingResult().getAllErrors().get(0).getDefaultMessage());
return GenericWebResult.error(e.getBindingResult().getAllErrors().get(0).getDefaultMessage());
}
/**
* 业务异常统一处理
* @return
*/
@ExceptionHandler(value = BusinessException.class)
@ResponseBody
public GenericWebResult validExceptionHandler(BusinessException e) {
log.error("业务异常:{}", e.getCode().getDesc());
return GenericWebResult.error(e.getCode().getDesc());
}
/**
* 其他异常统一处理
* @return
*/
@ExceptionHandler(value = Exception.class)
@ResponseBody
public GenericWebResult validExceptionHandler(Exception e) {
log.error("系统异常:{}", e);
return GenericWebResult.error("系统出现异常,请联系管理员");
}
}
9、日志拦截器
package com.gingko.interceptor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
//日志拦截器,记录每个请求的ip,执行时间等
@Component
@Slf4j
public class LogInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
log.info("====请求记录日志开始====");
log.info("请求地址:{}",request.getRequestURL().toString());
log.info("请求服务器地址:{}",request.getRemoteAddr());
long requestStartTime = System.currentTimeMillis();
request.setAttribute("requestStartTime",requestStartTime);
return true;//继续项下执行
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
long requestStartTime = (Long)request.getAttribute("requestStartTime");
long requestEndTime = System.currentTimeMillis();
log.info("====请求记录日志结束====,请求耗时:{}毫秒",requestEndTime - requestStartTime);
}
}
10、启动类
package com.gingko;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
@MapperScan(basePackages="com.ginkgo.mapper") //扫描mapper接口
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
11、postman接口测试及运行结果