1. 背景

由于微服务的流行,我们会动不动就建立一个新的项目作为一个服务,那么项目中的全局异常处理和统一数据格式是很重要的,如果设计不好,不仅开发时很乱,在查询日志时也会相当麻烦,所以我自己设计了一个简单的项目框架,个人感觉在小项目上会很好用,如果项目太大,可能需要再追求细节。

2. 项目的功能

2.1 整合了mybatis plus

2.2 整合了druid

2.3 整合了log4j2,并通过lombok,能更方便的打印日志

2.4 实现了统一数据返回格式(默认全部请求都是统一的返回格式)

2.5 实现了全局异常统一处理

2.6 利用aop打印了接口访问信息,如ip,接口访问时长等

3.功能详细讲解(由于某些文件内容过大,所以我就不在这里粘贴了,会在文章末尾提供一个项目下载地址的)

3.1 整合mybatis plus

由于本人喜欢用mybatis,所以就整合了mybatis plus作为简化mybatis操作的框架

3.1.1 安装mybatis plus

<dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.1.0</version>
</dependency>

建议安装3.1.0,因为大于该版本会有一个bug,那就是不能正确映射LocalDateTime类,会报错,当然如果你喜欢使用Date类,那就完全没问题,LocalDateTime是java8推出的时间类,从api的使用便捷性和性能上看比Date都要好一点,该bug可参考:https://mp.baomidou.com/guide/faq.html#error-attempting-to-get-column-create-time-from-result-set-cause-java-sql-sqlfeaturenotsupportedexception

3.1.2 在启动类上添加@MapperScan注解,并指定mapper文件夹位置

3.1.3 mybatis plus的自动填充功能

由于每张表基本上都会有created_time,updated_time等公共属性,并且我们又想在创建时自动设置时间,而不用我们在service层中手动设置时间属性,这就可以使用mybatis的自动填充功能呢。

首先创建一个MyBatisPlusConfig配置类,开启他的自动填充功能

@Configuration
public class MyBatisPlusConfig {/***     自动填充功能* @return*/@Beanpublic GlobalConfig globalConfig() {GlobalConfig globalConfig = new GlobalConfig();globalConfig.setMetaObjectHandler(new MetaHandler());return globalConfig;}
}

其次创建MetaHandler类,重写MetaObjectHandler接口中的方法即可,我的项目中有该文件,此处就不作展示了

3.2 整合druid

3.2.1 安装druid

<dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>1.1.16</version>
</dependency>

3.2.2 创建一个application-druid.yml文件,并添加配置

3.2.3 在application.yml添加application-druid.yml配置,使其生效

spring:profiles:active:- druid- dev

3.2.4 创建DruidConfig配置类

至此,druid的配置就结束了,可通过localhost:8080/druid 查看sql访问情况

3.3 整合log4j2

3.3.1 引入依赖

<dependency> <!-- 引入log4j2依赖 -->  <groupId>org.springframework.boot</groupId>  <artifactId>spring-boot-starter-log4j2</artifactId>
</dependency> 

3.3.2 去除springboot默认使用的logback

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId><exclusions><!-- 去掉springboot默认配置 -->  <exclusion>  <groupId>org.springframework.boot</groupId>  <artifactId>spring-boot-starter-logging</artifactId>  </exclusion>  </exclusions>
</dependency>

3.3.3 创建log4j2-sit.xml文件,并放在src/main/resource的目录下

3.3.4 在application.yml中进行配置,使得springboot使用log4j2作为默认日志框架

logging:config: classpath:log4j2-sit.xml

3.3.5 整合lombok,使用log打印日志

<dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional>
</dependency>
@Log4j2
public class TestService{public void insertUser() {log.info("aa");}
}

以往我们打印日志都需要先 Logger log = LoggerFactory.getLogger(xxx)一下,然后再使用,这种使用方式太麻烦,第一是各种框架获取logger对象的方法是不一样的,导致你使用前还要去看看以前代码,当然如果你记忆力好,那也没啥问题,第二是要传入当前类的class。而集成了lombok后

通过两步就可以很方便打印日志了,第一步,在class上添加@log4j2注解,这是lombok的注解,如果没有该注解,请检查下使用添加了lombok的依赖,第二部,直接log对象打印即可。

