SpringBoot高级用法

简介: SpringBoot高级用法

SpringBoot中高级用法

生产项目中一般会有项目改进


1、我们一般不会在controller中捕获异常进行处理,一般通过全局异常处理器进行拦截处理


2、为了让响应的格式进行统一,一般会对响应结果进行统一包装。


3、为了能够快速定位问题,一般会结合日志打印框架让每次http请求都能够打印一个唯一标识方便问题定位


1、统一异常处理

定义异常枚举

public enum ResultCode {
    /**
     * 成功
     */
    SUCCESS(0, "success"),
    FAIL(501,"操作失败"),
    /**
     * 未知错误
     */
    UNKNOWN_ERROR(500, "unkonwn error"),
    /**
     * 用户名错误或不存在
     */
    USERNAME_ERROR(401, "username error or does not exist"),
    /**
     * 密码错误
     */
    PASSWORD_ERROR(402, "password error"),
    /**
     * 用户名不能为空
     */
    USERNAME_EMPTY(403, "username can not be empty");
    /**
     * 结果码
     */
    private int code;
    /**
     * 结果码描述
     */
    private String msg;
    ResultCode(int code, String msg) {
        this.code = code;
        this.msg = msg;
    }
    public int getCode() {
        return code;
    }
    public String getMsg() {
        return msg;
    }
}

自定义业务异常


import cn.educate.boot.demo.enu.ResultCode;
/**
 * @author yinchong
 * @create 2021/4/25 16:24
 * @description 业务异常类
 */
public class BizRuntimeExcption extends RuntimeException {
    int code;
    String msg;
    public BizRuntimeExcption(ResultCode resultCode) {
        super(resultCode.getMsg());
        this.code = resultCode.getCode();
        this.msg = resultCode.getMsg();
    }
    public BizRuntimeExcption(int code, String msg) {
        super(msg);
        this.code = code;
        this.msg = msg;
    }
    public int getCode() {
        return code;
    }
    public void setCode(int code) {
        this.code = code;
    }
    public String getMsg() {
        return msg;
    }
    public void setMsg(String msg) {
        this.msg = msg;
    }
}

添加全局异常处理器

import cn.educate.boot.demo.enu.ResultCode;
import cn.educate.boot.demo.model.dto.ResultDTO;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
@ControllerAdvice
public class GlobalExceptionResolver {
    private static final Logger LOG = LoggerFactory.getLogger(GlobalExceptionResolver.class);
    /**
     * 处理所有不可知异常
     *
     * @param e 异常
     * @return json结果
     */
    @ExceptionHandler(Exception.class)
    @ResponseBody
    public ResultDTO handleException(Exception e) {
        // 打印异常堆栈信息
        LOG.error(e.getMessage(), e);
        return ResultDTO.of(ResultCode.UNKNOWN_ERROR);
    }
    /**
     * 处理所有业务异常
     *
     * @param e 业务异常
     * @return json结果
     */
    @ExceptionHandler(BizRuntimeExcption.class)
    @ResponseBody
    public ResultDTO handleOpdRuntimeException(BizRuntimeExcption e) {
        // 不打印异常堆栈信息
        LOG.error(e.getMsg());
        return ResultDTO.of(e.getCode(),e.getMsg());
    }
}

业务代码使用

service层超时失败抛出业务异常

import cn.educate.boot.demo.dao.UserMapper;
import cn.educate.boot.demo.enu.ResultCode;
import cn.educate.boot.demo.model.dto.UserDTO;
import cn.educate.boot.demo.model.po.UserPO;
import cn.educate.boot.demo.model.vo.UserQuery;
import cn.educate.boot.demo.model.vo.UserVO;
import cn.educate.boot.demo.service.IUserService;
import cn.educate.boot.demo.system.BizRuntimeExcption;
import org.apache.logging.log4j.util.Strings;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import tk.mybatis.mapper.entity.Example;
import java.util.LinkedList;
import java.util.List;
/**
 * @author yinchong
 * @create 2021/4/25 9:35
 * @description
 */
