初探Spring Boot

前言

Spring Boot是Spring家族最近几年最有影响力和前景的新成员,它对SpringMVC进行了相当程度的简化,尤其是在配置文件上面,做了很多简化,比如再也不需要配置json converter等等。一个Spring Boot工程,理论上只需要一个application.properties的配置文件,而不需要去劳心劳神地配置各种xml文件,既省事又不容易出错。下面我们来看看这样一个Spring Boot的简单工程。

正文

pom文件

这里我们使用的是Maven,在pom.xml中,加入如下代码:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <artifactId>spring-boot-Demo</artifactId>
    <packaging>war</packaging>
    <name>Spring Boot Example</name>
    <description>Spring Example</description>
    <version>1.0</version>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.4.2.RELEASE</version>
    </parent>

    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>

        <!-- Web -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!-- Web with Tomcat + Embed -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
            <scope>provided</scope>
        </dependency>

        <!-- JSTL -->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>jstl</artifactId>
        </dependency>

        <!-- Need this to compile JSP -->
        <dependency>
            <groupId>org.apache.tomcat.embed</groupId>
            <artifactId>tomcat-embed-jasper</artifactId>
            <scope>provided</scope>
        </dependency>

        <!-- Need this to compile JSP -->
        <dependency>
            <groupId>org.eclipse.jdt.core.compiler</groupId>
            <artifactId>ecj</artifactId>
            <version>4.6.1</version>
            <scope>provided</scope>
        </dependency>

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

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.40</version><!--$NO-MVN-MAN-VER$-->
        </dependency>

        <!--pagehelper-->
        <dependency>
            <groupId>com.github.pagehelper</groupId>
            <artifactId>pagehelper-spring-boot-starter</artifactId>
            <version>1.1.0</version>
        </dependency>

    </dependencies>
    <build>
        <plugins>
            <!-- Package as an executable jar/war -->
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

这里面加入了一些可选的依赖,比如内嵌的服务器,jsp的compiler等,可以用maven install的方式运行服务器。注意我们这里并没有使用这种方式,只是写出来以备以后使用。这里使用到mybatis和pagehelper插件用来做数据库查询和分页。
下面来看程序唯一的配置文件,application.properties里的代码:

application.properties

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

