上一节,我们入门了,那这一节我们讲下如何使用 SpringBoot 来搭建一个可以直接使用的系统架构。
本节内容较多,主要包括:
1、 添加 MyBatis 的一些配置
2、 连接数据库方面的知识点
3、 统一异常的处理
4、 日志的管理
下面, 我们来看看是如何配置的。
一、 添加 POM.XML 的依赖, 具体看依赖的说明。
<!-- Inherit defaults from Spring Boot --> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.2.RELEASE</version> </parent> <!-- Add typical dependencies for a web application --> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- 添加JDBC依赖 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> <!-- 添加MySQL依赖 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <!-- c3p0 configuration --> <dependency> <groupId>c3p0</groupId> <artifactId>c3p0</artifactId> <version>0.9.1.2</version> </dependency> <!-- MyBatis --> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>1.3.0</version> </dependency> <!-- MyBatis spring-boot pagehelper 的分页插件 --> <dependency> <groupId>com.github.pagehelper</groupId> <artifactId>pagehelper-spring-boot-starter</artifactId> <version>1.2.5</version> </dependency> </dependencies> <!-- Package as an executable jar --> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build>
说明:
1、 使用的是 MySql 数据库,依赖 Mysql的驱动, 连接池使用的是 C3P0
2、 分页插件我们引入的是 pagehelper-spring-boot-starter
二、 项目的目录结构说明
1.1 连接池的配置
首先看 yml 配置文件: 代码中都有注释。
# 数据源配置 c3p0 c3p0: datasource: jdbcUrl: jdbc:mysql://127.0.0.1:3306/springtest?characterEncoding=UTF-8 driverClass: com.mysql.jdbc.Driver user: spring password: spring # 连接池中保留的最小连接数 minPoolSize: 3 # 连接池中保留的最大连接数。Default:15 maxPoolSize: 20 # 最大空闲时间,1800秒内未使用则连接被丢弃,若为0则永不丢弃。Default:0 maxIdleTime: 1800 # 当连接池中的连接耗尽的时候c3p0一次同时获取的链接数。Default:3 acquireIncrement: 5 # 初始化时获取的连接数,取值应在minPoolSize与maxPoolSize之间。Default:3 initialPoolSize: 3 # 每3600秒检查所有的连接池中的空闲连接。Default:0 idleConnectionTestPeriod: 3600
对应的 java 代码:
/**
*@Description: c3p0 连接池
*@Author:杨攀
*@Since:2018年6月25日下午5:44:14
*/
@Configuration
@ConfigurationProperties(prefix = "c3p0.datasource")
public class C3p0DataSourceConfig {
private String driverClass;
private String jdbcUrl;
private String user;
private String password;
/**
*@Fields initialPoolSize : 初始化时获取的连接数,取值应在minPoolSize与maxPoolSize之间
*/
private int initialPoolSize;
/**
*@Fields minPoolSize : 连接池中保留的最小连接数
*/
private int minPoolSize;
/**
*@Fields maxPoolSize : 连接池中保留的最大连接数。
*/
private int maxPoolSize;
/**
*@Fields acquireIncrement : 当连接池中的连接耗尽的时候c3p0一次同时获取的链接数。Default:3
*/
private int acquireIncrement;
/**
*@Fields maxIdleTime : 最大空闲时间,1800秒内未使用则连接被丢弃,若为0则永不丢弃。
*/
private int maxIdleTime;
/**
*@Fields idleConnectionTestPeriod : 每3600秒检查所有的连接池中的空闲连接。Default:0
*/
private int idleConnectionTestPeriod;
@Bean
public DataSource dataSource(){
ComboPooledDataSource c3p0 = null;
try {
c3p0 = new ComboPooledDataSource ();
c3p0.setDriverClass (driverClass);
c3p0.setJdbcUrl (jdbcUrl);
c3p0.setUser (user);
c3p0.setPassword (password);
c3p0.setInitialPoolSize (initialPoolSize);
c3p0.setMinPoolSize (minPoolSize);
c3p0.setMaxPoolSize (maxPoolSize);
c3p0.setAcquireIncrement (acquireIncrement);
c3p0.setMaxIdleTime (maxIdleTime);
c3p0.setIdleConnectionTestPeriod (idleConnectionTestPeriod);
} catch (PropertyVetoException e) {
e.printStackTrace ();
}
return c3p0;
}
public String getDriverClass(){
return driverClass;
}
public void setDriverClass(String driverClass){
this.driverClass = driverClass;
}
public String getJdbcUrl(){
return jdbcUrl;
}
public void setJdbcUrl(String jdbcUrl){
this.jdbcUrl = jdbcUrl;
}
public String getUser(){
return user;
}
public void setUser(String user){
this.user = user;
}
public String getPassword(){
return password;
}
public void setPassword(String password){
this.password = password;
}
public int getInitialPoolSize(){
return initialPoolSize;
}
public void setInitialPoolSize(int initialPoolSize){
this.initialPoolSize = initialPoolSize;
}
public int getMinPoolSize(){
return minPoolSize;
}
public void setMinPoolSize(int minPoolSize){
this.minPoolSize = minPoolSize;
}
public int getMaxPoolSize(){
return maxPoolSize;
}
public void setMaxPoolSize(int maxPoolSize){
this.maxPoolSize = maxPoolSize;
}
public int getAcquireIncrement(){
return acquireIncrement;
}
public void setAcquireIncrement(int acquireIncrement){
this.acquireIncrement = acquireIncrement;
}
public int getMaxIdleTime(){
return maxIdleTime;
}
public void setMaxIdleTime(int maxIdleTime){
this.maxIdleTime = maxIdleTime;
}
public int getIdleConnectionTestPeriod(){
return idleConnectionTestPeriod;
}
public void setIdleConnectionTestPeriod(int idleConnectionTestPeriod){
this.idleConnectionTestPeriod = idleConnectionTestPeriod;
}
}
1.2 MyBatis 的使用
1、 首先创建表,然后通过工具生产对应的 java 和 xml 文件
UserBeanMapper.java
package com.topinfo.dao;
import com.github.pagehelper.Page;
import com.topinfo.bean.UserBean;
public interface UserBeanMapper {
public int insert(UserBean bean);
public UserBean selectById(Integer id);
public Page<UserBean> getUserList();
}
UserBeanMapper.xml
<?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.topinfo.dao.UserBeanMapper"> <resultMap id="BaseResultMap" type="com.topinfo.bean.UserBean"> <id column="id" jdbcType="INTEGER" property="id" /> <result column="name" jdbcType="VARCHAR" property="name" /> <result column="age" jdbcType="INTEGER" property="age" /> <result column="sex" jdbcType="VARCHAR" property="sex" /> </resultMap> <sql id="Base_Column_List"> id, name, age, sex </sql> <select id="selectById" parameterType="java.lang.Integer" resultMap="BaseResultMap"> select <include refid="Base_Column_List" /> from user where id = #{id,jdbcType=VARCHAR} </select> <insert id="insert" parameterType="com.topinfo.bean.UserBean"> insert into user (id, name, age, sex) values (#{id,jdbcType=INTEGER}, #{name,jdbcType=VARCHAR}, #{age,jdbcType=INTEGER}, #{sex,jdbcType=VARCHAR}) </insert> <!-- 分页查询 --> <select id="getUserList" resultMap="BaseResultMap"> select <include refid="Base_Column_List" /> from user </select> </mapper>
新建 service , 创建 接口 和 实现类
UserService.java
package com.topinfo.service;
import java.util.List;
import com.topinfo.bean.UserBean;
public interface UserService {
public int insert(UserBean bean);
public UserBean getUser(int id);
public List<UserBean> getUserList(int pageNo, int pageSize);
}
UserServiceImpl.java
package com.topinfo.service.impl;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.topinfo.bean.UserBean;
import com.topinfo.dao.UserBeanMapper;
import com.topinfo.service.UserService;
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserBeanMapper userBeanMapper;
@Override
public int insert(UserBean u){
// 保持用户信息简单示例
int result = userBeanMapper.insert (u);
return result;
}
@Override
public UserBean getUser(int id){
// 查询的简单示例
UserBean bean = userBeanMapper.selectById (id);
return bean;
}
@Override
public List<UserBean> getUserList(int pageNo,int pageSize){
//设置分页,后面必须紧跟 userBeanMapper 去查询,消费掉 PageHelper
PageHelper.startPage (pageNo, pageSize);
List<UserBean> list = userBeanMapper.getUserList ();
return list;
}
}
创建 Controller 层
TestController.java
package com.topinfo.controller;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import com.topinfo.bean.UserBean;
import com.topinfo.common.exception.BusinessException;
import com.topinfo.common.exception.ErrorEnum;
import com.topinfo.service.UserService;
@RestController
public class TestController {
@Autowired
private UserService userService;
@RequestMapping("/hello")
public String home(){
return "Hello World!";
}
@RequestMapping("/userAdd")
public String userAdd(UserBean user){
// 测试插入
int i = userService.insert (user);
int m = 5/0;
return "Hello World!";
}
@RequestMapping("/getUser")
public UserBean getUser(int id){
// 测试查询
UserBean bean = userService.getUser (id);
if(bean == null){
throw new BusinessException (ErrorEnum.USER_NOTEXIST);
}
return bean;
}
@RequestMapping("/getUserList")
public List<UserBean> getUserList(int pageNo, int pageSize){
List<UserBean> list = userService.getUserList (pageNo, pageSize);
return list;
}
}
接下来,我们在 配置 异常的处理 和 日志
三、 异常的统一处理
全局异常处理类:
package com.topinfo.common.exception;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import com.topinfo.common.utils.ResultUtil;
/**
*@Description: 全局异常处理
*@Author:杨攀
*@Since:2018年6月26日下午3:46:46
*/
@ControllerAdvice
public class GlobalExceptionHandler {
protected static Logger logger = LoggerFactory.getLogger (GlobalExceptionHandler.class);
/**
*@Description: 普通异常的处理
*@Author:杨攀
*@Since: 2018年6月26日下午3:28:14
*@param e
*@return
*/
@ExceptionHandler(value= Exception.class)
@ResponseBody
public ResultBean defultExcepitonHandler(Exception e) {
//打印错误日志
logger.error ("----Errcode:{}, Message:{}", ErrorEnum.ERROR.getErrcode (), e.getMessage ());
return ResultUtil.error (ErrorEnum.ERROR.getErrcode (), e.getMessage (), null);
}
/**
*@Description: 自定义的业务异常处理
*@Author:杨攀
*@Since: 2018年6月26日下午3:28:23
*@param e
*@return
*/
@ExceptionHandler(value= BusinessException.class)
@ResponseBody
public ResultBean businessExceptionHandler(BusinessException e) {
//打印错误日志
logger.error ("----Errcode:{}, Message:{}", e.getErrcode (), e.getMessage ());
return ResultUtil.error (e.getErrcode (), e.getMessage (), null);
}
}
我们的自定义异常(业务异常) BusinessException.java
package com.topinfo.common.exception;
/**
*@Description: 业务异常
*@Author:杨攀
*@Since:2018年6月26日下午3:20:45
*/
public class BusinessException extends RuntimeException{
private Integer errcode;
private String message;
public BusinessException(ErrorEnum errorEnum) {
super(errorEnum.getErrmsg ());
this.errcode = errorEnum.getErrcode ();
this.message = errorEnum.getErrmsg ();
}
public BusinessException(Integer errcode, String message) {
this.errcode = errcode;
this.message = message;
}
public Integer getErrcode(){
return errcode;
}
public String getMessage(){
return message;
}
}
统一类型管理: 定义异常的枚举 ErrorEnum.java
package com.topinfo.common.exception;
public enum ErrorEnum {
ERROR (-1, "错误"),
PARAM_ERROR(-100,"参数错误"),
USER_NOTEXIST(-200, "用户不存在"); //...继续添加其他异常
private Integer errcode;
private String errmsg;
private ErrorEnum(Integer errcode, String errmsg) {
this.errcode = errcode;
this.errmsg = errmsg;
}
public Integer getErrcode(){
return errcode;
}
public String getErrmsg(){
return errmsg;
}
}
统一结果的返回Json: ResultBean.java
package com.topinfo.common.exception;
import java.util.List;
/**
*@Description: 成功实体
*@Author:杨攀
*@Since:2018年6月26日下午2:38:39
*/
public class ResultBean {
/**
*@Fields 成功返回: 1 失败返回:-1 或 其他错误码
*/
private Integer errcode;
/**
*@Fields 返回成功或失败的信息提示.
*/
private String message;
/**
*@Fields 返回的数据,不管是单个对象还是对个对象,统一放到List中.
*/
private List<Object> data;
/**
*@Fields 分页的时候,返回总共有多少条记录数.
*/
private Long total;
public Integer getErrcode(){
return errcode;
}
public void setErrcode(Integer errcode){
this.errcode = errcode;
}
public String getMessage(){
return message;
}
public void setMessage(String message){
this.message = message;
}
public List<Object> getData(){
return data;
}
public void setData(List<Object> data){
this.data = data;
}
public Long getTotal(){
return total;
}
public void setTotal(Long total){
this.total = total;
}
}
定义返回结果的 工具类:
package com.topinfo.common.utils;
import java.util.ArrayList;
import java.util.List;
import com.topinfo.common.exception.ResultBean;
/**
*@Description: 返回的工具类
*@Author:杨攀
*@Since:2018年6月26日下午3:07:06
*/
public class ResultUtil {
/**
*@Description: 成功返回
*@Author:杨攀
*@Since: 2018年6月26日下午3:00:41
*@param object 数据
*@return
*/
public static ResultBean success(Object object){
ResultBean bean = new ResultBean();
bean.setErrcode (1);
bean.setMessage ("成功");
List<Object> data = new ArrayList<Object> ();
data.add (object);
bean.setData (data);
return bean;
}
/**
*@Description: 成功返回
*@Author:杨攀
*@Since: 2018年6月26日下午3:01:25
*@param data 数据
*@return
*/
public static ResultBean success(List<Object> data){
ResultBean bean = new ResultBean();
bean.setErrcode (1);
bean.setMessage ("成功");
if(data != null){
bean.setData (data);
}
return bean;
}
/**
*@Description: 错误返回
*@Author:杨攀
*@Since: 2018年6月26日下午3:04:27
*@param errcode 错误码
*@param message 错误消息
*@param object 数据
*@return
*/
public static ResultBean error(Integer errcode, String message, Object object){
ResultBean bean = new ResultBean();
bean.setErrcode (errcode);
bean.setMessage (message);
if(object != null){
List<Object> data = new ArrayList<Object> ();
data.add (object);
bean.setData (data);
}
return bean;
}
/**
*@Description: 错误返回
*@Author:杨攀
*@Since: 2018年6月26日下午3:03:44
*@param errcode 错误码
*@param message 错误消息
*@param data 数据
*@return
*/
public static ResultBean error(Integer errcode, String message, List<Object> data){
ResultBean bean = new ResultBean();
bean.setErrcode (errcode);
bean.setMessage (message);
if(data != null){
bean.setData (data);
}
return bean;
}
}
四:日志的配置
使用 yml 和 xml 配置:
logback-boot.xml:
<configuration> <!-- 用来定义变量值的标签 --> <property name="logging.dir" value="d:/upload"/> <!-- %m输出的信息,%p日志级别,%t线程名,%d日期,%c类的全名,%i索引【从数字0开始递增】,,, --> <!-- appender是configuration的子节点,是负责写日志的组件。 --> <!-- ConsoleAppender:把日志输出到控制台 --> <appender name="Console" class="ch.qos.logback.core.ConsoleAppender"> <encoder> <pattern>%d{HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n </pattern> <!-- 控制台也要使用UTF-8,不要使用GBK,否则会中文乱码 --> <charset>UTF-8</charset> </encoder> </appender> <!-- RollingFileAppender:滚动记录文件,先将日志记录到指定文件,当符合某个条件时,将日志记录到其他文件 --> <!-- 以下的大概意思是:1.先按日期存日志,日期变了,将前一天的日志文件名重命名为XXX%日期%索引,新的日志仍然是topinfo.log --> <!-- 2.如果日期没有发生变化,但是当前日志的文件大小超过1KB时,对当前日志进行分割 重命名 --> <appender name="LogFile" class="ch.qos.logback.core.rolling.RollingFileAppender"> <!-- rollingPolicy:当发生滚动时,决定 RollingFileAppender 的行为,涉及文件移动和重命名。 --> <!-- TimeBasedRollingPolicy: 最常用的滚动策略,它根据时间来制定滚动策略,既负责滚动也负责出发滚动 --> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <!-- 活动文件的名字会根据fileNamePattern的值,每隔一段时间改变一次 --> <!-- 文件名:log/topinfo.2018-06-26.0.log --> <fileNamePattern>${logging.dir}/topinfo.%d.%i.log</fileNamePattern> <!-- 每产生一个日志文件,该日志文件的保存期限为30天 --> <maxHistory>30</maxHistory> <!-- maxFileSize:这是活动文件的大小,默认值是10MB --> <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP"> <maxFileSize>20MB</maxFileSize> </timeBasedFileNamingAndTriggeringPolicy> </rollingPolicy> <encoder> <!-- pattern节点,用来设置日志的输入格式 --> <pattern> %d %p (%file:%line\)- %m%n </pattern> <!-- 记录日志的编码 --> <charset>UTF-8</charset> <!-- 此处设置字符集 --> </encoder> </appender> <!-- 配置日志文件输出 Error 级别 --> <appender name="LogErrorFile" class="ch.qos.logback.core.rolling.RollingFileAppender"> <filter class="ch.qos.logback.classic.filter.ThresholdFilter"> <level>ERROR</level> </filter> <!-- rollingPolicy:当发生滚动时,决定 RollingFileAppender 的行为,涉及文件移动和重命名。 --> <!-- TimeBasedRollingPolicy: 最常用的滚动策略,它根据时间来制定滚动策略,既负责滚动也负责出发滚动 --> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <!-- 活动文件的名字会根据fileNamePattern的值,每隔一段时间改变一次 --> <!-- 文件名:log/topinfo.2018-06-26.0.log --> <fileNamePattern>${logging.dir}/topinfo-error.%d.%i.log</fileNamePattern> <!-- 每产生一个日志文件,该日志文件的保存期限为30天 --> <maxHistory>30</maxHistory> <!-- maxFileSize:这是活动文件的大小,默认值是10MB --> <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP"> <maxFileSize>20MB</maxFileSize> </timeBasedFileNamingAndTriggeringPolicy> </rollingPolicy> <encoder> <!-- pattern节点,用来设置日志的输入格式 --> <pattern> %d %p (%file:%line\)- %m%n </pattern> <!-- 记录日志的编码 --> <charset>UTF-8</charset> <!-- 此处设置字符集 --> </encoder> </appender> <!-- 控制台输出日志级别: 级别依次为【从高到低】:FATAL > ERROR > WARN > INFO > DEBUG --> <root level="${logging.level}"> <appender-ref ref="Console" /> <appender-ref ref="LogFile" /> <appender-ref ref="LogErrorFile" /> </root> <!-- 指定项目中某个包,当有日志操作行为时的日志记录级别 --> <!-- com.topinfo为根包,也就是只要是发生在这个根包下面的所有日志操作行为的权限都是DEBUG --> <!-- 级别依次为【从高到低】:FATAL > ERROR > WARN > INFO > DEBUG > TRACE --> <!-- <logger name="com.topinfo" level="${logging.level}"> <appender-ref ref="LogFile" /> </logger> --> </configuration>
在 yml 中配置日志的级别
这里有个问题: yml 中设置日志的路径一直无效,实在是不知道问题出来哪里,暂时只能在 xml中配置。 如果知道如何配置的,请给我留言。
#logging 日志配置 logging: # 日志配置文件,Spring Boot默认使用classpath路径下的日志配置文件 config: classpath:logback-boot.xml # 日志文件,绝对路径或相对路径 D:/upload #file: # 在这里设置无效,请到 上面的logback-boot.xml 中设置 # 日志输出级别: 级别依次为【从高到低】:FATAL > ERROR > WARN > INFO > DEBUG level: root: INFO # 配置spring web日志级别
运行测试:
好了, 所有的工作就完成了。 感觉是不是好了很多其他配置。 总体来说,还是很清爽的。