@Service
public class UserServiceImpl implements IUserService {
    @Autowired
    private UserMapper userDao;
    @Override
    public void addUser(UserVO vo) {
        int rowCount = userDao.addUser(vo);
        checkRow(rowCount);
    }
    @Override
    public void updateUser(UserVO vo) {
        int rowCount = userDao.updateUser(vo);
        checkRow(rowCount);
    }
    @Override
    public void deleteUser(Long id) {
      int rowCount = userDao.deleteUser(id);
      checkRow(rowCount);
    }
    @Override
    public List<UserDTO> listUser(UserQuery query) {
        Example example = new Example(UserPO.class);
        Example.Criteria criteria = example.createCriteria();
        if(query.getId()!=null){
            criteria.andEqualTo("id",query.getId());
        }else if(Strings.isNotBlank(query.getName())){
            criteria.andLike("name","%"+query.getName()+"%");
        }
        List<UserPO> list = userDao.selectByExample(example);
        List<UserDTO> result = new LinkedList<>();
        for(UserPO po:list){
            UserDTO dto = new UserDTO();
            BeanUtils.copyProperties(po,dto);
            result.add(dto);
        }
        return result;
    }
    private void checkRow(int row){
        if(row<=0){
            throw new BizRuntimeExcption(ResultCode.FAIL);
        }
    }
}

controller层取消try、catch

import cn.educate.boot.demo.model.dto.UserDTO;
import cn.educate.boot.demo.model.vo.UserQuery;
import cn.educate.boot.demo.model.vo.UserVO;
import cn.educate.boot.demo.service.IUserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
/**
 * @author yinchong
 * @create 2021/4/25 9:29
 * @description
 */
@RestController
@RequestMapping("user")
@Slf4j
public class UserController {
    @Autowired
    private IUserService userService;
    /***
     * 添加用户
     * @param vo
     * @return
     */
    @RequestMapping("add")
    public String add(@RequestBody UserVO vo) {
        log.info("add user vo:{}", vo);
        userService.addUser(vo);
        return "SUCCESS";
    }
    /**
     * 更新用户
     *
     * @param vo
     * @return
     */
    @RequestMapping("update")
    public String update(@RequestBody UserVO vo) {
        log.info("update user vo:{}",vo);
        userService.updateUser(vo);
        return "SUCCESS";
    }
    /***
     * 删除用户
     * @param query
     * @return
     */
    @RequestMapping("delete")
    public String delete(@RequestBody UserQuery query) {
        log.info("delete User vo:{}",query);
        userService.deleteUser(query.getId());
        return "SUCCESS";
    }
    /***
     * 查询用户
     * @param query
     * @return
     */
    @RequestMapping("listUser")
    public List<UserDTO> listUser(@RequestBody UserQuery query) {
        log.info("query user vo:{}",query);
        return userService.listUser(query);
    }
}

统一格式输出

 

import cn.educate.boot.demo.model.dto.ResultDTO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
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;
/**
 * @author yinchong
 * @create 2021/4/25 16:33
 * @description
 */
@Slf4j
@ControllerAdvice
public class ResultResponseBodyAdvice implements ResponseBodyAdvice {
    @Override
    public boolean supports(MethodParameter returnType, Class converterType) {
        //TODO 这里可以根据自己需求比如有些方法不需要进行拦截,可以在这里进行改造
        return true;
    }
    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
        if (body instanceof ResultDTO) {
            return body;
        }
        return ResultDTO.success(body);
    }
}

http请求唯一标识打印

代码改造
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.UUID;
@Configuration
public class RequestIdLogAppendConfiguration extends WebMvcConfigurerAdapter {
    /**
     * 每次请求生成唯一标识
     **/
    public static final String REQUEST_ID = "requestId";
    private static Logger logger = LoggerFactory.getLogger(WebMvcConfigurerAdapter.class);
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        HandlerInterceptor handlerInterceptor = new HandlerInterceptor() {
            @Override
            public boolean preHandle(HttpServletRequest request,
                                     HttpServletResponse response, Object handler) throws Exception {
                //生成请求id
                String requestId = UUID.randomUUID().toString().replaceAll("-", "");
                MDC.put(REQUEST_ID, requestId);
                return true;
            }
            @Override
            public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
            }
            @Override
            public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
                //销毁请求id
                MDC.clear();
            }
        };
        registry.addInterceptor(handlerInterceptor).addPathPatterns("/**");
    }
}

