1. SpringBoot简介

解决了在使用Spring/SpringMVC/Mybatis等框架时,需要添加大量的依赖、添加固定的配置的问题,最直接的表现就是不必添加公共的依赖,也不必配置通用的配置。

2. 创建SpringBoot项目

打开https://start.spring.io/网站,填写创建项目时的选项,点击Generate Project按钮,则可以下载项目。

下载项目后,解压缩,并将项目文件夹剪切到Workspace中(便于后续查找和管理),然后打开Eclipse,通过Import > Existing Maven Projects导入项目。

第一次导入时,项目的结构非常简单,需要等待自动更新及自动下载所依赖的jar包,该过程完成后,项目结构就与普通项目基本一致了,整个过程不需要手动执行任何操作,但是,必须保证当前计算机能够连接到Maven服务器。

如果使用的Eclipse版本是Mars系列的版本,则pom.xml会提示错误,因为该系列版本的Eclipse内置的配置文件的版本较低,所以提示错误,但是,不影响程序运行!通常推荐使用Oxygen及以上版本。

当导入完成后,在src/main/java下可以看到生成的包,该包的名称是在网站上创建项目时填写的参数来决定的,同时,在该包下,有SpringbootApplication.java文件,该类的名称也是根据创建时参数决定的,该类是当前项目的启动类,当需要启动项目时,以普通Java程序的方式运行该类即可,即Run As Java Application

3. 创建主页

SpringBoot项目推荐将静态资源(html/css/js/图片等)放在src/main/resources下的static中:

static点击右键,创建HTML文件,默认选中的位置依然是webapp文件夹,需要手动选中到resources/static,然后,创建名为index.html的文件:

然后,打开浏览器,通过http://localhost:8080/即可访问:

当启动SpringBoot项目时,会启动内置的Tomcat,通过启动日志可以看到:

Tomcat started on port(s): 8080 (http) with context path ''

即项目部署到Tomcat时上下文路径为空的双引号,即空字符串,所以,在访问时,URL中不必添加项目名称。因为每个SpringBoot项目都有自已内置的Tomcat,该Tomcat也只为当前项目服务,所以,SpringBoot认为不需要在部署时添加项目名称!

index.html是默认显示的页面,在URL中如果没有明确的指定要访问哪个页面,就会自动的查找该页面,如果有,则显示,如果没有,则报告错误。

其它的网页或静态资源也都应该存储在static或其子级文件夹中!

4. 处理一个请求,转发到JSP页面

假设当前需要处理http://localhost:8080/user/reg的请求,则与使用Spring MVC相同,先创建UserController控制器类,并添加必要的注解:

@Controller
@RequestMapping("user")
public class UserController {

}

注意:SpringBoot不需要配置组件扫描(甚至根本就没有Spring的配置文件),默认扫描的位置就是项目的根包,所以,需要被扫描的类必须在根包或其子包中!

暂定目标:发出请求后,能够看到使用jsp制作的登录页面。

首先,需要在pom.xml中添加新的依赖,因为SpringBoot项目默认是不支持JSP显示的(更推荐的做法是服务器端的控制器都应该响应正文,而不是转发JSP):

<!-- 1.servlet依赖 -->
<dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>javax.servlet-api</artifactId>
    <scope>provided</scope>
</dependency>
<!-- 2.jstl -->
<dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>jstl</artifactId>
</dependency>
<!-- 3.jsp支持 -->
<dependency>
    <groupId>org.apache.tomcat.embed</groupId>
    <artifactId>tomcat-embed-jasper</artifactId>
    <scope>provided</scope>
</dependency>

src/main/resources下的application.properties是SpringBoot项目的配置文件:

需要在这个文件中配置视图解析器的前缀与后缀:

spring.mvc.view.prefix=/WEB-INF/
spring.mvc.view.suffix=.jsp

最后,在webapp下创建WEB-INF文件夹(SpringBoot项目不需要web.xml文件,默认也就没有这个文件夹),然后在该文件夹下创建reg.jsp文件:

