1. 程式人生 > 程式設計 >SpringBoot學習筆記(二)——Spring周邊生態系統

SpringBoot學習筆記(二)——Spring周邊生態系統

摘要

\quad 在前面的兩篇文章中,分別講解了Spring的IOC容器原理,以及如何從零開始建立一個Spring容器。但是實際工作中,光有這些肯定是不夠的,還需要在這個基礎上再擴充套件資料庫、Redis快取、訊息佇列等。所以接下來就一步步的從無到有,擴充套件這個基本的Spring容器。

1.從Spring容器中引入Bean

\quad 首先我們知道,一般情況下,我們會把業務邏輯分為三層,既Controller,Service,Dao。

  1. Controller層用以跟HTTP請求打交道。
  2. Service用於處理業務的具體邏輯。
  3. Dao只做與資料庫打交道的事情。

2.在Spring的基礎上增加MyBatis資料庫

2.1 配置資料庫

\quad 從引入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資料庫自動遷移工具

\quad 設計資料庫:

\quad 測試用資料:

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,(8,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

  • (一) 使用註解的方式
    \quad 首先宣告一個介面:
@Mapper
public interface MyMapper {
    @Select("select * from user where id = #{}")
    User getUser(@Param("id") Integer id);
}
複製程式碼

\quad 接著在mybatis目錄下的config.xml檔案中的mapper塊中宣告該介面:
<mapper class="hello.dao.MyMapper"/>。到現在可以發現,Spring關聯了MyBatis,MyBatis關聯了介面,那麼問題來了,接下來要怎麼實現這個MyMapper呢?
\quad 通過註解宣告他們之間的依賴關係,通過依賴注入實現這層關係。程式碼如下:

//使用Autowired註解或者Resource註解注入依賴關係
 @Autowired
    private UserMapper myMapper;

    @RequestMapping("/getUser")
    @ResponseBody
    public User getUser(){
        return myMapper.getUserById(3);
    }
複製程式碼

注意:現在最推薦的就是,引入@InjectMaven依賴之後在構造器中使用@Inject宣告注入依賴關係。比如這樣:

@Inject
    public OrderService(UserService userService) {
        this.userService = userService;
    }
複製程式碼

那麼接下來就可以在瀏覽器中通過訪問這個介面,獲取資料庫中的資料。

  • (二)配置xml檔案

\quad 現在有這樣一個需求:按總成績的降序,對使用者進行排序,這樣的話,就不好使用上面這種通過介面去實現的方式。 在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>
複製程式碼

\quad注意:由於已經在application.properties檔案中已經綁定了資料庫基本資訊,所以這時候已經不需要在xml檔案中配置資料來源了。直接著在Spring的application.properties檔案中寫入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>
複製程式碼

\quad 到這裡,該配置好的資訊基本都以配置完畢,接下來該進行的就是,通過分層的思想,在不同的邏輯層上實現對應的功能。 對於dao層,實現與資料庫的互動:

@Service
public class RankDao {

     @Autowired
     SqlSession sqlSession;

     public List<ScoreItem> getRankItem(){
          return sqlSession.selectList("MyUserMapper.rankUser");
     }
}
複製程式碼

\quad 在這裡可以看到,原本需要使用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;
    }
}
複製程式碼

\quad 在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();
    }
}
複製程式碼

最後結果如下:

\quad 可以看到,這樣就從頭到尾實現了一個較小的業務邏輯,如果加上一些渲染的話,就能變成一個真正的頁面。

3.渲染HTML

\quad 從資料庫中拿出資料後,接下來要做的就是對頁面進行渲染了,這個過程前端跟後端都可以實現。

3.1 使用FreeMarker進行後端渲染

\quad 何謂模板引擎呢,源自百度百科 的解釋:模板引擎(這裡特指用於Web開發的模板引擎)是為了使使用者介面與業務資料(內容)分離而產生的,它可以生成特定格式的檔案,用於網站的模板引擎就會生成一個標準的HTML檔案。在這裡使用FreeMarker工具渲染頁面。
\quad 何謂FreeMarker呢,源自維基百科 的解釋: FreeMarker是一個基於Java的模板引擎,最初專注於使用MVC軟體架構生成動態網頁。但是,它是一個通用的模板引擎,不依賴於servlets或HTTP或HTML,因此它通常用於生成原始碼,配置檔案或電子郵件。
\quad 所以我們可以通過FreeMarker來生成HTML頁面。引入FreeMarker的mavne依賴,根據官網提示:

resources
        ├── application.yml
        ├── data-h2.sql
        ├── schema-h2.sql
        ├── static
        │   └── css
        │       └── style.css
        └── templates   
            ├── index.ftl
            └── showCities.ftl
複製程式碼

\quad我們需要在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>
複製程式碼

\quad 在這裡定義好渲染的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);
    }
複製程式碼

結果:

\quad 可以看到這時候,我們就通過向模板檔案中傳入引數,實現了對頁面的渲染。這只是測試一下效果,實際上我們是需要通過迴圈將內容展現出來。那麼接下來就是把資料庫中真正的資料拿出來,放在頁面中了。問題來了:從資料庫中拿到的是六條資料,那麼實現迴圈渲染呢? 查閱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
複製程式碼

實現效果:

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

\quad 這裡原本是可以直接使用前端的框架進行JS的設計的,但是為了加深對前後端渲染的理解(實際上是自己不會),所以使用最原始的方式進行,工作中千萬不要用!!!
\quad 在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的:

接下來在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字串:

\quad 現在需要做的是,通過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",true);
        xhr.send(null);

    </script>
複製程式碼

實現效果如下:

4. 報錯解決

4.1mvn flyway:migrate命令報錯:

-> Applied to database : 1062144176
-> Resolved locally : 1432425380
複製程式碼

這種情況需要刪除資料庫中的記錄:

或者直接使用mvn flyway:repair修復版本。

4.2 程式碼檢查工具總是報File does not end with a newline.'錯誤

\quad 刪除.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》點選此處檢視原始檔