log4j配置变动

添加requestId输出

  <!--变量配置-->
    <Properties>
        <!-- 格式化输出:%date表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度 %msg:日志消息,%n是换行符-->
        <!-- %logger{36} 表示 Logger 名字最长36个字符 -->
        <property name="LOG_PATTERN" value="%date{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level [%X{requestId}] %logger{36} - %msg%n" />
        <!-- 定义日志存储的路径 -->
        <property name="FILE_PATH" value="D://data/log/boot-educate" />
        <property name="FILE_NAME" value="boot-educate" />
    </Properties>

github地址:https://github.com/yinkaihuang/boot-educate.git  分支:advance


目录
相关文章
|
7月前
|
消息中间件 缓存 监控
spring boot 高级篇
spring boot 高级篇
321 1
|
7月前
|
Java 数据安全/隐私保护
SpringBoot - 优雅的实现【参数分组校验】高级进阶
SpringBoot - 优雅的实现【参数分组校验】高级进阶
186 0
|
4月前
|
NoSQL Java MongoDB
SpringBoot中MongoDB的那些高级用法
本文探讨了在Spring Boot项目中使用MongoDB的多种方式及其高级用法。MongoDB作为一种NoSQL数据库,在某些场景下相较于SQL数据库有着独特的优势。文中详细介绍了在Spring Boot中使用MongoDB的三种主要方式:直接使用官方SDK、使用Spring JPA以及使用MongoTemplate,并对比分析了它们之间的差异。此外,文章深入讲解了Spring Data MongoDB提供的各种注解(如@Id, @Document, @Field等)以简化操作流程,并探讨了MongoTemplate监听器的应用,如设置主键值、记录日志等。
224 2
|
4月前
|
NoSQL Java MongoDB
SpringBoot中MongoDB的那些骚操作用法
MongoDB作为一种NoSQL数据库,在不需要传统SQL数据库的表格结构的情况下,提供了灵活的数据存储方案。在Spring Boot中可以通过官方SDK、Spring JPA或MongoTemplate等方式集成MongoDB。文章重点介绍了Spring Data MongoDB提供的注解功能,例如`@Id`、`@Document`和`@Field`等,这些注解简化了Java对象到MongoDB文档的映射。此外,文中还讨论了MongoTemplate监听器的使用,包括设置主键值和日志记录等高级特性。
265 0
SpringBoot中MongoDB的那些骚操作用法
|
4月前
|
Java API
SpringBoot Scheduled 常见用法
SpringBoot Scheduled 常见用法
50 0
|
6月前
|
JSON Java 数据格式
Spring Boot 中的 @DateTimeFormat 和 @JsonFormat 的用法及作用
【6月更文挑战第11天】在开发 Spring Boot 应用时,处理日期和时间数据是一个常见的需求。Spring Boot 提供了两个注解 @DateTimeFormat 和 @JsonFormat 来帮助我们处理这些问题。
519 4
|
6月前
|
前端开发 Java 开发者
深入理解 Spring Boot 注解:核心功能与高级用法详解
深入理解 Spring Boot 注解:核心功能与高级用法详解
376 1
|
7月前
|
Java 开发者 Spring
Springboot中的@Bean用法以及常见问题
【5月更文挑战第27天】@Bean 注解是Spring框架中用于声明Spring应用上下文中的bean的一种方式。在Spring Boot中,@Bean注解通常与@Configuration注解一起使用,在配置类(Configuration class)中定义bean。
211 2
|
7月前
|
XML Java API
springboot 常用的注解标签的概念及用法RequiredArgsConstructor 、RestController、RequestMapping
【4月更文挑战第12天】在 Spring Boot 中,@RequiredArgsConstructor, @RestController, 和 @RequestMapping 是常用的注解,每个都有其特定的功能和用法,它们合起来极大地简化了 Spring 应用程序的开发过程。
235 2
|
7月前
|
安全 Java Spring
springboot @Resource、@AutoWaire的用法实战
【4月更文挑战第3天】在Spring Boot中,@Autowired和@Resource注解都用于自动注入依赖,但它们在底层工作方式和使用的场合上存在一些差异。理解这些差异有助于更有效地使用Spring框架。
232 1