Spring的简史
第一阶段:XML配置,在Spring1.x时代,使用Spring开发满眼都是xml配置的Bean,随着项目的扩大,我们需要把xml配置文件分放到不同的配置文件里,那时候需要频繁的在开发的类和配置文件之间切换。
第二阶段:注解配置,在Spring2.x时代,Spring提供声明Bean的注解,大大减少了配置量。应用的基本配置用xml,业务配置用注解。
第三阶段:Java配置,从Spring3.x到现在,Spring提供了Java配置,使用Java配置可以让你更理解你所配置的Bean。
Spring Boot:使用“习惯优于配置”的理念让你的项目快速运行起来。使用Spring Boot很容易创建一个独立运行、准生产级别的基于Spring框架的项目,使用Spring Boot你可以不用或者只需要很少的Spring配置。
下面就来使用Spring Boot一步步搭建一个前后端分离的应用开发框架,并且以后不断的去完善这个框架,往里面添加功能。后面以实战为主,不会介绍太多概念,取而代之的是详细的操作。
零、开发技术简介
开发平台:windows
开发工具:Intellij IDEA 2017.1
JDK:Java 8
Maven:maven-3.3.9
服务器:tomcat 8.0
数据库:MySQL 5.7
数据源:Druid1.1.6
缓存:Redis 3.2
日志框架:SLF4J+Logback
Spring Boot:1.5.9.RELEASE
ORM框架:MyBatis+通用Mapper
Spring Boot官方文档:Spring Boot Reference Guide
一、创建项目
这一节创建项目的基础结构,按照spring boot的思想,将各个不同的功能按照starter的形式拆分开来,做到灵活组合,并简单介绍下Spring Boot相关的东西。
1、创建工程
① 通过File > New > Project,新建工程,选择Spring Initializr,然后Next。
② 尽量为自己的框架想个好点的名字,可以去申请个自己的域名。我这里项目名称为Sunny,项目路径为com.lyyzoo.sunny。
③ 这里先什么都不选,后面再去集成。注意我的Spring Boot版本为1.5.9。Next
④ 定义好工程的目录,用一个专用目录吧,不要在一个目录下和其它东西杂在一起。之后点击Finish。
上面说的这么详细,只有一个目的,从一个开始就做好规范。
⑤ 生成的项目结构如下,可以自己去看下pom.xml里的内容。
2、创建Starter
先创建一个core核心、cache缓存、security授权认证,其它的后面再集成进去。
跟上面一样的方式,在Sunny下创建sunny-starter-core、sunny-starter-cache、sunny-starter-security子模块。
这样分模块后,我们以后需要哪个模块就引入哪个模块即可,如果哪个模块不满足需求,还可以重写该模块。
最终的项目结构如下:
3、启动项目
首先在core模块下来启动并了解SpringBoot项目。
① 在com.lyyzoo.core根目录下,有一个SunnyStarterCoreApplication,这是SpringBoot的入口类,通常是*Application的命名。
入口类里有一个main方法,其实就是一个标准的Java应用的入口方法。在main方法中使用SpringApplication.run启动Spring Boot项目。
然后看看@SpringBootApplication注解,@SpringBootApplication是Spring Boot的核心注解,是一个组合注解。
@EnableAutoConfiguration让Spring Boot根据类路径中的jar包依赖为当前项目进行自动配置。
Spring Boot会自动扫描@SpringBootApplication所在类的同级包以及下级包里的Bean。
② 先启动项目,这里可以看到有一个Spring Boot的启动程序,点击右边的按钮启动项目。看到控制台Spring的标志,就算是启动成功了。
③ 替换默认的banner
4、Spring Boot 配置
① 配置文件
Spring Boot使用一个全局的配置文件application.properties或application.yaml,放置在src/main/resources目录下。我们可以在这个全局配置文件中对一些默认的配置值进行修改。
具体有哪些配置可到官网查找,有非常多的配置,不过大部分使用默认即可。Common application properties
然后,需要为不同的环境配置不同的配置文件,全局使用application-{profile}.properties指定不同环境配置文件。
我这里增加了开发环境(dev)和生产环境(prod)的配置文件,并通过在application.properties中设置spring.profiles.active=dev来指定当前环境。
② starter pom
Spring Boot为我们提供了简化开发绝大多数场景的starter pom,只要使用了应用场景所需的starter pom,无需繁杂的配置,就可以得到Spring Boot为我们提供的自动配置的Bean。
后面我们将会通过加入这些starter来一步步集成我们想要的功能。具体有哪些starter,可以到官网查看:Starters
③ 自动配置
Spring Boot关于自动配置的源码在spring-boot-autoconfigure中如下:
我们可以在application.properties中加入debug=true,查看当前项目中已启用和未启用的自动配置。
我们在application.properties中的配置其实就是覆盖spring-boot-autoconfigure里的默认配置,比如web相关配置在web包下。
常见的如HttpEncodingProperties配置http编码,里面自动配置的编码为UTF-8。
MultipartProperties,上传文件的属性,设置了上传最大文件1M。
ServerProperties,配置内嵌Servlet容器,配置端口、contextPath等等。
之前说@SpringBootApplication是Spring Boot的核心注解,但他的核心功能是由@EnableAutoConfiguration注解提供的。
@EnableAutoConfiguration注解通过@Import导入配置功能,在AutoConfigurationImportSelector中,通过SpringFactoriesLoader.loadFactoryNames扫描META-INF/spring.factories文件。
在spring.factories中,配置了需要自动配置的类,我们也可以通过这种方式添加自己的自动配置。
在spring-boot-autoconfigure下就有一个spring.factories,如下:
说了这么多,只为说明一点,Spring Boot为我们做了很多自动化的配置,搭建快速方便。
但是,正因为它为我们做了很多事情,就有很多坑,有时候,出了问题,我们可能很难找出问题所在,这时候,我们可能就要考虑下是否是自动配置导致的,有可能配置冲突了,或者没有使用上自定义的配置等等。
5、项目结构划分
core是项目的核心模块,结构初步规划如下:
base是项目的基础核心,定义一些基础类,如BaseController、BaseService等;
cache是缓存相关;\
config是配置中心,模块所有的配置放到config里统一管理;
constants里定义系统的常量。\
exception里封装一些基础的异常类;
system是系统模块;
util里则是一些通用工具类;
二、基础结构功能
1、web支持
只需在pom.xml中加入spring-boot-starter-web的依赖即可。
之后,查看POM的依赖树(插件:Maven Helper),可以看到引入了starter、tomcat、web支持等。可以看出,Sping Boot内嵌了servlet容器,默认tomcat。
自动配置在WebMvcAutoConfiguration和WebMvcProperties里,可自行查看源码,一般我们不需添加其他配置就可以启动这个web项目了。
2、基础功能
在core中添加一些基础的功能支持。
① 首先引入一些常用的依赖库,主要是一些常用工具类,方便以后的开发。
<!-- ******************************* 常用依赖库 ********************************** -->
<!-- 针对开发IO流功能的工具类库 -->
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>${commons.io.version}</version>
</dependency>
<!-- 文件上传 -->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>${commons.fileupload.version}</version>
<exclusions>
<exclusion>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- 常用的集合操作,丰富的工具类 -->
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>${commons.collections.version}</version>
</dependency>
<!-- 操作javabean的工具包 -->
<dependency>
<groupId>commons-beanutils</groupId>
<artifactId>commons-beanutils</artifactId>
<version>${commons.beanutils.version}</version>
<exclusions>
<exclusion>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- 包含一些通用的编码解码算法. 如:MD5、SHA1、Base64等 -->
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>${commons.codec.version}</version>
</dependency>
<!-- 包含丰富的工具类如 StringUtils -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>${commons.lang3.version}</version>
</dependency>
<!--
Guava工程包含了若干被Google的Java项目广泛依赖的核心库. 集合[collections] 、缓存[caching] 、原生类型支持[primitives support] 、
并发库[concurrency libraries] 、通用注解[common annotations] 、字符串处理[string processing] 、I/O 等等。
-->
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>${guava.version}</version>
</dependency>
版本号如下:
② 在base添加一个Result类,作为前端的返回对象,Controller的直接返回对象都是Result。
package com.lyyzoo.core.base;
import com.fasterxml.jackson.annotation.JsonInclude;
import java.io.Serializable;
/**
* 前端返回对象
*
* @version 1.0
* @author bojiangzhou 2017-12-28
*/
public class Result implements Serializable {
private static final long serialVersionUID = 1430633339880116031L;
/**
* 成功与否标志
*/
private boolean success = true;
/**
* 返回状态码,为空则默认200.前端需要拦截一些常见的状态码如403、404、500等
*/
@JsonInclude(JsonInclude.Include.NON_NULL)
private Integer status;
/**
* 编码,可用于前端处理多语言,不需要则不用返回编码
*/
@JsonInclude(JsonInclude.Include.NON_NULL)
private String code;
/**
* 相关消息
*/
@JsonInclude(JsonInclude.Include.NON_NULL)
private String msg;
/**
* 相关数据
*/
@JsonInclude(JsonInclude.Include.NON_NULL)
private Object data;
public Result() {}
public Result(boolean success) {
this.success = success;
}
public Result(boolean success, Integer status) {
this.success = success;
this.status = status;
}
public Result(boolean success, String code, String msg){
this(success);
this.code = code;
this.msg = msg;
}
public Result(boolean success, Integer status, String code, String msg) {
this.success = success;
this.status = status;
this.code = code;
this.msg = msg;
}
public Result(boolean success, String code, String msg, Object data){
this(success);
this.code = code;
this.msg = msg;
this.data = data;
}
public boolean isSuccess() {
return success;
}
public void setSuccess(boolean success) {
this.success = success;
}
public Integer getStatus() {
return status;
}
public void setStatus(Integer status) {
this.status = status;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
}
之后在util添加生成Result的工具类Results,用于快速方便的创建Result对象。
package com.lyyzoo.core.util;
import com.lyyzoo.core.base.Result;
/**
* Result生成工具类
*
* @version 1.0
* @author bojiangzhou 2017-12-28
*/
public class Results {
protected Results() {}
public static Result newResult() {
return new Result();
}
public static Result newResult(boolean success) {
return new Result(success);
}
//
// 业务调用成功
// ----------------------------------------------------------------------------------------------------
public static Result success() {
return new Result();
}
public static Result success(String msg) {
return new Result(true, null, msg);
}
public static Result success(String code, String msg) {
return new Result(true, code, msg);
}
public static Result successWithStatus(Integer status) {
return new Result(true, status);
}
public static Result successWithStatus(Integer status, String msg) {
return new Result(true, status, null, msg);
}
public static Result successWithData(Object data) {
return new Result(true, null, null, data);
}
public static Result successWithData(Object data, String msg) {
return new Result(true, null, msg, data);
}
public static Result successWithData(Object data, String code, String msg) {
return new Result(true, code, msg, data);
}
//
// 业务调用失败
// ----------------------------------------------------------------------------------------------------
public static Result failure() {
return new Result(false);
}
public static Result failure(String msg) {
return new Result(false, null, msg);
}
public static Result failure(String code, String msg) {
return new Result(false, code, msg);
}
public static Result failureWithStatus(Integer status) {
return new Result(false, status);
}
public static Result failureWithStatus(Integer status, String msg) {
return new Result(false, status, null, msg);
}
public static Result failureWithData(Object data) {
return new Result(false, null, null, data);
}
public static Result failureWithData(Object data, String msg) {
return new Result(false, null, msg, data);
}
public static Result failureWithData(Object data, String code, String msg) {
return new Result(false, code, msg, data);
}
}
③ 在base添加BaseEnum<K, V>枚举接口,定义了获取值和描述的接口。
package com.lyyzoo.core.base;
/**
* 基础枚举接口
*
* @version 1.0
* @author bojiangzhou 2017-12-31
*/
public interface BaseEnum<K, V> {
/**
* 获取编码
*
* @return 编码
*/
K code();
/**
* 获取描述
*
* @return 描述
*/
V desc();
}
然后在constants下定义一个基础枚举常量类,我们把一些描述信息维护到枚举里面,尽量不要在代码中直接出现魔法值(如一些编码、中文等),以后的枚举常量类也可以按照这种模式来写。
package com.lyyzoo.core.constants;
import com.lyyzoo.core.base.BaseEnum;
import java.util.HashMap;
import java.util.Map;
/**
* 基础枚举值
*
* @version 1.0
* @author bojiangzhou 2018-01-01
*/
public enum BaseEnums implements BaseEnum<String, String> {
SUCCESS("request.success", "请求成功"),
FAILURE("request.failure", "请求失败"),
OPERATION_SUCCESS("operation.success", "操作成功"),
OPERATION_FAILURE("operation.failure", "操作失败"),
ERROR("system.error", "系统异常"),
NOT_FOUND("not_found", "请求资源不存在"),
FORBIDDEN("forbidden", "无权限访问"),
VERSION_NOT_MATCH("record_not_exists_or_version_not_match", "记录版本不存在或不匹配"),
PARAMETER_NOT_NULL("parameter_not_be_null", "参数不能为空");
private String code;
private String desc;
private static Map<String, String> allMap = new HashMap<>();
BaseEnums(String code, String desc) {
this.code = code;
this.desc = desc;
}
static {
for(BaseEnums enums : BaseEnums.values()){
allMap.put(enums.code, enums.desc);
}
}
@Override
public String code() {
return code;
}
@Override
public String desc() {
return desc;
}
public String desc(String code) {
return allMap.get(code);
}
}
④ 再添加一个常用的日期工具类对象,主要包含一些常用的日期时间格式化,后续可再继续往里面添加一些公共方法。
package com.lyyzoo.core.util;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateUtils;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* 日期时间工具类
*
* @version 1.0
* @author bojiangzhou 2017-12-28
*/
public class Dates {
/**
* 日期时间匹配格式
*/
public interface Pattern {
//
// 常规模式
// ----------------------------------------------------------------------------------------------------
/**
* yyyy-MM-dd
*/
String DATE = "yyyy-MM-dd";
/**
* yyyy-MM-dd HH:mm:ss
*/
String DATETIME = "yyyy-MM-dd HH:mm:ss";
/**
* yyyy-MM-dd HH:mm
*/
String DATETIME_MM = "yyyy-MM-dd HH:mm";
/**
* yyyy-MM-dd HH:mm:ss.SSS
*/
String DATETIME_SSS = "yyyy-MM-dd HH:mm:ss.SSS";
/**
* HH:mm
*/
String TIME = "HH:mm";
/**
* HH:mm:ss
*/
String TIME_SS = "HH:mm:ss";
//
// 系统时间格式
// ----------------------------------------------------------------------------------------------------
/**
* yyyy/MM/dd
*/
String SYS_DATE = "yyyy/MM/dd";
/**
* yyyy/MM/dd HH:mm:ss
*/
String SYS_DATETIME = "yyyy/MM/dd HH:mm:ss";
/**
* yyyy/MM/dd HH:mm
*/
String SYS_DATETIME_MM = "yyyy/MM/dd HH:mm";
/**
* yyyy/MM/dd HH:mm:ss.SSS
*/
String SYS_DATETIME_SSS = "yyyy/MM/dd HH:mm:ss.SSS";
//
// 无连接符模式
// ----------------------------------------------------------------------------------------------------
/**
* yyyyMMdd
*/
String NONE_DATE = "yyyyMMdd";
/**
* yyyyMMddHHmmss
*/
String NONE_DATETIME = "yyyyMMddHHmmss";
/**
* yyyyMMddHHmm
*/
String NONE_DATETIME_MM = "yyyyMMddHHmm";
/**
* yyyyMMddHHmmssSSS
*/
String NONE_DATETIME_SSS = "yyyyMMddHHmmssSSS";
}
public static final String DEFAULT_PATTERN = Pattern.DATETIME;
public static final String[] PARSE_PATTERNS = new String[]{
Pattern.DATE,
Pattern.DATETIME,
Pattern.DATETIME_MM,
Pattern.DATETIME_SSS,
Pattern.SYS_DATE,
Pattern.SYS_DATETIME,
Pattern.SYS_DATETIME_MM,
Pattern.SYS_DATETIME_SSS
};
/**
* 格式化日期时间
*
* @param date 日期时间
*
* @return yyyy-MM-dd HH:mm:ss
*/
public static String format(Date date) {
return format(date, DEFAULT_PATTERN);
}
/**
* 格式化日期
*
* @param date 日期(时间)
*
* @param pattern 匹配模式 参考:{@link Dates.Pattern}
*
* @return 格式化后的字符串
*/
public static String format(Date date, String pattern) {
if (date == null) {
return null;
}
pattern = StringUtils.isNotBlank(pattern) ? pattern : DEFAULT_PATTERN;
SimpleDateFormat sdf = new SimpleDateFormat(pattern);
return sdf.format(date);
}
/**
* 解析日期
*
* @param date 日期字符串
*
* @return 解析后的日期 默认格式:yyyy-MM-dd HH:mm:ss
*/
public static Date parseDate(String date) {
if (StringUtils.isBlank(date)) {
return null;
}
try {
return DateUtils.parseDate(date, PARSE_PATTERNS);
} catch (ParseException e) {
e.printStackTrace();
}
return null;
}
/**
* 解析日期
*
* @param date 日期
*
* @param pattern 格式 参考:{@link Dates.Pattern}
*
* @return 解析后的日期,默认格式:yyyy-MM-dd HH:mm:ss
*/
public static Date parseDate(String date, String pattern) {
if (StringUtils.isBlank(date)) {
return null;
}
String[] parsePatterns;
parsePatterns = StringUtils.isNotBlank(pattern) ? new String[]{pattern} : PARSE_PATTERNS;
try {
return DateUtils.parseDate(date, parsePatterns);
} catch (ParseException e) {
e.printStackTrace();
}
return null;
}
}
⑤ Constants定义系统级的通用常量。
package com.lyyzoo.core.constants;
import com.google.common.base.Charsets;
import java.nio.charset.Charset;
/**
* 系统级常量类
*
* @version 1.0
* @author bojiangzhou 2017-12-28
*/
public class Constants {
public static final String APP_NAME = "sunny";
/**
* 系统编码
*/
public static final Charset CHARSET = Charsets.UTF_8;
/**
* 标识:是/否、启用/禁用等
*/
public interface Flag {
Integer YES = 1;
Integer NO = 0;
}
/**
* 操作类型
*/
public interface Operation {
/**
* 添加
*/
String ADD = "add";
/**
* 更新
*/
String UPDATE = "update";
/**
* 删除
*/
String DELETE = "delete";
}
/**
* 性别
*/
public interface Sex {
/**
* 男
*/
Integer MALE = 1;
/**
* 女
*/
Integer FEMALE = 0;
}
}
⑥ 在base添加空的BaseController、BaseDTO、Service、Mapper,先定义好基础结构,后面再添加功能。
BaseDTO:标准的who字段、版本号、及10个扩展字段。
因为这里用到了@Transient注解,先引入java持久化包:
package com.lyyzoo.core.base;
import com.fasterxml.jackson.annotation.*;
import com.lyyzoo.core.Constants;
import com.lyyzoo.core.util.Dates;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import javax.persistence.Transient;
import java.io.Serializable;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
/**
* 基础实体类
*
* @version 1.0
* @author bojiangzhou 2017-12-29
*/
public class BaseDTO implements Serializable {
private static final long serialVersionUID = -4287607489867805101L;
public static final String FIELD_OPERATE = "operate";
public static final String FIELD_OBJECT_VERSION_NUMBER = "versionNumber";
public static final String FIELD_CREATE_BY = "createBy";
public static final String FIELD_CREATOR = "creator";
public static final String FIELD_CREATE_DATE = "createDate";
public static final String FIELD_UPDATE_BY = "updateBy";
public static final String FIELD_UPDATER = "updater";
public static final String FIELD_UPDATE_DATE = "updateDate";
/**
* 操作类型,add/update/delete 参考:{@link Constants.Operation}
*/
@Transient
private String _operate;
/**
* 数据版本号,每发生update则自增,用于实现乐观锁.
*/
private Long versionNumber;
//
// 下面是标准 WHO 字段
// ----------------------------------------------------------------------------------------------------
/**
* 创建人用户名
*/
@JsonInclude(JsonInclude.Include.NON_NULL)
private Long createBy;
/**
* 创建人名称
*/
@JsonInclude(JsonInclude.Include.NON_NULL)
@Transient
private String creator;
/**
* 创建时间
*/
@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonFormat(pattern = Dates.DEFAULT_PATTERN)
private Date createDate;
/**
* 更新人用户名
*/
@JsonInclude(JsonInclude.Include.NON_NULL)
private Long updateBy;
/**
* 更新人名称
*/
@JsonInclude(JsonInclude.Include.NON_NULL)
@Transient
private String updater;
/**
* 更新时间
*/
@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonFormat(pattern = Dates.DEFAULT_PATTERN)
private Date updateDate;
/**
* 其它属性
*/
@JsonIgnore
@Transient
protected Map<String, Object> innerMap = new HashMap<>();
//
// 下面是扩展属性字段
// ----------------------------------------------------------------------------------------------------
@JsonInclude(JsonInclude.Include.NON_NULL)
private String attribute1;
@JsonInclude(JsonInclude.Include.NON_NULL)
private String attribute2;
@JsonInclude(JsonInclude.Include.NON_NULL)
private String attribute3;
@JsonInclude(JsonInclude.Include.NON_NULL)
private String attribute4;
@JsonInclude(JsonInclude.Include.NON_NULL)
private String attribute5;
@JsonInclude(JsonInclude.Include.NON_NULL)
private String attribute6;
@JsonInclude(JsonInclude.Include.NON_NULL)
private String attribute7;
@JsonInclude(JsonInclude.Include.NON_NULL)
private String attribute8;
@JsonInclude(JsonInclude.Include.NON_NULL)
private String attribute9;
@JsonInclude(JsonInclude.Include.NON_NULL)
private String attribute10;
public String get_operate() {
return _operate;
}
public void set_operate(String _operate) {
this._operate = _operate;
}
@Override
public String toString() {
return ToStringBuilder.reflectionToString(this, ToStringStyle.MULTI_LINE_STYLE);
}
public String toJSONString() {
return ToStringBuilder.reflectionToString(this, ToStringStyle.JSON_STYLE);
}
public Long getVersionNumber() {
return versionNumber;
}
public void setVersionNumber(Long versionNumber) {
this.versionNumber = versionNumber;
}
public Long getCreateBy() {
return createBy;
}
public void setCreateBy(Long createBy) {
this.createBy = createBy;
}
public String getCreator() {
return creator;
}
public void setCreator(String creator) {
this.creator = creator;
}
public Date getCreateDate() {
return createDate;
}
public void setCreateDate(Date createDate) {
this.createDate = createDate;
}
public Long getUpdateBy() {
return updateBy;
}
public void setUpdateBy(Long updateBy) {
this.updateBy = updateBy;
}
public String getUpdater() {
return updater;
}
public void setUpdater(String updater) {
this.updater = updater;
}
public Date getUpdateDate() {
return updateDate;
}
public void setUpdateDate(Date updateDate) {
this.updateDate = updateDate;
}
@JsonAnyGetter
public Object getAttribute(String key) {
return innerMap.get(key);
}
@JsonAnySetter
public void setAttribute(String key, Object obj) {
innerMap.put(key, obj);
}
public String getAttribute1() {
return attribute1;
}
public void setAttribute1(String attribute1) {
this.attribute1 = attribute1;
}
public String getAttribute2() {
return attribute2;
}
public void setAttribute2(String attribute2) {
this.attribute2 = attribute2;
}
public String getAttribute3() {
return attribute3;
}
public void setAttribute3(String attribute3) {
this.attribute3 = attribute3;
}
public String getAttribute4() {
return attribute4;
}
public void setAttribute4(String attribute4) {
this.attribute4 = attribute4;
}
public String getAttribute5() {
return attribute5;
}
public void setAttribute5(String attribute5) {
this.attribute5 = attribute5;
}
public String getAttribute6() {
return attribute6;
}
public void setAttribute6(String attribute6) {
this.attribute6 = attribute6;
}
public String getAttribute7() {
return attribute7;
}
public void setAttribute7(String attribute7) {
this.attribute7 = attribute7;
}
public String getAttribute8() {
return attribute8;
}
public void setAttribute8(String attribute8) {
this.attribute8 = attribute8;
}
public String getAttribute9() {
return attribute9;
}
public void setAttribute9(String attribute9) {
this.attribute9 = attribute9;
}
public String getAttribute10() {
return attribute10;
}
public void setAttribute10(String attribute10) {
this.attribute10 = attribute10;
}
}
同时,重写了toString方法,增加了toJsonString方法,使得可以格式化输出DTO的数据:
直接打印DTO,输出的格式大概就是这个样子:
⑦ 在exception添加BaseException,定义一些基础异常类
基础异常类都继承自运行时异常类(RunntimeException),尽可能把受检异常转化为非受检异常,更好的面向接口编程,提高代码的扩展性、稳定性。
BaseException:添加了一个错误编码,其它自定义的异常应当继承该类。
package com.lyyzoo.core.exception;
/**
* 基础异常类
*
* @version 1.0
* @author bojiangzhou 2017-12-31
*/
public class BaseException extends RuntimeException {
private static final long serialVersionUID = -997101946070796354L;
/**
* 错误编码
*/
protected String code;
public BaseException() {}
public BaseException(String message) {
super(message);
}
public BaseException(String code, String message) {
super(message);
this.code = code;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
}
ServiceException:继承BaseException,Service层往Controller抛出的异常。
package com.lyyzoo.core.exception;
/**
* Service层异常
*
* @version 1.0
* @author bojiangzhou 2017-12-31
*/
public class ServiceException extends BaseException {
private static final long serialVersionUID = 6058294324031642376L;
public ServiceException() {}
public ServiceException(String message) {
super(message);
}
public ServiceException(String code, String message) {
super(code, message);
}
}
3、添加系统用户功能,使用Postman测试接口
① 在system模块下,再分成dto、controller、service、mapper、constants子包,以后一个模块功能开发就是这样一个基础结构。
User:系统用户
package com.lyyzoo.core.system.dto;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.lyyzoo.core.base.BaseDTO;
import com.lyyzoo.core.util.Dates;
import java.util.Date;
/**
* 系统用户
*
* @version 1.0
* @author bojiangzhou 2017-12-31
*/
@JsonInclude(JsonInclude.Include.NON_NULL)
public class User extends BaseDTO {
private static final long serialVersionUID = -7395431342743009038L;
/**
* 用户ID
*/
private Long userId;
/**
* 用户名
*/
private String username;
/**
* 密码
*/
private String password;
/**
* 昵称
*/
private String nickname;
/**
* 生日
*/
@JsonFormat(pattern = Dates.Pattern.DATE)
private Date birthday;
/**
* 性别:1-男/0-女
*/
private Integer sex;
/**
* 是否启用:1/0
*/
private Integer enabled;
public Long getUserId() {
return userId;
}
public void setUserId(Long userId) {
this.userId = userId;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getNickname() {
return nickname;
}
public void setNickname(String nickname) {
this.nickname = nickname;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
public Integer getSex() {
return sex;
}
public void setSex(Integer sex) {
this.sex = sex;
}
public Integer getEnabled() {
return enabled;
}
public void setEnabled(Integer enabled) {
this.enabled = enabled;
}
}
UserController:用户控制层;用@RestController注解,前后端分离,因为无需返回视图,采用Restful风格,直接返回数据。
package com.lyyzoo.core.system.controller;
import com.lyyzoo.core.Constants;
import com.lyyzoo.core.base.BaseController;
import com.lyyzoo.core.base.BaseEnums;
import com.lyyzoo.core.base.Result;
import com.lyyzoo.core.system.dto.User;
import com.lyyzoo.core.util.Dates;
import com.lyyzoo.core.util.Results;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.ArrayList;
import java.util.List;
/**
* 用户Controller
*
* @version 1.0
* @author bojiangzhou 2017-12-31
*/
@RequestMapping("/sys/user")
@RestController
public class UserController extends BaseController {
private static List<User> userList = new ArrayList<>();
// 先静态模拟数据
static {
User user1 = new User();
user1.setUserId(1L);
user1.setUsername("lufei");
user1.setNickname("蒙奇D路飞");
user1.setBirthday(Dates.parseDate("2000-05-05"));
user1.setSex(Constants.Sex.MALE);
user1.setEnabled(Constants.Flag.YES);
userList.add(user1);
User user2 = new User();
user2.setUserId(2L);
user2.setUsername("nami");
user2.setNickname("娜美");
user2.setBirthday(Dates.parseDate("2000/7/3"));
user2.setSex(Constants.Sex.FEMALE);
user2.setEnabled(Constants.Flag.YES);
userList.add(user2);
}
@RequestMapping("/queryAll")
public Result queryAll(){
return Results.successWithData(userList, BaseEnums.SUCCESS.code(), BaseEnums.SUCCESS.description());
}
@RequestMapping("/queryOne/{userId}")
public Result queryOne(@PathVariable Long userId){
User user = null;
for(User u : userList){
if(u.getUserId().longValue() == userId){
user = u;
}
}
return Results.successWithData(user);
}
}
② Postman请求:请求成功,基础的HTTP服务已经实现了。