3.3.6 当你使用了lombok的@Data注解,会遇到一个问题,当你继承了一个父类时,会有警告,此时需要在/src/main/java目录下创建一个lombok.config文件:

config.stopBubbling=true
lombok.equalsAndHashCode.callSuper=call

3.4 统一数据返回格式

3.4.1 思路

我设计的思路:由于目前是前后端分离,所以现在的后端项目基本上返回的都是json,并不会进行页面跳转控制。我们规定所有返回给前端的数据格式均为{code,msg,data}这三个参数,报错另说。我添加了一个拦截器,该拦截器会拦截所有请求,并判断controller层的方法上是否有被ResponseNature注解修饰,该注解是自定义的注解,如果没有,那么就添加一个attr在request中做一个标记,如果有该注解就不添加该attr。并添加一个controllerAdvice增强器,在被@responseBody处理数据前,对数据进行自定义的处理,处理时判断是否有在拦截器中做的attr标记,如果有,那么就说明需要做同一格式,没有,就不做。所以写代码时,不用添加任何注解,默认就是将数据进行统一返回格式处理,如果你不想处理,就要返回原本的对象,那么就在方法或者类上添加@ResponseNature注解

3.4.2 创建ResponseResultInterceptor类

/*** */
package com.rewa.test.interceptor;import java.lang.reflect.Method;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import com.rewa.test.annotations.ResponseNature;
import com.rewa.test.util.DBConstants;
import com.rewa.test.util.RequestContextUtil;@Component
public class ResponseResultInterceptor implements HandlerInterceptor {/*** 拦截请求,默认给所有请求添加RESPONSE_RESULT标识,如果controller层的类或方法有被ResponseNature注解修饰,那么就不添加标识,即不进行统一数据返回*/@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {RequestContextUtil.setRequestId();if (handler instanceof HandlerMethod) {final HandlerMethod handlerMethod = (HandlerMethod) handler;final Class<?> clazz = handlerMethod.getBeanType();final Method method = handlerMethod.getMethod();if (clazz.isAnnotationPresent(ResponseNature.class) ||  method.isAnnotationPresent(ResponseNature.class)) {return true;}request.setAttribute(DBConstants.RESPONSE_RESULT,DBConstants.RESPONSE_RESULT);}return true;}@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {}
}

3.4.3 创建InterceptorConfig配置文件

package com.rewa.test.config;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;import com.rewa.test.interceptor.ResponseResultInterceptor;@Configuration
public class InterceptorConfig implements WebMvcConfigurer{public static String ALLPATH = "/**";@Autowiredprivate ResponseResultInterceptor responseResultInterceptor;@Overridepublic void addInterceptors(InterceptorRegistry registry) {// 对所有的请求都要拦截registry.addInterceptor(responseResultInterceptor).addPathPatterns(ALLPATH);}
}

注意:有些人看别人的博客在开启拦截器时会添加@EnableWebMvc注解,该注解添加后会有非常多的问题,我个人不建议使用该注解,不添加该注解,拦截器也是可以正常工作的,具体有哪些坑,我遇到的一个是application.yml里面对json的配置失效了,即

spring:jackson:default-property-inclusion: non-null

具体有哪些坑,可以参考该文章:https://blog.csdn.net/zxc123e/article/details/84636521

通过以上配置,我们已经成功对request进行了标识,现在我们就要开始处理返回数据了

3.4.4 添加ResponseResultHandle类

package com.rewa.test.handle;import javax.servlet.http.HttpServletRequest;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
import com.rewa.test.constants.DBConstants;
import com.rewa.test.msg.response.UnitiveResponse;
import com.rewa.test.util.JsonUtil;
import com.rewa.test.util.RequestContextUtil;@ControllerAdvice
public class ResponseResultHandle implements ResponseBodyAdvice<Object>{@Overridepublic boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {HttpServletRequest request = RequestContextUtil.getRequest();String flag = (String) request.getAttribute(DBConstants.RESPONSE_RESULT);return flag != null && flag.equals(DBConstants.RESPONSE_RESULT);}@Overridepublic Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType,Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request,ServerHttpResponse response) {if (body instanceof UnitiveResponse) {return body;} else if (body instanceof String) {return JsonUtil.object2Json(UnitiveResponse.success(body));}else {return UnitiveResponse.success(body);} }
}

