导入SpringBoot 先导入空项目
修改依赖的版本:
重新导入依赖:
引入 mybatis-spring-boot-starter依赖和 mysql连接的依赖:
<!-- mybatis-spring-boot-starter依赖-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.2</version>
</dependency>
<!-- mysql连接的依赖-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
添加自动生成文件插件:
<plugin>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-maven-plugin</artifactId>
<version>1.3.7</version>
<configuration>
<verbose>true</verbose>
<overwrite>true</overwrite>
</configuration>
</plugin>
自动生成文件配置文件:generatorConfig.xml(放在resources目录下)
<?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>
<!-- 配置文件,放在resource目录下即可 -->
<!--数据库驱动个人配置-->
<!-- 本地存放mysql-connector-java-8.0.18.jar的路径-->
<classPathEntry
location="C:\Users\luo\.m2\repository\mysql\mysql-connector-java\8.0.18\mysql-connector-java-8.0.18.jar"/>
<context id="MysqlTables" targetRuntime="MyBatis3">
<property name="autoDelimitKeywords" value="true"/>
<!--可以使用``包括字段名,避免字段名与sql保留字冲突报错-->
<property name="beginningDelimiter" value="`"/>
<property name="endingDelimiter" value="`"/>
<!-- optional,旨在创建class时,对注释进行控制 -->
<commentGenerator>
<property name="suppressDate" value="true"/>
<property name="suppressAllComments" value="true"/>
</commentGenerator>
<!--数据库链接地址账号密码-->
<jdbcConnection driverClass="com.mysql.cj.jdbc.Driver"
connectionURL="jdbc:mysql://127.0.0.1:3306/bbs2020?serverTimezone=Hongkong&useUnicode=true&characterEncoding=utf-8&userSSL=false"
userId="root"
password="123456">
<property name="nullCatalogMeansCurrent" value="true"/>
</jdbcConnection>
<!-- 非必需,类型处理器,在数据库类型和java类型之间的转换控制-->
<javaTypeResolver>
<property name="forceBigDecimals" value="false"/>
</javaTypeResolver>
<!--生成Model类存放位置-->
<javaModelGenerator targetPackage="com.luo.bbs.model.pojo"
targetProject="src/main/java">
<!-- 是否允许子包,即targetPackage.schemaName.tableName -->
<property name="enableSubPackages" value="true"/>
<!-- 是否对类CHAR类型的列的数据进行trim操作 -->
<property name="trimStrings" value="true"/>
<!-- 建立的Model对象是否 不可改变 即生成的Model对象不会有 setter方法,只有构造方法 -->
<property name="immutable" value="false"/>
</javaModelGenerator>
<!--生成mapper映射文件存放位置-->
<sqlMapGenerator targetPackage="mappers" targetProject="src/main/resources">
<property name="enableSubPackages" value="true"/>
</sqlMapGenerator>
<!--生成Dao类存放位置-->
<javaClientGenerator type="XMLMAPPER" targetPackage="com.luo.bbs.model.dao"
targetProject="src/main/java">
<property name="enableSubPackages" value="true"/>
</javaClientGenerator>
<!--生成对应表及类名 domainObjectName是数据表对应的Javabean类名-->
<table schema="root" tableName="post" domainObjectName="Post"
enableCountByExample="false"
enableUpdateByExample="false" enableDeleteByExample="false" enableSelectByExample="false"
selectByExampleQueryId="false">
</table>
<table tableName="user" domainObjectName="User" enableCountByExample="false"
enableUpdateByExample="false" enableDeleteByExample="false" enableSelectByExample="false"
selectByExampleQueryId="false">
</table>
</context>
</generatorConfiguration>
在application.properties中书写与数据库相关的配置文件:
server.port=8092
spring.datasource.name=bbs_database
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/bbs2020?useUnicode=true&characterEncoding=utf8&autoReconnect=true&useSSL=false&serverTimezone=Asia/Shanghai
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.username=root
spring.datasource.password=123456
配置好这些后就可以写controller和service进行数据库的测试了。
在测试前还需要加入一些配置:
在入口类前面加上:
@MapperScan(basePackages = "com.luo.bbs.model.dao")
@ComponentScan(basePackages = {"com.luo.*"})
@MapperScan
作用:指定要变成实现类的接口所在的包,然后包下面的所有接口在编译之后都会生成相应的实现类
添加位置:是在Springboot启动类上面添加,
@ComponentScan 的作用就是根据定义的扫描路径,把符合扫描规则的类装配到spring容器中
在application.properties配置文件中:
#指定mappers文件在哪,从哪里去找
mybatis.mapper-locations=classpath:mappers/*.xml
这些都配置好后在调用mapper时idea会报红,但是不影响程序的正常运行:
这是因为idea不能识别这个类,@MapperScan(basePackages = “com.luo.bbs.model.dao”)只是让mybatis识别,idea不能识别,所以需要在每个Mapper接口前面加上@Repository注解
一切准备就绪后便可尝试跑通数据库连接
引入log4j日志组件
先在spring-boot-starter-web中去除自带的日志组件
,以便排除冲突
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
引入log4j2依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>
不需要配置版本,springboot会根据springboot的版本自动配置版本
引入log4j2配置文件:
log4j2.xml:
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="fatal">
<Properties>
<!--日志储存的地址-->
<Property name="baseDir" value="D:\log\bbs"/>
</Properties>
<Appenders>
<Console name="Console" target="SYSTEM_OUT">
<!--控制台只输出level及以上级别的信息(onMatch),其他的直接拒绝(onMismatch) -->
<ThresholdFilter level="info" onMatch="ACCEPT"
onMismatch="DENY"/>
<PatternLayout
pattern="[%d{MM:dd HH:mm:ss.SSS}] [%level] [%logger{36}] - %msg%n"/>
</Console>
<!--debug级别日志文件输出-->
<RollingFile name="debug_appender" fileName="${baseDir}/debug.log"
filePattern="${baseDir}/debug_%i.log.%d{yyyy-MM-dd}">
<!-- 过滤器 -->
<Filters>
<!-- 限制日志级别在debug及以上在info以下 -->
<ThresholdFilter level="debug"/>
<ThresholdFilter level="info" onMatch="DENY" onMismatch="NEUTRAL"/>
</Filters>
<!-- 日志格式 -->
<PatternLayout pattern="[%d{HH:mm:ss:SSS}] [%p] - %l - %m%n"/>
<!-- 策略 -->
<Policies>
<!-- 每隔一天转存 -->
<TimeBasedTriggeringPolicy interval="1" modulate="true"/>
<!-- 文件大小 -->
<SizeBasedTriggeringPolicy size="100 MB"/>
</Policies>
</RollingFile>
<!-- info级别日志文件输出 -->
<RollingFile name="info_appender" fileName="${baseDir}/info.log"
filePattern="${baseDir}/info_%i.log.%d{yyyy-MM-dd}">
<!-- 过滤器 -->
<Filters>
<!-- 限制日志级别在info及以上在error以下 -->
<ThresholdFilter level="info"/>
<ThresholdFilter level="error" onMatch="DENY" onMismatch="NEUTRAL"/>
</Filters>
<!-- 日志格式 -->
<PatternLayout pattern="[%d{HH:mm:ss:SSS}] [%p] - %l - %m%n"/>
<!-- 策略 -->
<Policies>
<!-- 每隔一天转存 -->
<TimeBasedTriggeringPolicy interval="1" modulate="true"/>
<!-- 文件大小 -->
<SizeBasedTriggeringPolicy size="100 MB"/>
</Policies>
</RollingFile>
<!-- error级别日志文件输出 -->
<RollingFile name="error_appender" fileName="${baseDir}/error.log"
filePattern="${baseDir}/error_%i.log.%d{yyyy-MM-dd}">
<!-- 过滤器 -->
<Filters>
<!-- 限制日志级别在error及以上 -->
<ThresholdFilter level="error"/>
</Filters>
<!-- 日志格式 -->
<PatternLayout pattern="[%d{HH:mm:ss:SSS}] [%p] - %l - %m%n"/>
<Policies>
<!-- 每隔一天转存 -->
<TimeBasedTriggeringPolicy interval="1" modulate="true"/>
<!-- 文件大小 -->
<SizeBasedTriggeringPolicy size="100 MB"/>
</Policies>
</RollingFile>
</Appenders>
<Loggers>
<Root level="debug">
<AppenderRef ref="Console"/>
<AppenderRef ref="debug_appender"/>
<AppenderRef ref="info_appender"/>
<AppenderRef ref="error_appender"/>
</Root>
</Loggers>
</Configuration>
AOP统一打印请求和返回信息
先引入AOP所需的依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
然后新建一个过滤器:
名为:WebLogAspect.java :
package com.luo.bbs.filter;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import java.util.Arrays;
/**
* 打印请求和响应信息
*/
@Aspect
@Component
public class WebLogAspect {
//导入log类,用于记录日志
private final Logger log = LoggerFactory.getLogger(WebLogAspect.class);
/**
* 指定拦截点的方法
*/
//配置拦截的点,拦截controller层以及controller层以下的所有内容
@Pointcut("execution(public * com.luo.bbs.controller.*.*(..))")
public void webLog() {
}
/**
* 拦截点之前 JoinPoint是提供请求参数的,记录的是关于类的信息,方法信息
*/
@Before("webLog()")
public void doBefore(JoinPoint joinPoint) {
//收到请求,记录请求内容
//拿到当前的请求
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
//对日志的记录
log.info("URL:" + request.getRequestURI().toString());
log.info("HTTP_METHOD:" + request.getMethod());
log.info("IP:" + request.getRemoteAddr());
//类的一些方法
//joinPoint.getSignature().getDeclaringTypeName()获取类名
//joinPoint.getSignature().getName() 获取方法名
log.info("CLASS_METHOD" + joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName());
//打印参数
log.info("ARGS:" + Arrays.toString(joinPoint.getArgs()));
}
/**
* 拦截点之后的方法
* res是webLog将要返回的内容
* @param res
*/
@AfterReturning(returning = "res",pointcut = "webLog()")
public void doAfterReturning(Object res) throws JsonProcessingException {
//new ObjectMapper().writeValueAsString(res) 把res转换为json格式
log.info("RESPONSE:"+new ObjectMapper().writeValueAsString(res));
}
}
编写统一返回对象;
ApiRestResponse.java中:
package com.luo.bbs.common;
import com.luo.bbs.exception.ExceptionEnum;
/***
* 描述: 通用返回对象
*/
public class ApiRestResponse<T> {
private Integer status;
private String msg;
private T data;
private static final int OK_COOE = 10000;
private static final String OK_MSG = "SUCCESS";
public ApiRestResponse(Integer status, String msg, T data) {
this.status = status;
this.msg = msg;
this.data = data;
}
public ApiRestResponse(Integer status, String msg) {
this.status = status;
this.msg = msg;
}
public ApiRestResponse() {
this(OK_COOE, OK_MSG);
}
//成功且不返回参数时
public static <T> ApiRestResponse<T> success() {
return new ApiRestResponse<>();
}
//成功且需要返回参数时
public static <T> ApiRestResponse<T> success(T result) {
ApiRestResponse<T> response = new ApiRestResponse<>();
response.setData(result);
return response;
}
//错误时(错误码和错误信息需要自己输)
public static <T> ApiRestResponse<T> error(Integer code,String msg) {
return new ApiRestResponse<>(code, msg);
}
//错误时,错误码和错误信息在枚举中定义
public static <T> ApiRestResponse<T> error(ExceptionEnum ex) {
return new ApiRestResponse<>(ex.getCode(), ex.getMsg());
}
public Integer getStatus() {
return status;
}
public void setStatus(Integer status) {
this.status = status;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
public static int getOkCooe() {
return OK_COOE;
}
public static String getOkMsg() {
return OK_MSG;
}
@Override
public String toString() {
return "ApiRestResponse{" +
"status=" + status +
", msg='" + msg + '\'' +
", data=" + data +
'}';
}
}
并新建一个枚举类,用户存放错误码和错误信息
ExceptionEnum.java:
package com.luo.bbs.exception;
/**
* 异常枚举
*/
public enum ExceptionEnum {
NEED_USER_NAME(1001, "用户名不能为空");
Integer code;
String msg;
ExceptionEnum(Integer code, String msg) {
this.code = code;
this.msg = msg;
}
public Integer getCode() {
return code;
}
public void setCode(Integer code) {
this.code = code;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
}
处理统一异常:(主要为自定义异常)
BbsException.java:
package com.luo.bbs.exception;
/**
* 统一异常
*/
public class BbsException extends RuntimeException {
private final Integer code;
private final String message;
public BbsException(Integer code, String message) {
this.code = code;
this.message = message;
}
public BbsException(ExceptionEnum exceptionEnum) {
this(exceptionEnum.getCode(), exceptionEnum.getMsg());
}
public Integer getCode() {
return code;
}
@Override
public String getMessage() {
return message;
}
}
统一处理系统的异常:
package com.luo.bbs.exception;
import com.luo.bbs.common.ApiRestResponse;
import com.luo.bbs.filter.WebLogAspect;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.validation.BindingResult;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import java.util.ArrayList;
import java.util.List;
/**
* 描述: 处理统一异常的Handler
*/
//@ControllerAdvice作用:拦截异常
@ControllerAdvice
public class GlobalExceptionHandler {
private final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class);
//处理系统异常
@ExceptionHandler(Exception.class)
@ResponseBody
public Object handleException(Exception e) {
log.error("Default Exception:",e);
return ApiRestResponse.error(ExceptionEnum.SYSTEM_ERROR);
}
//处理业务异常(编写程序时抛出的异常)
@ExceptionHandler(BbsException.class)
@ResponseBody
public Object handleBbsException(BbsException e) {
log.error("BbsException:",e);
return ApiRestResponse.error(e.getCode(), e.getMessage());
}
//以下两个可能不会用到
//处理传递参数异常时返回的错误信息
@ExceptionHandler(MethodArgumentNotValidException.class)
@ResponseBody
public ApiRestResponse handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
log.error("MethodArgumentNotValidException",e);
return handleBindingResult(e.getBindingResult());
}
private ApiRestResponse handleBindingResult(BindingResult result) {
//把异常处理为对外暴露的提示
List<String> list = new ArrayList<>();
if (result.hasErrors()) {
List<ObjectError> allErrors = result.getAllErrors();
for (int i = 0; i < allErrors.size(); i++) {
ObjectError objectError = allErrors.get(i);
String message = objectError.getDefaultMessage();
list.add(message);
}
}
if (list.size()==0) {
return ApiRestResponse.error(ExceptionEnum.REQUEST_PARAM_ERROR);
}
return ApiRestResponse.error(ExceptionEnum.REQUEST_PARAM_ERROR.getCode(), list.toString());
}
}
过滤器的开发:(拦截需要登录之后才能操作的URL)
过滤器:UserFilter.java
package com.luo.bbs.filter;
import com.luo.bbs.common.ApiRestResponse;
import com.luo.bbs.common.Constant;
import com.luo.bbs.exception.ExceptionEnum;
import com.luo.bbs.model.pojo.User;
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.IOException;
import java.io.PrintWriter;
/**
* 用户校验过滤器
*/
public class UserFilter implements Filter {
public static User currentUser;
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpSession session = request.getSession();
currentUser= (User) session.getAttribute(Constant.NOW_USER);
if (currentUser == null) {
PrintWriter out = new HttpServletResponseWrapper(
(HttpServletResponse) servletResponse).getWriter();
out.write("{\n"
+ " \"status\": 10004,\n"
+ " \"msg\": \" NEED_LOGIN\",\n"
+ " \"data\": null\n"
+ "}");
out.flush();
out.close();
return;
}
filterChain.doFilter(servletRequest, servletResponse);
}
@Override
public void destroy() {
}
}
过滤器配置文件:
package com.luo.bbs.config;
import com.luo.bbs.filter.UserFilter;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* User过滤器的配置
*/
@Configuration
public class UserFilterConfig {
@Bean
public UserFilter userFilter() {
return new UserFilter();
}
@Bean(name = "userFilterConf")
public FilterRegistrationBean userFilterConfig() {
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
filterRegistrationBean.setFilter(userFilter());
filterRegistrationBean.addUrlPatterns("/update/*");
filterRegistrationBean.addUrlPatterns("/delete/*");
filterRegistrationBean.setName("userFilterConf");
return filterRegistrationBean;
}
}
自动生成API文档:
先引入依赖:
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.9.2</version>
</dependency>
然后在main函数中加上注解:@EnableSwagger2,代表开启了Swagger能力
然后再添加swagger的配置文件:SpringFoxConfig.java :
package com.luo.bbs.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
@Configuration
public class SpringFoxConfig {
//访问http://localhost:8092/swagger-ui.html可以看到API文档
@Bean
public Docket api() {
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
.select()
.apis(RequestHandlerSelectors.any())
.paths(PathSelectors.any())
.build();
}
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title("BBS系统")
.description("")
.termsOfServiceUrl("")
.build();
}
}
再配置MvcConfig: WebMvcConfig.java :(配置地址映射)
package com.luo.bbs.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
* 配置地址映射
*/
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("swagger-ui.html").addResourceLocations(
"classpath:/META-INF/resources/");
registry.addResourceHandler("/webjars/**").addResourceLocations(
"classpath:/META-INF/resources/webjars/");
}
}
参数校验@Valid
在请求参数前加上@Valid注解:
然后再到对应的实体类中添加验证规范以及错误时的提示信息:
再到之前处理统一异常的Handler中 GlobalExceptionHandler.java中
//处理传递参数异常时返回的错误信息
@ExceptionHandler(MethodArgumentNotValidException.class)
@ResponseBody
public ApiRestResponse handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
log.error("MethodArgumentNotValidException",e);
return handleBindingResult(e.getBindingResult());
}
private ApiRestResponse handleBindingResult(BindingResult result) {
//把异常处理为对外暴露的提示
List<String> list = new ArrayList<>();
if (result.hasErrors()) {
List<ObjectError> allErrors = result.getAllErrors();
for (int i = 0; i < allErrors.size(); i++) {
ObjectError objectError = allErrors.get(i);
String message = objectError.getDefaultMessage();
list.add(message);
}
}
if (list.size()==0) {
return ApiRestResponse.error(ExceptionEnum.REQUEST_PARAM_ERROR);
}
return ApiRestResponse.error(ExceptionEnum.REQUEST_PARAM_ERROR.getCode(), list.toString());
}
分页对象:
SQL语句:
<select id="selectListForAdmin" resultMap="BaseResultMap">
select
<include refid="Base_Column_List" />
from imooc_mall_product
order by update_time desc
</select>
Mapper:
实现分页:
public PageInfo listForAdmin(Integer pageNum, Integer pageSize) {
PageHelper.startPage(pageNum, pageSize);
List<Product> products = productMapper.selectListForAdmin();
PageInfo pageInfo = new PageInfo(products);
return pageInfo;
}
复制类:(将相同类型相同字段的属性复制)
BeanUtils.copyProperties(addPostReq, post);
将addPostReq复制到post