秒杀项目-- 1、项目框架搭建

本文详细介绍了如何在Springboot项目中进行基础框架搭建,包括配置文件的管理(application.yml和环境变量)、公共返回结果的设计、全局异常处理机制及日志记录。重点展示了如何通过自定义异常类和全局异常处理器来统一异常处理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

项目框架搭建

这一块做 一般项目搭建后的准备工作,主要包括 新建项目、配置文件、公共返回类、异常处理、日志;
这些应该是 所有项目通用的,把它放到这一块了。

1、新建项目

选择组件,一般选择:Spring boot devtools、Mysql Driver、Mybatis framework、Thymeleaf、Spring web、lombok。

先配置文件,然后再测试能否启动
(1)配置文件 application.yml
要做事情:

  • 创建一个管理公共配置的 application.yml
  • 创建开发环境 application-dev.yml、 生产环境application-pro.yml,【测试环境application-test.yml】的配置文件;
    注意:这两个文件 分别连接对应环境的 数据库、redis、rabbitmq等服务,设置不同端口日志输出级别等等,对应了其所在的环境

2、配置文件

1、公共配置 application.yml
spring:
#如果有thymeleaf 模板引擎
  thymeleaf:
    mode: HTML	
    cache: false	#关闭缓存
   # 选用哪个配置文件,对应与 application-dev.yml和application-pro.yml 的文件
  profiles:
    active: dev

# 如果用 mybatis-plus,就换成下面
#mybatis-plus:
mybatis:
# 对应的 实体类的包
  type-aliases-package: com.tab.entity
 #对应的 mapper文件的包,在resources文件夹下面
  mapper-locations: classpath:mapper/*.xml
  configuration:
  #下划线 - 驼峰映射关系
    map-underscore-to-camel-case: true

下面举例 开发环境 和 生产环境,实际有所不同

2、开发环境 application-dev.yml
spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    # xxxx对应要连接数据库的名字, 后面的是可选的 修改配置信息,看实际情况修改,比如时区等
    url: jdbc:mysql://localhost:3306/xxxx?characterEncoding=utf8&useSSL=true&serverTimezone=UTC&zeroDateTimeBehavior=CONVERT_TO_NULL&serverTimezone=Asia/Shanghai&useSSL=false&allowPublicKeyRetrieval=true
    username: root
    password: 'root'

	  #数据库连接池 hikari:
	  hikari:
	      pool-name: DateHikariCP
	      #最小链接数
	      minimum-idle: 5
	      #空闲链接存活最大时间, 默认
	      idle-timeout: 1000000
	      #最大连接数
	      maximum-pool-size: 10
	      #从连接池返回的连接自动提交
	      auto-commit: true
	      #连接最大存活时间; 0 永久存活 默认
	      max-lifetime: 1000000
	      #连接超时时间, 默认30
	      connection-timeout: 30000
	      # 测试连接是否可以用
	      #connection-init-sql: SELECT 1

#日志输出级别、文件
logging:
  level:
    root: info
    com.tab: debug
  file:
    path: log/blog-dev.log

#服务端口
server:
  port: 1316
3、生产环境(日志级别可以设 高一点)
spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/xxxx?characterEncoding=utf8&useSSL=true&serverTimezone=UTC&zeroDateTimeBehavior=CONVERT_TO_NULL&serverTimezone=Asia/Shanghai&useSSL=false&allowPublicKeyRetrieval=true
    username: root
    password: 'root'
    
	  #数据库连接池 hikari:
	 hikari:
	     pool-name: DateHikariCP
	     #最小链接数
	     minimum-idle: 5
	     #空闲链接存活最大时间, 默认
	     idle-timeout: 1000000
	     #最大连接数
	     maximum-pool-size: 10
	     #从连接池返回的连接自动提交
	     auto-commit: true
	     #连接最大存活时间; 0 永久存活 默认
	     max-lifetime: 1000000
	     #连接超时时间, 默认30
	     connection-timeout: 30000
	     # 测试连接是否可以用
	     #connection-init-sql: SELECT 1

logging:
  level:
    root: warn
    com.tab: info
  file:
    path: log/blog-pro.log

server:
  port: 1317

配置文件弄好了,可以尝试开启项目,成功表示数据库连接 ok;

3、创建公共返回结果

公共返回结果,含有 状态信息、传递信息,方便后续在 controller层 作为返回值;
要创建两个, 一个 RespBeanEnum, 一个 RespBean;

RespBeanEnum:设置一些状态码
package com.tab.seckilltest.dto;


import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.ToString;

@AllArgsConstructor
@Getter
@ToString
public enum RespBeanEnum {
    //通用状态码
    SUCCESS(200, "success"),
    ERROR(500, "服务端异常");
    //登录模块状态码

    private final Integer code;
    private final String meesage;

}

RespBean 设置返回的状态信息的 通用函数
/**
 * projectName: SeckillTest
 * fileName: RespBean.java
 * packageName: com.tab.seckilltest.dto
 * date: 2021-10-03 21:05
 * copyright(c) 2017-2020 xxx公司
 */
package com.tab.seckilltest.dto;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * @version: V1.0
 * @author: Tab
 * @className: RespBean
 * @packageName: com.tab.seckilltest.dto
 * @description: 返回的结果对象
 * @data: 2021-10-03 21:05
 **/

@AllArgsConstructor
@NoArgsConstructor
@Data
public class RespBean {
    private long code;
    private String message;
    private Object obj;


    public static RespBean success(){
        return new RespBean(RespBeanEnum.SUCCESS.getCode(),
                RespBeanEnum.SUCCESS.getMeesage(), null);
    }

