SpringBoot

1. SpringBoot的作用

SpringBoot是默认整合了Spring、SpringMVC及相关常用框架的一个综合性框架,大量的减少了相关的配置,使得创建项目和使用变得更加简单。

在常规配置方面,SpringBoot的思想是“约定大于配置”,即:大多数开发者都会使用某种配置方式的话,则SpringBoot就会直接配置成那个样子,然后,开发者在使用SpringBoot时就不用再进行相关配置,只需要知道已经被配置完成了!

2. 创建SpringBoot项目

打开浏览器,访问https://start.spring.io/,填写创建项目的参数,配置完成后,点击Generate the project即可生成项目。

得到压缩文件解压后,通过EclipseImportExsiting Maven Projects导入该项目,务必保证当前可以连接到Maven服务器,导入后,会自动下载大量依赖,直至项目结构完整。

3. SpringBoot HelloWorld

resources下默认存在名为static的文件夹,该文件是SpringBoot项目用于存放静态资源的文件夹,例如(.html文件、图片、.css文件、.js文件等)相当于传统项目中的webapp文件夹,则可以在static创建index.html欢迎页面。

java栏下默认存在cn.tedu.springboot.sample包,该包名是根据创建项目时的参数决定的,这个包就是当前项目的根包(Base-Package),并且在该包下已经存在SampleApplication.java文件,该文件的名称也是根据创建项目时填写的artifact决定的,该文件中包含main()方法,直接执行main()方法就可以启动当前项目,所以,该类也是SpringBoot的启动类!

SpringBoot项目在启动时会启动内置的Tomcat,默认占用8080端口,如果此前该端口已经处于占用状态,则项目会启动失败!

通过http://localhost:8080即可访问所涉及的网页,由于SpringBoot项目内置Tomcat,该Tomcat只为当前项目服务,所以启动时设置的Context Path是空字符串,在访问时URL中不必添加项目名称,而index.html是默认的欢迎页面,其文件名也不必体现在URL中!

4. SpringBoot + MyBatis环境

SpringBoot项目默认没有集成持久层相关依赖,需要手动补充,或者创建项目时选中:

1
2
3
4
5
6
7
8
9
10
11
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.0</version>
</dependency>

<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>

当添加以上依赖之后,SpringBoot项目再启动时就会尝试读取连接数据库的相关配置。

如果还没有配置,则会启动失败!

resources下有application.properties,该文件就是SpringBoot的配置文件,在该文件中添加数据库相关配置:

1
2
3
spring.datasource.url=jdbc:mysql://localhost:3306/tedu_ums?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai
spring.datasource.username=root
spring.datasource.password=root

一般情况下,不需要配置连接数据库的driverClassName,因为SpringBoot会自动从jar中读取!

添加以上配置后,项目可以正常启动,但是,如果以上配置信息是错误的(格式错误除外),也不影响启动过程,也就是说,SpringBoot启动时,会读取相关配置,但是,并不执行数据库连接,所以,就算是配置错误也并不会体现出来。

5. 测试数据库连接

src/test/java下,默认已经存在项目的根包及测试类,且测试类中已经存在一个空的测试方法:

1
2
3
4
5
6
7
8
9
@RunWith(SpringRunner.class)
@SpringBootTest
public class SampleApplicationTests {

@Test
public void contextLoads() {
}

}

可以先执行以上contextLoads()方法的单元测试,如果测试出错,一定是测试环境或者框架环境有问题,多考虑为jar包已经损坏,应该重新下载或者更换版本!

可以在该测试类中编写单元测试:

1
2
3
4
5
6
7
8
@Autowired
private DataSource dataSource;

@Test
public void getConnection() throws SQLException {
Connection conn = dataSource.getConnection();
System.err.println(conn);
}

至此,测试通过,此前配置数据库连接信息是正确的!

6. 注册功能的持久层接口

先创建与数据表对应的实体类cn.tedu.springboot.sample.entity.User

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class User implements Serializable {

private static final long serialVersionUID = 7019981109167736281L;

private Integer id;
private String username;
private String password;
private Integer age;
private String phone;
private String email;
private Integer isDelete;
private Integer departmentId;

// ...
}

然后,创建持久层接口cn.tedu.springboot.sample.mapper.UserMapper

1
2
3
4
5
6
7
public interface UserMapper {

Integer insert(User user);

User findByUsername(String username);

}

为了保证MyBatis框架能确定接口文件的位置,可以在接口的声明之前添加@Mapper注解,不过,这样的做法就要求每一个持久层接口之前都需要添加该注解,也可以在启动类SampleApplication之前添加@MapperScan注解进行配置,则后续只需要把持久层接口都放在这个包中就可以了,无需反复添加注解:

1
2
3
4
5
6
7
@SpringBootApplication
@MapperScan("cn.tedu.springboot.sample.mapper")
public class SampleApplication {
public static void main(String[] args) {
SpringApplication.run(SampleApplication.class, args);
}
}

7. 注册功能的映射

  1. 可以在每一个抽象方法之前使用注解配置所对应的SQL语句,例如:
1
2
3
@Options(useGeneratedKeys=true, keyProperty="id")
@Insert("insert into t_user (字段列表) values (值列表)")
Integer insert(User user);

这种做法是MyBatis本身就支持的,并不是SpringBoot所特有的!这种做法最大的优点在于:对应关系非常直观。
主要的缺陷在于:配置长篇的SQL语句时,代码不易于阅读和维护!所以,一般仍然推荐使用XML配置映射的SQL语句!

  1. 则是与此前一样,在resources下创建mappers的文件夹,然后使用UserMapper.xml进行配置:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
