web開發--靜態規則與定制化
springboot對靜態資源的映射規則:在類路徑下面定義目錄static或public或resources或者META-INF/resources,訪問時項目根目錄+靜態資源的名稱
在springboot中,如果項目中存在同名的靜態資源和同名的動態資源。那么我們會優先去訪問動態資源,如果動態資源不存在,然后再去訪問對應的靜態資源,如果靜態資源也找不到,那么就報404 的異常?,為了解決這個問題。
- 可以在配置文件中自定義靜態資源的映射規則,例如:spring.mvc.static-path-pattern=/resources/**,那么訪問靜態資源時根目錄+resources+靜態資源名稱。
- 也可以給靜態資源設置自定義的存放目錄:例如spring:web:resourcesstatic-locations:[classpath:/hello/],那么在資源下創建hello這個文件夾,下面放靜態資源
- 我們也可以訪問webjars的資源(webjars 就是將靜態資源打成jar包。)
1、引入相關靜態資源的jar包(依賴)
<dependency>
<groupId>org.webjars</groupId>
<artifactId>jquery</artifactId)
<version>3.5.1</version>
</dependency>
2、通過官方給定的訪問路徑去訪問里面的資源
http://localhost:8082/webjars/jquery/3.5.1/jquery.js
靜態資源和首頁映射規則底層原理
源碼
webMvcAutoConfiguration底層是如何進行裝配的。在org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration中的注解
?在這個類中,主要看WebMvcAutoConfigurationAdapter,是WebMvc自動配置的適配器,
看下面的這個方法?WebMvcAutoConfigurationAdapter,它是一個構造方法,參數從哪里來?
public WebMvcAutoConfigurationAdapter(WebProperties webProperties, WebMvcProperties mvcProperties, ListableBeanFactory beanFactory, ObjectProvider<HttpMessageConverters> messageConvertersProvider, ObjectProvider<ResourceHandlerRegistrationCustomizer> resourceHandlerRegistrationCustomizerProvider, ObjectProvider<DispatcherServletPath> dispatcherServletPath, ObjectProvider<ServletRegistrationBean<?>> servletRegistrations) {this.resourceProperties = webProperties.getResources();this.mvcProperties = mvcProperties;this.beanFactory = beanFactory;this.messageConvertersProvider = messageConvertersProvider;this.resourceHandlerRegistrationCustomizer = (ResourceHandlerRegistrationCustomizer)resourceHandlerRegistrationCustomizerProvider.getIfAvailable();this.dispatcherServletPath = dispatcherServletPath;this.servletRegistrations = servletRegistrations;}
- WebProperties 和 WebMvcProperties:被SpringBoot自動創建并填充到配置文件中,通過@EnableConfigurationProperties注解進行引入,上面圖片可以看到
-
ListableBeanFactory: 代表了一個可以列出所有已注冊bean定義的bean工廠。它是Spring IoC容器的一部分,不需要特別指定,Spring會自動將其傳遞給需要它的bean。
-
ObjectProvider<HttpMessageConverters> 和 ObjectProvider<ResourceHandlerRegistrationCustomizer>: 這兩個
ObjectProvider
是用來延遲加載特定類型的bean的。如果Spring上下文中存在類型為HttpMessageConverters
或ResourceHandlerRegistrationCustomizer
的bean,那么它們就會被注入到這里。如果沒有找到匹配的bean,也不會導致錯誤,因為ObjectProvider
支持可選的依賴項 -
ObjectProvider<DispatcherServletPath> 和 ObjectProvider<ServletRegistrationBean<?>>: 類似地,這兩個也是
ObjectProvider
實例,用于提供對DispatcherServlet
路徑和ServletRegistrationBean
的訪問。如果有相應的bean存在于上下文中,它們將會被注入。
那么適配器初始化這些信息后,那么靜態資源是如何生效的?
靜態資源是如何生效的
在這個類(
public static class WebMvcAutoConfigurationAdapter implements WebMvcConfigurer, ServletContextAware
)下,有一個addResourceHandlers方法,下面進行圖解。
也可以點進去,可以得到
?歡迎頁
在WelcomePageHandlerMapping類下的WelcomePageHandlerMapping方法中
WelcomePageHandlerMapping(TemplateAvailabilityProviders templateAvailabilityProviders, ApplicationContext applicationContext, Resource indexHtmlResource, String staticPathPattern) {this.setOrder(2); // 優先級為 2。// 確定是否有可用的歡迎頁面WelcomePage welcomePage = WelcomePage.resolve(templateAvailabilityProviders, applicationContext, indexHtmlResource, staticPathPattern);if (welcomePage != WelcomePage.UNRESOLVED) { // 如果找到了歡迎頁面// 根據歡迎頁面是否為模板,記錄日志logger.info(LogMessage.of(() -> {return !welcomePage.isTemplated() ? "Adding welcome page: " + String.valueOf(indexHtmlResource) : "Adding welcome page template: index";}));// 創建一個新的實例,可以指定視圖名稱ParameterizableViewController controller = new ParameterizableViewController();// 設置控制器的視圖名稱為歡迎頁面的視圖名稱controller.setViewName(welcomePage.getViewName());// 將創建的控制器設置為此 HandlerMapping 的根處理器this.setRootHandler(controller);}
}
springboot中rest請求處理原理
@RestController
public class HelloController {@RequestMapping(value = "/hello",method = RequestMethod.GET)public String sayHello(){return "Hello World!";}
}
在非 REST 風格的傳統代碼中,為了執行數據的增刪改查操作,通常需要將每個操作映射到不同的路徑上。然而,采用 REST 風格后,可以使用相同的路徑來表示同一個資源,并通過不同的 HTTP 方法(如 POST
用于創建,GET
用于查詢,PUT
或 PATCH
用于更新,以及 DELETE
用于刪除)來區分這些操作。這樣,我們就可以通過單一的端點路徑結合適當的請求方法來管理資源,從而提高 API 的清晰度和可維護性。例如,在 Spring MVC 中,你可以通過 @RequestMapping
注解的 method
屬性指定支持的 HTTP 方法類型,或者直接使用簡化的組合注解如 @GetMapping
, @PostMapping
, @PutMapping
, 和 @DeleteMapping
來實現這一點。同時,@RequestMapping
注解中的 path
和 value
屬性互為別名,都可以用來指定請求路徑。這樣的設計有助于構建更加簡潔、一致的服務接口。
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;@RestController
public class HelloController {@RequestMapping(value = "user",method = RequestMethod.GET)public String get(){return "Hello User Get!";}@RequestMapping(value = "user",method = RequestMethod.POST)public String post(){return "Hello User Post!";}@RequestMapping(value = "user",method = RequestMethod.PUT)public String put(){return "Hello User Put!";}@RequestMapping(value = "user",method = RequestMethod.DELETE)public String delete(){return "Hello User Delete!";}
}// demo1.html
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title>
</head>
<body><form action="/user" method="get"><input value="GET提交" type="submit"></form><form action="/user" method="post"><input value="Post提交" type="submit"></form><form action="/user" method="post"><input value="Put提交" type="submit"></form><form action="/user" method="post"><input value="Delete提交" type="submit"></form>
</body>
</html>
由于表單中只能由get和post,沒有put和delete,因此它們用post進行代替。設想點擊不同的按鈕進入不同的表單頁,顯示不同的值,是這樣嗎?來看下結果,結果顯示:對于get和post提交是正常的,但是對于put和delete提交顯示的是Hello User Post!這是由于表單中只能由get和post,為了弄清楚它,回到D:\java\mvn_repository\org\springframework\boot\spring-boot-autoconfigure\3.4.3\spring-boot-autoconfigure-3.4.3.jar!\org\springframework\boot\autoconfigure\web\servlet\這個路徑下的WebMvcAutoConfiguration.class類中。過濾器默認不開啟,開啟需要添加配置文件
在HiddenHttpMethodFilter中,有一個doFilterInternal方法,
進行debug,先發送get請求,進不到if,執行filterChain.doFilter((ServletRequest)requestToUse, response);進行放行
?下面看下post請求,其中this.methodParam是一個_method參數,需要在html文件中進行配置
下面看下加上_method的put請求,delete同理
?注意:下面兩者等價?
@RequestMapping(value = "user",method = RequestMethod.GET) @GetMapping("user")
springboot處理器映射器工作原理
????????在SpringMVC中有一個組件DispatcherServlet,在DispatcherServlet.class這個類下,它是用于處理前端用戶的請求。體系結構如下:
進入FrameworkServlet中,有doGet,doPost,doPut和doDelete,它們四個處理Http請求,這四個方法都調用了processRequest方法,在processRequest方法中,首先進行一些初始化,然后在doService方法中提供服務,doService方法中又有doDispatch方法,這個方法中關注這句話:mappedHandler = this.getHandler(processedRequest);
?
如果是不大于1的話:?
總結:用戶的請求交給DispatcherServlet前端控制器中的doDispatch方法進行處理,其中被doDispath方法中的getHandler獲取想要的handler對象,這個handler對象被包裝到HandlerExecutionChain里面。那么handler如何被獲取?mappedHandler=this.getHandler(processedRequest)方法幫助處理,this.getHandler內部,有5個映射器,RequestMappingHandlerMapping處理被@RequestMapping注解修飾的處理器方法,返回一個handler。這個handler最終交給處理器適配器進行處理。