1. 前言
前面文章整合过了ssm的,是相对spring的,不过在现在微服务流行之际,为了往后面的springcloud发展学习,先学习一下springboot,在学习的过程中用spring boot+spring mvc+mybatis进行搭建接口平台。
2. 简介
spring boot:Spring Boot是由Pivotal团队提供的全新框架,其设计目的是用来简化新Spring应用的初始搭建以及开发过程。该框架使用了特定的方式来进行配置,从而使开发人员不再需要定义样板化的配置。通过这种方式,Spring Boot致力于在蓬勃发展的快速应用开发领域(rapid application development)成为领导者。(来自百度百科)。
2.1 spring boot特点
- 创建独立的Spring应用程序
- 嵌入的Tomcat,无需部署WAR文件
- 开箱即用,提供各种默认配置来简化项目配置
- 没有冗余代码生成和XML配置的要求
2.2 个人理解
在我看来,spring boot并不是什么新的框架,它只是默认配置了很多框架的使用方式;类似于maven整合了jar,而spring boot整合了很多的框架。从本质上来讲,spring boot就是spring,它做了那些你需要去做的复杂配置。它使用“习惯优于配置”的理念让你的项目快速运行起来。
2.2 回顾一下spring web项目步骤
- 配置web.xml,加载spring和spring mvc
- 配置数据库连接、配置spring事务
- 配置加载配置文件的读取,开启注解
- 配置日志文件
......
配置完成后部署tomcat调试...
2.3 spring boot项目
只需要非常少的几个配置就可以迅速方便的搭建起来一套web项目或者是构建一个微服务!下面就让我们一起来领略spring boot的魅力
3. 项目搭建
整个项目使用maven构建,有关idea集成maven、jdk、tomcat等可翻我上一篇文章。
3.1 在这里使用spring提供的SPRING INITIALIZR工具来产生基础项目。
- 访问 :http://start.spring.io/
- 选择构建工具maven、语言java、版本1.5.10(支持jdk1.7)
- 点击Generate Project下载项目压缩包
- 解压项目,导入进idea,说一下简要步骤
- File -> New -> Project from Existing Sources
- 选择你解压的项目文件夹
- 点击OK
- 选择Import project from external model并选择Maven,点击Next到底为止。
3.2 编译错误
若在项目编译过程中,遇到[Information:java: javacTask: 源发行版 1.8 需要目标发行版 1.8]这个错误,可按照如下更改,这里我统一改为了1.7
1,Project Structure里确认两个地方:Project sdk以及project language level
2,Project Structure->Modules里Sources里的Language level
3,Preferences->java Compiler->Per-module bytecode Version
至此基础项目准备完毕,运行一下项目
看见Spring Boot这个图案证明基础项目搭建成功。
4. 编写代码
4.1 项目结构
系统整个架构为springboot+springmvc+mybatis,restful的接口风格。只是一个示例项目,也没有进行模块分包。整个结构的话还是controller、service、dao的三层结构。在这里面dao层多写了一个接口和实现。对于service层没有写接口,因为我觉得简单的业务没必要写接口(包括这里的dao也是)。
我也一直在思考,对于service和dao层,到底需不需要接口和实现。网上查了一些,有些是瞎扯了一堆“接口和实现分离”、“面向接口编程”、“设计模式”、“解耦”等,但是也没说出个所以然;有些是说没必要分离,或者看系统架构。自己也是经历不多,不知道确切的区别或者说有什么效率上的区别。这个待我日后慢慢积累,有机会咨询长者学习,或者评论区静待大神的解答。
4.2 项目配置
springboot 遵循"习惯优于配置"原则,使用Spirng Boot只需很少的配置,大部分时候可以使用默认配置。
使用INITIALIZR工具创建的springboot项目,默认会在resource目录下创建application.properties文件,另外也可以使用yml类型的配置文件代替properties文件。在这里我是使用的是yml文件。
application.yml
spring:
datasource:
# 驱动配置信息
url: jdbc:mysql://localhost:3306/spring_boot?useUnicode=true&characterEncoding=utf8
username: root
password: root
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.jdbc.Driver
# 连接池的配置信息
filters: stat
maxActive: 20
initialSize: 1
maxWait: 60000
minIdle: 1
timeBetweenEvictionRunsMillis: 60000
minEvictableIdleTimeMillis: 300000
validationQuery: select 'x'
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
poolPreparedStatements: true
maxOpenPreparedStatements: 20
这里主要就是配置了阿里的druid连接池信息。上面的就是类型和驱动这些,然后就是mysql数据库的url、用户名和密码,相应改成自己的就行。下面的是druid的参数配置项,这里随便设置了一些,具体可详查Druid。
mybatis-config.xml
这个配置主要就是关于mybatis的配置了,这里就提两点,一个是setting里面的logImpl配置,可以把执行的sql语句打印在控制台,便于排查sql错误;二个是使用typeAliases标签元素来对类型进行别名控制,也就是给具体的实体类一个别名,不用写完整路径。具体使用在后面mapper中会讲到。
以上就是这个项目目前用到的两个配置了,是不是比起spring来说简便多了。
pom.xml
4.0.0
com.bgy
springboot
0.0.1-SNAPSHOT
jar
springboot
Demo project for Spring Boot
org.springframework.boot
spring-boot-starter-parent
1.5.10.RELEASE
UTF-8
UTF-8
1.7
org.springframework.boot
spring-boot-starter
org.springframework.boot
spring-boot-starter-test
test
org.springframework.boot
spring-boot-starter-web
spring-boot-starter-logging
org.springframework.boot
org.springframework.boot
spring-boot-starter-log4j2
org.springframework.boot
spring-boot-starter-web
1.3.6.RELEASE
com.alibaba
druid
1.0.11
mysql
mysql-connector-java
org.mybatis.spring.boot
mybatis-spring-boot-starter
1.3.0
org.springframework.boot
spring-boot-starter-redis
1.3.8.RELEASE
com.alibaba
fastjson
1.1.27
com.google.code.gson
gson
org.springframework.boot
spring-boot-starter-aop
net.minidev
json-smart
RELEASE
org.springframework.boot
spring-boot-maven-plugin
这些就是当前项目中用到的一些maven依赖了。
DbDataSource.class
@Configuration
@MapperScan(basePackages = "com.bgy.springboot.dao",sqlSessionTemplateRef = "dbSqlSessionTemplate")
public class DbDataSource {
@Bean(name="dbData")
@ConfigurationProperties(prefix = "spring.datasource")
public DataSource dbDataSource() {
return DataSourceBuilder.create().build();
}
@Bean(name = "dbSqlSessionFactory")
public SqlSessionFactory dbSqlSessionFactory(@Qualifier("dbData") DataSource dataSource) throws Exception {
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(dataSource);
sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapper/*.xml"));
sqlSessionFactoryBean.setConfigLocation( new ClassPathResource("mybatis-config.xml"));
return sqlSessionFactoryBean.getObject();
}
@Bean(name="dbSqlSessionTemplate")
public SqlSessionTemplate dbSqlSessionTemplate(@Qualifier("dbSqlSessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception{
return new SqlSessionTemplate(sqlSessionFactory);
}
@Bean(name = "dbTransactionManager")
public DataSourceTransactionManager dataSourceTransactionManager(@Qualifier("dbData") DataSource dataSource) throws Exception {
return new DataSourceTransactionManager(dataSource);
}
}
SqlSessionTemplate是MyBatis提供的持久层访问模板化的工具,这个类负责管理MyBatis的SqlSession,用于调用MyBatis的SQL方法。因为SqlSessionTemplate是线程安全的,可以被多个DAO所共享使用,所以项目中只建立了一个SqlSessionTemplate。
在这里使用的是mybatis注解需要的配置。mybatis3开始支持java注解,使用java注解可以替代xml配置文件,简化代码。上面的代码中,使用@MapperScan来扫描注册mybatis数据库接口类,其中basePackages属性表明接口类所在的包,sqlSessionTemplateRef表明接口类使用的SqlSessionTemplate。
@Configuration 申明这是一个配置类相当于xml配置文件,@Bean表示这是一个Spring管理的bean。
@ConfigurationProperties用于装载yml的配置信息
这里面其他关于SqlSessionTemplate的用法和细节就不一一讲了,不明白的可百度学习一下SqlSessionTemplate。提一下setMapperLocations是用于加载以xml结尾的mapper配置文件,这里注意路径就行了,根路径是resources。setConfigLocation是加载mybatis的配置文件mybatis-config.xml。
4.3 数据库文件
这个在上篇文章已经介绍过作用,就是方便对sql语句的查阅和修改。
db_ddl.sql
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
`id` VARCHAR (45) NOT NULL ,
`user_name` VARCHAR (100) ,
`nick_name` VARCHAR (100),
`password` CHAR (32),
`email` VARCHAR (50),
`phone` VARCHAR (50),
`sex` ENUM('S_MALE','S_FEMALE','S_BM'),
`status` ENUM('S_OFF','S_NORMAL'),
`avatar` VARCHAR (100),
`remarks` VARCHAR (200),
`add_at` BIGINT,
`update_at` BIGINT,
PRIMARY KEY (`id`)
) DEFAULT CHARSET=utf8;
这里表名和字段与上一篇文章相比修改了一些,因为看了《阿里巴巴 Java开发手册》中写道:
表名、字段名必须使用小写字母或数字,禁止出现数字开头,禁止两个下划线中间只出现数字。数据库字段名的修改代价很大,因为无法进行预发布,所以字段名称需要慎重考虑。 说明:MYSQL在Windows下不区分大小写,但在Linux上默认区分大小写。因此,数据库名、表名、字段名都不允许出现任何大写字母,避免节外生枝。
4.4 实体类
建立一个MUser的实体类
MUser.class
public class MUser {
private String id;
private String userName;
private String nickName;
private String password;
private String email;
private String phone;
private String sex;
private String status;
private String avatar;
private String remarks;
private Long addAt;
public MUser(){}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getNickName() {
return nickName;
}
public void setNickName(String nickName) {
this.nickName = nickName;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
public String getAvatar() {
return avatar;
}
public void setAvatar(String avatar) {
this.avatar = avatar;
}
public String getRemarks() {
return remarks;
}
public void setRemarks(String remarks) {
this.remarks = remarks;
}
public Long getAddAt() {
return addAt;
}
public void setAddAt(Long addAt) {
this.addAt = addAt;
}
}
4.5 Controller
4.5.1 BaseController
public class BaseController {
private static final long serialVersionUID = 6357869213649815390L;
/**
* @param fastJson
*/
protected JSONObject json = new JSONObject();
/**
* fastjson JSONArray
*/
protected JSONArray jsonArray = new JSONArray();
/**
* fastjson用法
* 对象转json字符串 String json = json.toJSONString(对象);
* 字符串转json对象 json =json.parseObject(jsonStr);
* 字符串转java对象 Object object = JSON.parseObject(jsonStr, Object.class);
* 字符串转list List
在这里抽出了一个BaseController的父类,可放置多个Controller都会用到的一些对象或方法,这个父类被子类Controller继承。目前此项目中的BaseController只放置了fastjson的两个对象,正常项目中肯定会有不少共有的对象或方法都可放置这里面。
4.5.2 UserController
@Controller
@RequestMapping("/user")
public class UserController extends BaseController {
@Resource(name = "userService")
private UserService userService;
/**
* 添加用户
*
* @param mUserJson
* @return
* @throws Exception
*/
@RequestMapping(value = "", method = RequestMethod.POST)
@ResponseBody
public String addUser(@RequestBody String mUserJson) throws Exception {
String resultInfo = "";
try {
resultInfo = userService.addUser(mUserJson);
} catch (Exception e) {
e.printStackTrace();
}
return resultInfo;
}
/**
* 通过用户名称获取用户
*
* @param userName
* @return
* @throws Exception
*/
@RequestMapping(value = "/{userName}", method = RequestMethod.GET)
@ResponseBody
public String getUserByName(@PathVariable String userName) throws Exception {
String resultInfo = "";
try {
resultInfo = userService.getUserByName(userName);
} catch (Exception e) {
e.printStackTrace();
}
return resultInfo;
}
/**
* 修改用户
*
* @param mUserJson
* @return
* @throws Exception
*/
@RequestMapping(value = "/{id}", method = RequestMethod.PUT)
@ResponseBody
public String updateUser(@PathVariable("id") String id, @RequestBody String mUserJson) throws Exception {
String resultInfo = "";
try {
resultInfo = userService.updateUser(id, mUserJson);
} catch (Exception e) {
e.printStackTrace();
}
return resultInfo;
}
/**
* 删除用户
*
* @param id
* @return
* @throws Exception
*/
@RequestMapping(value = "/{id}", method = RequestMethod.DELETE)
@ResponseBody
public String deleteUser(@PathVariable("id") String id) throws Exception {
String resultInfo = "";
try {
resultInfo = userService.deleteUser(id);
} catch (Exception e) {
e.printStackTrace();
}
return resultInfo;
}
}
这里使用springmvc 相关注解,集成restful风格的接口,具体注解和含义、及restful风格的理解可翻上一篇文章。
与上一篇略有不同的本项目使用的是@Resource注解注入bean。
关于@Resource和@Autowired:
- 两者都可以写在字段和setter方法上。两者如果都写在字段上,那么就不需要再写setter方法。
- @Autowired默认按类型装配(这个注解属于spring的),默认情况下必须要求依赖对象必须存在,如果要允许null值,可以设置它的required属性为false,如:@Autowired(required=false)
- @Resource默认安装名称进行装配(这个注解属于J2EE的),名称可以通过name属性进行指定,如果没有指定name属性,当注解写在字段上时,默认取字段名进行安装名称查找。
- 在我个人推荐用@Resource,因为这个注解是属于J2EE的,减少了与spring的耦合,并且代码看起更优雅。若有高见,欢迎指教。
这个示例代码实现了增删改查四个基础功能,前后端完全以JSON字符串进行交互。(这里前后端以JSON字符串交互的方式有待商榷,以前认知是用JSON字符串便于统一风格;不过最近一年以来的学习和工作,现在会直接使用实体类进行接收对象,免去JSON转对象的步骤。)
4.6 Service
4.6.1 BaseService
public class BaseService {
private static final long serialVersionUID = 6357869213649815390L;
/**
* 得到32位的uuid
*
* @return
*/
public String get32UUID() {
String uuid = UUID.randomUUID().toString().trim().replaceAll("-", "");
return uuid;
}
/**
* @param fastJson
*/
protected JSONObject json = new JSONObject();
/**
* fastjson JSONArray
*/
protected JSONArray jsonArray = new JSONArray();
/**
* fastjson用法
* 对象转json字符串 String json = json.toJSONString(对象);
* 字符串转json对象 json =json.parseObject(jsonStr);
* 字符串转java对象 Object object = JSON.parseObject(jsonStr, Object.class);
* 字符串转list List
也是抽出了一个父类BaseService,放置共用的对象或方法。这里虽然与BashController内容相似,不过没有与BaseController共用,因为想到如果项目复杂的话,Controller与Service层共用的东西会有较大差别。
4.6.2 UserService
@Service("userService")
public class UserService extends BaseService {
@Resource(name = "userDaoImpl")
private IUserDao iUserDao;
/**
* 添加用户
*
* @param mUserJson
* @return
*/
public String addUser(String mUserJson) {
BgyResult br = new BgyResult();
MUser mUser = json.parseObject(mUserJson, MUser.class);
int count = iUserDao.countUserName(mUser);
if (count > 0) {
br.setCode("400");
br.setMsg("用户名已存在");
return json.toJSONString(br);
}
mUser.setId(get32UUID());
boolean result = iUserDao.addUser(mUser);
if (result) {
br.setCode("200");
br.setMsg("注册成功");
br.setData(mUser);
} else {
br.setCode("400");
br.setMsg("注册失败");
}
return json.toJSONString(br);
}
/**
* 通过用户名获取用户
*
* @param userName
* @return
*/
public String getUserByName(String userName) {
BgyResult br = new BgyResult();
MUser mUser = iUserDao.getUserByName(userName);
br.setCode("200");
br.setMsg("Ok");
br.setData(mUser);
return json.toJSONString(br);
}
/**
* 编辑用户
*
* @param mUserJson
* @return
*/
public String updateUser(String id, String mUserJson) {
BgyResult br = new BgyResult();
MUser mUser = json.parseObject(mUserJson, MUser.class);
MUser myMUser = iUserDao.getUserById(id);
if (myMUser == null) {
br.setCode("400");
br.setMsg("用户不存在");
return json.toJSONString(br);
}
boolean result = iUserDao.updateUser(mUser);
if (result) {
br.setCode("200");
br.setMsg("修改成功");
} else {
br.setCode("400");
br.setMsg("修改失败");
}
return json.toJSONString(br);
}
/**
* 删除用户
*
* @param id
* @return
*/
public String deleteUser(String id) {
BgyResult br = new BgyResult();
MUser myMUser = iUserDao.getUserById(id);
if (myMUser == null) {
br.setCode("400");
br.setMsg("用户不存在");
return json.toJSONString(br);
}
boolean result = iUserDao.deleteUser(id);
if (result) {
br.setCode("200");
br.setMsg("删除成功");
} else {
br.setCode("400");
br.setMsg("删除失败");
}
return json.toJSONString(br);
}
}
这一层主要就是处理业务逻辑了,没什么特别的地方,也是使用@Resource注入Bean。
这里用到了BgyResult类作为返回类。
4.7 BgyResult
public class BgyResult implements Serializable {
private static final long serialVersionUID = 4832771715671880043L;
private String code;
private String msg;
private Object data;
public BgyResult(){
this.code = "200";
this.msg = "SUCCESS";
this.data = null;
}
public BgyResult(String msg) {
this.code = "400";
this.msg = msg;
this.data = null;
}
public BgyResult(String code, String msg, Object data) {
this.code = code;
this.msg = msg;
this.data = data;
}
public String getCode() {
return this.code;
}
public void setCode(String code) {
this.code = code;
}
public String getMsg() {
return this.msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public Object getData() {
return this.data;
}
public void setData(Object data) {
this.data = data;
}
}
为了统一与前端的交互,定义了BgyResult类进行标准返回,统一返回格式code、msg、data的json字符串。
4.8 Dao
4.8.1 IUserDao
public interface IUserDao {
int countUserName(MUser mUser);
boolean addUser(MUser mUser);
MUser getUserByName(String userName);
MUser getUserById(String id);
boolean updateUser(MUser mUser);
boolean deleteUser(String id);
}
这里就是dao的接口层,用于访问数据库,实现数据的持久化。这里提一下,《阿里巴巴Java开发手册》中写道:
接口类中的方法和属性不要加任何修饰符号(public也不要加),保持代码的简洁性。
4.8.2 UserDaoImpl
@Repository("userDaoImpl")
public class UserDaoImpl implements IUserDao {
@Resource(name = "dbSqlSessionTemplate")
private SqlSessionTemplate sqlSessionTemplate;
@Override
public int countUserName(MUser mUser) {
return sqlSessionTemplate.selectOne("UserMapper.countUserName", mUser);
}
@Override
public boolean addUser(MUser mUser) {
int num = sqlSessionTemplate.insert("UserMapper.addUser", mUser);
boolean result = false;
if (num > 0) {
result = true;
}
return result;
}
@Override
public MUser getUserByName(String userName) {
MUser mUser = sqlSessionTemplate.selectOne("UserMapper.getUserByName", userName);
return mUser;
}
@Override
public MUser getUserById(String id) {
MUser mUser = sqlSessionTemplate.selectOne("UserMapper.getUserById", id);
return mUser;
}
@Override
public boolean updateUser(MUser mUser) {
int num = sqlSessionTemplate.update("UserMapper.updateUser", mUser);
boolean result = false;
if (num > 0) {
result = true;
}
return result;
}
@Override
public boolean deleteUser(String id){
int num = sqlSessionTemplate.update("UserMapper.deleteUser", id);
boolean result = false;
if (num > 0) {
result = true;
}
return result;
}
}
这个类就是dao接口的具体实现类了,没什么特别的。使用的是前面配置的SqlSessionTemplate模板化工具,与mapper.xml结合实现操作数据库。不过这里把所有返回int的操作做了一下boolean转换,便于service层处理。
4.9 UserMapper.xml
INSERT INTO `user` (id,user_name,nick_name,password,email,phone,sex,status,avatar,remarks,add_at)
VALUES (#{id},#{userName},#{nickName},#{password},#{email},#{phone},#{sex},#{status},#{avatar},#{remarks},unix_timestamp(now()),unix_timestamp(now()))
UPDATE
`user`
SET
nick_name = #{nickName},
password = #{password},
email = #{email},
phone = #{phone},
sex = #{sex},
status = #{status},
avatar = #{avatar},
remarks = #{remarks},
update_at = unix_timestamp(now())
WHERE id = #{id}
DELETE FROM `user` WHERE
id = #{id} AND
1=1
这个是mybatis中sql的映射文件。命名空间即是sqlSessionTemplate.insert("UserMapper.addUser", mUser)中的UserMapper。
上面还提到了使用typeAliases标签元素来对类型进行别名控制,也就是给具体的实体类一个别名,不用写完整路径。在这里的type或者parameterType里写的MUser就是我们的实体类,如果不使用typeAliases,这里则应写完整路径,即
com.bgy.springboot.model.MUser
到此,整个项目的代码已经编写完成。实现了最基础的增删改查四个功能。
运行项目,测试一下试试。由于springboot内置了tomcat,所以不用单独放在tomcat中部署。直接运行SpringbootApplication类,即可运行项目。看见这个即表示运行成功:
下面以postman测试接口
添加用户
编辑用户
查询用户
删除用户
至此,基于springboot+springmvc+mybatis框架的项目已经完全整合与测试通过。这个项目结合上一个项目的一些东西,当然也改进了一些东西。项目中涉及到的技术都没有难点,就算对于新手也很容易搞懂,也有完整的代码,已测试编译通过。
编者水平有限,若有错误或者更优的建议欢迎指出。
目前全部文章列表:
idea整合restful风格的ssm框架(一)
idea整合restful风格的ssm框架(二)
idea整合spring boot+spring mvc+mybatis框架
idea整合springboot+redis
JVM学习之—Java内存区域
JVM学习之—垃圾回收与内存分配策略
专题整理之—不可变对象与String的不可变
专题整理之—String的字符串常量池