Web開發探究
SpringBoot的最大特點:自動裝配。
使用SpringBoot的步驟:
1、創建一個SpringBoot應用,選擇模塊、然後等待項目構建完成即可。
2、編寫一些需要的配置文件。
3、專注於編寫業務代碼。其餘東西不需要手動配置。
配置相關的類:
xxxAutoConfiguration
:向容器中自動配置組件。xxxProperties
:自動配置類,封裝配置文件的內容。
靜態資源處理
簡介
SpringBoot中,所有的配置文件都在application.yaml或application.properties文件中,並沒有web.xml文件。我們需要想辦法實現*.css , *.js等靜態資源文件生效,我們就需要明白其中的規則。
學習方法:
1、從源碼分析,然後得出結論。
2、嘗試去測試,驗證結論是否正確。
看源碼之前,拋出一個問題:SpringBoot默認沒有webapp目錄,那我們的資源文件應該放在哪裏呢?
嘗試分析源碼並進行探究
在SSM架構中,整個SSM都是基於SpringMVC的。因此我們第一步應該去研究SpringBoot中關於MVC的自動配置。
研究之後可以得到以下結論:
1、所有MVC相關的配置都在WebMvcAutoConfiguration類中,比如視圖解析器、靜態資源過濾等。
2、addResourceHandlers靜態資源處理方法分析:
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
//禁用默認規則配置,若手動添加了資源映射路徑的配置,這些配置將直接失效
if (!this.resourceProperties.isAddMappings()) {
logger.debug("Default resource handling disabled");
return;
}
//緩存控制
Duration cachePeriod = this.resourceProperties.getCache().getPeriod();
CacheControl cacheControl = this.resourceProperties.getCache().getCachecontrol().toHttpCacheControl();
//分析源代碼,需要掌握看對象的方法調用
// localhost:8080/webjars/jquery.js
// 判斷是否存在一個映射路徑 /webjars/**
// addResourceHandler 處理邏輯 /webjars/a.js
// addResourceLocations 處理資源的地址 classpath:/META- INF/resources/webjars/a.js
if (!registry.hasMappingForPattern("/webjars/**")) {
customizeResourceHandlerRegistration(registry.addResourceHandler("/webjars/**")
.addResourceLocations("classpath:/META-INF/resources/webjars/")
.setCachePeriod(getSeconds(cachePeriod)).setCacheControl(cacheControl));
}
//獲取靜態資源路徑
String staticPathPattern = this.mvcProperties.getStaticPathPattern();
// localhost:8080/
// 如果訪問映射的路徑是 staticPathPattern = "/**"
// this.resourceProperties.getStaticLocations())
if (!registry.hasMappingForPattern(staticPathPattern)) {
customizeResourceHandlerRegistration(registry.addResourceHandler(staticPathPattern)
.addResourceLocations(getResourceLocations(this.resourceProperties.getStaticLocations()))
.setCachePeriod(getSeconds(cachePeriod)).setCacheControl(cacheControl));
}
}
什麼是webjars
webjars,簡單來說就是使用maven的方式導入前端靜態資源,比如jQuery的依賴如下:
<dependency>
<groupId>org.webjars</groupId>
<artifactId>jquery</artifactId>
<version>3.4.1</version>
</dependency>
導入依賴後可以發現項目中導入瞭如下圖所示jar包。
測試訪問一下,訪問地址:http://localhost:8080/webjars/jquery/3.4.1/jquery.js,發現可以成功訪問到jQuery源文件。
第二種靜態資源處理規則
SpringBoot默認的靜態資源處理映射目錄如下代碼所示:
private static final String[] CLASSPATH_RESOURCE_LOCATIONS =
{
"classpath:/META-INF/resources/", //在starter中使用,如SWAGGER-UI
"classpath:/resources/",//文件資源
"classpath:/static/", //靜態資源
"classpath:/public/"//公共的文件,比如圖標...
};
classpath映射的是resources目錄,這些靜態資源處理映射目錄的優先級由高到低跟其索引順序保持一致。
自定義配置靜態資源映射路徑
我們可以配置自己的靜態資源映射目錄,只不過這樣配置之後,一切原來的配置都會失效。
spring.resources.static-locations=classpath:/wunian
首頁和圖標處理
分析源碼
歡迎頁(首頁)處理器映射器方法位於WebMvcAutoConfiguration類中,其源代碼如下:
@Bean //歡迎頁(首頁)會被映射到這個處理器下
public WelcomePageHandlerMapping welcomePageHandlerMapping(ApplicationContext applicationContext,
FormattingConversionService mvcConversionService, ResourceUrlProvider mvcResourceUrlProvider) {
WelcomePageHandlerMapping welcomePageHandlerMapping = new WelcomePageHandlerMapping(
new TemplateAvailabilityProviders(applicationContext), applicationContext, getWelcomePage(),
this.mvcProperties.getStaticPathPattern());
welcomePageHandlerMapping.setInterceptors(getInterceptors(mvcConversionService, mvcResourceUrlProvider));
return welcomePageHandlerMapping;
}
//獲得歡迎頁
private Optional<Resource> getWelcomePage() {
String[] locations = getResourceLocations(this.resourceProperties.getStaticLocations());
// stream 流
// map 映射
// filter 過濾
// findFirst 找第一個符合條件的
// 調用對象方法的語法糖! this 對象 :: getIndexHtml 方法名字
// this::getIndexHtml
return Arrays.stream(locations).map(this::getIndexHtml).filter(this::isReadable).findFirst();
}
private Resource getIndexHtml(String location) {
return this.resourceLoader.getResource(location + "index.html");
}
只要在上面的靜態資源映射目錄下存在的index.html就會被自動映射爲首頁(歡迎頁)。
網站圖標
在StaticResourceLocation類中定義了網站圖標的映射規則。
/**
* The {@code "favicon.ico"} resource.
*/
FAVICON("/**/favicon.ico");
由源碼可知,要設置自定義的網站圖標,只需要將一個favicon.ico放入靜態資源目錄即可。一般把它放在public目錄下。
Thymeleaf模板引擎
什麼是模板引擎?
模板引擎是爲了使用戶界面與業務數據(內容)分離而產生的,它可以生成特定格式的文檔,用於網站的模板引擎就會生成一個標準的html文檔。
前端給後端的頁面一般都是一些靜態資源或者html等文件,在傳統開發中,後端開發人員需要把它們轉換爲jsp頁面,通過嵌入Java變量或代碼來實現一些功能,jsp無疑是非常強大的。
但問題在於:在SpringBoot項目中無法編寫jsp頁面,因爲SpringBoot項目是以jar包方式運行而不是war包方式運行。運行jar包命令:java -jar xxx.jar
這就涉及到頁面處理和前後端交互的問題,這時就需要使用模板引擎了。
現今較爲知名的模板引擎有:jsp、freemarker、Thymeleaf等。
SpringBoot推薦我們使用Thymeleaf模板引擎,該引擎使用的就是html頁面。
我們只需要記住一句話:所有模板引擎原理都是一樣的,唯一區別就是每個模板的語法有些不一樣而已。
Thymeleaf使用
官網:https://www.thymeleaf.org/documentation.html
github:https://github.com/thymeleaf/thymeleaf/blob/3.0-master
Thymeleaf的依賴如下:
<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf</artifactId>
<version>3.0.12-SNAPSHOT</version>
</dependency>
1、SpringBoot提供了Thymeleaf的啓動器,導入以下依賴即可:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
2、配置Thymeleaf,參考配置類ThymeleafProperties。
@ConfigurationProperties(prefix = "spring.thymeleaf")
public class ThymeleafProperties {
private static final Charset DEFAULT_ENCODING = StandardCharsets.UTF_8;
public static final String DEFAULT_PREFIX = "classpath:/templates/";
public static final String DEFAULT_SUFFIX = ".html";
}
3、使用Thymeleaf並測試。
Controller代碼如下:
@Controller // 可以被視圖解析器解析
public class HelloController {
@GetMapping("/test")
public String test(Model model){
//classpath:/templates/test.html
model.addAttribute("msg","Hello Thymeleaf");
model.addAttribute("msg2","<h2>Hello Thymeleaf<h2>");
model.addAttribute("users", Arrays.asList("coding","qinjiang"));
return "test";
}
}
html代碼如下:
<!DOCTYPE html>
<!--
xmlns xml namespace 命名空間,加上了纔可以支持thymeleaf
-->
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<!--
模板引擎:後端可以進行跳轉,給出相應數據時前端可以接收一些數據
-->
<body>
<h1>測試頁面</h1>
<p th:text="${msg}"></p>
<!--轉義和不轉義-->
<p th:text="${msg2}"></p>
<p th:utext="${msg2}"></p>
<!--遍歷-->
<h3 th:each="user:${users}" th:text="${user}"></h3>
</body>
</html>
Thymeleaf語法
查閱官方文檔,Thymeleaf常用標籤如下圖所示。
除此之外,還有很多表達式也可以使用。
- Variable Expressions:
${...}
獲取一些基本的變量值。使用場景:
1、獲取對象的屬性、調用方法。
2、使用內置的基本對象,如下代碼所示:
${#ctx.locale}
${param.foo}
${session.foo}
${application.foo}
${#request.getAttribute('foo')}
${#servletContext.contextPath}
3、工具對象,如下代碼所示:
${#messages.msg('msgKey')}
${#uris.escapePath(uri)}
${#conversions.convert(object, 'java.util.TimeZone')}
${#dates.format(date, 'dd/MMM/yyyy HH:mm')}
${#calendars.format(cal)}
${#numbers.formatInteger(num,3)}
${#strings.toString(obj)}
${#arrays.toArray(object)}
......
- Selection Variable Expressions:
*{...}
選擇表達式,和 ${} 是一樣的; - Message Expressions:
#{...}
國際化內容獲取! - Link URL Expressions:
@{...}
URL表達式; th:href=“@{/login}” - 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:
MVC自動配置原理
閱讀官方文檔
在進行項目開發的學習之前,我們必須知道的最後一個東西就是MVC自動配置原理,我們的學習方式依然是源碼+官方文檔。
官方文檔地址:https://docs.spring.io/spring-boot/docs/2.2.5.RELEASE/reference/htmlsingle/#boot-features-spring-mvc-auto-configuration
下面是一段關於Spring MVC自動配置的描述,詳細介紹了Spring MVC自動配置的功能:
// Spring MVC 自動配置
Spring MVC Auto-configuration
// SpringBoot爲SpringMVC 提供提供了自動配置,他可以很多好的工作於大多數的應用!
Spring Boot provides auto-configuration for Spring MVC that works well with most applications.
// 自動配置在Spring默認配置的基礎上添加了以下功能:
The auto-configuration adds the following features on top of Spring’s defaults:
// 包含視圖解析器
Inclusion of ContentNegotiatingViewResolver and BeanNameViewResolver beans.
// 支持靜態資源文件的路徑嗎,包含webjar的支持
Support for serving static resources, including support for WebJars (covered later in this document)).
// 自動註冊了轉換器
// 轉換器 網頁提交的前端對象,到後臺自動封裝爲具體的對象;"1" 自動轉換爲 數字 1;
// 格式化器Formatter 【2020-03-18 後臺可以自動封裝爲Date】
Automatic registration of Converter, GenericConverter, and Formatter beans.
// 支持消息轉換
// request、response,對象自動轉換爲 json對象 S
upport for HttpMessageConverters (covered later in this document).
// 定錯代碼生成規則
Automatic registration of MessageCodesResolver (covered later in this document).
// 支持首頁定製
Static index.html support.
// 支持自定義圖標
Custom Favicon support (covered later in this document).
//配置web數據綁定
Automatic use of a ConfigurableWebBindingInitializer bean (covered later in this document).
內容協商視圖解析器ContentNegotiatingViewResolver
這是SpringBoot自動配置的視圖解析器,所有的視圖解析器都要經過它來進行協商,最終返回一個最好的視圖。其源碼如下所示:
@Bean
@ConditionalOnBean(ViewResolver.class)
@ConditionalOnMissingBean(name = "viewResolver", value = ContentNegotiatingViewResolver.class)
public ContentNegotiatingViewResolver viewResolver(BeanFactory beanFactory) {
ContentNegotiatingViewResolver resolver = new ContentNegotiatingViewResolver();
resolver.setContentNegotiationManager(beanFactory.getBean(ContentNegotiationManager.class));
// ContentNegotiatingViewResolver uses all the other view resolvers to locate
// a view so it should have a high precedence
// ContentNegotiatingViewResolver
//使用其他所有的視圖解析器定位視圖,因此它應該具有一個高的優先級!
resolver.setOrder(Ordered.HIGHEST_PRECEDENCE);
return resolver;
}
//ContentNegotiatingViewResolver 解析視圖名字
@Override
@Nullable // 參數可以爲空
public View resolveViewName(String viewName, Locale locale) throws Exception {
RequestAttributes attrs = RequestContextHolder.getRequestAttributes();
Assert.state(attrs instanceof ServletRequestAttributes, "No current ServletRequestAttributes");
List<MediaType> requestedMediaTypes = getMediaTypes(((ServletRequestAttributes) attrs).getRequest());
if (requestedMediaTypes != null) {
//獲取所有候選的視圖
List<View> candidateViews = getCandidateViews(viewName, locale, requestedMediaTypes);
//獲取最好的視圖
View bestView = getBestView(candidateViews, requestedMediaTypes, attrs);
//返回最好的視圖
if (bestView != null) {
return bestView;
}
}
String mediaTypeInfo = logger.isDebugEnabled() && requestedMediaTypes != null ?
" given " + requestedMediaTypes.toString() : "";
if (this.useNotAcceptableStatusCode) {
if (logger.isDebugEnabled()) {
logger.debug("Using 406 NOT_ACCEPTABLE" + mediaTypeInfo);
}
return NOT_ACCEPTABLE_VIEW;
}
else {
logger.debug("View remains unresolved" + mediaTypeInfo);
return null;
}
}
分析源碼可知,它是從容器中加載所有的視圖解析器,那麼我們可以猜想,我們自定義一個視圖解析器,也可以被掃描並加載。
編寫自定義視圖解析器。
//自己寫一個視圖解析器
@Bean
public ViewResolver myViewResolver(){
return new MyViewResolver();
}
private static class MyViewResolver implements ViewResolver{
@Override
public View resolveViewName(String s, Locale locale) throws Exception {
return null;
}
}
在DispatcherServlet類下的doDispatch方法的第一行代碼打上斷點,啓動項目以debug模式運行,當程序運行到斷點處,可以看DispatcherServlet對象的viewResolvers數組中已經包含了我們的自定義視圖解析器。
由此我們可以得出一個結論:在SpringBoot中,如果我們想使用自己定製化的組件,只需要往容器中添加這個組件即可。因爲剩下的事情SpringBoot已經自動幫我們處理了。
格式化轉換器Formatter
Formatter用於對日期類型進行格式化操作,在SpringBoot中是作爲服務註冊的。註冊Formatter的源碼如下:
@Bean
@Override // 服務
public FormattingConversionService mvcConversionService() {
// 默認的時間 Formatting 的格式:
WebConversionService conversionService = new WebConversionService(this.mvcProperties.getDateFormat());
addFormatters(conversionService);
return conversionService;
}
// 源碼中默認的格式是通過 /來分割的
//Date format to use. For instance, `dd/MM/yyyy`.
private String dateFormat;
// 只要在 mvcProperties 中的,我們都可以進行手動的配置!
修改SpringBoot默認配置
官方文檔中有如下一段描述:
// 如果你希望保持 Spring Boot MVC 一些功能,並且希望添加一些其他的
//MVC配置(攔截器、格式化 器、視圖控制器、或其他的配置),你可以
//添加自己的配置類 (類型爲WebMvcConfigurer) 需要添加註解
//@Configuration ,一定不能擁有註解@EnableWebMvc.
If you want to keep those Spring Boot MVC customizations and make more MVC
customizations (interceptors, formatters, view controllers, and other features),
you can add your own @Configuration class of type WebMvcConfigurer but without @EnableWebMvc.
If you want to provide custom instances of RequestMappingHandlerMapping, RequestMappingHandlerAdapter,
or ExceptionHandlerExceptionResolver, and still keep the Spring Boot MVC customizations, you can declare
a bean of type WebMvcRegistrations and use it to provide custom instances of those components.
//全面接管Spring MVC,自己配置配置類的時候加上 @EnableWebMvc即可!
If you want to take complete control of Spring MVC, you can add your own @Configuration annotated with
@EnableWebMvc, or alternatively add your own @Configuration-annotated DelegatingWebMvcConfiguration as described
in the Javadoc of @EnableWebMvc.
擴展MVC方法步驟
1、編寫一個自己的config配置類。
2、實現WebMvcConfigurer接口。
3、重寫該接口下的方法即可。
@Configuration
//@EnableWebMvc 不要加這個註解,會使WebMvcAutoConfiguration配置失效
public class MyMvcConfig implements WebMvcConfigurer {
//自己編寫一個視圖解析路由
@Override
public void addViewControllers(ViewControllerRegistry registry) {
//視圖跳轉的控制
registry.addViewController("/test").setViewName("test");
}
}
注意:@EnableWebMvc這個註解不能加,會使WebMvcAutoConfiguration配置失效。因爲在WebMvcAutoConfiguration類中有如下註解:
// 如果這個bean不存在,這個類才生效!~
@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
// @EnableWebMvc 源碼
@Import(DelegatingWebMvcConfiguration.class) public @interface EnableWebMvc
// 點進DelegatingWebMvcConfiguration繼承了WebMvcConfigurationSupport
public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport
實際上@EnableWebMvc註解就是導入了WebMvcConfigurationSupport類但是源碼中有@ConditionalOnMissingBean這個註解判斷是否導入了這個類,一旦導入了這個類,我們的自動配置類就會全部失效。
建議
如果你要擴展MVC配置,但是又需要保留原來的配置,可以直接實現WebMvcConfigurer接口,重寫方法進行擴展即可,但是不要添加@EnableWebMvc註解。
@Configuration
public class MyMvcConfig implements WebMvcConfigurer { }
全面接管Spring MVC
只需要增加一個@EnableWebMvc註解即可,此時WebMvcAutoConfiguration只提供基本的MVC功能,其餘功能需要自己開發編寫。在SpringBoot中有非常多的這種擴展配置,我們只要看見了這種配置,就應該多留心注意。
配置項目環境及首頁
準備工作
1、將我們學習MyBatis的文件代碼拷貝過來。
2、導入maven依賴。
3、導入實體類。
4、導入mapper及mapper.xml文件。
5、配置資源問題、druid配置。
6、導入前端素材。
首頁實現
1、添加視圖控制。
@Override
public void addViewControllers(ViewControllerRegistry registry) {
//視圖跳轉的控制
registry.addViewController("/index").setViewName("login");
registry.addViewController("/").setViewName("login");
registry.addViewController("/index.html").setViewName("login");
}
2、添加控制器。
@Controller
public class IndexController {
@GetMapping({"/","/index.html","index"})
public String index(){
return "login";
}
}
修改前端頁面鏈接
1、配置頁面支持 Thymeleaf。
2、正式開發項目之前,需要將所有頁面的中鏈接的路徑改爲Thymeleaf的路徑配置。所有頁面的鏈接改爲@{}
進行支持適配。
<link th:href="@{/asserts/css/bootstrap.min.css}" rel="stylesheet">