support方法是用来判断是否需要执行下面的beforeBodyWrite方法,通过代码可以发现,只有request中有了RESPONSE_RESULT标识才允许执行beforeBodyWrite方法,那在beforeBodyWrite中只需要使用创建出一个统一格式返回的对象即可:UnitiveResponse类如下所示,我们可以暂时只看success方法,BusinessException 是自定义的异常对象,可以先不考虑。注意,当返回值为String的时候,处理是有区别的,所以可以看到我对String进行了单独处理

/*** */
package com.rewa.test.msg.response;import com.rewa.test.enums.ResultCode;
import com.rewa.test.exception.BusinessException;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;/*** @author thinker**/
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Data
public class UnitiveResponse{private Integer code;private String msg;private Object data;// 错误原因protected String mainCause;// 详细堆栈信息protected String trace;// 请求id,唯一的private Long requestId;public static UnitiveResponse success() {UnitiveResponse result = new UnitiveResponse();result.setResultCode(ResultCode.SUCCESS);return result;}public static UnitiveResponse success(Object data) {UnitiveResponse result = new UnitiveResponse();result.setResultCode(ResultCode.SUCCESS);result.setData(data);return result;}private void setResultCode(ResultCode code) {this.code = code.code();this.msg = code.message();}public static UnitiveResponse error(BusinessException e) {UnitiveResponse u = new UnitiveResponse();u.setCode(e.getCode());u.setMainCause(e.getMainCause());u.setMsg(e.getMessage());u.setTrace(e.getTrace());u.setRequestId(e.getRequestId());return u;}
}

ResultCode是一个枚举,其中包含了所有的提示,包括正确的和错误的

/*** */
package com.rewa.test.enums;/*** @author thinker**/
public enum ResultCode {/* 成功状态码 */SUCCESS(0, "成功"),/* 系统错误码 */SYSTEM_INNER_ERROR(-1, "The system is busy, please try again"),//前端错误//后端错误TEST(2,"test fail {}");private Integer code;private String message;public Integer code() {return this.code;}public String message() {return this.message;}ResultCode(Integer code, String message) {this.code = code;this.message = message;}public static String getMessage(String name) {for (ResultCode item : ResultCode.values()) {if (item.name().equals(name)) {return item.message;}}return name;}public static Integer getCode(String name) {for (ResultCode item : ResultCode.values()) {if (item.name().equals(name)) {return item.code;}}return null;}@Overridepublic String toString() {return this.name();}
}

至此统一数据格式返回就已经完成了

3.5 全局异常处理

3.5.1 创建一个BusinessException类,代表所有的业务异常,我们这里没分那么细,该异常就代表了所有异常

package com.rewa.test.exception;import java.util.Arrays;
import com.rewa.test.enums.ResultCode;
import com.rewa.test.util.RequestContextUtil;
import com.rewa.test.util.StrUtils;
import lombok.Data;@Data
public class BusinessException extends RuntimeException{/*** */private static final long serialVersionUID = 3275057317616326272L;protected Integer code;protected String message;// 原因protected String mainCause;// 详细堆栈信息,只取了前5条protected String trace;private Long requestId;public BusinessException (Integer code,String message, Exception e) {this.requestId = RequestContextUtil.getRequestId();this.code = code;this.message = message;this.mainCause = e.getMessage();this.trace = getStackMsg(e);}public BusinessException (String message) {this.requestId = RequestContextUtil.getRequestId();this.code = ResultCode.SYSTEM_INNER_ERROR.code();this.message = message;}public BusinessException(Integer code, String format,Exception e, Object... objects) {this.requestId = RequestContextUtil.getRequestId();this.code = code;this.message = StrUtils.formatIfArgs(format, "{}", objects);this.mainCause = e.getMessage();this.trace = getStackMsg(e);}public BusinessException(ResultCode resultCode) {this.requestId = RequestContextUtil.getRequestId();this.code = resultCode.code();this.message = resultCode.message();}public BusinessException(ResultCode resultCode, Object... objects) {this.requestId = RequestContextUtil.getRequestId();this.code = resultCode.code();this.message = StrUtils.formatIfArgs(resultCode.message(), "{}", objects);}public BusinessException(ResultCode resultCode,Exception e, Object... objects) {this.requestId = RequestContextUtil.getRequestId();this.code = resultCode.code();this.message = StrUtils.formatIfArgs(resultCode.message(), "{}", objects);this.mainCause = e.getMessage();this.trace = getStackMsg(e);}public BusinessException(ResultCode resultCode,Exception e) {this(resultCode);this.mainCause = e.getMessage();this.trace = getStackMsg(e);}private static String getStackMsg(Exception e) {StackTraceElement[] copyOfRange = Arrays.copyOfRange(e.getStackTrace(), 0, 5);return Arrays.toString(copyOfRange);}
}

