转载

SpringBoot学习笔记(二)——Spring周边生态系统

在前面的两篇文章中,分别讲解了Spring的IOC容器原理,以及如何从零开始创建一个Spring容器。但是实际工作中,光有这些肯定是不够的,还需要在这个基础上再扩展数据库、Redis缓存、消息队列等。所以接下来就一步步的从无到有,扩展这个基本的Spring容器。

1.从Spring容器中引入Bean

首先我们知道,一般情况下,我们会把业务逻辑分为三层,既Controller,Service,Dao。

  1. Controller层用以跟HTTP请求打交道。
  2. Service用于处理业务的具体逻辑。
  3. Dao只做与数据库打交道的事情。

2.在Spring的基础上增加Mybatis数据库

2.1 配置数据库

从引入Maven依赖开始,从 官网 中获取SpringBoot的Maven依赖,接着引入数据库,先使用最简单的H2数据库作为例子。引入H2数据库的 Maven依赖 之后,就可以开始着手为Spring配置数据库了,在main目录下新建resources文件夹,在其中新建application.properties文件,写入基本配置信息:

spring.datasource.url=jdbc:h2:file:C:/Users/catsme/Desktop/my-first-spring
spring.datasource.username=root
spring.datasource.password=password
spring.datasource.driver-class-name=org.h2.Driver
复制代码

2.2 引入FlyWay数据库自动迁移工具

设计数据库:

SpringBoot学习笔记(二)——Spring周边生态系统

测试用数据:

User Id Name
User_1 1 大娃
User_2 2 二娃
User_3 3 三娃
User_4 4 四娃
User_5 5 五娃
User_6 6 六娃
Score Id User_id Score
Score_1 1 1 90
Score_2 2 2 91
Score_3 3 3 89
Score_4 4 4 88
Score_5 5 5 92
Score_6 6 6 94
Socre_7 7 2 2
Socre_8 8 4 3

在resources目录下,新建db/migration/V1__CreateTables.sql文件,写入sql语句:

--用户表
create table user(name varchar(200),
id bigint primary key auto_increment);

--成绩表
create table score(
id bigint primary key auto_increment,
user_id bigint,
Score bigint);

insert into user(name,id) values
('大娃',1),('二娃',2),('三娃',3),('四娃',4),('五娃',5),('六娃',6);

insert into score(id,user_id,score) values
    (1,1,90),(2,2,91),(3,3,89),(4,4,88),(5,5,92),(6,6,94),(7,2,2),(8,4,3);
复制代码

添加以下信息至pom.xml中,用以引入flyway插件:

<plugin>
         <groupId>org.flywaydb</groupId>
         <artifactId>flyway-maven-plugin</artifactId>
         <version>6.0.6</version>
         <configuration>
             <url>jdbc:h2:file:C:/Users/catsme/Desktop/my-first-spring</url>
             <user>root</user>
             <password>password</password>
          </configuration>
 </plugin>
复制代码

接着,使用 mvn flyway:migrate 命令初始化数据库,接下来就是引用MyBatis进行复杂的sql操作了。

2.3 使用两种方法在Spring中配置和引用MyBatis

  • (一) 使用注解的方式

    首先声明一个接口:

@Mapper
public interface MyMapper {
    @Select("select * from user where id = #{}")
    User getUser(@Param("id") Integer id);
}
复制代码

接着在mybatis目录下的config.xml文件中的mapper块中声明该接口:

<mapper class="hello.dao.MyMapper"/> 。到现在可以发现,Spring关联了MyBatis,MyBatis关联了接口,那么问题来了,接下来要怎么实现这个MyMapper呢?

通过注解声明他们之间的依赖关系,通过依赖注入实现这层关系。代码如下:

//使用Autowired注解或者Resource注解注入依赖关系
 @Autowired
    private UserMapper myMapper;

    @RequestMapping("/getUser")
    @ResponseBody
    public User getUser(){
        return myMapper.getUserById(3);
    }
复制代码

那么就可以在浏览器中通过访问这个接口,实现获取数据库中的数据。

SpringBoot学习笔记(二)——Spring周边生态系统
  • (二)配置xml文件

现在有这样一个需求:按总成绩的降序,对用户进行排序,这样的话,就不好使用上面这种通过接口去实现的方式。 在db/mybatis目录下新建config.xml文件,写入基本配置信息:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <settings>
        <setting name="logImpl" value="LOG4J"/>
    </settings>
    <typeAliases>
        <typeAlias alias="User" type="hello.entity.User"/>
        <typeAlias alias="ScoreItem" type="hello.entity.ScoreItem"/>
    </typeAliases>
    <mappers>
        <mapper resource="db/mybatis/MyMapper.xml"/>
        <mapper class="hello.dao.UserMapper"/>
    </mappers>
</configuration>
复制代码

