SpringBoot项目搭建

SpringBoot项目搭建

本文主要讲解如何快速搭建一个springboot项目,文章最末尾有代码 github 地址

Intellij创建项目

1、使用intellij创建一个新项目

 

2、选择springboot项目

 

3、输入包名称和项目名称

4、项目目录结构如下

 

功能介绍

  1. 数据库简单操作
  2. 使用log4j格式化日志打印

核心maven引入

<dependencies>
    <!--引入web相关包-->
    <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>

    <!--引入mybatis插件-->
    <dependency>
        <groupId>org.mybatis.spring.boot</groupId>
        <artifactId>mybatis-spring-boot-starter</artifactId>
        <version>2.1.4</version>
    </dependency>

    <!--导入mysql连接-->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <scope>runtime</scope>
    </dependency>

    <!--引入boot测试包-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>

    <!--引入lombok依赖,自动生成set/get等方法-->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.18.16</version>
    </dependency>

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

    <!--引入日志打印模块-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-log4j2</artifactId>
    </dependency>

    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>fastjson</artifactId>
        <version>1.2.70</version>
    </dependency>

    <dependency>
        <groupId>org.springframework.restdocs</groupId>
        <artifactId>spring-restdocs-mockmvc</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>

核心功能代码

配置文件

核心配置

spring:
  datasource:
    #数据URL配置
    url: jdbc:mysql://127.0.0.1:3306/educate?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=UTF-8
    #执行SQL语句模式,这里为每次启动都会执行
    initialization-mode: always
    schema: classpath:db/table.sql
    data: classpath:db/data.sql
    username: root
    password: 123456
    driver-class-name: com.mysql.cj.jdbc.Driver
  #    schema-username: root
  #    schema-password: yinchong@123
  #    data-username: root
  #    data-password: yinchong@123
  application:
    name: boot-educate

server:
  port: 8090
  servlet:
    context-path: /educate