    public static RespBean success(Object obj){
        return new RespBean(RespBeanEnum.SUCCESS.getCode(),
                RespBeanEnum.SUCCESS.getMeesage(), obj);
    }

    public static RespBean error(RespBeanEnum respBeanEnum){
        return new RespBean(respBeanEnum.getCode(),
                respBeanEnum.getMeesage(),null);
    }
}

4、全局异常处理

spingmvc将所有类型的异常处理 从各处理过程 解耦出来,实现了异常信息的统一处理和维护;
Springboot全局异常处理主要种种方式

  • 使用 @ControllerAdvice 和 @ExceptionHandler 注解
  • 使用 ErrorController类来实现

区别:
1、使用@ControllerAdvice方式只能处理控制器抛出的异常,此时请求已经进入控制器中。
2、ErrorContorller类方式可以处理所有异常,包括未进入控制器的错误,比如404、401等错误。
3、如果应用中两者共同存在,则@ControllerAdvice方式处理控制器抛出的异常,ErrorController处理未进入控制器的异常;
4、@ControllerAdvice方式可以定义多个拦截器方法,拦截不同的异常类,并且可以获取抛出异常信息,自由度更大;

(1)我们创建 一个自定义的全局异常类(根据实际情况,我们可以定义不同的异常类), 和一个处理全局异常的 GlobalException;
这个类 有一个成员 RespBeanEnum(看上面), 里面有 异常的 信息;

/**
 * projectName: SeckillTest
 * fileName: GlobalException.java
 * packageName: com.tab.seckilltest.exception
 * date: 2021-10-03 21:27
 * copyright(c) 2017-2020 xxx公司
 */
package com.tab.seckilltest.exception;

import com.tab.seckilltest.dto.RespBeanEnum;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.Getter;
import lombok.NoArgsConstructor;

/**
 * @version: V1.0
 * @author: Tab
 * @className: GlobalException
 * @packageName: com.tab.seckilltest.exception
 * @description: 设定一个 异常种类的 成员变量
 * @data: 2021-10-03 21:27
 **/

@Data
@Getter
@NoArgsConstructor
@AllArgsConstructor
public class GlobalException extends RuntimeException{
    private RespBeanEnum respBeanEnum;
}

(2)GlobalExceptionHandler
可以在 controller 直接抛出 自定义类型的异常信息(本身也要返回 RespBean对象),前端能获取信息,呈现给用户,比如:用户名密码错误;

/**
 * projectName: SeckillTest
 * fileName: GlobalExceptionHandler.java
 * packageName: com.tab.seckilltest.exception
 * date: 2021-10-03 21:30
 * copyright(c) 2017-2020 xxx公司
 */
package com.tab.seckilltest.exception;

import com.tab.seckilltest.dto.RespBean;
import com.tab.seckilltest.dto.RespBeanEnum;
import org.springframework.validation.BindException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;


/**
 * @version: V1.0
 * @author: Tab
 * @className: GlobalExceptionHandler
 * @packageName: com.tab.seckilltest.exception
 * @description:
 * @data: 2021-10-03 21:30
 **/
 
@RestControllerAdvice
//这里用 RestController,返回的是 json数据包
public class GlobalExceptionHandler {

    @ExceptionHandler(Exception.class)
    public RespBean ExceptionHandler(Exception e){

        //如果是 自定义异常,就返回他的 异常信息
        if(e instanceof GlobalException){
            GlobalException ex = (GlobalException) e;
            return RespBean.error(ex.getRespBeanEnum());
        }
        //如果是 springboot 框架绑定的异常, 就处理
        else if(e instanceof BindException){
            BindException ex = (BindException) e;
            RespBean respBean = RespBean.error(RespBeanEnum.BIND_ERROR);
            respBean.setMessage("参数校验异常" + ex.getBindingResult().getAllErrors().get(0).getDefaultMessage());

            return respBean;
        }
        //返回错误
        return RespBean.error(RespBeanEnum.ERROR);
    }
}
5、日志处理

如果我们想要 知道用户访问 一些信息,比如 url、ip、classMethod、args等等,可以在用户访问Controller的 时候,用aop 来截取信息;

/**
 * projectName: SeckillTest
 * fileName: LogAspect.java
 * packageName: com.tab.seckilltest.aspect
 * date: 2021-10-03 21:58
 * copyright(c) 2017-2020 xxx公司
 */
package com.tab.seckilltest.aspect;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
import lombok.extern.slf4j.Slf4j;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;

/**
 * @version: V1.0
 * @author: Tab
 * @className: LogAspect
 * @packageName: com.tab.seckilltest.aspect
 * @description:
 * @data: 2021-10-03 21:58
 **/
@Slf4j
@Aspect
@Component
public class LogAspect {

    //定义切面,到controller包
    @Pointcut("execution(* com.tab.seckilltest.controller.*.*(..))")
    public void log(){}

    //定义请求信息 类
    @AllArgsConstructor
    @Data
    @ToString
    @NoArgsConstructor
    private class RequestLog{
        private String url;
        private String ip;
        private String classMethod;
        private Object[] args;
    }

    @Before("log()")
    public void doBefore(JoinPoint joinPoint){
        log.info("-----doBefore-----");
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();

        String url = request.getRequestURL().toString();
        String ip = request.getRemoteAddr();

        String classMethod = joinPoint.getSignature().getDeclaringTypeName() + "." +
                joinPoint.getSignature().getName();

        Object[] args = joinPoint.getArgs();
        RequestLog requestLog = new RequestLog(url,ip,classMethod,args);
        log.info("Request : {}", requestLog);
    }
    @After("log()")
    public void doAfter(JoinPoint joinpoint){
        log.info("-----doAfter-----");
    }


}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值