IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> Springboot通过Filter过滤HttpServletRequest和HttpServletResponse的数据作为日志并通过@Async异步入库 -> 正文阅读

[Java知识库]Springboot通过Filter过滤HttpServletRequest和HttpServletResponse的数据作为日志并通过@Async异步入库

R是统一泛型返回对象,Log是日志对象,LogHttpServletResponseWrapper是防止请求数据获取后丢失

建好数据库即可,表不存在会自动创建,别说什么高并发会怎么怎么样了,高并发不用搜索引擎,还想用数据库记录日志就是疯了

异步入库结果图

在这里插入图片描述
A线程获取完数据,以后入库由B线程去执行,不影响主线程,即是入库失败也不会影响主线程的功能

LogFilter.java

过滤请求头和返回内容

import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.*;

/**
 * 过滤器
 */
@Slf4j
@Component
@WebFilter(urlPatterns = "/*", filterName = "logFilter")
public class LogFilter implements Filter {
    @Value("${spring.application.name}")
    private String applicationName;
    @Value("${log-application.ignore-uri}")
    private String ignoreURIs;
    @Resource
    private LogService logService;

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {

        HttpServletRequest request = (HttpServletRequest) servletRequest;
        HttpServletResponse response = (HttpServletResponse) servletResponse;

        Map<String, String[]> parameterMap = request.getParameterMap();
        Map<String, Object> map = new HashMap<>();
        if (parameterMap != null) {
            parameterMap.forEach((key, value) -> {
                map.put(key, value[0]);
            });
        }

        LogHttpServletResponseWrapper logHttpServletResponseWrapper = new LogHttpServletResponseWrapper(response);//获取返回内容
        filterChain.doFilter(request, logHttpServletResponseWrapper);//必须放到logHttpServletResponseWrapper.getContent()前面,否则拿不到数据

        //异步写入日志入库,用try catch报错就报错,不用管,不影响正常业务逻辑即可,日志并不是特别的重要
        try {
            String content = new String(logHttpServletResponseWrapper.getContent(), StandardCharsets.UTF_8);
//            log.info("原返回值:{}",content);
            ObjectMapper om = new ObjectMapper();
            R r = om.readValue(content, R.class);

//            log.info(ignoreURI);
            //URI白名单
            boolean haveIgnoreURI = Arrays.stream(ignoreURIs.split(",")).anyMatch(ignoreURI -> request.getRequestURI().contains(ignoreURI));
            if (!haveIgnoreURI) {
                Log logObj = new Log();
//            logObj.setId(UUID.randomUUID().toString());
                logObj.setUserId("1");
                logObj.setUsername("Meta");
                logObj.setApplicationName(applicationName);
                logObj.setMethod(request.getMethod());
                logObj.setRequestURI(request.getRequestURI());
                logObj.setRequestData(String.valueOf(map));
                logObj.setCode(r.getCode());
                logObj.setRespondData(om.writeValueAsString(r.getData()));
                logObj.setErrorMsg(om.writeValueAsString(r.getDetailErrorMsg()));
                logObj.setCreateTime(new Date());
                log.info("打印当前A线程名称:{}",Thread.currentThread().getName());
                logService.insert(logObj);
                r.setDetailErrorMsg(null);//获取完以后,设置详细异常为null
            }
            String newContent = om.writeValueAsString(r);

            //修改返回内容长度,解决返回内容长度不一致导致请求卡住的问题
            response.setContentLength(newContent.getBytes(StandardCharsets.UTF_8).length);//这里要注意:setContentLength(字符串的字节长度,不是字符串的长度)
            //修改完写入输出流,返回给前端
            ServletOutputStream out = response.getOutputStream();
            out.write(newContent.getBytes(StandardCharsets.UTF_8));//写入返回内容
            out.flush();//刷新
            out.close();//关闭流
        } catch (Exception e) {
            log.error("日志入库异常:", e);
        }
    }
}

LogHttpServletResponseWrapper.java

防止请求数据丢失,回写数据

import javax.servlet.ServletOutputStream;
import javax.servlet.WriteListener;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;
import java.io.*;

public class LogHttpServletResponseWrapper extends HttpServletResponseWrapper {
    private final ByteArrayOutputStream buffer;
    private final ServletOutputStream out;

    public LogHttpServletResponseWrapper(HttpServletResponse httpServletResponse) {
        super(httpServletResponse);
        buffer = new ByteArrayOutputStream();
        out = new WrapperOutputStream(buffer);
    }

