(第二講)Spring&Spring MVC&Spring Boot三者之間的區別與聯絡

譚朝紅發表於2019-04-24

Spring Framework的誕生讓開發人員的工作從石器時代跨域到了工業時代,你是否還能記起手擼Servlet和JDBC的歲月?,你是否還對Struts1以及Struts2莫名其妙的404錯誤記憶猶新?從2004年3月Spring 1.0釋出到至今,Spring的發展已經走過了15個年頭,其創造的價值讓人矚目。今天,帶著這樣一個背景來梳理一下Spring Framework,Spring MVC和Spring Boot三者之間的區別。

我們使用Spring家族的系列產品這麼長時間,不禁會問這樣幾個問題:Spring Framework是什麼?Spring MVC是什麼?Spring Boot又是什麼?它們被設計出來的目的是什麼?

你需要了解的知識

在接下來的內容中,將梳理這樣幾個知識點:

  • Spring Framework基本概述
  • Spring Framework主要解決的問題是什麼?
  • Spring MVC基本概述
  • Spring MVC主要解決的問題是什麼?
  • Spring Boot主要解決的問題是什麼?
  • Spring,Spring MVC和Spring Boot三者之間的區別是什麼?

Spring Framework 解決了哪些核心問題?

當你仔細思考這個問題的時候你會發現,很多地方它都有滲透到,貌似一個Spring就可以撐起開發的半邊天,以至於很難一下子回答這個問題。那Spring Framework到底解決了哪些核心問題?

Spring Framework最重要也是最核心的特性是依賴注入。所有的Spring模組的核心就是DI(依賴注入)或者IoC(控制反轉)。

依賴注入或控制反轉是Spring Framework最大的特性,當我們正確使用DI(依賴注入)或IoC時,可以開發出一個高內聚低耦合的應用程式,而這一一個低耦合的應用程式可以輕鬆的對其實施單元測試。這就是Spring Framework解決的最核心的問題。

無依賴注入

請考慮這一一個案例:UserAction依賴於UserService來獲取使用者資訊,在沒有依賴注入的情況下,我們需要手動在UserAction中例項化一個UserService物件,這樣的手工作業意味著UserAction和UserService必須精密的聯絡在一起,才能正常工作。如果一個Action需要多個Service提供服務,那例項化這些Service將是一個繁重的工作。下面我們給出一個不使用依賴注入的程式碼片段加以說明:

UserService.java

public interface UserService{
    User profile();
}
複製程式碼

UserServiceImpl.java

public class UserServiceImpl implements UserService{
    @Override
    User profile(){
        // TODO
    }
}
複製程式碼

UserAction.java

@RestController
public class UserAction{
    
    private UserService userService = new UserServiceImpl();
    // other services...
    
    @GetMapping("/profile")
    public User profile(){
        return userService.profile();
    }
}
複製程式碼

引入依賴注入

引入依賴注入將會使整個程式碼看起來很清爽。為了能夠開發出高內聚低耦合的應用程式,Spring Framework為我們做了大量的準備工作。下面我們使用兩個簡單的註解**@Component@Autowired**來實現依賴注入。

  • @Component : 該註解將會告訴Spring Framework,被此註解標註的類需要納入到Bean管理器中。
  • @Autowired : 告訴Spring Framework需要找到一與其型別匹配的物件,並將其自動引入到所需要的類中。

在接下來的示例程式碼中,我們會看到Spring Framework將為UserService建立一個Bean物件,並將其自動引入到UserAction中。

UserService.java

public interface UserService{
    User profile();
}
複製程式碼

UserServiceImpl.java

@Component
public class UserServiceImpl implements UserService{
    @Override
    User profile(){
        // TODO
    }
}
複製程式碼

UserAction.java

@RestController
public class UserAction{
    @Autowired
    private UserService userService;
    // other services...
    
    @GetMapping("/profile")
    public User profile(){
        return userService.profile();
    }
}
複製程式碼

對比上下兩部分的程式碼,你是否發現了他們之間的區別?Action所依賴的Service的初始化工作全部交由Spring Framework來管理,我們只需要在適當的地方向Spring Framework索取想要服務即可。這就好比當我想要吃薯片的時候,我不需要自己親自種土豆,施肥,收穫...清洗,切片...一直到最後炸土豆片,想想都覺得累,而更簡單的方法是直接去超市購買自己想要的薯片即可。

Spring Framework還有其他的核心特性嗎?

#1:衍生的特性