3.5.2 创建GlobalExceptionHandle类用来拦截所有的异常

package com.rewa.test.handle;import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import com.rewa.test.enums.ResultCode;
import com.rewa.test.exception.BusinessException;
import com.rewa.test.msg.response.UnitiveResponse;
import lombok.extern.log4j.Log4j2;@RestControllerAdvice
@Log4j2
public class GlobalExceptionHandle {@ExceptionHandler(value = BusinessException.class)public UnitiveResponse errorHandler(BusinessException e) {log.error("请求id:"+e.getRequestId() + ",错误原因:" + e.getMainCause(),e);return UnitiveResponse.error(e);}@ExceptionHandler(value = Exception.class)public UnitiveResponse errorHandler(Exception e) {BusinessException ex = new BusinessException(ResultCode.SYSTEM_INNER_ERROR,e);log.error("错误id:"+ex.getRequestId() + ",错误原因:" + ex.getMainCause(),ex);return UnitiveResponse.error(ex);}
}

3.5.3 使用时有两种抛异常的方式,第一种手动try-catch后抛出,第二是没有捕获到直接抛出

对于第一种,类似下面这种形式

public void getUser() {try {System.out.println(1/1);}catch (Exception e) {throw new BusinessException("sssssss");}
}

当手动抛出时,会匹配到GlobalExceptionHandle类中的第一个方法,然后返回一个UnitiveResponse对象

对于第二种,即没有try-catch就直接抛出异常的情况,会匹配到GlobalExceptionHandle类中的第二个方法,返回一个UnitiveResponse对象

3.6 利用aop打印接口访问信息

3.6.1 添加依赖

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId>
</dependency>

3.6.2 开启aop功能

在启动类上添加@EnableAspectJAutoProxy注解

3.6.3 创建ApiLogAop文件

package com.rewa.test.aop;import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Arrays;
import javax.servlet.http.HttpServletRequest;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.context.annotation.Configuration;
import com.rewa.test.util.RequestContextUtil;
import lombok.extern.log4j.Log4j2;@Aspect
@Configuration
@Log4j2
public class ApiLogAop {public static DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");/*** 定义一个切入点.* 解释下:**/@Pointcut("execution(* com.rewa.test.controller.*.*(..))")public void webLog(){}@Around("webLog()")public Object around(ProceedingJoinPoint joinPoint) throws Throwable{Object result = null;long startTime = System.currentTimeMillis();try {result = joinPoint.proceed();} catch (Throwable e) {logInfo(joinPoint,startTime,false);throw e;}logInfo(joinPoint,startTime,true);return result;}public void logInfo (ProceedingJoinPoint joinPoint,long startTime,boolean status) {long endTime = System.currentTimeMillis();HttpServletRequest request = RequestContextUtil.getRequest();String requestMethod = joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName();StringBuffer sb = new StringBuffer("");sb.append("api接口访问日志-----").append("请求id:").append(RequestContextUtil.getRequestId()).append(",").append("执行方法:").append(requestMethod).append(",").append("执行状态:").append(status == true?"成功":"失败").append(",").append("执行时间:").append(dtf.format(LocalDateTime.now())).append(",").append("耗时(毫秒):").append(endTime-startTime).append(",").append("URL:").append(request.getRequestURL().toString()).append(" ").append(request.getMethod()).append(",").append("IP:").append(request.getRemoteAddr()).append(",").append("ARGS:").append(Arrays.toString(joinPoint.getArgs()));log.info(sb.toString());}
}

4. 注意点

4.1 其实做全局异常处理有两种方法,第一种就是通过@ControllerAdvice,第二种就是通过aop来做,但是要注意这两种方法的调用先后顺序

aop中如下处理异常,即通过try-catch来做

@Around("webLog()")
/**
*要有返回值,否则就返回不了joinPoint.proceed()的返回值了
*/
public Object around(ProceedingJoinPoint joinPoint) throws Throwable{Object result = null;long startTime = System.currentTimeMillis();try {result = joinPoint.proceed();} catch (Throwable e) {logInfo(joinPoint,startTime,false);throw e;}logInfo(joinPoint,startTime,true);return result;
}