注意:由于已经在application.properties文件中已经绑定了数据库基本信息,所以这时候已经不需要在xml文件中配置数据源了。直接着在Spring中写入MyBatis的声明,代码如下:

mybatis.config-location=classpath:db/mybatis/config.xml 接在在MyBatis的映射文件中写好sql语句,代码如下:

<?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="MyUserMapper">
    <select id="rankUser" resultMap="scoreItem">
        select t.user_id,t.score_sum,user.name as user_name from
        (select user_id,sum(score) as score_sum from score group by user_id)t
        inner join user
        on user.id = t.user_id
        order by t.score_sum
        desc
    </select>

    <!-- 非常复杂的结果映射 -->
    <resultMap id="scoreItem" type="ScoreItem">
        <result property="score" column="score_sum"/>
        <association property="user" javaType="User">
            <result property="name" column="user_name"/>
            <result property="id" column="user_id"/>
        </association>
    </resultMap>
</mapper>
复制代码

到这里,该配置好的信息基本都以配置完毕,接下来该进行的就是,通过分层的思想,在不同的逻辑层上实现对应的功能。 对于dao层,实现与数据库的交互:

@Service
public class RankDao {

     @Autowired
     SqlSession sqlSession;

     public List<ScoreItem> getRankItem(){
          return sqlSession.selectList("MyUserMapper.rankUser");
     }
}
复制代码

在这里可以看到,原本需要使用SqlSessionFactory获取sqlSession,现在只需要在Spring中声明Autowired然后直接创建实例即可调用sqlSession的方法。接下来在entiy层中创建实体对象,用以存储从dao层拿到的数据,代码:

public class ScoreItem {
    private Integer id;
    private Integer score;
    private User user;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public User getUser() {
        return user;
    }

    public void setUser(User user) {
        this.user = user;
    }

    public Integer getScore() {
        return score;
    }

    public void setScore(Integer score) {
        this.score = score;
    }
}
复制代码

在service层实现业务逻辑:

@Service
public class RankService {
    @Autowired
    private RankDao rankDao;


    public List<ScoreItem> sort() {
        return rankDao.getRankItem();
    }
}
复制代码

最后在Controller层实现与用户界面的交互:

@RestController
public class HelloController {

    @Autowired
    private RankService rankService;

    @RequestMapping("/getUser")
    @ResponseBody
    public List<ScoreItem> getUserFromDatabase() {
      return rankService.sort();
    }
}
复制代码

最后结果如下:

SpringBoot学习笔记(二)——Spring周边生态系统

可以看到,这样就从头到尾实现了一个较小的业务逻辑,如果加上一些渲染的话,就能变成一个真正的页面。

3.渲染HTML

从数据库中拿出数据后,接下来要做的就是对页面进行渲染了,这个过程前端跟后端都可以实现。

3.1 使用FreeMarker进行后端渲染

何谓 模板引擎 呢,源自 百度百科 的解释:模板引擎(这里特指用于Web开发的模板引擎)是为了使用户界面与业务数据(内容)分离而产生的,它可以生成特定格式的文档,用于网站的模板引擎就会生成一个标准的HTML文档。在这里使用FreeMarker工具渲染页面。

何谓 FreeMarker 呢,源自 维基百科 的解释: FreeMarker是一个基于Java的模板引擎,最初专注于使用MVC软件架构生成动态网页。但是,它是一个通用的模板引擎,不依赖于servlets或HTTP或HTML,因此它通常用于生成源代码,配置文件或电子邮件。

所以我们可以通过FreeMarker来生成HTML页面。引入FreeMarker的 mavne依赖 ,根据官网提示:

resources
        ├── application.yml
        ├── data-h2.sql
        ├── schema-h2.sql
        ├── static
        │   └── css
        │       └── style.css
        └── templates   
            ├── index.ftl
            └── showCities.ftl
复制代码

我们需要在resources目录下创建一个templates文件夹,这样FreeMarker默认会去templates文件夹中寻找。然后新建index.ftl文件,在.ftl模板文件中写好除初始格式,然后把内容传入其中。.ftl文件中写入HTML格式的代码:

<!DOCTYPE html>
    <html lang="zh-CN">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <meta http-equiv="X-UA-Compatible" content="ie=edge">
        <title>成绩排行榜</title>
    </head>
    <body>
    <table>
        <thead>
        <tr>
            <th>用户号码</th>
            <th>成绩</th>
            <th>用户名</th>
        </tr>
        </thead>
        <tbody>
        <tr>
            <td>${index}</td>
            <td>${name}</td>
            <td>${score}</td>
        </tr>
        </tbody>
    </table>
    </body>
    </html>
复制代码

在这里定义好渲染的HTML文档的模板之后,就是想办法将数据传入其中了。与MyBatis中类似,代码如下:

@RequestMapping("/getUser")
    @ResponseBody
    public ModelAndView getUserFromDatabase() {
        Map<String, Object> model = new HashMap<>();
        model.put("index",1);
        model.put("name","zhangsan");
        model.put("score",97);
        //接受两个参数,一个模板文件一个数据
        return new ModelAndView("index",model);
    }
