随着 Spring Boot 逐步全面覆盖到我们的项目之中,我们已经基本忘却当年经典的 Servlet + Spring MVC 的组合,那让人熟悉的 web.xml 配置。而本文,我们想先抛开 Spring Boot 到一旁,回到从前,一起来看看 Servlet 是怎么和 Spring MVC 集成,怎么来初始化 Spring 容器的。
MVC:Model + View + Controller(数据模型 + 视图 + 控制器)
三层架构:Presentation tier + Application tier + Data tier(展示层 + 应用层 + 数据访问层)
MVC和三层架构的关系,MVC只存在三层架构的展示层。
M实际是数据模型,是包含数据的对象。在Spring MVC里,有一个专门的类叫Model,用来和V之间的数据交互、传值。
V指的是视图界面,包含JSP、freeMarker、Velocity、Thymeleaf、Tile等。
C就是控制器(Spring MVC的注解@Controller的类)。
三层架构是整个应用的的架构,是由Spring框架负责管理的,一般项目结构中都由Service层、Dao层,这两个反馈在应用层和数据访问层。
Spring MVC框架围绕DispatcherServlet这个核心展开,它负责截获请求并将其分派给相应的处理器处理。Spring MVC框架包括注解驱动控制器、请求及响应的信息处理、视图解析、本地化解析、上传文件解析、异常处理以及表单标签绑定等内容。
Spring MVC是基于Model 2实现的技术框架。Spring MVC通过一个DispatcherServlet接收所有请求,并将具体工作委托给其他组件进行处理。
可以在web.xml中配置一个Servlet,并通过指定其处理的URL。
,
分割)。/*
全部的HTTP请求。一个web.xml可以配置多个DispatcherServlet,通过其对应的配置,让每个DispatcherServlet处理不同的请求。可以通过的属性指定配置参数:
Spring 4.0已经全面支持Servlet 3.0,可以使用编程的方式配置Servlet容器。在Servlet 3.0环境中,容器会在类路径中查找实现javax.servlet.ServletContainerInitializer接口的类,如果发现实现类,就会用它来配置Servlet容器。Spring提供了这个接口的实现,名为SpringServletContainerInitializer,这个类反过来又查找实现WebApplicationInitializer的类并将配置的任务交给它们来完成。Spring还提供了一个WebApplicationInitializer基础实现类AbstractAnnotationConfigDispatcherServletInitializer,使得它在注册DispatcherServlet时只需要简单地指定它的Servlet映射即可。
public class WebApplicationInitilalizer implements WebApplicationInitializer {@Overridepublic void onStartup(ServletContext servletContext) throws ServletException {ServletRegistration.Dynamic registration = servletContext.addServlet("web", new DispatcherServlet());registration.setLoadOnStartup(1);registration.addMapping("/");}
}
protected void initStrategies(ApplicationContext context) {initMultipartResolver(context);initLocaleResolver(context);initThemeResolver(context);initHandlerMappings(context);initHandlerAdapters(context);initHandlerExceptionResolvers(context);initRequestToViewNameTranslator(context);initViewResolvers(context);initFlashMapManager(context);}
DispatcherServlet#initStrategies()方法将在WebApplicationContext初始化后执行,此时Spring上下文中的Bean已经初始化完毕,该方法通过反射查找并装配Spring容器中用户自定义的Bean,如果找不到就装配默认的组件实例。
在DispatcherServlet.properties配置文件里边,指定了DispatcherServlet所使用的默认组件。如果用户希望采用非默认的组件,只需在Spring配置文件中配置自定义的组件Bean即可。
# 本地化解析器
org.springframework.web.servlet.LocaleResolver=org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver# 主题解析器
org.springframework.web.servlet.ThemeResolver=org.springframework.web.servlet.theme.FixedThemeResolver# 处理器解析器
org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping# 处理器适配器
org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter# 异常处理器
org.springframework.web.servlet.HandlerExceptionResolver=org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerExceptionResolver,org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver,org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver# 视图名称处理器
org.springframework.web.servlet.RequestToViewNameTranslator=org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator# 视图解析器
org.springframework.web.servlet.ViewResolver=org.springframework.web.servlet.view.InternalResourceViewResolverorg.springframework.web.servlet.FlashMapManager=org.springframework.web.servlet.support.SessionFlashMapManager
DispatcherServlet装配各型组件的逻辑
org.springframework.web.context.request
定义了若干个可代理Servlet原生API类的接口,如WebRequest和NativeWebRequest。HttpMessageConverter接口可以将请求信息转换为一个对象(类型为T),并将对象(类型为T)绑定到请求方法的参数中或输出为响应信息。DispatcherServlet默认已经安装了RequestMethodHandlerAdapter作为HandlerAdapter组件的实现类,HttpMessageConverter即由RequestMethodHandlerAdapter使用,将请求信息转换为对象,或将对象转换为响应信息。
Spring为HttpMessageConverter提供了众多的实现类:
实现类
实现类
RequestMappingHandlerAdapter已经默认装配了以下的HttpMessageConverter:
如果需要装配其他类型的HttpMessageConverter,可以在Spring的Web容器上下文中自行定义一个RequestMappingHandlerAdapter,注册若干HttpMessageConverter。如果在Spring web容器中显式定义了一个RequestMappingHandlerAdapter,则Spring MVC将使用它覆盖默认的RequestMappingHandlerAdapter。
RestTemplate是Spring的模板类,可以使用该类调用Web服务端的服务,它支持Rest风格的URL。
Spring MVC提供了几个处理XML和JSON格式的请求、响应消息的HttpMessageConverter:
只要在Spring Web容器中为RequestMappingHandlerAdapter装配好相应的HttpMessageConverter,并在交互中通过请求的Accept指定MIME类型,Spring MVC就可以是服务器段的处理方法和客户端透明的通过XML或JSON格式进行通信。
@RestController已经标注了@ResponseBody和@Controller,可以直接在控制器上标注该注解,就不用在每个@RequestMapping方法上添加@ResponseBody了。
Spring 4.0提供了AsyncRestTemplate用于以异步无阻塞的方式进行服务访问。
public class WebApplicationInitilalizer implements WebApplicationInitializer {@Overridepublic void onStartup(ServletContext servletContext) throws ServletException {ServletRegistration.Dynamic registration = servletContext.addServlet("web", new DispatcherServlet());registration.setLoadOnStartup(1);// 此处要设置为trueregistration.setAsyncSupported(true);registration.addMapping("/");}
}@RestController
public class AsyncController {@RequestMapping(value = "/async", method = RequestMethod.GET)public Callable
}
public class Main {public static void main(String[] args) {AsyncRestTemplate asyncRestTemplate = new AsyncRestTemplate();ListenableFuture
}
Spring MVC提供了多种途径输出模型数据:
Spring会根据请求方法签名的不同,将请求中的信息以一定方式转换并绑定到请求方法的入参中,还会进行数据转换、数据格式化及数据校验等。
Spring MVC通过反射对目标签名进行分析,将请求消息绑定到处理方法的入参中。数据绑定的核心部件是DataBinder。Spring MVC主框架将ServletRequest对象及处理方法的入参对象实例传递给 DataBinder,DataBinder 首先调用装配在 Spring Web 上下文中的 ConversionService 组件进行数据类型转换、数据格式化等工作,将ServletRequest中的消息填充到入参对象中, 然后调用Validator组件对己经绑定了请求消息数据的入参对象进行数据合法性校验,最 终生成数据绑定结果BindingResult对象。BindingResult包含了已完成数据绑定的入参 对象,还包含相应的校验错误对象。Spring MVC抽取BindingResult中的入参对象及校验错误对象,将它们赋给处理方法的相应入参。
类型转换模块位于org.framework.core.convert包中,同时由于历史原因,Spring还支持JDK的PropertyEditor。
ConversionService 是 Spring 类型转换体系的核心接口,它定义了以下4个方法:
第一个和第三个接口方法类似于PmpertyEditor,它们不关注类型对象所在的上下文 信息,只简单地完成两个类型对象的转换,唯一的区别在于这两个方法支持任意两个类型的转换。而第二个和第四个接口方法会参考类型对象所在宿主类的上下文信息,并利用这些信息进行类型转换。
可以利用 org.springframework.context.support.ConversionServiceFactoryBean 在 Spring 的 上下文中定义一个ConversionService。Spring将自动识别出上下文中的ConversionService, 并在Bean属性配置及Spring MVC处理方法入参绑定等场合使用它进行数据转换。该FactoryBean创建ConversionService内建了很多转换器,可完成大多数Java类型的转换工作。除了包括将String对象转换为各种基础类型的对象外,还包括String、 Number、Array、Collection、Map、Properties 及 Object 之间的转换器。可通过ConversionServiceFactoryBean的converters属性注册自定义的类型转换器:
Spring 在 org.springframework.core.convert.converter 包中定义了3种类型的转换器接口,实现任意一个转换器接口都可以作为自定义转换器注册到ConversionServiceFactoryBean中。这3种类型的转换器接口分别为:
ConversionServiceFactoryBean 的 converters 属性可接受 Converter、ConverterFactory、 GenericConverter或ConditionalGenericConverter接口的实现类,并把这些转换器的转换逻辑统一封装到一个 ConversionService 实例对象中(GenericConversionService)。Spring 在Bean属性配置及Spring MVC请求消息绑定时将利用这个ConversionService实例完成类型转换工作。
Spring也支持JavaBeans的PropertyEditor。可以在控制器中使用@InitBinder添加自定义的编辑器,也可以通过 WebBindingInitializer 装配在全局范围内使用的编辑器。
@InitBinderpublic void initBinder(WebDataBinder binder) {binder.registerCustomEditor(User.class, new PropertyEditorSupport() {@Overridepublic void setAsText(String text) throws IllegalArgumentException {User user = new User();user.setName(text);this.setValue(user);}});}
如果希望在全局范围内使用,则可实现WebBindingInitializer接口并在该实现类中注册。
对于同一个类型对象来说,如果既在ConversionService中装配了自定义转换器,又通过WebBindinglnitializer装配了自定义编辑器,同时还在控制器中通过@InitBinder装 配了自定义编辑器,那么Spring MVC将按以下优先顺序查找对应类型的编辑器:
Spring的转换器并不提供输入及输出信息格式化的工作,一般需要转换的源类型数据(一般是字符串)都是具有一定格式的,在不同的本地化环境中, 同一类型的数据还会相应地呈现不同的显示格式。Spring引入了一个新的格式化框架,这个框架位于org.springframework.format类包中。
为了让注解和格式化的属性类型关联起来,Spring在Formatter所在的包中还提供了一个 AnnotationFormatterFactory接口。
对属性对象的输入/输出进行格式化,从本质上讲依然属于“类型转换”的范畴。 Spring就是基于对象转换框架植入“格式化”功能的。Spring 在格式化模块中定义了一个实现 ConversionService 接口的 FormattingConversionService实现类,该实现类扩展了 GenericConversionService,因此它既具有类型转换功能,又具有格式化功能。
FormattingConversionService 也拥有一个对应的 FormattingConversionServiceFactoryBean 工厂类,后者用于在Spring上下文中构造一个FormattingConversionService。通过这个工厂类,既可以注册自定义的转换器,还可以注册自定义的注解驱动逻辑。由于 FormattingConversionServiceFactoryBean 在内部会自动注册 NumberFormatAnnotationFormatterFactory 和 JodaDateTimeFormatAnnotationFormatterFactory,因此装配了 FormattingConversionServiceFactoryBean 后,就可以在 Spring MVC 入参绑定及模型数据输出时使用注解驱动的格式化功能。
值得注意的是,mvc:annotation-driven/标签内部默认创建的ConversionService实例就是一个 FormattingConversionServiceFactoryBean。
Spring拥有自己独立的数据校验框架,同时支持JSR-303标准的校验框架。Spring 的DataBinder在进行数据绑定时,可同时调用校验框架完成数据校验工作。在Spring MVC中,则可直接通过注解驱动的方式进行数据校验。
LocalValidatorFactoryBean 既实现了 Spring 的 Validator 接口,又实现了 JSR-303 的 Validator 接口。只要在 Spring 容器中定义了一个 LocalValidatorFactoryBean,即可将其注入需要数据校验的Bean中。值得注意的是,Spring本身没有提供JSR-303的实现,所以必须将JSR-303的实现 者(如Hibernate Validator)的JAR文件放到类路径下,Spring将自动加载并装配好 JSR-303的实现者。
mvc:annotation-driven/会默认装配一个 LocalValidatorFactoryBean,通过在处理方法的入参上标注@Valid注解,即可让Spring MVC在完成数据绑定后执行数据校验工作。
Spring提供了以下4个本地化解析器。
Spring MVC为文件上传提供了直接支持,这种支持是通过即插即用的MultipartResolver 实现的。Spring 使用 Jakarta Commons FileUpload 技术实现了一个 MultipartResolver 实现 类:CommonsMultipartResolver。
在Spring MVC上下文中默认没有装配MultipartResolver,因此默认情况下不能 处理文件的上传工作。如果想使用Spring的文件上传功能,则需要先在上下文中配置 MultipartResolver。
当收到请求时,DispatcherServlet将请求交给处理器映射(HandlerMapping),让它找出对应该请求的HandlerExecutionChain对象。在讲解HandlerMapping之前,有必要 认识一下这个 HandlerExecutionChain 对象。
HandlerExecutionChain负责处理请求并返回ModelAndView的处理执行链,它包含一个处理该请求的处理器 (Handler),同时包括若干个对该请求实施拦截的拦截器(HandlerInterceptor)。当 HandlerMapping 返回 HandlerExecutionChain 后,DispatcherServlet 将请求交给定义在 HandlerExecutionChain中的拦截器和处理器一并处理。
位于处理器链末端的是一个 Handler,DispatcherServlet通过 Handler Adapter适配器对 Handler进行封装,并按统一的适配器接口对 Handler处理方法进行调用。可以在 web-servlet.xml
中配置多个拦截器,每个拦截器都可以指定一个匹配的映射路径,以限制拦截器的作用范围。
Spring MVC通过 HandlerExceptionResolver处理程序的异常,包括处理器映射、数据绑定及处理器执行时发生的异常。 HandlerExceptionResolver仅有一个接口方法:Modelandview resolveException(HttpServletRequest request HttpServletResponse response Object handler, Exception ex)。当发生异常时,Spring MVC将调用 resolveException方法,并转到 ModelAndView 对应的视图中,作为一个异常报告页面反馈给用户。
实现类
文章到这里就结束了
小编这里总结一份springMVC的思维导图,想了解的小伙伴可以看看呢
作者:麒麟才子
链接:https://juejin.cn/post/6902238540466225159
来源:掘金