所以执行的顺序应该是先被aop中的异常捕获,如果在catch时throw了这个异常,这时才会被@ControllerAdvice的异常机制处理

4.2 我在返回错误时,我返回了一个requestId,该requestId就是在拦截器中设置到request的attr中的,这个requestId还是很有用的,可以在log中快速定位到错误

4.3 分布式唯一id问题(Long转String)

现在分布式唯一id一般都是使用long类型,但是前端js在处理long类型时会出现精度问题,所以在返回给前端时,应该bean中的id全部转为string类型,但是bean的属性已经被定义为Long了,应该怎么改呢?可以使用@JsonSerialize(using=Long2StringHandle.class)注解,该注解是springboot自带的,Long2StringHandle是一个自定义的class,专门是用来处理Long转String

public class Long2StringHandle extends JsonSerializer<Long>{@Overridepublic void serialize(Long value, JsonGenerator gen, SerializerProvider serializers) throws IOException {gen.writeString(String.valueOf(value));}
}

4.4 当controller层方法上有response参数引起的问题

一共有2种情况,如代码所示

// 1.情况1,返回值为void
@RequestMapping(value="/log",method=RequestMethod.GET)
public void log(HttpServletResponse response) {}//2. 情况2,返回值为对象
@RequestMapping(value="/log",method=RequestMethod.GET)
public User log(HttpServletResponse response) {return new User("tfp");
}

情况1,正常情况下应该是{code: 0,msg:"成功"},但是实际上会导致没有任何结果返回

情况2,和正常情况一致,没有任何问题

所以这就说明了当controller层方法上有response参数时,会导致结果和想象中的不一致,这是因为springboot源码对于这种情况有特殊处理的,所以建议不要在controller层上写response参数,完全可以使用注入对象的方式。如果你非要使用response参数,并且返回值还是void,那么你至少需要抛出一个异常才行,否则返回值为空

@Autowired
protected HttpServletResponse response;

4.5 当返回值为String时的特殊处理

@RequestMapping(value="/log",method=RequestMethod.GET)
public String log(HttpServletResponse response) {return "11";
}

当返回值为String时,在response时是需要特殊处理的,否则会报错:UnitiveResponse类型转不了String类型

package com.rewa.test.handle;import javax.servlet.http.HttpServletRequest;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
import com.rewa.test.constants.DBConstants;
import com.rewa.test.msg.response.UnitiveResponse;
import com.rewa.test.util.JsonUtil;
import com.rewa.test.util.RequestContextUtil;@ControllerAdvice
public class ResponseResultHandle implements ResponseBodyAdvice<Object>{@Overridepublic boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {HttpServletRequest request = RequestContextUtil.getRequest();String flag = (String) request.getAttribute(DBConstants.RESPONSE_RESULT);return flag != null && flag.equals(DBConstants.RESPONSE_RESULT);}@Overridepublic Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType,Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request,ServerHttpResponse response) {if (body instanceof UnitiveResponse) {return body;} else if (body instanceof String) {return JsonUtil.object2Json(UnitiveResponse.success(body));}else {return UnitiveResponse.success(body);} }
}

5 说明

可能你看这篇文章会有点难看懂,但是如果你结合整个项目来看,就会很容易看懂了。

项目地址:https://github.com/tanfangping/springboot-base-template