    @Override
    public ServletOutputStream getOutputStream() {
        return out;
    }

    @Override
    public void flushBuffer() throws IOException {
        if (out != null) {
            out.flush();
        }
    }

    public byte[] getContent() throws IOException {
        flushBuffer();
        return buffer.toByteArray();
    }

    static class WrapperOutputStream extends ServletOutputStream {
        private final ByteArrayOutputStream bos;

        public WrapperOutputStream(ByteArrayOutputStream bos) {
            this.bos = bos;
        }

        @Override
        public void write(int b) {
            bos.write(b);
        }

        @Override
        public boolean isReady() {
            return false;
        }

        @Override
        public void setWriteListener(WriteListener arg0) {
        }
    }

}

Log.java

日志对象

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

import java.util.Date;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Log {
    private Long id;
    private String userId;
    private String username;
    private String applicationName;
    private Integer code;
    private String method;
    private String requestURI;
    private String requestData;
    private String respondData;
    private String errorMsg;
    private Date createTime;
}

LogDTO.java

日志请求参数

import lombok.Data;

@Data
public class LogDTO {
    /**
     * 年月
     */
    private String yyyyMM;
    /**
     * 用户ID
     */
    private String userId;
    /**
     * 用户名
     */
    private String username;
    /**
     * 应用名称
     */
    private String applicationName;
    /**
     * 状态码
     */
    private Integer code;
    /**
     * 请求方法
     */
    private String method;
    /**
     * 请求URI
     */
    private String requestURI;
    /**
     * 请求数据
     */
    private String requestData;
    /**
     * 返回数据
     */
    private String respondData;
    /**
     * 详细错误信息
     */
    private String errorMsg;
    /**
     * 当月时间段-开始时间
     */
    private String startTime;
    /**
     * 当月时间段-结束时间
     */
    private String endTime;
}

LogController.java

日志控制层

import javax.annotation.Resource;

import com.fu.work.entity.LogDTO;
import com.fu.work.entity.R;
import com.fu.work.service.LogService;
import org.springframework.web.bind.annotation.*;


/**
 * 日志
 */
@RestController
@RequestMapping("log")
public class LogController {

    @Resource
    private LogService logService;

    /**
     * 根据年月和ID查询
     * @param yyyyMM 年月
     * @param id ID
     */
    @GetMapping("queryById")
    public R queryById(@RequestParam(required = false) String yyyyMM, @RequestParam Long id) {
        return R.ok(logService.queryById(yyyyMM,id));
    }

    /**
     * 分页查询
     */
    @PostMapping("findAll")
    public R findAll(@RequestBody LogDTO logDTO) {
        return R.ok(logService.findAll(logDTO));
    }

}

LogService.java

异步线程入库

import javax.annotation.Resource;

import lombok.extern.slf4j.Slf4j;
import org.springframework.jdbc.BadSqlGrammarException;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.List;

@Slf4j
@Service
public class LogService {

    @Resource
    private LogDao logDao;

    /**
     * 根据ID查询
     */
    public Log queryById(String tableName, Long id) {
        //不传则默认是当前年月
        if (!StringUtils.hasLength(tableName)) {
            tableName = getYearMonth(0);
        }
        return logDao.queryById(tableName, id);
    }

    /**
     * 查询全部
     */
    public List<Log> findAll(LogDTO logDTO) {
        String tableName = logDTO.getYyyyMM();
        //不传则默认是当前年月
        if (!StringUtils.hasLength(tableName)) {
            tableName = getYearMonth(0);
        }
        return logDao.findAll(tableName, logDTO);
    }

    /**
     * 异步日志入库
     */
    @Async("logAsync")
    public void insert(Log logObj) {
        log.info("打印当前B线程名称:{}",Thread.currentThread().getName());
        //获取当前年月
        String tableName = getYearMonth(0);
        try {
            logDao.insert(tableName, logObj);
        } catch (BadSqlGrammarException e) {
            log.error("入库异常:", e);
            //如果是不存在表,则创建表
            if (1146 == e.getSQLException().getErrorCode()) {
                //判断下个月的日志表是否存在,如果不存在则创建
                logDao.crateTable(getYearMonth(0));
                //再次入库
                logDao.insert(tableName, logObj);
            }
        }
    }

    //自定义方法==============================================================================================================
    /**
     * 获取年月
     * @param addOrReduceMonth 正数表示后几个月,负数表示前几个月,0表示当前月
     */
    public static String getYearMonth(int addOrReduceMonth) {
        //获取当前月份的表
        Calendar cal = Calendar.getInstance();
        cal.add(Calendar.MONTH, addOrReduceMonth);
        SimpleDateFormat dft = new SimpleDateFormat("yyyyMM");
        return dft.format(cal.getTime());
    }
}