mybatis:
  #mapper文件路径位置
  mapper-locations: classpath:mapper/*Mapper.xml
  #别名扫描的包路径
  type-aliases-package: cn.educate.boot.demo.model
  configuration:
    #是否将对象中驼峰写法转变到数据库中下划线
    map-underscore-to-camel-case: true
logging:
  # 指定加载log4j的配置文件位置 详情讲解可以查看https://www.cnblogs.com/keeya/p/10101547.html
  config: classpath:log4j/log4j.xml

日志配置

<?xml version="1.0" encoding="UTF-8"?>
<!--Configuration后面的status,这个用于设置log4j2自身内部的信息输出,可以不设置,当设置成trace时,你会看到log4j2内部各种详细输出-->
<!--monitorInterval:Log4j能够自动检测修改配置 文件和重新配置本身,设置间隔秒数-->
<configuration monitorInterval="5">
    <!--日志级别以及优先级排序: OFF > FATAL > ERROR > WARN > INFO > DEBUG > TRACE > ALL -->

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

    <appenders>

        <console name="Console" target="SYSTEM_OUT">
            <!--输出日志的格式-->
            <PatternLayout pattern="${LOG_PATTERN}"/>
            <!--控制台只输出level及其以上级别的信息(onMatch),其他的直接拒绝(onMismatch)-->
            <ThresholdFilter level="info" onMatch="ACCEPT" onMismatch="DENY"/>
        </console>

        <!--文件会打印出所有信息,这个log每次运行程序会自动清空,由append属性决定,适合临时测试用-->
        <File name="Filelog" fileName="${FILE_PATH}/test.log" append="false">
            <PatternLayout pattern="${LOG_PATTERN}"/>
        </File>

        <!-- 这个会打印出所有的info及以下级别的信息,每次大小超过size,则这size大小的日志会自动存入按年份-月份建立的文件夹下面并进行压缩,作为存档-->
        <RollingFile name="RollingFileInfo" fileName="${FILE_PATH}/info.log" filePattern="${FILE_PATH}/${FILE_NAME}-INFO-%d{yyyy-MM-dd}_%i.log.gz">
            <!--控制台只输出level及以上级别的信息(onMatch),其他的直接拒绝(onMismatch)-->
            <ThresholdFilter level="info" onMatch="ACCEPT" onMismatch="DENY"/>
            <PatternLayout pattern="${LOG_PATTERN}"/>
            <Policies>
                <!--interval属性用来指定多久滚动一次,默认是1 hour-->
                <TimeBasedTriggeringPolicy interval="1"/>
                <SizeBasedTriggeringPolicy size="10MB"/>
            </Policies>
            <!-- DefaultRolloverStrategy属性如不设置,则默认为最多同一文件夹下7个文件开始覆盖-->
            <DefaultRolloverStrategy max="15"/>
        </RollingFile>

        <!-- 这个会打印出所有的warn及以下级别的信息,每次大小超过size,则这size大小的日志会自动存入按年份-月份建立的文件夹下面并进行压缩,作为存档-->
        <RollingFile name="RollingFileWarn" fileName="${FILE_PATH}/warn.log" filePattern="${FILE_PATH}/${FILE_NAME}-WARN-%d{yyyy-MM-dd}_%i.log.gz">
            <!--控制台只输出level及以上级别的信息(onMatch),其他的直接拒绝(onMismatch)-->
            <ThresholdFilter level="warn" onMatch="ACCEPT" onMismatch="DENY"/>
            <PatternLayout pattern="${LOG_PATTERN}"/>
            <Policies>
                <!--interval属性用来指定多久滚动一次,默认是1 hour-->
                <TimeBasedTriggeringPolicy interval="1"/>
                <SizeBasedTriggeringPolicy size="10MB"/>
            </Policies>
            <!-- DefaultRolloverStrategy属性如不设置,则默认为最多同一文件夹下7个文件开始覆盖-->
            <DefaultRolloverStrategy max="15"/>
        </RollingFile>

        <!-- 这个会打印出所有的error及以下级别的信息,每次大小超过size,则这size大小的日志会自动存入按年份-月份建立的文件夹下面并进行压缩,作为存档-->
        <RollingFile name="RollingFileError" fileName="${FILE_PATH}/error.log" filePattern="${FILE_PATH}/${FILE_NAME}-ERROR-%d{yyyy-MM-dd}_%i.log.gz">
            <!--控制台只输出level及以上级别的信息(onMatch),其他的直接拒绝(onMismatch)-->
            <ThresholdFilter level="error" onMatch="ACCEPT" onMismatch="DENY"/>
            <PatternLayout pattern="${LOG_PATTERN}"/>
            <Policies>
                <!--interval属性用来指定多久滚动一次,默认是1 hour-->
                <TimeBasedTriggeringPolicy interval="1"/>
                <SizeBasedTriggeringPolicy size="10MB"/>
            </Policies>
            <!-- DefaultRolloverStrategy属性如不设置,则默认为最多同一文件夹下7个文件开始覆盖-->
            <DefaultRolloverStrategy max="15"/>
        </RollingFile>

    </appenders>

    <!--Logger节点用来单独指定日志的形式,比如要为指定包下的class指定不同的日志级别等。-->
    <!--然后定义loggers,只有定义了logger并引入的appender,appender才会生效-->
    <loggers>

        <!--过滤掉spring和mybatis的一些无用的DEBUG信息-->
        <logger name="org.mybatis" level="info" additivity="false">
            <AppenderRef ref="Console"/>
        </logger>
        <!--监控系统信息-->
        <!--若是additivity设为false,则 子Logger 只会在自己的appender里输出,而不会在 父Logger 的appender里输出。-->
        <Logger name="org.springframework" level="info" additivity="false">
            <AppenderRef ref="Console"/>
        </Logger>

        <root level="info">
            <appender-ref ref="Console"/>
            <appender-ref ref="Filelog"/>
            <appender-ref ref="RollingFileInfo"/>
            <appender-ref ref="RollingFileWarn"/>
            <appender-ref ref="RollingFileError"/>
        </root>
    </loggers>

</configuration>

 

启动SQL

create table  if not exists t_user_table (
    id bigint primary key auto_increment comment '主键ID',
    name varchar(50) not null comment '用户姓名',
    gender tinyint comment '性别 1男 2女 3未知',
    identity_card varchar(255) comment '身份证',
    birth_day datetime comment '出生日期',
    create_time datetime comment '创建时间',
    update_time datetime comment '更新时间',
    remark varchar(255) comment '备注',
    index index_card(identity_card)
)engine=innodb comment '用户表';

 

Dao层

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 org.apache.ibatis.annotations.Param;

import java.util.List;

/**
 * 数据操作层,这里的实现是resource的mapper文件
 * 框架会根据mapper文件动态创建实现类
 */