基于springboot整合了mybatis plus,lombok,log4j2并实现了全局异常处理及统一数据返回格式(code,msg,data)相关推荐

  1. 基于Springboot整合RestTemplate调用Webservice接口

    1.基于Springboot整合RestTemplate调用Webservice接口,如果感觉使用webservice客户端调用服务器端不会,或者不方便 的时候,可以尝试使用RestTemplate来 ...

  2. SpringBoot + Vue前后端分离开发:全局异常处理及统一结果封装

    SpringBoot + Vue前后端分离开发:全局异常处理及统一结果封装 文章目录 SpringBoot + Vue前后端分离开发:全局异常处理及统一结果封装 前后端分离开发中的异常处理 统一结果封 ...

  3. 基于springboot整合spring-retry

    1.背景 本系统调用外围系统接口(http+json),但是发现有时外围系统服务不太稳定,有时候会出现返回一串xml或者gateway bad的信息,导致调用失败,基于这一原因,采用基于springb ...

  4. SpringBoot整合Redis+mybatis,封装RedisUtils工具类等实战(附源码)

    点击上方蓝色字体,选择"标星公众号" 优质文章,第一时间送达 关注公众号后台回复pay或mall获取实战项目资料+视频 作者:陈彦斌 cnblogs.com/chenyanbin/ ...

  5. SpringBoot整合的Mybatis出现的问题:org.springframework.beans.factory.NoSuchBeanDefinitionException:

    在学习springboot整合Mybatis时,遇到的一个错误,跟着老师的步伐敲 结果还是出现了一系列的问题 自己搜了三四个小时都找不出问题(本人比较钻牛角尖!!!...) 出现的报错内容 org.s ...

  6. SpringBoot 如何统一后端返回格式?老鸟们都是这样玩的!

    大家好,我是磊哥. 今天我们来聊一聊在基于SpringBoot前后端分离开发模式下,如何友好的返回统一的标准格式以及如何优雅的处理全局异常. 首先我们来看看为什么要返回统一的标准格式? 为什么要对Sp ...

  7. SpringBoot整合Freemarker+Mybatis

    开发工具 , 开始 新建工程 .选择Spring Initializr 下一步 下一步,选择需要的组件 ..改一下工程名,Finish ..目录结构 首先,修改pom文件 然后,将applicatio ...

  8. 基于springboot整合的rabbitmq

    技术:springboot1.5.2 + maven3.0.5 + rabbitmq3.7.13 + jdk1.8 概述 RabbitMQ是对高级消息队列协议(Advanced Message Que ...

  9. springboot整合之统一异常处理

    特别说明:本次项目整合基于idea进行的,如果使用Eclipse可能操作会略有不同,不过总的来说不影响. springboot整合之如何选择版本及项目搭建 springboot整合之版本号统一管理  ...

最新文章

  1. 有查看自己dian nao mi |W| ma 的软件
  2. 错误 - 无法访问IIS元数据库
  3. css技巧之如何实现ul li边框重合
  4. LeetCode 1161. 最大层内元素和(层序遍历)
  5. 【连载】如何掌握openGauss数据库核心技术?秘诀三:拿捏存储技术(4)
  6. 网络爬虫python的特点有哪些_为什么写网络爬虫天然就是择Python而用
  7. 吴恩达深度学习4.3练习_Convolutional Neural Networks_Car detection
  8. 广东技能大赛软件测试项目,我校喜获2019年全国职业院校技能大赛软件测试赛项二等奖...
  9. 路畅安卓最新固件升级_路畅车载导航系统刷机-路畅导航系统刷机固件大全下载最新完整版-《百度网盘下载》西西软件下载...
  10. 工商银行网银支付问题 有网站想要安装以下加载项 来自Industrial and Commercial Bank of China Limited‘ 第三方支付机构上送网联跳转报文请求参数错误
  11. VBS写出有趣的整人代码
  12. win7台式计算机型号怎么查,win7系统电脑查看主板型号的四种方法
  13. 计算机键盘设置功能键取消,键盘insert操作怎么取消?电脑键盘insert操作取消教程...
  14. 计算机家庭组无法访问,Win7共享文件夹无法访问解决方法
  15. java double 保留小数_java使double类型保留两位小数的方法
  16. HTML+CSS系列学习 第五篇
  17. 数据分析-C端与B端数据分析的异同
  18. 几种快速传输大文件的方式
  19. 位时间(Tbit) 时间份额(TQ) CAN波特率
  20. Linux IPC总结(全)

热门文章

  1. 社保html源码,社保查询流程图.html
  2. vue 组件递归(组件自己调用自己)
  3. 查询仅仅选修了指定的两门课程的学生学号
  4. mysql 逻辑值的真和假_( )逻辑值的“真”和“假”可以用逻辑常量TRUE和FALSE表示。_学小易找答案...
  5. 移动端-手机端-日历选择控件(支持Zepto和JQuery)
  6. scrapy框架---带你飞向爬虫路(九)
  7. r语言删除csv中na行_R中去除为NA的行--转载
  8. LTE中常见的缩写含义
  9. DEBUG STACK TRACE for PoolBackedDataSource.close() 异常
  10. 设计模式六大原则(一)----单一职责原则