mybatis.type-aliases-package = com.test.entity
mybatis.mapperLocations = classpath*:**/mapping/*.xml

spring.datasource.driverClassName = com.mysql.jdbc.Driver
spring.datasource.url = jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=UTF-8
spring.datasource.username = root
spring.datasource.password = root

#pagehelper
pagehelper.helperDialect=mysql
pagehelper.reasonable=true
pagehelper.supportMethodsArguments=true
pagehelper.params=count=countSql

上图的前两行代码指定了返回的jsp文件的前缀与后缀,这是SpringMVC唯一需要配置的。如果你的工程里没有jsp文件,而全部是html文件,并且以REST风格进行所有的请求,那么这两句话都可以省略掉。后面几行代码也很通俗易懂,分别指定了mybatis的配置,以及pageHelper的配置。pageHelper是一个物理分页的插件,实现方式是Interceptor,非常好用,对各大关系型数据库都有很好的支持,这里的配置方式也很简单。这里我指定了mysql作为我的数据库。
下面来看Spring Boot的核心代码,一个继承了SpringBootServletInitializer的初始化类。

SpringBootWebApplication

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.support.SpringBootServletInitializer;

@MapperScan("com.test.Dao")

@SpringBootApplication
public class SpringBootWebApplication extends SpringBootServletInitializer {

    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
        return application.sources(SpringBootWebApplication.class);
    }

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

}

可以看到这段代码中有一个main函数,如果你使用的是Maven Install的方式来运行内嵌的服务器,那么这段代码就是程序的入口。注意@MapperScan这个注解,指定了Mapper所在的程序包,mybatis会从这里指定的包里面寻找Mapper的java类。
接下来是最重要的Controller的代码。

Controller

import java.util.HashMap;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

import com.github.pagehelper.Page;
import com.test.entity.Student;
import com.test.service.IStudentService;

@RestController
public class StudentController {

    @Autowired
    private IStudentService iStudentService;

    @RequestMapping("/getstu")
    public HashMap<String, Object> getStudents(@RequestParam(value="parameter") String parameter,
            @RequestParam(value="pageNum")int pageNum, @RequestParam(value="pageSize")int pageSize) {
        HashMap<String, Object> studentMap = new HashMap<String, Object>();
        Page<Student> page = iStudentService.getStudents(parameter, pageNum, pageSize);
        studentMap.put("studentdata", page);
        studentMap.put("number", page.getTotal());
        return studentMap;
    }

    @RequestMapping(value = "/add", method = RequestMethod.POST)  
    public void add(@RequestParam(value="name") String name) {  
        iStudentService.addStudents(name);
    }  

    @RequestMapping(value = "/delete", method = RequestMethod.POST)  
    public void delete(@RequestParam(value="array[]") int[] array)
    {
        iStudentService.deleteStudents(array);
    }

    @RequestMapping(value = "/update", method = RequestMethod.POST) 
    public void update(@RequestParam(value="id")int id, @RequestParam(value="name")String name)
    {
        iStudentService.updateStudents(id, name);
    }
}

注意@RestController这个注解,这是Spring Boot中的一大亮点。它的意思就是以REST的方式进行响应。加了这个注解之后,这个Controller里面所有的方法返回的都是json对象,我们既不需要在配置文件里指定JSON Converter,也不需要在方法前面加入@ResponseBody这样的注解。

接下来是mybatis的代码,很简单的CRUD功能。

Mybatis

<?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.test.Dao.StudentMapper"> 
    <resultMap id="NewStudentResultMap" type="com.test.entity.Student">  
        <id column="id" property="id" jdbcType="INTEGER" />  
        <result column="name" property="name" jdbcType="VARCHAR" />  
    </resultMap>   

    <select id="getStudents" resultMap="NewStudentResultMap">
        select id,name from student where id=#{parameter} or name like CONCAT('%',#{parameter},'%')  
    </select>

    <insert id="addStudents">
        insert into student(name) values (#{name})
    </insert>

    <update id="updateStudents">
        update student set name = #{1} where id = #{0}
    </update>

    <delete id="deleteStudents" parameterType="Integer[]">
        delete from student 
        <where>
            id in
            <foreach collection="ids" item="id" index="index" open="(" close=")" separator=",">
                #{id}
            </foreach>
        </where>
    </delete>

</mapper>

这里简单说一下pageHelper的用法,非常简单,就是在service的实现方法里加入一行代码:

pageHelper

    @Override
    public Page<Student> getStudents(String parameter, int pageNum, int pageSize) {
        Page<Student> page = PageHelper.startPage(pageNum, pageSize);
        studentMapper.getStudents(parameter);
        return page;
    }

startPage接受两个参数,第一个是要查询的页码,第二个是该页要显示多少条数据。

前端的代码使用的是最近一直比较火的vuejs2.0和基于vuejs的UI库elementUI。

前端代码

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
<head>
<meta charset="UTF-8">
    <title>ElementDemo</title>
    <link rel="stylesheet" href="../element-ui/lib/theme-default/index.css">
    <script src="../js/jquery-3.1.1.min.js"></script>
    <script src="../js/json2.js"></script>
    <script src="../js/vue.min.js"></script>  
    <script src="../js/vue-resource.js"></script>
    <script src="../element-ui/lib/index.js"></script>

    <style>
      .el-select .el-input {
        width: 110px;
      }

      .el-table .info-row {
            background: #c9e5f5;
      }   

      #top {
          background:#20A0FF;
          padding:5px;
          overflow:hidden
      }
    </style>

</head>
<body>
    <div id="test">             

        <div id="top">          
            <span>  
                <el-button type="text" @click="add" style="color:white">添加</el-button>  
                <el-button type="text" @click="deletenames" style="color:white">批量删除</el-button>        
            </span>                     
        </div>  


        <br/>

        <div style="margin-top:15px">
           <el-input placeholder="请输入内容" v-model="criteria" style="padding-bottom:10px;">
              <el-select v-model="select" slot="prepend" placeholder="请选择">
                 <el-option label="id" value="1"></el-option>
                 <el-option label="name" value="2"></el-option>
              </el-select>
              <el-button slot="append" icon="search" v-on:click="search"></el-button>
          </el-input>       

          <el-table
            ref="testTable"       
            :data="tableData"
            style="width:100%"
            border
            :default-sort = "{prop: 'id', order: 'ascending'}"
            @selection-change="handleSelectionChange"   
            @row-click="handleclick"
            :row-class-name = "tableRowClassName"
            >
            <el-table-column
              type="selection"
              >
            </el-table-column>
            <el-table-column
              prop="id"
              label="Id"
              sortable
              show-overflow-tooltip>
            </el-table-column>
            <el-table-column
              prop="name"
              label="姓名"
              sortable>
            </el-table-column>
            <el-table-column label="操作">
              <template scope="scope">
                <el-button
                  size="small"
                  type="primary"
                  @click="handleEdit(scope.$index, scope.row)">编辑</el-button>
                <el-button
                  size="small"
                  type="danger"
                  @click="handleDelete(scope.$index, scope.row)">删除</el-button>
              </template>
            </el-table-column>
          </el-table>

          <div align="center">
              <el-pagination
                  @size-change="handleSizeChange"
                  @current-change="handleCurrentChange"
                  :current-page="currentPage"
                  :page-sizes="[10, 20, 30, 40]"
                  :page-size="pagesize"
                  layout="total, sizes, prev, pager, next, jumper"
                  :total="totalCount">
              </el-pagination>
          </div>
        </div> 
    </div>

    <footer align="center">
        <p>&copy; Spring Boot Demo</p>
    </footer>

    <script>
    var vue = new Vue({         
            el:"#test",
            data: {       
                //表格当前页数据
                tableData: [],

                //多选数组
                multipleSelection: [],

                //请求的URL
                url:'../getstu',

                //搜索条件
                criteria: '',

                //下拉菜单选项
                select: '',

                //默认每页数据量
                pagesize: 10,

                //默认高亮行数据id
                highlightId: -1,

                //当前页码
                currentPage: 1,

                //查询的页码
                start: 1,

                //默认数据总数
                totalCount: 1000,
            },

            methods: {

                //从服务器读取数据
                loadData: function(criteria, pageNum, pageSize){                    
                    this.$http.get(this.url,{parameter:criteria, pageNum:pageNum, pageSize:pageSize}).then(function(res){
                        this.tableData = res.data.studentdata;
                        this.totalCount = res.data.number;
                    },function(){
                        console.log('failed');
                    });                 
                },

                //多选响应
                handleSelectionChange: function(val) {
                    this.multipleSelection = val;
                },

                //点击行响应
                handleclick: function(row, event, column){
                    this.highlightId = row.id;
                },

                //编辑
                handleEdit: function(index, row) {
                    this.$prompt('请输入新名称', '提示', {
                          confirmButtonText: '确定',
                          cancelButtonText: '取消',
                        }).then(({ value }) => {
                            if(value==''||value==null)
                                return;
                            this.$http.post('../update',{"id":row.id,"name":value},{emulateJSON: true}).then(function(res){
                                this.loadData(this.criteria, this.currentPage, this.pagesize);                              
                            },function(){
                                console.log('failed');
                            });
                        }).catch(() => {

                    });
                },


                //单行删除
                handleDelete: function(index, row) {
                    var array = [];
                    array.push(row.id);
                    this.$http.post('../delete',{"array":array},{emulateJSON: true}).then(function(res){
                        this.loadData(this.criteria, this.currentPage, this.pagesize);
                    },function(){
                        console.log('failed');
                    });
                },

                //搜索
                search: function(){
                    this.loadData(this.criteria, this.currentPage, this.pagesize);
                },

                //添加
                add: function(){
                        this.$prompt('请输入名称', '提示', {
                          confirmButtonText: '确定',
                          cancelButtonText: '取消',
                        }).then(({ value }) => {
                            if(value==''||value==null)
                                return;
                            this.$http.post('../add',{"name":value},{emulateJSON: true}).then(function(res){
                                this.loadData(this.criteria, this.currentPage, this.pagesize);
                            },function(){
                                console.log('failed');
                            });
                        }).catch(() => {

                    });

                },

                //多项删除
                deletenames: function(){
                    if(this.multipleSelection.length==0)
                        return;
                    var array = [];
                    this.multipleSelection.forEach((item) => {
                        array.push(item.id);
                    })
                    this.$http.post('../delete',{"array":array},{emulateJSON: true}).then(function(res){
                        this.loadData(this.criteria, this.currentPage, this.pagesize);
                    },function(){
                        console.log('failed');
                    });
                },

                //改变当前点击的行的class,高亮当前行
                tableRowClassName: function(row, index){
                   if(row.id == this.highlightId)
                   {
                      return 'info-row';
                   }
                },

                //每页显示数据量变更
                handleSizeChange: function(val) {
                    this.pagesize = val;
                    this.loadData(this.criteria, this.currentPage, this.pagesize);
                },

                //页码变更
                handleCurrentChange: function(val) {
                    this.currentPage = val;
                    this.loadData(this.criteria, this.currentPage, this.pagesize);
                },        

            },      


          });

          //载入数据
          vue.loadData(vue.criteria, vue.currentPage, vue.pagesize);
    </script>  

</body>
</html>

本工程的完整代码可以在我的github中找到,下载解压后启动服务器,在浏览器中输入http://localhost:8080/spring-boot-Demo/html/Hello.html可以打开页面,如果想看到数据,需要配置你自己的数据库的网址到application.properties中。
截图:


初探Spring Boot

结束语

Spring Boot有很多强大而又简便的功能值得我们去探索,这里所介绍的只是冰山一角,不过用来做一个简单的web工程已经足够。可以说Spring Boot,尤其是注解这一方面,值得我们去进行更深入的学习和研究。

上一篇:jquery-weui 时间选择器datetimepicker只要年月日不要时间解决方案


下一篇:Nuget控制台 - 给你的快速添加缺少的包