public interface UserMapper {

    int addUser(UserVO vo);

    int updateUser(UserVO vo);

    int deleteUser(@Param("id") Long id);

    List<UserDTO> listUser(UserQuery query);
}

Mapper文件

<?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="cn.educate.boot.demo.dao.UserMapper">


    <sql id="userTable">
        t_user_table
    </sql>

    <insert id="addUser" >
        insert into <include refid="userTable"/>
        (name,identity_card,birth_day,remark,gender,create_time,update_time) value(#{name},#{identityCard},#{birthDay},#{remark},#{gender},now(),now())
    </insert>

    <update id="updateUser">
        update <include refid="userTable"/>
        set name = #{name},identity_card =#{identityCard},
            birth_day =#{birthDay},remark =#{remark},update_time=now()
        where id = #{id}
    </update>

    <delete id="deleteUser">
        delete from <include refid="userTable"/>
        where id = #{id}
    </delete>

    <select id="listUser" resultType="cn.educate.boot.demo.model.dto.UserDTO">
        select id,name,identity_card,birth_day,remark
        from <include refid="userTable"/>
        <where>
            <if test="id!=null">
                and id = #{id}
            </if>
            <if test="name!=null and name!=''">
                and name like '%${name}%'
            </if>
        </where>
    </select>


</mapper>

 

 

Service层

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 java.util.List;

public interface IUserService {

    void addUser(UserVO vo);

    void updateUser(UserVO vo);

    void deleteUser(Long id);

    List<UserDTO> listUser(UserQuery query);
}

 

实现类:

import cn.educate.boot.demo.dao.UserMapper;
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 org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

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) {
        return userDao.listUser(query);
    }


    private void checkRow(int row){
        if(row<=0){
            throw new RuntimeException("操作数据库失败");
        }
    }
}

 

 

Web层

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 cn.educate.boot.demo.util.BeanUtils;
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;
import java.util.Map;

/**
 * @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 Map<String, Object> add(@RequestBody UserVO vo) {
        try {
            log.info("add user vo:{}",vo);
            userService.addUser(vo);
            return BeanUtils.success();
        } catch (Exception e) {
            log.error("add user fail,{} ", vo, e);
            return BeanUtils.fail();
        }
    }

    /**
     * 更新用户
     * @param vo
     * @return
     */
    @RequestMapping("update")
    public Map<String, Object> update(@RequestBody UserVO vo) {
        try {
            userService.updateUser(vo);
            return BeanUtils.success();
        } catch (Exception e) {
            log.error("update user fail,{} ", vo, e);
            return BeanUtils.fail();
        }
    }

    /***
     * 删除用户
     * @param query
     * @return
     */
    @RequestMapping("delete")
    public Map<String, Object> delete(@RequestBody UserQuery query) {
        try {
            userService.deleteUser(query.getId());
            return BeanUtils.success();
        } catch (Exception e) {
            log.error("delete user fail,{} ", query, e);
            return BeanUtils.fail();
        }
    }

    /***
     * 查询用户
     * @param query
     * @return
     */
    @RequestMapping("listUser")
    public Map<String, Object> listUser(@RequestBody UserQuery query) {
        try {
            List<UserDTO> list = userService.listUser(query);
            return BeanUtils.success(list);
        } catch (Exception e) {
            log.error("query user list fail,{} ", query, e);
            return BeanUtils.fail();
        }
    }
}

 

