SpringBoot 架构搭建

       上一节,我们入门了,那这一节我们讲下如何使用 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

二、 项目的目录结构说明


SpringBoot  架构搭建
 

     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日志级别

    运行测试:
    
SpringBoot  架构搭建
 

    好了, 所有的工作就完成了。 感觉是不是好了很多其他配置。 总体来说,还是很清爽的。

    

相关推荐