在SpringBoot项目中,默认也配置了DispatcherServlet,且映射的路径是/*

所以,处理请求的方法是:

@RequestMapping("reg")
public String showReg() {
    return "reg.jsp";
}

然后,打开浏览器,访问http://localhost:8080/user/reg即可访问。

5. 处理一个请求,并响应正文

仍然请求http://localhost:8080/user/reg路径,希望得到的响应是一个字符串!

则在原有的处理请求的方法中添加@ResponseBody注解即可:

@RequestMapping("reg")
@ResponseBody
public String showReg() {
    return "这是服务器端的控制器响应的正文";
}

在SpringBoot中,已经将所有能够配置字符编码的位置,全部默认设置为UTF-8,所以,在响应正文时,用到的StringHttpMessageConverter也是设置了UTF-8编码格式的,所以,能够直接显示中文!

由于当下比较推荐的做法就是:服务器统一都响应正文给客户端,不考虑转发或重定向的做法,所以,可以将控制器类之前的@Controller注解换成@RestController注解,并且,删除处理请求的方法之前的@ResponseBody

@RestController
@RequestMapping("user")
public class UserController {

    @RequestMapping("reg")
    public String showReg() {
        return "这是服务器端的控制器响应的正文!!!";
    }
    
}

使用@RestController表示当前类是一个控制器组件类,具有@Controller的效果,同时,添加该注解后,当前类中所有处理请求的方法都是响应正文的,相当于每个方法之前都添加了@ResponseBody

当然,使用这个注解后,当前类中处理请求的方法就不可能再转发或重定向了,如果一定要转发或重定向,则不可以使用这个注解!

在SpringBoot中,默认也添加了Jackson,所以,当控制器的方法的返回值是某个SpringMVC默认不识别的类型时,也会调用Jackson将返回的对象组织为了JSON格式的数据,并修改响应头,响应给客户端。

6. 数据库编程

SpringBoot并不清楚开发人员将使用什么样的数据库或持久层框,所以,在SpringBoot中默认并没有添加这些相关依赖!

所以,需要手动添加这些依赖,当然,也可以在创建项目时就直接勾选:

<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>2.0.1</version>
</dependency>

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

如果添加了以上依赖,却不进行任何配置,启动项目时会报告错误,因为一旦添加以上依赖,SpringBoot会自动读取与数据库连接的相关配置,结果读取不到。

所以,需要在application.properties中添加数据库连接相关的配置:

spring.datasource.url=jdbc:mysql://localhost:3306/22bat_usm?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.username=root
spring.datasource.password=root

其中,spring.datasource.driver-class-name可以缺省的配置,SpringBoot会自动的从数据库连接的jar包中找到配置文件读取数据库驱动类的类名。

以上各项配置的值如果有误,也不会导致启动时报告错误,因为SpringBoot在启动只是加载以上配置,并没有尝试连接数据库。

接下来,应该检查配置是否正确,可以通过执行单元测试来检查!

src/test/java中已经有项目的根包,并有默认存在的测试类:

@RunWith(SpringRunner.class)
@SpringBootTest
public class SpringbootApplicationTests {

    @Test
    public void contextLoads() {
    }

}

在编写测试方法之前,应该先测试以上contextLoads()方法,如果运行出错,则表示依赖的jar可能已经损坏,应该删除jar包文件并更新项目,如果运行正常,则编写并执行数据库连接的单元测试:

@Autowired
DataSource dataSource;

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

当测试连接完成后,就可以开始持久层的开发,例如先开发“注册”功能,直接使用“MyBatis”案例时的数据库与数据表,则首先需要创建User实体类:

public class User {

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

    // SET/GET/toString
}

接下来,应该创建UserMapper持久层接口,并添加抽象方法:

public interface UserMapper {

    Integer addnew(User user);

}

为了让框架知道持久层接口在哪里,可以为持久层接口添加@Mapper注解,但是,这种做法就要求每个持久层接口都必须添加以上注解,可以改为在启动类的声明之前添加@MapperScan注解,以配置持久层接口所在的包:

@SpringBootApplication
@MapperScan("cn.22bat.springboot.mapper")
public class SpringbootApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringbootApplication.class, args);
    }

}

接下来,需要配置抽象方法匹配的映射,与“MyBatis”案例相同,应该先在src/main/resources下创建mappers文件夹,再将持久层配置文件复制到该文件夹中,并进行配置:

<?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">

<mapper namespace="cn.22bat.springboot.mapper.UserMapper">

    <insert id="addnew"
        useGeneratedKeys="true" keyProperty="id">
        INSERT INTO t_user (
            username, password,
            age, phone,
            email, is_delete
        ) VALUES (
            #{username}, #{password},
            #{age}, #{phone},
            #{email}, #{isDelete}
        )
    </insert>

</mapper>

然后,还需要在application.properties中配置这些XML映射文件的位置:

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

最后,在SpringbootApplicationTests测试类中编写并执行单元测试:

@Autowired
UserMapper userMapper;

@Test
public void addnew() {
    User user = new User();
    user.setUsername("SpringBoot");
    user.setPassword("6666");
    Integer rows = userMapper.addnew(user);
    System.out.println("rows=" + rows);
}

7. 整合控制器层与持久层

可以通过控制器接收用户的注册请求,当接收到请求后,控制器自身并不实现插入数据的操作,而是调用持久层对象来完成插入数据,最后,将结果响应给客户端。

首先,应该创建cn.22bat.springboot.util.ResponseResult类用于向客户端响应JSON数据:

public class ResponseResult<T> {

    private Integer state;
    private String message;
    private T data;
    // SET/GET/toString
}

然后,需要在控制器类中添加持久层对象:

@Autowired
private UserMapper userMapper;

调整处理请求的方法:

@RequestMapping("reg")
public ResponseResult<Void> reg(User user) {
    // 创建返回值对象
    // try {
    //      调用持久层对象实现插入用户数据
    //      返回值对象:state -> 1
    //      返回
    // } catch (DuplicateKeyException e) {
    //      返回值对象:state -> 2
    //      返回值对象:message -> 用户名已被占用
    //      返回
    // }
}

完成后,打开浏览器,通过http://localhost:8080/user/reg?username=Javascript&password=888888进行测试。

8. 整合前端界面

直接修改index.html页面,添加表单,使用AJAX提交请求并处理结果:

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>欢迎使用SpringBoot</title>
</head>
<body>
<form id="form-reg" action="handle_reg.do" method="get">
    <h1>用户注册</h1>
    <p>请输入用户名:</p>
    <p><input name="username" /><span id="hint-username"></span></p>
    <p>请输入密码:</p>
    <p><input name="password" /></p>
    <p>请输入年龄:</p>
    <p><input name="age" /></p>
    <p>请输入手机号码:</p>
    <p><input name="phone" /></p>
    <p>请输入电子邮箱:</p>
    <p><input name="email" /></p>
    <p><input id="btn-reg" type="button" value="注册" /></p>
</form>
<script type="text/javascript" src="jquery-3.4.0.min.js"></script>
<script type="text/javascript">
$("#btn-reg").click(function() {
    $("#hint-username").html("");
    $.ajax({
        "url":"user/reg",
        "data":$("#form-reg").serialize(),
        "type":"post",
        "dataType":"json",
        "success":function(json) {
            if (json.state == 1) {
                alert("注册成功!");
            } else {
                $("#hint-username").html(json.message);
            }
        }
    });
});
</script>