相关测试代码

package cn.educate.boot.demo;

import cn.educate.boot.demo.model.vo.UserQuery;
import cn.educate.boot.demo.model.vo.UserVO;
import com.alibaba.fastjson.JSON;
import org.hamcrest.core.StringStartsWith;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultHandlers;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;

import java.util.Date;


@SpringBootTest
@AutoConfigureMockMvc
class BootstrapApplicationTests {
    @Autowired
    private MockMvc mockMvc;

    @Test
    void contextLoads() {
    }

    /**
     * 测试WEB添加
     */
    @Test
    public void testAdd() {
        UserVO userVO = new UserVO();
        userVO.setName("测试2");
        userVO.setBirthDay(new Date());
        userVO.setGender(1);
        userVO.setIdentityCard("431523200302156992");
        userVO.setRemark("这是测试添加");
        try {
            mockMvc.perform(MockMvcRequestBuilders
                    .post("/user/add")
                    .contentType(MediaType.APPLICATION_JSON_VALUE)
                    .content(JSON.toJSONString(userVO))
                    .accept(MediaType.APPLICATION_JSON_VALUE))
                    .andExpect(MockMvcResultMatchers.status().isOk())
                    .andExpect(MockMvcResultMatchers.content().string(StringStartsWith.startsWith("{\"code\":0")))
                    .andDo(MockMvcResultHandlers.print());
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 测试WEB更新
     */
    @Test
    public void testUpdate() {
        UserVO userVO = new UserVO();
        userVO.setId(3L);
        userVO.setName("测试2");
        userVO.setBirthDay(new Date());
        userVO.setGender(1);
        userVO.setIdentityCard("431523200302156992");
        userVO.setRemark("这是测试添加");
        try {
            mockMvc.perform(MockMvcRequestBuilders
                    .post("/user/update")
                    .contentType(MediaType.APPLICATION_JSON_VALUE)
                    .content(JSON.toJSONString(userVO)))
                    .andExpect(MockMvcResultMatchers.status().isOk())
                    .andExpect(MockMvcResultMatchers.content().string(StringStartsWith.startsWith("{\"code\":0")))
                    .andDo(MockMvcResultHandlers.print());
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 测试WEB删除
     */
    @Test
    public void testDelete() {
        UserQuery query = new UserQuery();
        query.setId(4L);
        try {
            mockMvc.perform(MockMvcRequestBuilders
                    .post("/user/delete")
                    .contentType(MediaType.APPLICATION_JSON_VALUE)
                    .content(JSON.toJSONString(query))
                    .accept(MediaType.APPLICATION_JSON_UTF8_VALUE))
                    .andExpect(MockMvcResultMatchers.status().isOk())
                    .andExpect(MockMvcResultMatchers.content().string(StringStartsWith.startsWith("{\"code\":0")))
                    .andDo(MockMvcResultHandlers.print());
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 测试WEB查询
     */
    @Test
    public void testList(){
        UserQuery query = new UserQuery();
        query.setName("测试");
        try {
            mockMvc.perform(MockMvcRequestBuilders
                    .post("/user/listUser")
                    .contentType(MediaType.APPLICATION_JSON_VALUE)
                    .content(JSON.toJSONString(query))
                    .accept(MediaType.APPLICATION_JSON_UTF8_VALUE))
                    .andExpect(MockMvcResultMatchers.status().isOk())
                    .andExpect(MockMvcResultMatchers.content().string(StringStartsWith.startsWith("{\"code\":0")))
                    .andDo(MockMvcResultHandlers.print());
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

}

 

 

Github地址

https://github.com/yinkaihuang/boot-educate.git  分支: simple


版权声明:本文为yinbucheng原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。