在前面的兩篇文章中,分別講解了Spring的IOC容器原理,以及如何從零開始建立一個Spring容器。可是實際工做中,光有這些確定是不夠的,還須要在這個基礎上再擴展數據庫、Redis緩存、消息隊列等。因此接下來就一步步的從無到有,擴展這個基本的Spring容器。
javascript
首先咱們知道,通常狀況下,咱們會把業務邏輯分爲三層,既Controller,Service,Dao。css
從引入Maven依賴開始,從官網中獲取SpringBoot的Maven依賴,接着引入數據庫,先使用最簡單的H2數據庫做爲例子。引入H2數據庫的Maven依賴以後,就能夠開始着手爲Spring配置數據庫了,在main目錄下新建resources文件夾,在其中新建application.properties文件,寫入基本配置信息:html
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
複製代碼
設計數據庫:
前端
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,2,2),(8,4,3);
複製代碼
添加如下信息至pom.xml中,用以引入flyway插件:java
<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操做了。git
@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呢?
經過註解聲明他們之間的依賴關係,經過依賴注入實現這層關係。代碼以下:
github
//使用Autowired註解或者Resource註解注入依賴關係
@Autowired
private UserMapper myMapper;
@RequestMapping("/getUser")
@ResponseBody
public User getUser(){
return myMapper.getUserById(3);
}
複製代碼
注意:如今最推薦的就是,引入@Inject
的Maven依賴以後在構造器中使用@Inject聲明注入依賴關係。好比這樣:spring
@Inject
public OrderService(UserService userService) {
this.userService = userService;
}
複製代碼
那麼接下來就能夠在瀏覽器中經過訪問這個接口,獲取數據庫中的數據。 sql
如今有這樣一個需求:按總成績的降序,對用戶進行排序,這樣的話,就很差使用上面這種經過接口去實現的方式。 在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();
}
}
複製代碼
最後結果以下:
從數據庫中拿出數據後,接下來要作的就是對頁面進行渲染了,這個過程前端跟後端均可以實現。
何謂模板引擎呢,源自百度百科 的解釋:模板引擎(這裏特指用於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);
}
複製代碼
結果:
<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
複製代碼
實現效果:
這裏本來是能夠直接使用前端的框架進行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的:
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字符串:
<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>
複製代碼
實現效果以下:
mvn flyway:migrate
命令報錯:-> Applied to database : 1062144176
-> Resolved locally : 1432425380
複製代碼
這種狀況須要刪除數據庫中的記錄:
或者直接使用mvn flyway:repair
修復版本。
File does not end with a newline.'
錯誤 刪除.circle目錄下的
<module name="NewlineAtEndOfFile">
<property name="lineSeparator" value="lf" />
</module>
複製代碼
便可。