springboot知识点以及源码解析2
springboot知识点以及源码解析(2)
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包(依赖)
org.webjars jquery</artifactId) 3.5.1 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
和 ObjectProvider - 这两个
ObjectProvider
是用来延迟加载特定类型的bean的。如果Spring上下文中存在类型为HttpMessageConverters
或ResourceHandlerRegistrationCustomizer
的bean,那么它们就会被注入到这里。如果没有找到匹配的bean,也不会导致错误,因为ObjectProvider
支持可选的依赖项
- ObjectProvider
- ObjectProvider
和 ObjectProvider<ServletRegistrationBean> - 类似地,这两个也是
ObjectProvider
实例,用于提供对DispatcherServlet
路径和ServletRegistrationBean
的访问。如果有相应的bean存在于上下文中,它们将会被注入。
- ObjectProvider
那么适配器初始化这些信息后,那么静态资源是如何生效的?
静态资源是如何生效的
在这个类(
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最终交给处理器适配器进行处理。