10 个知识点让你读懂 Spring MVC 容器,mysql 主从复制原理
contextConfigLocation:如果 DispatcherServlet 上下文对应的 Spring 配置文件有多个,则可以使用该属性按照 Spring 资源路径的方式指定,如 classpath:sample1.xml,classpath:sample2.xml。
publishContext:默认为 true。DispatcherServlet 根据该属性决定是否将 WebApplicationContext 发布到 ServletContext 的属性列表中,方便调用者可借由 ServletContext 找到 WebApplicationContext 实例,对应的属性名为 DispatcherServlet#getServletContextAttributeName()的返回值。
publishEvents:默认为 true。当 DispatcherServlet 处理完一个请求后,是否需要向容器发布一个 ServletRequestHandleEvent 事件。
推荐观看:[传送门](
)
=======================================================
Spring 容器配置
==============
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.0.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd">
<context:component-scan base-package="com.ankeetc.web"/>
<!-- 会自动注册 RequestMappingHandlerMapping 与 RequestMappingHandlerAdapter 两个 Bean,这是 SpringMVC 为 @Controllers 分发请求所必需的 --
<mvc:annotation-driven />
</beans>
复制代码
基于编程的配置
===========
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 {
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
ServletRegistration.Dynamic registration = servletContext.addServlet("web", new DispatcherServlet());
registration.setLoadOnStartup(1);
registration.addMapping("/");
}
}
复制代码
DispatcherServlet 的内部逻辑
==========================
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.InternalResourceViewResolver
org.springframework.web.servlet.FlashMapManager=org.springframework.web.servlet.support.SessionFlashMapManager
复制代码
DispatcherServlet 装配各型组件的逻辑
==============================
二、注解驱动的控制器
==============
@RequestMapping 映射请求
=======================
在 POJO 类上标注 @Controller,再通过 context:component-scan 扫描到该类,可以是 POJO 成为一个能处理 HTTP 请求的控制器。
在控制器的类定义和方法定义处都可以使用 @RequestMapping 映射对应的处理方法。
@RequestMapping 不但支持标准的 URL,还支持 Ant 风格和{XXX}占位符的 URL。
@RequestMapping 和 value、method、params 及 headers 分别表示请求路径、请求方法、请求参数及报文头的映射条件。
获取请求内容
==========
org.springframework.web.context.request
使用 HttpMessageConverter
==========================
HttpMessageConverter 接口可以将请求信息转换为一个对象(类型为 T),并将对象(类型为 T)绑定到请求方法的参数中或输出为响应信息。DispatcherServlet 默认已经安装了 RequestMethodHandlerAdapter 作为 HandlerAdapter 组件的实现类,HttpMessageConverter 即由 RequestMethodHandlerAdapter 使用,将请求信息转换为对象,或将对象转换为响应信息。
HttpMessageConverter 的实现类
============================
Spring 为 HttpMessageConverter 提供了众多的实现类:
实现类
=======
实现类
=======
默认的 HttpMessageConverter
===========================
RequestMappingHandlerAdapter 已经默认装配了以下的 HttpMessageConverter:
StringHttpMessageConverter
ByteArrayHttpMessageConverter
SourceHttpMessageConverter
AllEncompassingFormHttpMessageConverter
装配其他类型的 HttpMessageConverter
===============================
如果需要装配其他类型的 HttpMessageConverter,可以在 Spring 的 Web 容器上下文中自行定义一个 RequestMappingHandlerAdapter,注册若干 HttpMessageConverter。如果在 Spring web 容器中显式定义了一个 RequestMappingHandlerAdapter,则 Spring MVC 将使用它?覆盖?默认的 RequestMappingHandlerAdapter。
使用 HttpMessageConverter
==========================
可以使用 @RequestBody、@ResponseBody 对处理方法进行标注
可以使用 HttpEntity、ResponseEntity 作为处理方法的入参或返回值
RestTemplate 是 Spring 的模板类,可以使用该类调用 Web 服务端的服务,它支持 Rest 风格的 URL。
结论
======
当控制器处理方法使用到 @RequestBody、@ResponseBody 或 HttpEntity、ResponseEntity 时,Spring MVC 才会使用注册的 HttpMessageConvertor 对请求、相应消息进行处理。
当控制器处理方法使用到 @RequestBody、@ResponseBody 或 HttpEntity、ResponseEntity 时,Spring 首先根据请求头或响应的 Accept 属性选择匹配的 HttpMessageConverter,进而根据参数类型或泛型类型的过滤得到匹配的 HttpMessageConverter,若找不到可用的 HttpMessageConverter 将报错。
@RequestBody、@ResponseBody 不需要成对出现。
处理 XML 和 JSON
==============
Spring MVC 提供了几个处理 XML 和 JSON 格式的请求、响应消息的 HttpMessageConverter:
MarshallingHttpMessageConverter:处理 XML
Jaxb2RootElementHttpMessageConverter:处理 XML,底层使用 JAXB
MappingJackson2HttpMessageConverter:处理 JSON 格式
只要在 Spring Web 容器中为 RequestMappingHandlerAdapter 装配好相应的 HttpMessageConverter,并在交互中通过请求的 Accept 指定 MIME 类型,Spring MVC 就可以是服务器端的处理方法和客户端透明的通过 XML 或 JSON 格式进行通信。
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
<property name="messageConverters">
<list>
<bean class="org.springframework.http.converter.StringHttpMessageConverter"/>
<bean class="org.springframework.http.converter.xml.Jaxb2RootElementHttpMessageConverter"/>
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"/>
</list>
</property>
</bean>
复制代码
使用 @RestController
=====================
@RestController 已经标注了 @ResponseBody 和 @Controller,可以直接在控制器上标注该注解,就不用在每个 @RequestMapping 方法上添加 @ResponseBody 了。
AsyncRestTemplate
=====================
Spring 4.0 提供了 AsyncRestTemplate 用于以异步无阻塞的方式进行服务访问。
public class WebApplicationInitilalizer implements WebApplicationInitializer {
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
ServletRegistration.Dynamic registration = servletContext.addServlet("web", new DispatcherServlet());
registration.setLoadOnStartup(1);
// 此处要设置为 true
registration.setAsyncSupported(true);
registration.addMapping("/");
}
}
@RestController
public class AsyncController {
@RequestMapping(value = "/async", method = RequestMethod.GET)
public Callable<String> async() {
System.out.println("hello!");
return new Callable<String>() {
@Override
public String call() throws Exception {
TimeUnit.SECONDS.sleep(5);
return "ASYNC";
}
};
}
}
public class Main {
public static void main(String[] args) {
AsyncRestTemplate asyncRestTemplate = new AsyncRestTemplate();
ListenableFuture<ResponseEntity<String>> future = asyncRestTemplate.getForEntity("http://localhost:8080/async", String.class);
System.out.println("return");
future.addCallback(new ListenableFutureCallback<ResponseEntity<String>>() {
@Override
public void onFailure(Throwable ex) {
System.out.println("Failure");
}
@Override
public void onSuccess(ResponseEntity<String> result) {
System.out.println("Success");
}
});
}
}
复制代码
处理模型数据
==========
Spring MVC 提供了多种途径输出模型数据:
ModelAndView:当处理方法返回值类型为 ModelAndView 时,方法体即可通过该对象添加模型数据;
@ModelAttribute:在方法入参标注该注解后,入参的对象就会放到数据模型中;
Map 和 Model:如果方法入参为 org.framework.ui.Model、org.framework.ui.ModelMap、java.util.Map,当处理方法返回时,Map 中的数据会自动添加到模型中;
@SessionAttributes:将模型中的某个属性暂存到 HttpSession 中,以便多个请求之间可以共享这个属性。
三、处理方法的数据绑定
===============
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 简介
=======================
ConversionService 是 Spring 类型转换体系的核心接口,它定义了以下 4 个方法:
boolean canConvert(Class sourceType, Class targetType):判断是否可以将一个 Java 类转换为另一个 Java 类。
Boolean canConvert(TypeDescriptor sourceType, TypeDescriptor targetType):需转换的类将以成员变量的方式出现在宿主类中。TypeDescriptor 不但描述了需转换类的信息,还描述了从宿主类的上下文信息,如成员变量上的注解,成员变量是否以数组、集合或 Map 的方式呈现等。类型转换逻辑可以利用这些信息做出 各种灵活的控制。
T convert(Object source, Class targetType):将原类型对象转换为目标类型对象。
Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType):
将对象从原类型对象转换为目标类型对象,此时往往会用到所在宿主类的上下 文信息。
第一个和第三个接口方法类似于 PmpertyEditor,它们不关注类型对象所在的上下文 信息,只简单地完成两个类型对象的转换,唯一的区别在于这两个方法支持任意两个类型的转换。而第二个和第四个接口方法会参考类型对象所在宿主类的上下文信息,并利用这些信息进行类型转换。
使用 ConversionService
=======================
可以利用 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 属性注册自定义的类型转换器:
<bean class="org.springframework.context.support.ConversionServiceFactoryBean">
<property name="converters">
<list>
<bean class="com.ankeetc.MyConverter"/>
</list>
</property>
</bean>
复制代码
Spring 支持的转换器
================
Spring 在 org.springframework.core.convert.converter 包中定义了 3 种类型的转换器接口,实现任意一个转换器接口都可以作为自定义转换器注册到 ConversionServiceFactoryBean 中。这 3 种类型的转换器接口分别为:
Converter<S, T>:将 S 类型的对象转换为 T 类型的对象
GenericConverter:根据源类对象及目标类对象所在的宿主类的上下文信息进行类型转换工作。该类还有一个子接口 ConditionalGenericConverter,它添加了一个接口方法根据源类型及目标类型所在宿主类的上下文信息决定是否要进行类型转换。
ConverterFactory:
ConversionServiceFactoryBean 的 converters 属性可接受 Converter、ConverterFactory、 GenericConverter 或 ConditionalGenericConverter 接口的实现类,并把这些转换器的转换逻辑统一封装到一个 ConversionService 实例对象中(GenericConversionService)。Spring 在 Bean 属性配置及 Spring MVC 请求消息绑定时将利用这个 ConversionService 实例完成类型转换工作。
在 Spring 中使用 @lnitBinder 和 WebBindingInitializer 装配自定义编辑器
=========================================================
Spring 也支持 JavaBeans 的 PropertyEditor。可以在控制器中使用 @InitBinder 添加自定义的编辑器,也可以通过 WebBindingInitializer 装配在全局范围内使用的编辑器。
@InitBinder
public void initBinder(WebDataBinder binder) {
binder.registerCustomEditor(User.class, new PropertyEditorSupport() {
@Override
public void setAsText(String text) throws IllegalArgumentException {
User user = new User();
user.setName(text);
评论