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最终交给处理器适配器进行处理。