AsyncConfig.java

异步线程参数配置需要在@SpringBootApplication注解上方加上@EnableAsync注解开启异步线程

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;

/**
 * 异步线程配置:确保执行方法和异步执行的方法不在同一个类
 */
@Configuration
public class AsyncConfig {
    @Value("${spring.task.execution.pool.core-size}")
    private int corePoolSize;
    @Value("${spring.task.execution.pool.max-size}")
    private int maxPoolSize;
    @Value("${spring.task.execution.pool.queue-capacity}")
    private int queueCapacity;
    @Value("${spring.task.execution.thread-name-prefix}")
    private String threadNamePrefix;
    @Value("${spring.task.execution.pool.keep-alive}")
    private int keepAliveSeconds;

    @Bean("logAsync")
    public Executor logAsync() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(corePoolSize);
        executor.setMaxPoolSize(maxPoolSize);
        executor.setQueueCapacity(queueCapacity);
        executor.setThreadNamePrefix(threadNamePrefix);
        executor.setKeepAliveSeconds(keepAliveSeconds);
        /*
          拒绝处理策略
          CallerRunsPolicy():交由调用方线程运行,比如 main 线程。
          AbortPolicy():直接抛出异常。
          DiscardPolicy():直接丢弃。
          DiscardOldestPolicy():丢弃队列中最老的任务。
         */
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy());
        executor.initialize();
        return executor;
    }
}

LogDao.java

import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;

import java.util.List;

@Mapper
public interface LogDao{

    /**
     * 创建日志表
     */
    int crateTable(@Param("tableName") String tableName);

    /**
     * 根据ID查询
     */
    Log queryById(@Param("tableName") String tableName,@Param("id") Long id);

    /**
     * 查询全部
     */
    List<Log> findAll(@Param("tableName") String tableName,@Param("logDTO") LogDTO logDTO);

    /**
     * 新增
     */
    int insert(@Param("tableName") String tableName,@Param("log") Log log);

}

LogMapper.xml