<?xml version="1.0" encoding="UTF-8" ?>  
<!DOCTYPE mapper PUBLIC "-//ibatis.apache.org//DTD Mapper 3.0//EN"
"http://ibatis.apache.org/dtd/ibatis-3-mapper.dtd">

<!-- namespace:当前XML文件用于配置哪个接口中抽象方法对应的SQL语句 -->
<mapper namespace="cn.tedu.springboot.sample.mapper.UserMapper">

<!-- resultMap节点:指导MyBatis如何封装查询结果 -->
<!-- id:自定义名称 -->
<!-- column:查询结果中的列名 -->
<!-- property:type对应的类中的属性名 -->
<resultMap id="UserEntityMap"
type="cn.tedu.springboot.sample.entity.User">
<id column="id" property="id" />
<result column="username" property="username" />
<result column="password" property="password" />
<result column="age" property="age" />
<result column="phone" property="phone" />
<result column="email" property="email" />
<result column="is_delete" property="isDelete" />
<result column="department_id" property="departmentId" />
</resultMap>

<!-- 使用insert节点配置插入数据的SQL语句 -->
<!-- id:抽象方法的方法名 -->
<!-- 在#{}中间的是方法的参数User类中的属性名称 -->
<insert id="insert"
useGeneratedKeys="true"
keyProperty="id">
INSERT INTO t_user (
username, password,
age, phone,
email, is_delete,
department_id
) VALUES (
#{username}, #{password},
#{age}, #{phone},
#{email}, #{isDelete},
#{departmentId}
)
</insert>

<select id="findByUsername"
resultMap="UserEntityMap">
SELECT
*
FROM
t_user
WHERE
username=#{username}
</select>

</mapper>

另外,还需要配置XML文件的位置,则打开application.properties文件添加配置:

1
mybatis.mapper-locations=classpath:mappers/*.xml

8. 持久层单元测试

src/test/java下创建新的cn.tedu.springboot.sample.mapper.UserMapperTests单元测试类,将默认存在的SampleApplicationTests类之前的2行注解复制到UserMapperTests类之前:

1
2
3
4
@RunWith(SpringRunner.class)
@SpringBootTest
public class UserMapperTests {
}

然后,在类中声明持久层对象的属性:

1
2
3
4
5
6
7
8
@RunWith(SpringRunner.class)
@SpringBootTest
public class UserMapperTests {

@Autowired
private UserMapper mapper;

}

凡是以前在SSM项目中可以通过getBean()方式获取的对象,在SpringBoot项目中都可以使用自动装配!

然后,编写并执行测试方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@RunWith(SpringRunner.class)
@SpringBootTest
public class UserMapperTests {

@Autowired
private UserMapper mapper;

@Test
public void insert() {
User user = new User();
user.setUsername("springboot");
user.setPassword("1234");
Integer rows = mapper.insert(user);
System.err.println("rows=" + rows);
}

@Test
public void findByUsername() {
String username = "springboot";
User user = mapper.findByUsername(username);
System.err.println(user);
}
}

9. 编写控制器处理请求

先创建控制器处理请求后的返回结果对象的类型cn.tedu.springboot.sample.util.JsonResult

1
2
3
4
5
6
public class JsonResult {
private Integer state;
private String message;

// SET/GET ...
}

SpringBoot项目不需要开发者配置组件扫描,它默认的组件扫描就是项目的根包,即cn.tedu.springboot.sample包,当前项目中所有的组件都必须在这个包或者其子包下!

所以,创建cn.tedu.springboot.sample.controller.UserController控制器类,在类之前添加@RestController注解和@RequestMapping("user")注解:

1
2
3
4
5
@RestController
@RequestMapping("user")
public class UserController {

}

使用@RestController相当于@Controller@ResponseBody的组合使用方式,当使用了@RestController时,该控制器类中所有处理请求的方法都是相当于添加了@ResponseBody注解!一旦使用了该注解,该控制器类中的方法将不可以转发或者重定向,如果一定要转发或者重定向,必须使用ModelAndView作为处理请求的方法的返回值!

然后,在控制器类中添加处理请求的方法:

1
2
3
4
// /user/reg
@RequestMapping("reg")
public JsonResult reg(User user) {
}

在SpringBoot项目中,默认已经将DispatcherServlet映射的路径配置为/*,即所有请求。

在处理过程中,显然需要使用到持久层对象来完成数据操作,所以,应该声明持久层对象的属性:

1
2
@Autowired
private UserMapper userMapper;

然后,完成处理请求的细节:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@RequestMapping("reg")
public JsonResult reg(User user) {
// 创建返回值对象
JsonResult jsonResult = new JsonResult();
// 从参数user中获取尝试注册的用户名
String username = user.getUsername();
// 根据以上用户名查询用户数据
User result = userMapper.findByUsername(username);
// 检查查询结果是否为null
if (result == null) {
// 是:用户名未被占用
// 执行注册
userMapper.insert(user);
// 封装返回值对象的属性:1
jsonResult.setState(1);
} else {
// 否:用户名已经被占用
// 封装返回值对象的属性:2, 错误提示信息
jsonResult.setState(2);
jsonResult.setMessage("注册失败!尝试注册的用户名(" + username + ")已经被占用!");
}
// 返回
return jsonResult;
}

完成后,通过启动类启动项目,打开浏览器,输入:
http://localhost:8080/user/reg?username=junit&password=1234进行测试。