复制代码

结果:

SpringBoot学习笔记(二)——Spring周边生态系统
可以看到这时候,我们就通过向模板文件中传入参数,实现了对页面的渲染。这只是测试一下效果,实际上我们是需要通过循环将内容展现出来。那么接下来就是把数据库中真正的数据拿出来,放在页面中了。问题来了:从数据库中拿到的是六条数据,那么实现循环渲染呢?查阅 FreeMarker官网

后,修改模板文件中的部分代码:

<thead>
    <tr>
        <th>序号</th>
        <th>成绩</th>
        <th>姓名</th>
        <th>学号</th>
    </tr>
    </thead>
    <tbody>

    <#list items as item>
         <tr>
            <td>${item?index+1}</td>
            <td>${item.user.name}</td>
            <td>${item.score}</td>
            <td>${item.user.id}</td>
         </tr>
    </#list>
    </tbody
复制代码

实现效果:

SpringBoot学习笔记(二)——Spring周边生态系统

3.2 使用JS和JSON进行前端渲染

这里原本是可以直接使用前端的框架进行JS的设计的,但是为了加深对前后端渲染的理解(实际上是自己不会),所以使用最原始的方式进行,工作中千万不要用!!!

在resources目录下新建static文件夹,在这个文件夹中的内容可以被直接访问。在其中新建index.html页面文档,在文档中写入:

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>成绩排行榜</title>
</head>
<body>
<table id="rank-table">
    <thead>
    <tr>
        <th>序号</th>
        <th>成绩</th>
        <th>姓名</th>
        <th>学号</th>
    </tr>
    
    </thead>
    <tbody>
    
    </tbody>
</table>
</body>
</html>
复制代码

这时候是可以通过浏览器直接访问index.html的:

SpringBoot学习笔记(二)——Spring周边生态系统
接下来在html中添加 script

标签,用以发送http请求:

<script>
        var xhr = new XMLHttpRequest();
        xhr.onreadystatechange = function(){
          if( xhr.readyState == 4){
             if( xhr.status >= 200 && xhr.status < 300 || xhr.status == 304){
             var json = xhr.responseText;
             <!--后将获取的结果打印至控制台-->
             console.log(json);
          }
       }
    };
     <!--先通过getRankItem接口发送请求-->
        xhr.open("get", "/getRankItem", true);
        xhr.send(null);
    </script>
复制代码

接下来就可以在浏览器中看到获取的JSON字符串:

SpringBoot学习笔记(二)——Spring周边生态系统
现在需要做的是,通过javascript将script标签中获取的结果,拼接到html中,展示到页面上。注意:现实工作中不要这样做!

现学现卖,使用最基础的方式实现字符串的拼接,然后将其写入html中:

<script>
        var xhr = new XMLHttpRequest();
        xhr.onreadystatechange = function(){
          if( xhr.readyState == 4){
             if( xhr.status >= 200 && xhr.status < 300 || xhr.status == 304){
             var json = xhr.responseText;
             var list = JSON.parse(json);
             var str1 = '';
             var step;
             for (step = 0; step < 5; step++) {
                str1 += '<tr><td>'+(step+1)+'</td><td>'+list[step].score+'</td><td>'+list[step].user.name+'</td><td>'+list[step].user.id+'</td></tr> '
             }

             var str = '<tr><th>序号</th><th>成绩</th><th>姓名</th><th>学号</th>'+str1+
             '<tr><td>6</td><td>'+list[5].score+'</td><td>'+list[5].user.name+'</td><td>'+list[5].user.id+'</td></tr>'

          document.getElementById('rank-table').innerHTML = str;
          }
       }
    };
        xhr.open("get", "/getRankItem", true);
        xhr.send(null);

    </script>
复制代码

实现效果如下:

SpringBoot学习笔记(二)——Spring周边生态系统

4. 报错解决

4.1 mvn flyway:migrate 命令报错:

-> Applied to database : 1062144176
-> Resolved locally : 1432425380
复制代码

这种情况需要删除数据库中的记录:

SpringBoot学习笔记(二)——Spring周边生态系统
SpringBoot学习笔记(二)——Spring周边生态系统

4.2 代码检查工具总是报 File does not end with a newline.' 错误

删除.circle目录下的

<module name="NewlineAtEndOfFile">
        <property name="lineSeparator" value="lf" />
    </module>
复制代码

即可。

项目地址: github.com/Scott-YuYan…

5.参考资料

  1. 掘金 .《你的项目应该如何正确分层?》点击此处查看源文档
  2. CSDN 《Flyway Validate failed:migration checksum mismatch for migration 1.0.0003》 点击此处查看源文档
原文  https://juejin.im/post/5dc51da7e51d452bff030cd6
正文到此结束
Loading...