【Spring Boot課程】5.web開發
使用spring boot的開發流程:
- 建立Spring Boot應用,選中我們需要的模組;
- Spring Boot已經為我們將所有的場景配置好了(spring-boot-autoconfigure包自動配置),我們只需要在配置檔案中指定對應Properties相關的少量配置就可以執行起來;
- 編寫業務程式碼。
自動配置原理?
請記住,飲水則思源,在每運用一個場景的時候,都要記住這是自動配置原理生效的。同時也要思考一下,spring-boot為我們配置了什麼?能不能修改?有哪些配置可以修改?是否可以擴充套件等。
一定要記住:xxxAutoConfiguration幫我們在容器中自動的配置相關元件,而其xxxProperties封裝了配置檔案的內容。
5.1 spring boot對靜態資源的對映規則
查詢類(ctrl+shift+n)WebMvcAutoConfiguration
檢視其原始碼,與web相關的配置在此處都可以看到:
@ConfigurationProperties(
prefix = "spring.resources",
ignoreUnknownFields = false
)
public class ResourceProperties {
也就是說有關配置可以通過spring.resourcec進行設定。
webjars
再來看
public void addResourceHandlers(ResourceHandlerRegistry registry) {
if (!this.resourceProperties.isAddMappings()) {
logger.debug("Default resource handling disabled");
} else {
Duration cachePeriod = this.resourceProperties.getCache().getPeriod();
CacheControl cacheControl = this.resourceProperties.getCache().getCachecontrol().toHttpCacheControl();
if (!registry.hasMappingForPattern("/webjars/**")) {
this.customizeResourceHandlerRegistration(registry.addResourceHandler(new String[]{"/webjars/**"}).addResourceLocations(new String[]{"classpath:/META-INF/resources/webjars/"}).setCachePeriod(this.getSeconds(cachePeriod)).setCacheControl(cacheControl));
}
String staticPathPattern = this.mvcProperties.getStaticPathPattern();
if (!registry.hasMappingForPattern(staticPathPattern)) {
this.customizeResourceHandlerRegistration(registry.addResourceHandler(new String[]{staticPathPattern}).addResourceLocations(getResourceLocations(this.resourceProperties.getStaticLocations())).setCachePeriod(this.getSeconds(cachePeriod)).setCacheControl(cacheControl));
}
}
}
從該程式碼中我們可以看到,所有/webjars/**
都去classpath:/META-INF/resources/webjars
中找資源;
- webjars: 以jar包的方式引入靜態資源,如jquery等,我們只需要配置pom檔案即可引入這些靜態資源,參考:官網地址
引入jquery:
<dependency>
<groupId>org.webjars</groupId>
<artifactId>jquery</artifactId>
<version>3.3.0</version>
</dependency>
我們將下載到的jquery包展開可以看清楚目錄結構,結合上面有關webjars的解釋,我們可以嘗試通過訪問127.0.0.1:8081/webjars/jquery/3.3.0/jquery.js,發現可以成功的訪問到該靜態資源。也就是說,打包後的專案的確是可以訪問到的。
靜態資原始檔夾
還有一種配置資源的方式,"/**"訪問當前專案的任何資源:
- classpath:/META-INF/resources/
- classpath:/resources/
- classpath:/static/
- classpath:/public/
- /:當前專案的根路徑
也就說,我們在上述資料夾上面放置靜態資源,如果沒有特別處理,系統預設都是可以訪問到的。
歡迎頁
繼續檢視該類的原始碼:
@Bean
public WelcomePageHandlerMapping welcomePageHandlerMapping(ApplicationContext applicationContext) {
return new WelcomePageHandlerMapping(new TemplateAvailabilityProviders(applicationContext), applicationContext, this.getWelcomePage(), this.mvcProperties.getStaticPathPattern());
}
此處配置的是歡迎頁的對映。他都是靜態資原始檔夾下的所有index.html檔案。被/**
對映。
如localhost:8008則就是訪問歡迎頁面。
預設圖示
@Bean
public SimpleUrlHandlerMapping faviconHandlerMapping() {
SimpleUrlHandlerMapping mapping = new SimpleUrlHandlerMapping();
mapping.setOrder(-2147483647);
mapping.setUrlMap(Collections.singletonMap("**/favicon.ico", this.faviconRequestHandler()));
return mapping;
}
該處配置我們喜歡的圖示,即瀏覽器表標籤頁左方的小圖示。還是對映到了**.favicon.ico
,也就是我們的所有靜態資料夾下面。
從上面可以看出,系統預設用一個staticLocations
作為對映路徑,該路徑預設為剛才所列出的預設路徑,如果要配置自定義的資源路徑,則可以:
spring.resources.static-locations=classpath:/hello,classpath:/hello2
就可以了,但要注意之前的預設值就不生效了。
5.2 模板引擎
市面上比較常用的有JSP、Velocity、FreeMarker、以及Spring boot推薦的Thymeleaf。
雖然模板引擎很多,但是其核心思想都是一樣的,即按照一定的語法標準,將你的資料轉化為實際的html頁面,他們的區別只在於語法。
spring boot推薦的themeleaf語法比較簡單,而且功能更強大。
引入themeleaf
注意,如果是1.x版本的spring boot此處若想使用3.x版本的thymeleaf的話,請在properties配置節配置其版本號以及佈局版本號,以覆蓋SB中預設的低版本thymeleaf。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
檢視spring-boot-autoconfigure
包可以檢視到有關thymeleaf相關的配置資訊ThymeleafAutoConfiguration
。其無非就是為thymeleaf新增相關的元件。我們主要關注的是其相關的配置規則:ThymeleafProperties。
@ConfigurationProperties(
prefix = "spring.thymeleaf"
)
public class ThymeleafProperties {
private static final Charset DEFAULT_ENCODING;
public static final String DEFAULT_PREFIX = "classpath:/templates/";
public static final String DEFAULT_SUFFIX = ".html";
private boolean checkTemplate = true;
private boolean checkTemplateLocation = true;
private String prefix = "classpath:/templates/";
private String suffix = ".html";
private String mode = "HTML";
private Charset encoding;
private boolean cache;
private Integer templateResolverOrder;
private String[] viewNames;
private String[] excludedViewNames;
private boolean enableSpringElCompiler;
private boolean renderHiddenMarkersBeforeCheckboxes;
private boolean enabled;
private final ThymeleafProperties.Servlet servlet;
private final ThymeleafProperties.Reactive reactive;
從預設規則裡面我們不難看出很多東西其實已經無需修改,就按該預設配置進行業務程式碼編寫就行了。也就是從配置中可以看出,我們的所有頁面只需要放在classpath:/templates/
資原始檔夾下面,並以.html
即為即可,根本無需其他多餘的配置:
@RequestMapping("success")
public String success(){
return "success";
}
該程式碼交給thymeleaf渲染template/success.html
的檔案。
5.3 thymeleaf語法
詳細的官方檔案請點選:參考文件
使用步驟:
- 匯入thymeleaf的名稱空間在html標籤中,這樣就有語法提示了;
<html xmlns:th="http://www.thymeleaf.org">
- controller傳值
@RequestMapping("success")
public String success(Map<String, Object> map){
map.put("hello", "你好");
return "success";
}
- 渲染頁使用thymeleaf語法
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
this is success page.
<p th:text="${hello}">這裡顯示預設資訊。</p>
</body>
</html>
從輸出結果可以看出,該頁面完全可以獨立於出來直接執行,其輸出的結果不經過渲染只不過只會輸出
這裡是預設資訊
這樣的欄位而已,做到了很好的前後端分離。
標籤語法
改變當前元素的文字內容等,例如:th:html屬性
其值都可以替換原生的元素對應的值。
相關的介紹參考官方文件;
標籤語法詳解
th:each
參考文件Iteration
部分。
該標籤用於遍歷陣列資料。需要注意的是,該標籤寫在那個標籤上,那個標籤都會在每次遍歷中生成一次。由此我們可以瞭解到,該標籤經常會和表格行標籤<tr>
搭配使用。
<h1>Product list</h1>
<table>
<tr>
<th>NAME</th>
<th>PRICE</th>
<th>IN STOCK</th>
</tr>
<tr th:each="prod : ${prods}">
<td th:text="${prod.name}">Onions</td>
<td th:text="${prod.price}">2.41</td>
<td th:text="${prod.inStock}? #{true} : #{false}">yes</td>
</tr>
</table>
表示式語法
- 簡單表示式:
- Variable Expressions: ${…}
- Selection Variable Expressions: *{…}
- Message Expressions: #{…}
- Link URL Expressions: @{…}
- Fragment Expressions: ~{…}
- Literals(字面量)
- Text literals: ‘one text’ , ‘Another one!’ ,…
- Number literals: 0 , 34 , 3.0 , 12.3 ,…
- Boolean literals: true , false
- Null literal: null
- Literal tokens: one , sometext , main ,…
- Text operations(文字操作)
- String concatenation: +
- Literal substitutions: |The name is ${name}|
- Arithmetic operations(數學運算)
- Binary operators: + , - , * , / , %
- Minus sign (unary operator): -
- Boolean operations(布林運算)
- Binary operators: and , or
- Boolean negation (unary operator): ! , not
- Comparisons and equality(比較運算)
- Comparators: > , < , >= , <= ( gt , lt , ge , le )
- Equality operators: == , != ( eq , ne )
- Conditional operators(條件操作)
- If-then: (if) ? (then)
- If-then-else: (if) ? (then) : (else)
- Default: (value) ?: (defaultvalue)
- Special tokens(特殊標記-token:特徵;記號)
- No-Operation: _
以上所有的片段都可以組合使用,例如:
'User is of type ' + (${user.isAdmin()} ? 'Administrator' : (${user.type} ?: 'Unknown'))
表示式語法詳解
${…}
該表示式用於獲取變數的值,使用的是OGNL語法:
- 獲取物件的屬性、呼叫方法
- 使用內建的基本物件
${session.user.id}
- 內建的工具物件
*{…}
和${…}的功能是一樣的,不過有一個補充使用,即配合<th:object>
使用,如下面的示例:
<div th:object="${session.user}">
<p>Name: <span th:text="*{firstName}">Sebastian</span>.</p>
<p>Surname: <span th:text="*{lastName}">Pepper</span>.</p>
<p>Nationality: <span th:text="*{nationality}">Saturn</span>.</p>
</div>
其等價於:
<div>
<p>Name: <span th:text="${session.user.firstName}">Sebastian</span>.</p>
<p>Surname: <span th:text="${session.user.lastName}">Pepper</span>.</p>
<p>Nationality: <span th:text="${session.user.nationality}">Saturn</span>.</p>
</div>
也就是說,用*{}
可以省略公共的物件資訊。當然,我們也可以在迴圈體內部混合使用這兩種語法:
<div th:object="${session.user}">
<p>Name: <span th:text="*{firstName}">Sebastian</span>.</p>
<p>Surname: <span th:text="${session.user.lastName}">Pepper</span>.</p>
<p>Nationality: <span th:text="*{nationality}">Saturn</span>.</p>
</div>
#{…}
該表示式用於獲取國際化內容。
@{…}
定義url。
<!-- Will produce 'http://localhost:8080/gtvg/order/details?orderId=3' (plus rewriting) -->
<a href="details.html"
th:href="@{http://localhost:8080/gtvg/order/details(orderId=${o.id})}">view</a>
<!-- Will produce '/gtvg/order/details?orderId=3' (plus rewriting) -->
<a href="details.html" th:href="@{/order/details(orderId=${o.id})}">view</a>
<!-- Will produce '/gtvg/order/3/details' (plus rewriting) -->
<a href="details.html" th:href="@{/order/{orderId}/details(orderId=${o.id})}">view</a>
如果需要新增多個引數的話,用逗號分隔即可。
@{/order/process(execId=${execId},execType='FAST')}
~{…}
片段引用表示式。
<div th:insert="~{commons :: main}">...</div>
行內表示式
參考文件Inlining
部分。
很多時候,我們想顯示的文字其實是單純的文字節點,完全不想使用html標籤包裹,這時候需要怎麼寫呢。我們顯然不能這樣寫:
my name is ${$name}.
正確的寫法應該使用行內表示式
my name is [[${name}]]!
關於行內式有兩種標籤寫法:
[[...]]
等價於th:text
,會轉義特殊字元,即按原樣輸出,<h1>
標籤會格式化內部文字;[(...)]
等價於th:utext
,不會轉義特殊字元,即按相應的標籤格式輸出,例如<h1>
標籤直接輸出為<h1>
。
轉義這個有點繞,不小心把自己也繞進去了。簡單一點的記法:想要有html標籤格式化文字,用
text
([[...]]
),想原樣輸出標籤用utext
,。
5.4 spring mvc自動配置
通過官方文件查閱
原始碼查閱
Spring boot 自動配置好了Spring mvc,以下是SB對spring mvc的預設配置,這些配置元件都可以在WebMvcConfigurationSupport
類中檢視相應原始碼:
-
自動配置了ViewResoulver(檢視解析器:根據方法的返回值得到檢視物件(View),檢視物件決定如何渲染…轉發、重定向)
ContentNegotiatingViewResolver
: 組合所有的檢視解析器。- 如何定製:我們可以自己給容器中新增一個檢視解析器,他會自動的將其組合起來。
-
Support for serving static resources, including support for WebJars (covered later in this document)): 靜態資原始檔夾路徑,webjars.
-
Automatic registration of Converter, GenericConverter, and Formatter beans.
- Converter:轉換器,型別轉化
- Formatter 格式化器:2017.1.1===Date;
-
Support for
HttpMessageConverters
(covered later in this document)- springMVC中用來轉換http請求和響應的User===Json
- HttpMessageConverters是從容器中確定獲取所有的HttpMessageConver;自己給容器中新增賭贏的元件,只需要將自己的元件註冊到容器中即可(@Bean,@Component)
-
Automatic registration of
MessageCodesResolver
(covered later in this document).- 定義錯誤程式碼的生成規則的;
-
Static
index.html
support.- 靜態頁面支援
-
Custom Favicon support (covered later in this document).
- 圖示支援
-
Automatic use of a
ConfigurableWebBindingInitializer
bean (covered later in this document).- 作用為初始化WebDataBinder的,例如請求資料JavaBean,就需要用到資料繫結器,裡面牽涉到型別轉化、格式化等等;
- 我們可以配置一個ConfigurableWebBindingInitializer來替換預設的。
SB對web的自動配置其實不止這個類中做了,其實還有其他的以***AutoConfiguration的類,都對web場景進行了一些配置。
5.5 修改SpringBoot的預設配置
模式:給容器中新增對應的自定義元件就可以了。
- Spring Boot在自動配置很多元件的時候,都會先看容器中有沒有使用者自己配置(@Bean、@Component),如果有就用使用者配置的,如果沒有就用Spring Boot自己預設配置的;但是如果元件可以有多個的話,則是將其所有配置的組合起來;
- 僅靠Spring boot的預設配置還是不夠的,我們還需要一些自己新的配置,那麼我們則需要擴充套件以及全面接管springMVC的相關功能。如何做呢?見下一節。
5.6 擴充套件與全面接管SpringMVC
我們以前配置試圖對映、攔截器都可以在springmvc.xml中進行配置,如下圖所示:
```xml
<mvc:view-controller path="/hello" view-name="success"/>
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/hello"/>
<bean></bean>
</mvc:interceptor>
</mvc:interceptors>
那在spring boot中該怎麼做呢?
編寫一個配置類(@Configuration
),是WebMvcConfigurerAdapter
型別;不能標註@EnableWebMvc
;
注意此處是1.x版本,2.x版本提示這個類準備移除了,我們不再需要繼承
WebMvcConfigurerAdapter
,而是直接實現介面WebMvcConfigurer
來寫配置類即可。
@Configuration
public class MyConfig implements WebMvcConfigurer {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
// 瀏覽器傳送/restweb也會來到hello頁面.
registry.addViewController("/restweb").setViewName("hello");
}
}
該方式即保留了spring boot的所有自動配置,也能用我們的擴充套件配置。可以檢視如下了解原理:
- WebMvcAutoConfiguration是springmvc的自動配置類;
- 再做其他自動配置時會匯入,
@import(EnableWebMvcConfiguration.class)
@Autowired(required = false)
public void setConfigurers(List<WebMvcConfigurer> configurers) {
if (!CollectionUtils.isEmpty(configurers)) {
this.configurers.addWebMvcConfigurers(configurers);
}
}
spring boot 會將所有的WebMvcConfiguration相關配置一起呼叫
- 容器中所有的WebMvcConfigurer都會起作用,包括我們自己配置的;
全面接管SpringMVC
這種情況下,我們一般是不想要SpringBoot的所有配置,所有的都是由我們自己來指定。只需要在自定義配置類中新增一個配置@EnableWebMvc
即可,這樣之後所有的SpringMVC的自動配置都失效了。
@EnableWebMvc
原理:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(DelegatingWebMvcConfiguration.class)
public @interface EnableWebMvc {
}
注意其中的DelegatingWebMvcConfiguration
,其原始碼如下:
@Configuration
public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {
我們再來看自動配置類的原始碼:
@Configuration
@ConditionalOnWebApplication(
type = Type.SERVLET
)
@ConditionalOnClass({Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class})
@ConditionalOnMissingBean({WebMvcConfigurationSupport.class})
@AutoConfigureOrder(-2147483638)
@AutoConfigureAfter({DispatcherServletAutoConfiguration.class, TaskExecutionAutoConfiguration.class, ValidationAutoConfiguration.class})
public class WebMvcAutoConfiguration {
我們可以看到這一句
@ConditionalOnMissingBean({WebMvcConfigurationSupport.class})
其生效方式剛好就是若容器中沒有WebMvcConfigurationSupport
,由於前面的註解導致該元件匯入了進來,因此自動配置類就不生效了。匯入的WebMvcConfigurationSupport
只是SpringMVC最基本的功能。
使用SpringBoot一般