sql创建按月分表日志表和查询日志数据

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.fu.work.dao.LogDao">

    <resultMap type="com.fu.work.entity.Log" id="BaseResultMap">
        <result property="id" column="id" jdbcType="BIGINT"/>
        <result property="userId" column="user_id" jdbcType="VARCHAR"/>
        <result property="username" column="username" jdbcType="VARCHAR"/>
        <result property="applicationName" column="application_name" jdbcType="VARCHAR"/>
        <result property="code" column="code" jdbcType="SMALLINT"/>
        <result property="method" column="method" jdbcType="VARCHAR"/>
        <result property="requestURI" column="request_uri" jdbcType="VARCHAR"/>
        <result property="requestData" column="request_data" jdbcType="VARCHAR"/>
        <result property="respondData" column="respond_data" jdbcType="VARCHAR"/>
        <result property="errorMsg" column="error_msg" jdbcType="VARCHAR"/>
        <result property="createTime" column="create_time" jdbcType="TIMESTAMP"/>
    </resultMap>

    <sql id="Base_Column_List">id, user_id, username, application_name, code, `method`, request_uri, request_data, respond_data, error_msg, create_time</sql>

    <!-- 创建日志表 -->
    <update id="crateTable" parameterType="String">
        CREATE TABLE IF NOT EXISTS log_${tableName} (
            `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
            `user_id` char(36) DEFAULT NULL COMMENT '用户ID',
            `username` varchar(64) DEFAULT NULL COMMENT '用户名',
            `application_name` varchar(255) DEFAULT NULL COMMENT '应用名称',
            `code` smallint(6) DEFAULT NULL COMMENT '状态码',
            `method` varchar(16) DEFAULT NULL COMMENT '请求方法',
            `request_uri` varchar(2048) DEFAULT NULL COMMENT '请求URI',
            `request_data` longtext DEFAULT NULL COMMENT '请求数据',
            `respond_data` longtext DEFAULT NULL COMMENT '返回数据',
            `error_msg` longtext DEFAULT NULL COMMENT '详细错误信息',
            `create_time` datetime DEFAULT NULL COMMENT '创建时间',
            PRIMARY KEY (`id`),
            KEY `create_time` (`create_time`) USING BTREE COMMENT '创建时间索引',
            KEY `user_id` (`user_id`) COMMENT '用户ID索引',
            KEY `application_name` (`application_name`) COMMENT '应用名称索引'
            ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
    </update>

    <!-- 根据ID查询 -->
    <select id="queryById" resultMap="BaseResultMap">
        select
        <include refid="Base_Column_List"/>
        from log_${tableName} where id = #{id}
    </select>

    <!-- 查询全部 -->
    <select id="findAll" resultMap="BaseResultMap">
        select
        <include refid="Base_Column_List"/>
        from log_${tableName}
        <where>
            <if test="logDTO.userId != null and logDTO.userId != '' ">
                AND    user_id = #{logDTO.userId}
            </if>
            <if test="logDTO.code != null">
                AND    code = #{logDTO.code}
            </if>
            <if test="logDTO.method != null and logDTO.method != ''">
                AND    `method` = #{logDTO.method}
            </if>
            <if test="logDTO.applicationName != null and logDTO.applicationName != ''">
                AND    application_name = #{logDTO.applicationName}
            </if>
            <if test="logDTO.username != null and logDTO.username != ''">
                AND    username LIKE CONCAT('%',#{logDTO.username,jdbcType=VARCHAR},'%')
            </if>
            <if test="logDTO.requestURI != null and logDTO.requestURI != ''">
                AND    request_uri LIKE CONCAT('%',#{logDTO.requestURI},'%')
            </if>
            <if test="logDTO.requestData != null and logDTO.requestData != ''">
                AND    request_data LIKE CONCAT('%',#{logDTO.requestData,jdbcType=VARCHAR},'%')
            </if>
            <if test="logDTO.respondData != null and logDTO.respondData != ''">
                AND    respond_data LIKE CONCAT('%',#{logDTO.respondData,jdbcType=VARCHAR},'%')
            </if>
            <if test="logDTO.errorMsg != null and logDTO.errorMsg != ''">
                AND    error_msg LIKE CONCAT('%',#{logDTO.errorMsg,jdbcType=VARCHAR},'%')
            </if>
            <if test="logDTO.startTime != null">
                AND    create_time <![CDATA[>=]]> #{logDTO.startTime}
            </if>
            <if test="logDTO.endTime != null">
                AND    create_time <![CDATA[<=]]> #{logDTO.endTime}
            </if>
        </where>
        ORDER BY create_time DESC
    </select>

    <!-- 新增 -->
    <insert id="insert">
        insert into log_${tableName}
        <trim prefix="(" suffix=")" suffixOverrides=",">
            <if test="log.id != null">
                id,
            </if>
            <if test="log.userId != null">
                user_id,
            </if>
            <if test="log.username != null">
                username,
            </if>
            <if test="log.applicationName != null">
                application_name,
            </if>
            <if test="log.code != null">
                code,
            </if>
            <if test="log.method != null">
                `method`,
            </if>
            <if test="log.requestURI != null">
                request_uri,
            </if>
            <if test="log.requestData != null">
                request_data,
            </if>
            <if test="log.respondData != null">
                respond_data,
            </if>
            <if test="log.errorMsg != null">
                error_msg,
            </if>
            <if test="log.createTime != null">
                create_time,
            </if>
        </trim>
        <trim prefix="values (" suffix=")" suffixOverrides=",">
            <if test="log.id != null">
                #{log.id},
            </if>
            <if test="log.userId != null">
                #{log.userId},
            </if>
            <if test="log.username != null">
                #{log.username},
            </if>
            <if test="log.applicationName != null">
                #{log.applicationName},
            </if>
            <if test="log.code != null">
                #{log.code},
            </if>
            <if test="log.method != null">
                #{log.method},
            </if>
            <if test="log.requestURI != null">
                #{log.requestURI},
            </if>
            <if test="log.requestData != null">
                #{log.requestData},
            </if>
            <if test="log.respondData != null">
                #{log.respondData},
            </if>
            <if test="log.errorMsg != null">
                #{log.errorMsg},
            </if>
            <if test="log.createTime != null">
                #{log.createTime},
            </if>
        </trim>
    </insert>

</mapper>

R.java

统一泛型返回对象

import lombok.*;
import lombok.experimental.Accessors;

import java.io.Serializable;

@Builder
@ToString
@NoArgsConstructor
@AllArgsConstructor
@Accessors(chain = true)
public class R<T> implements Serializable {

	private static final long serialVersionUID = 1L;

	@Getter
	@Setter
	private int code;


	@Setter
	private String msg;
	public String getMsg(){
		if (msg==null){
			msg=ResultCode.getMsg(this.code);
		}
		return msg;
	}

	@Getter
	@Setter
	private T data;

	/**
	 * 新增详细错误信息
	 */
	@Getter
	@Setter
	private Exception detailErrorMsg;

	public static <T> R<T> ok() {
		return restResult(null, 0, null);
	}

	public static <T> R<T> ok(T data) {
		return restResult(data, 0, null);
	}

	public static <T> R<T> ok(T data, String msg) {
		return restResult(data, 0, msg);
	}

	public static <T> R<T> failed() {
		return restResult(null, 1, null);
	}
	public static <T> R<T> failed(int code) {
		return restResult(null, code, null);
	}
	public static <T> R<T> failed(String msg) {
		return restResult(null, 1, msg);
	}

	public static <T> R<T> failed(T data) {
		return restResult(data, 1, null);
	}

	public static <T> R<T> failed(T data, String msg) {
		return restResult(data, 1, msg);
	}
	public static <T> R<T> failed(T data, int code, String msg) {
		return restResult(data, code, msg);
	}
	public static <T> R<T> failed(int code, String msg) {
		return restResult(null, code, msg);
	}
	private static <T> R<T> restResult(T data, int code, String msg) {
		R<T> apiResult = new R<>();
		apiResult.setCode(code);
		apiResult.setData(data);
		apiResult.setMsg(msg);
		return apiResult;
	}

	/**
	 * 详细错误信息
	 */
	public static <T> R<T> failed(String msg,Exception detailErrorMsg) {
		return restResult(null, 1, msg,detailErrorMsg);
	}

	/**
	 * 重载
	 */
	private static <T> R<T> restResult(T data, int code, String msg,Exception detailErrorMsg) {
		R<T> apiResult = new R<>();
		apiResult.setCode(code);
		apiResult.setData(data);
		apiResult.setMsg(msg);
		apiResult.setDetailErrorMsg(detailErrorMsg);
		return apiResult;
	}
}

GlobalExceptionHandler.java

全局异常处理类

@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {
    @ExceptionHandler(Exception.class)
    public R Exception(Exception e) {
        log.error("全局异常信息:", e);
        return R.failed(e.Message(), e);
    }
}

ReturnRes.java

拦截返回内容,把不符合统一泛型返回对象的转成统一泛型返回对象

import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.SneakyThrows;
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.RestControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;


@RestControllerAdvice
public class ReturnRes implements ResponseBodyAdvice<Object> {

    @Override
    public boolean supports(MethodParameter methodParameter, Class<? extends HttpMessageConverter<?>> aClass) {
        return true;
    }

    @SneakyThrows
    @Override
    public Object beforeBodyWrite(Object o, MethodParameter methodParameter, MediaType mediaType, Class<? extends HttpMessageConverter<?>> aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {
         if (o instanceof String) {//String要特殊处理
                return new ObjectMapper().writeValueAsString(R.ok(o));
        } else if (o instanceof R) {//本身是Res直接返回即可
            return o;
        }
        return R.ok(o);
    }
}

application.yml

配置文件

#忽略写入日志的URI
log-application:
  ignore-uri: "/log/findAll,/log/queryById"

server:
  port: 88

spring:
  application:
    name: log
  jackson:
    date-format: yyyy-MM-dd hh:mm:ss
    time-zone: GMT+8
  task:
    execution:
      #异步线程名称
      thread-name-prefix: log-async
      pool:
        core-size: 8
        max-size: 16
        queue-capacity: 500
        keep-alive: 60

  datasource:
  	#mariadb和mysql都行,改下驱动即可
    driver-class-name: org.mariadb.jdbc.Driver
    url: jdbc:mariadb://localhost:3306/db_log?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8&useSSL=false
    username: root
    password: 123456

mybatis:
  mapper-locations: classpath:mapper/*.xml

logging:
  level:
    com.fu.work.dao: info

build.gradle(和maven的pom.xml差不多)

依赖

dependencies {//类似于maven的dependencies
    implementation 'org.springframework.boot:spring-boot-starter-web'//格式为groupId:artifactId:version
    compileOnly 'org.projectlombok:lombok'//仅编译时使用,不参与打包
    annotationProcessor 'org.projectlombok:lombok'
    runtimeClasspath 'org.mariadb.jdbc:mariadb-java-client'//mariadb驱动
    implementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter:2.2.2'//mybatis对象关系映射框架
}
  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2022-10-31 11:40:47  更:2022-10-31 11:44:23 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/19 10:19:34-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码