SpringBoot项目搭建
本文主要讲解如何快速搭建一个springboot项目,文章最末尾有代码 github 地址
Intellij创建项目
1、使用intellij创建一个新项目

2、选择springboot项目

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

4、项目目录结构如下

功能介绍
- 数据库简单操作
- 使用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地址
版权声明:本文为yinbucheng原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。