SpringBoot學習筆記(二)——Spring周邊生態系統
摘要
在前面的兩篇文章中,分別講解了Spring的IOC容器原理,以及如何從零開始建立一個Spring容器。但是實際工作中,光有這些肯定是不夠的,還需要在這個基礎上再擴充套件資料庫、Redis快取、訊息佇列等。所以接下來就一步步的從無到有,擴充套件這個基本的Spring容器。
1.從Spring容器中引入Bean
首先我們知道,一般情況下,我們會把業務邏輯分為三層,既Controller,Service,Dao。
- Controller層用以跟HTTP請求打交道。
- Service用於處理業務的具體邏輯。
- 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資料庫自動遷移工具
設計資料庫:
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 |
--使用者表
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
- (一) 使用註解的方式
首先宣告一個介面:
@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);
}
複製程式碼
注意:現在最推薦的就是,引入@Inject
的Maven依賴之後在構造器中使用@Inject宣告注入依賴關係。比如這樣:
@Inject
public OrderService(UserService userService) {
this.userService = userService;
}
複製程式碼
那麼接下來就可以在瀏覽器中通過訪問這個介面,獲取資料庫中的資料。
- (二)配置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的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>
複製程式碼
到這裡,該配置好的資訊基本都以配置完畢,接下來該進行的就是,通過分層的思想,在不同的邏輯層上實現對應的功能。 對於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();
}
}
複製程式碼
最後結果如下:
可以看到,這樣就從頭到尾實現了一個較小的業務邏輯,如果加上一些渲染的話,就能變成一個真正的頁面。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);
}
複製程式碼
結果:
可以看到這時候,我們就通過向模板檔案中傳入引數,實現了對頁面的渲染。這只是測試一下效果,實際上我們是需要通過迴圈將內容展現出來。那麼接下來就是把資料庫中真正的資料拿出來,放在頁面中了。問題來了:從資料庫中拿到的是六條資料,那麼實現迴圈渲染呢? 查閱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進行前端渲染
這裡原本是可以直接使用前端的框架進行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的:
接下來在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字串:
現在需要做的是,通過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.'
錯誤
刪除.circle目錄下的
<module name="NewlineAtEndOfFile">
<property name="lineSeparator" value="lf" />
</module>
複製程式碼
即可。