Second kill project -- 1. Project framework construction

Posted by halojoy on Mon, 04 Oct 2021 04:23:14 +0200

Project framework construction

This section is used to prepare for the construction of general projects, mainly including new projects, configuration files, public return classes, exception handling and logs;
These should be common to all projects. Put it here.

1. New project

Select components, generally Spring boot devtools, Mysql Driver, Mybatis framework, Thymeleaf, Spring web and lombok.

First configure the file and then test whether it can be started
(1) Configuration file application.yml
What to do:

  • Create an application.yml that manages public configuration
  • Create the configuration files of development environment application-dev.yml, production environment application-pro.yml and [test environment application-test.yml];
    Note: these two files are respectively connected to the database, redis, rabbitmq and other services of the corresponding environment. Different ports and log output levels are set to correspond to their environment

2. Configuration file

1. Common configuration application.yml

spring:
#If there is thymeleaf template engine
  thymeleaf:
    mode: HTML	
    cache: false	#Close cache
   # Which configuration file to choose corresponds to the files of application-dev.yml and application-pro.yml
  profiles:
    active: dev

# If you use mybatis plus, change to the following
#mybatis-plus:
mybatis:
# Package of the corresponding entity class
  type-aliases-package: com.tab.entity
 #The package of the corresponding mapper file is under the resources folder
  mapper-locations: classpath:mapper/*.xml
  configuration:
  #Underline hump mapping relationship
    map-underscore-to-camel-case: true

The following examples show that the development environment and production environment are actually different

2. Development environment application-dev.yml

spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    # xxxx corresponds to the name of the database to be connected, followed by optional configuration information to be modified according to the actual situation, such as time zone, etc
    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'

	  #Database connection pool hikari:
	  hikari:
	      pool-name: DateHikariCP
	      #Minimum number of links
	      minimum-idle: 5
	      #Maximum idle link lifetime, default
	      idle-timeout: 1000000
	      #maximum connection
	      maximum-pool-size: 10
	      #Connections returned from the connection pool are automatically submitted
	      auto-commit: true
	      #Maximum connection lifetime; 0 persistent default
	      max-lifetime: 1000000
	      #Connection timeout, 30 by default
	      connection-timeout: 30000
	      # Test whether the connection can be used
	      #connection-init-sql: SELECT 1

#Log output level, file
logging:
  level:
    root: info
    com.tab: debug
  file:
    path: log/blog-dev.log

#Service port
server:
  port: 1316

3. Production environment (log level can be set higher)

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'
    
	  #Database connection pool hikari:
	 hikari:
	     pool-name: DateHikariCP
	     #Minimum number of links
	     minimum-idle: 5
	     #Maximum idle link lifetime, default
	     idle-timeout: 1000000
	     #maximum connection
	     maximum-pool-size: 10
	     #Connections returned from the connection pool are automatically submitted
	     auto-commit: true
	     #Maximum connection lifetime; 0 permanent lifetime default
	     max-lifetime: 1000000
	     #Connection timeout, 30 by default
	     connection-timeout: 30000
	     # Test whether the connection can be used
	     #connection-init-sql: SELECT 1

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

server:
  port: 1317

After the configuration file is ready, you can try to open the project. Success indicates that the database connection is ok;

3. Create public return results

The public return result contains status information and transfer information, which is convenient for subsequent use as the return value in the controller layer;
To create two, one RespBeanEnum and one RespBean;

RespBeanEnum: set some status codes
package com.tab.seckilltest.dto;


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

@AllArgsConstructor
@Getter
@ToString
public enum RespBeanEnum {
    //General status code
    SUCCESS(200, "success"),
    ERROR(500, "Server exception");
    //Login module status code

    private final Integer code;
    private final String meesage;

}

RespBean sets the general function of the returned status information
/**
 * projectName: SeckillTest
 * fileName: RespBean.java
 * packageName: com.tab.seckilltest.dto
 * date: 2021-10-03 21:05
 * copyright(c) 2017-2020 xxx company
 */
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: Returned result object
 * @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. Global exception handling

Spin MVC decouples all types of exception handling from each processing process, and realizes the unified processing and maintenance of exception information;
There are various ways to handle Springboot global exceptions

  • Use @ ControllerAdvice and @ ExceptionHandler annotations
  • Use the ErrorController class to implement

difference:
1. The @ ControllerAdvice method can only handle the exceptions thrown by the controller. At this time, the request has entered the controller.
2. The errorcontroller class can handle all exceptions, including errors that do not enter the controller, such as 404, 401 and other errors.
3. If the two exist together in the application, the exception thrown by the controller is handled in @ ControllerAdvice mode, and the ErrorController handles the exception that does not enter the controller;
4. The @ ControllerAdvice method can define multiple interceptor methods to intercept different exception classes, and can obtain the thrown exception information with greater freedom;

(1) We create a custom global exception class (we can define different exception classes according to the actual situation) and a GlobalException to handle global exceptions;
This class has a member RespBeanEnum (see above), which contains exception information;

/**
 * projectName: SeckillTest
 * fileName: GlobalException.java
 * packageName: com.tab.seckilltest.exception
 * date: 2021-10-03 21:27
 * copyright(c) 2017-2020 xxx company
 */
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: Set a member variable of an exception type
 * @data: 2021-10-03 21:27
 **/

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

(2)GlobalExceptionHandler
You can throw custom type exception information directly in the controller (you should also return RespBean object). The front end can obtain information and present it to the user, such as user name and password error;

/**
 * projectName: SeckillTest
 * fileName: GlobalExceptionHandler.java
 * packageName: com.tab.seckilltest.exception
 * date: 2021-10-03 21:30
 * copyright(c) 2017-2020 xxx company
 */
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
 **/
@ControllerAdvice
public class GlobalExceptionHandler {

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

        //If it is a custom exception, its exception information is returned
        if(e instanceof GlobalException){
            GlobalException ex = (GlobalException) e;
            return RespBean.error(ex.getRespBeanEnum());
        }
        //If it is an exception bound by the springboot framework, handle it
        else if(e instanceof BindException){
            BindException ex = (BindException) e;
            RespBean respBean = RespBean.error(RespBeanEnum.BIND_ERROR);
            respBean.setMessage("Parameter verification exception" + ex.getBindingResult().getAllErrors().get(0).getDefaultMessage());

            return respBean;
        }
        //Return error
        return RespBean.error(RespBeanEnum.ERROR);
    }
}

5. Log processing

If we want to know the user's access to some information, such as url, ip, classMethod, args, etc., we can use aop to intercept the information when the user accesses the Controller;

/**
 * projectName: SeckillTest
 * fileName: LogAspect.java
 * packageName: com.tab.seckilltest.aspect
 * date: 2021-10-03 21:58
 * copyright(c) 2017-2020 xxx company
 */
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 {

    //Define the facet to the controller package
    @Pointcut("execution(* com.tab.seckilltest.controller.*.*(..))")
    public void log(){}

    //Define request information class
    @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-----");
    }


}

Topics: Spring Boot