Spring Framework的依賴注入是核心中的核心,在依賴注入核心特性的基礎上,Spring Framework還衍生出了很多的高階模組:

  • Spring JDBC
  • Spring MVC
  • Spring AOP
  • Spring ORM
  • Spring JMS
  • Spring Test

對於這些新的高階模組,可能會產生這一一個問題:它們是否是一個全新的功能?答案是否定的,在不使用Spring Framework的情況下,我們依然能夠使用JDBC連線資料庫,依然能夠對檢視和資料模型進行控制,依然能夠使用第三方的ORM框架。那Spring Framework幹了什麼?Spring Framework站在巨人的肩膀上,對這些原生的模組進行了抽象,而抽象可以帶來這樣一些好處:

  • 減少了應用中模板程式碼的數量
  • 降低了原生框架的技術門檻
  • 基於依賴注入特性,實現了程式碼的解耦,真正的高內聚、低耦合
  • 更細粒度的單元測試

這樣的好處是顯而易見的,比如與傳統的JDBC相比,使用JDBCTemplate運算元據庫,首先是程式碼量小了,其次是我們不需要再面對恐怖的try-catch。

#2:優秀的整合能力

Spring Framework還具備另外一個重要特性,那就是能夠快速的與其他三方框架進行整合。與其自己造輪子,還不如想辦法將好的輪子整合在一起,我想這句話應該可以用來概況Spring Framework這一特性。Spring Framework對於整合其他的框架,給出了不錯的解決方案,下面將列舉一些常見的方案:

  • 與Hibernate ORM框架整合
  • 與MyBatis 物件對映框架整合
  • 與Junit單元測試框架整合
  • 與Log4J日誌記錄框架整合

Spring MVC是什麼?

Spring MVC提供了構建Web應用程式的全功能MVC模組,實現了Web MVC設計模式以及請求驅動型別的輕量級Web框架,即採用了MVC架構模式的思想,將Web層進行職責解耦。基於請求驅動指的是使用請求-響應模型,檢視與資料模型分離,以簡化Web應用的開發。

使用Spring MVC提供的Dispatcher Servlet,ModelAndView和ViewResolver等功能,可以輕鬆的開發出一個Web應用程式。

Spring Boot出現的原因是什麼?

我們都知道,使用Spring Framework來開發應用程式,需要進行大量的配置工作以及依賴包的管理,工作繁重而且極易出現配置錯誤,尤為明顯的是依賴包之間的版本衝突問題。

舉一個簡單的案例,當我們使用Spring MVC來開發Web應用程式時,我們大致需要經歷這樣幾個步驟:

  • #1:配置元件掃描的包路徑
  • #2:配置對應的Servlet程式
  • #3:配置檢視解析器
  • #4:配置頁面模板引擎(JSP、Freemarker等)

下面給出了一個小範圍的舉例:

...
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
	<property name="prefix">
    	<value>/WEB-INF/pages</value>
    </property>
    <property name="suffix">
    	<value>.jsp</value>
    </property>
</bean>
<mvc:resources mapping="/static/**" location="/static/"/>
...
複製程式碼

此外,我們還需要配置先關的Servlet處理程式,它們大致是這樣的:

<servlet>
    <servlet-name>dispatcher</servlet-name>
    <servlet-class>
        org.springframework.web.servlet.DispatcherServlet
    </servlet-class>
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/todo-servlet.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
    <servlet-name>dispatcher</servlet-name>
    <url-pattern>/</url-pattern>
</servlet-mapping>
複製程式碼

如果我們的應用程式還需要連結資料,則還需要配置資料來源,實體物件管理器,事務管理器等眾多配置:

<bean id="datasource" class="">
	...
</bean>
<bean id="entityManagerFactory" class="">
	...
</bean>
<bean id="transactionManager" class="">
	...
</bean>
...
複製程式碼

面對眾多的配置檔案,我們需要花費大量的時間去處理,這時你可能會問,我為什麼要花費那麼多的時間去管理Spring的配置工作?不是應該專注於應用本身的業務邏輯嗎?現在,有了Spring Boot,這些煩心事就不需要你去操心了。

#1:Spring Boot的自動化配置能力

我為什麼會把Spring Boot的自動化配置能力放在第一位,因為它極大的降低了我們使用Spring Framework所付出的成本。這是Spring Boot的自動化配置是一個最具價值的解決方案。

