仍是以前的三个套路前端
Spring
提供一套视图层的处理框架,他基于Servlet
实现,能够经过XML
或者注解进行咱们须要的配置。java
他提供了拦截器,文件上传,CORS
等服务。web
原生Servlet
在大型项目中须要进过多重封装,来避免代码冗余,其次因为不一样接口须要的参数不一样,咱们须要本身在Servlet
层 封装咱们须要的参数,这对于开发者来讲是一种重复且枯燥的工做,因而出现了视图层框架,为咱们进行参数封装等功能。让开发者的注意力所有放在逻辑架构中,不须要考虑参数封装等问题。面试
再聊怎么用以前,咱们须要了解一下MVC
的工做原理。spring
他基于一个DispatcherServlet
类实现对各类请求的转发,即前端的全部请求都会来到这个Servlet中,而后这个类进行参数封装和请求转发,执行具体的逻辑。(第二章咱们细聊)shell
DispatcherServlet
来为咱们提供基础的Servlet
服务,咱们能够经过servlet
规范的web.xml
文件,对该类进行初始化。而且声明该类处理全部的请求,而后经过这个类实现请求转发。mvc
信息。下面来看一个完整的web.xml
配置编程
<web-app> <servlet> <servlet-name>dispatchServlet</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:springmvc.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>dispatchServlet</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> </web-app>
注解方式也是如今主流,SpringBoot
基于JavaConfig
实现了自动配置后端
实现方式:架构
在Servlet3.0
的时候定义了一个规范SPI
规范。mvc
SPI
,全称为 Service Provider Interface
,是一种服务发现机制。它经过在ClassPath
路径下的META-INF/services
文件夹查找文件,自动加载文件里所定义的类。也就是在服务启动的时候会Servlet会自动加载该文件定义的类
咱们看一眼这个文件里的内容。他内部定义了SpringServletContainerInitializer
容器初始化类,也就是说在Servlet
启动的时候会自动初始化这个类,这个类也是注解实现的关键。
这个类中存在一个onStartup
方法,这个也是当容器初始化的时候调用的方法,这个方法有两参数
Set<Class<?>> webAppInitializerClasses
他表明了当前咱们的Spring
容器中存在的web
初始化类。咱们本身能够经过实现WebApplicationInitializer
类来自定义Servlet
初始化的时候执行的方法。ServletContext servletContex
表明了Servlet
上下文对象org.springframework.web.SpringServletContainerInitializer
@HandlesTypes(WebApplicationInitializer.class) public class SpringServletContainerInitializer implements ServletContainerInitializer { @Override public void onStartup(Set<Class<?>> webAppInitializerClasses, ServletContext servletContext) throws ServletException { //启动逻辑 } }
具体看一下注解配置方式:
public class MyWebApplicationInitializer implements WebApplicationInitializer { @Override public void onStartup(ServletContext servletCxt) { // Load Spring web application configuration AnnotationConfigWebApplicationContext ac = new AnnotationConfigWebApplicationContext(); //一个配置类,@Configuration ac.register(AppConfig.class); //spring的那个refresh方法 ac.refresh(); // Create and register the DispatcherServlet DispatcherServlet servlet = new DispatcherServlet(ac); ServletRegistration.Dynamic registration = servletCxt.addServlet("app", servlet); registration.setLoadOnStartup(1); registration.addMapping("/app/*"); } }
经过实现WebApplicationInitializer
接口,来做为MVC
的配置类,在加载SpringServletContainerInitializer
的时候加载这个类。
不过在具体的实现中,Spring
不建议咱们这样作,他建议将Spring
和SpringMvc
分开,看个图
他在Spring之上加了一层Web环境配置。至关于在Spring的外面包装了一层Servlet
看一下此时的代码
public class MyWebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer { //Spring配置文件 @Override protected Class<?>[] getRootConfigClasses() { return new Class<?>[] { RootConfig.class }; } //SpringMVC的配置文件 @Override protected Class<?>[] getServletConfigClasses() { return new Class<?>[] { App1Config.class }; } //指定DispatcherServlet能够拦截的路径 @Override protected String[] getServletMappings() { return new String[] { "/app1/*" }; } }
经过AbstractAnnotationConfigDispatcherServletInitializer
能够看到他实现了WebApplicationInitializer
接口,即在Servlet
初始化的时候会加载这个类。
AbstractContextLoaderInitializer
类,他初始化了Spring
AbstractDispatcherServletInitializer
类,初始化了DispatcherServlet
AbstractAnnotationConfigDispatcherServletInitializer
类,将两个类整合到一块儿
聊这个原理以前,先来聊聊他要干什么?
需求:请求分发;参数封装;结果返回
那若是咱们本身来实现,该怎么办?(单说注解,先来看看咱们怎么使用MVC
的)
@Controller
注解,标识当前类为控制层接口,RequestMapping
标识这个方法的URI
和请求方式等信息@ResponseBody
标识这个方法的返回类型为JSON
test01
标识这个方法用来处理/test
请求@Controller public class UserController { @GetMapping("/test") @ResponseBody public String test01(){ return "success" ; } }
接下来,咱们经过咱们已有的东西,看一下咱们本身去处理请求的逻辑
先来想一下咱们的请求过程:
Http
请求,经过不一样的uri
实现不一样逻辑的处理uri
和咱们后端的定义的@RequestMapping
中的value
值相同Map
结构,将value
做为key
,将method
的Class
对象做为一个value
存到一个MappingRegister
中URI
从这个Map
中获取相应的Method
执行,若是没有对应的Method
给一个404
.在上面的怎么用中提到了,他经过AbstractContextLoaderInitializer
来加载Spring
配置文件的。
此时关于Spring的东西已经加载好了,但并未进行初始化
一样也是经过AbstractDispatcherServletInitializer
类实现
接下来咱们具体看一下在这个期间,DispatcherServlet
如何处理请求的
做用:分发全部的请求
类继承结构图
能够看到他继承了HttpServlet
类,属于一个Servlet
,而在以前咱们配置了这个Servlet
的拦截路径。他会将全部的请求拦截,而后作一个分发。
下面这个图各位看官应该很是熟悉:
其实DispatcherServlet
处理全部请求的方式在这个图里彻底都体现了。
接下来聊一下他的设计思路吧。
当一个请求来的时候,进入doDispatch
方法中,而后处理这个请求,也是返回一个执行链
Spring
提供了三种方式的处理器映射器来处理不一样的请求。
BeanNameUrlHandlerMapping
处理单独Bean
的请求。适用于实现Controller
和HttpRequestHandler
接口的类@Component("/test02") public class HttpController implements Controller { @Override public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception { System.out.println("HttpController执行"); return null; } }
@Component("/test01") public class HandlerController implements HttpRequestHandler { @Override public void handleRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { System.out.println("handlerRequest"); } }
RequestMappingHandlerMapping
适用于方法类型的处理器映射。@Controller public class UserController { @GetMapping("/test") public String test01(){ System.out.println("执行了"); return "success" ; } }
RouterFunctionMapping
,MVC
提供的一个处理经过函数式编程定义控制器的一个映射器处理器。须要直接添加到容器中,而后 经过路由一个地址,返回对应的数据@Configuration @ComponentScan("com.bywlstudio.controller") @EnableWebMvc public class MvcConfig implements WebMvcConfigurer { @Override public void configureViewResolvers(ViewResolverRegistry registry) { registry.jsp("/WEB-INF/pages/",".jsp"); } @Bean public RouterFunction<?> routerFunctionA() { return RouterFunctions.route() .GET("/person/{id}", request1 -> ServerResponse.ok().body("Hello World")) .build(); } }
聊完了处理器映射器,再来聊一下处理器适配器
不一样的请求方式,须要不一样的处理方式,这也是Spring
为何要提供一个适配器的缘由。
RequestMappingHandlerAdapter
用来处理全部的方法请求,即经过@Controller
注解定义的HandlerFunctionAdapter
用来处理函数式的映射,即经过RouterFunctionMapping
定义的HttpRequestHandlerAdapter
用来处理实现了HttpRequestHandler
接口的SimpleControllerHandlerAdapter
用来处理实现了Controller
接口的请求经过处理器适配器拿到适合的处理器,来处理对应的请求。
在处理器执行具体的请求的过程,实际上就是调用咱们的方法的过程,因而就会出现返回值
一般对于返回值咱们有两种方法:
@ResponseBody
直接返回JSON
数据。对于返回值解析,MVC
提供了一个接口用于处理全部的返回值,这里咱们仅仅谈上面的两种
ModelAndViewMethodReturnValueHandler
用于处理返回视图模型的请求RequestResponseBodyMethodProcessor
用于处理返回JSON
在咱们拿到方法返回值之后,会调用this.returnValueHandlers.handleReturnValue
返回值解析器的这个方法,用于对视图模型的返回和JSON
数据的回显(直接回显到网页,此时返回的视图对象为null)
对于视图对象,经过视图解析器直接解析,进行数据模型渲染,而后回显给前端。
这个类存放了method
的映射信息。
class MappingRegistry { private final Map<T, MappingRegistration<T>> registry = new HashMap<>(); private final Map<T, HandlerMethod> mappingLookup = new LinkedHashMap<>(); private final MultiValueMap<String, T> urlLookup = new LinkedMultiValueMap<>(); private final Map<String, List<HandlerMethod>> nameLookup = new ConcurrentHashMap<>(); private final Map<HandlerMethod, CorsConfiguration> corsLookup = new ConcurrentHashMap<>(); private final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
MVC
会从这个类中获取方法和URL
的引用。至关于Spring MVC
的容器。
答:MVC
是一个架构模式,它有三个核心
MVVM
也是一种架构模式,它也是三个核心
Model
)。后端数据ViewModel
)。它完成了数据和视图的绑定View
)。用户界面它的核心思想是:经过ViewModel
将数据和视图绑定,用数据操做视图,常见框架为Vue
DispatcherServlet
DispatcherServelt
收到请求之后调用HandlerMapping
,找到请求处理器映射器(三选一)URI
的处理器执行链(包含了拦截器,和处理器对象)处理器具体执行,返回ModelAndView
对象
@ResponseBody
注解,直接进行数据回显ModelAndView
对象传给ViewResove
视图解析器解析,返回视图DispatcherServlet
对View
进行渲染视图更多原创文章请关注笔者公众号@MakerStack,转载请联系做者受权