這難道不值得我們拍案叫好嗎?如果你想要開發一個Web應用程式,你需要做的事情就是將Spring Boot Web包引入到專案的類路徑下,Spring Boot就可以幫你解決後續的大多數配置工作。

  • 如果Hibernate的依賴被放到了類路徑上,Spring Boot會自動配置資料來源
  • 如果Spring MVC的依賴被放到了類路徑上,Spring Boot又會自動配置Dispatcher Servlet

當Spring Boot檢測到有新的依賴包新增到類路徑上,Spring Boot會採用預設的配置對新的依賴包進行設定,如果我們想自己配置依賴包時,只需要手動覆蓋預設的配置項即可。

  1. Spring Boot掃描類路徑上可用的框架資訊
  2. 獲取應用程式現有的配置資訊
  3. 如果應用程式沒有提供框架的配置資訊,Spring Boot將採用預設的配置來配置框架,這就是Spring Boot的自動配置特性(Auto Configuration)

#2:Spring Boot Starter專案

在傳統模式的開發過程中,我們需要反覆的確認應用程式所需要的第三方JAR包,以及這些JAR的版本和依賴關係。例如,現在我們打算開發一款Web應用程式,應用程式大概需要如下的一些依賴包:Spring MVC,Jackson Databind(用於資料繫結),Hibernate-Validator(用於服務端的資料校驗)和Log4j(用於日誌記錄)。現在,我們需要去下載對應的jar包到應用程式中,並且還需要處理依賴包之間版本衝突的問題。

<dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-webmvc</artifactId>
   <version>4.2.2.RELEASE</version>
</dependency>

<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.5.3</version>
</dependency>

<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-validator</artifactId>
    <version>5.0.2.Final</version>
</dependency>

<dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.17</version>
</dependency>
複製程式碼

這不是一件簡單的事情,特別是發生版本衝突的時候,讓應用程式能夠正常執行起來就需要花費一定的時間。

Spring Boot Starter是一組用於管理依賴關係的描述符,通過這些描述符,我們可以在應用程式中輕鬆的管理依賴包,你可以以開箱即用的方式獲取想要的依賴包,而無需去Maven倉庫總檢索對應的依賴,並將依賴配置複製貼上到應用程式的pom檔案中。例如,如果你想要使用Spring和JPA進行資料庫訪問,只需要在pom中新增spring-boot-starter-data-jpa依賴項就可以。

現在,如果我們想要開發一個Web應用程式,使用Spring Boot Starter Web依賴會是一個不錯的選擇。我們可以通過使用Spring INitializr快速構建一個Web應用程式,並將Spring Boot Starter Web新增到專案中。此時我們的pom檔案只需要很少的配置:

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-web</artifactId>
</dependency>
複製程式碼

Spring Boot Starter Web會為我們預裝如下的一些依賴:

  • Spring : core,beans,context,aop
  • Web MVC : Spring MVC
  • Jackson : JSON Binding
  • Validation : Hibernate Validator,Validation API
  • Embedded Servlet Container: Tomcat
  • Logging : logback,slf4j

對於開發人員而言,我們不需要去擔心這些依賴項的管理工作以及解決他們之間的相容性問題,Spring Boot已經幫我們解決了。

Spring Boot的核心目標

Spring Boot的核心目標在於快速實現生產就緒的應用程式,這將包含這樣幾個部分:

  • 執行器 : 啟用高階監控和跟蹤應用程式功能
  • 嵌入式伺服器:Spring Boot已經內建了多個Web伺服器,如Undertow,jetty,tomcat,因此我們不需要再額外的配置伺服器,就可以完成應用程式的除錯工作。
  • 預設的異常處理機制
  • 開箱即用的依賴項管理機制
  • 自動化配置

總結

通過上述的梳理,我們可以看到,Spring Framework是一個提供了DI(依賴注入)和IoC(控制反轉)的開發框架,使用Spring Framework可以幫助我們開發出高內聚,低耦合的應用程式,Spring MVC是在Spring Framework基礎上發展出來的基於MVC模式的全功能Web開發框架,實現了Model,View和Controller之間的職責解耦;Spring Boot為我們提供了一個能夠快速使用Spring Framework的優秀解決方案,通過最小化的配置,我們就可以使用Spring Framework,嚴格意義上講,Spring Boot並不是某種框架,它只是為開發人員提供了一個更好的更方便的使用Spring Framework的解決方案。

作者:譚朝紅

原文:Spring&Spring MVC&Spring Boot三者之間的區別與聯絡

相關文章