spring mvc
MVC
Model(模型):数据模型,包含数据和行为:Value Object(数据) 和 服务层(行为)。
View(视图):负责进行模型的展示。
Controller(控制器):接收用户请求,委托给模型进行处理(状态改变),处理完毕后把返回的模型数据返回给视图,由视图负责展示。 也就是说控制器做了个调度员的工作。
模型无法主动推数据给视图,如果用户想要视图更新,需要发送一次请求(即请求-响应模型)。
Web端开发发展历程
CGI(Common Gateway Interface):在web服务端使用的脚本技术,用于接收web用户请求并处理,最后动态产生响应给用户,但每次请求将产生一个进程,重量级。
Servlet:JavaEE web组件技术,一种在服务器端执行的web组件,用于接收web用户请求并处理,最后动态产生响应给用户。每次请求只产生一个线程(而且有线程池),轻量级。在java代码里面输出html流,表现逻辑、控制逻辑、业务逻辑调用混杂。
JSP(Java Server Page):一种在服务器端执行的web组件,嵌入脚本语言的模板页面技术。在html代码中嵌入java代码。JSP最终会被编译为Servlet,比纯Servlet开发页面简单、方便,但表现逻辑、控制逻辑、业务逻辑调用还是混杂。
Model1:JSP+JavaBean,使用<jsp:useBean>标准动作,将请求参数封装为JavaBean组件,还须使用java脚本执行控制逻辑。
Model2:控制器采用Servlet、模型采用JavaBean、视图采用JSP。只是一种代码上物理的分离,实则依赖关系很严重。
服务到工作者:Front Controller + Application Controller + Page Controller + Context
即,前端控制器+应用控制器+页面控制器(也有称其为动作)+上下文,也是Web MVC,只是责任更加明确。
Spring Web MVC
基于Java,实现了Web MVC设计模式,请求驱动类型的轻量级Web框架(基于请求驱动:使用请求-响应模型)
前端控制器是DispatcherServlet
;
应用控制器拆为处理器映射器Handler Mapping
进行处理器管理和视图解析器View Resolver
进行视图管理;
页面控制器/动作为Controller
接口(仅包含ModelAndView handleRequest(request, response)
方法)的实现(也可以是任何的POJO类);
Spring MVC执行流程
- 用户请求DispatcherServlet。
- DispatcherServlet接受到请求,将根据请求信息交给处理器映射器。
- 处理器映射器(HandlerMapping)根据请求路径查找匹配的Handler,并返回一个执行链。
- DispatcherServlet再根据执行链请求处理器适配器(HandlerAdapter)。
- 处理器适配器调用相应的handler进行功能处理。
- 对应的handler处理完成后返回ModelAndView给处理器适配器。
- 处理器适配器将接受的ModelAndView返回给DispatcherServlet。
- DispatcherServlet请求视图解析器来解析视图。
- 视图解析器处理完后返回View对象给DispatcherServlet。
- 最后前端控制器对View进行视图渲染(即将模型数据填充至视图中)。
- DispatcherServlet返回响应给用户
DispatcherServlet:拦截请求到Spring Web MVC
HandlerMapping:将请求映射到处理器
HandlerAdapter:支持多种类型的处理器
Handler:如Controller,进行功能处理
ViewResolver:将逻辑视图名解析为具体视图技术
View:视图
DispatcherServlet配置
1、基于xml配置
<servlet>
<servlet-name>springMVC</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-servlet-config.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springMVCDemo</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
其中<load-on-startup>1</load-on-startup>
标志容器是否在启动时就加载该servlet。值表示被载入的顺序:>=0表示容器启动时就加载,否则被请求才加载;值越小,优先级越高。
2、基于Spring Boot配置
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
@SpringBootApplication
这个注解等同于:
@Configuration // @SpringBootConfiguration就是用的这个
@EnableAutoConfiguration
@ComponentScan
关注@EnableAutoConfiguration
注解,它有一个@Import({AutoConfigurationImportSelector.class})
注解。借助AutoConfigurationImportSelector
,@EnableAutoConfiguration
可以帮助SpringBoot应用将所有符合条件的@Configuration
配置都加载到当前SpringBoot创建并使用的IoC容器。这里也是DispatcherServlet初始化的关键。
// AutoConfigurationImportSelector
@Override
// 该方法返回的String数组是全类名,会被纳入容器中。
// DispatcherServlet的全类名就在这个数组中
public String[] selectImports(AnnotationMetadata annotationMetadata) {
// getAutoConfigurationEntry-->getCandidateConfigurations
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
// 把spring-boot-autoconfigure.jar的spring.factories中
// 以EnableAutoConfiguration为key的value类加载到容器中
// this.getSpringFactoriesLoaderFactoryClass()就是EnableAutoConfiguration.class
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader());
...
return configurations;
}
org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration
就在其中,于是DispatcherServletAutoConfiguration
被载入。
// SpringFactoriesLoader
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
// EnableAutoConfiguration
String factoryClassName = factoryClass.getName();
return loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
}
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
// 先看一下缓存里有没有这个map,有就直接返回了。
...
try {
// 从spring.factories文件获取资源
Enumeration<URL> urls = (classLoader != null ?
classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
result = new LinkedMultiValueMap<>();
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
UrlResource resource = new UrlResource(url);
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
for (Map.Entry<?, ?> entry : properties.entrySet()) {
String factoryClassName = ((String) entry.getKey()).trim();
for (String factoryName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
// 以键值对形式存储
result.add(factoryClassName, factoryName.trim());
}
}
}
// 放到缓存map中
cache.put(classLoader, result);
return result;
}...
}
控制器定义
@Controller
注解表明了一个类是作为控制器的角色而存在的。Spring不要求你去继承任何控制器基类,也不要求你去实现Servlet的那套API。
// 用于标识是处理器类
@Controller
public class GreetingController {
// @GetMapping确保到/greeting的HTTP GET请求被映射到当前这个方法上
@GetMapping("/greeting")
// @RequestParam将请求参数绑定到方法参数上:required=false该查询参数非必须;defaultValue="World"如果没有传入该值,还有默认值
public String greeting(@RequestParam(name="name", required=false, defaultValue="World") String name, Model model) {
// name参数的值被加到Model对象上, 最终在视图上展现
model.addAttribute("name", name);
return "greeting";
}
}
分派器(DispatcherServlet
)会扫描所有注解了@Controller
的类,检测其中通过@RequestMapping
注解配置的方法。(RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter)
DispatcherServlet初始化
1、初始化参数
基于xml
DispatcherServlet
的初始化过程中,Spring MVC会在web应用的WEB-INF
目录下查找一个名为[servlet-name]-servlet.xml的配置文件,并创建其中所定义的bean。(就是IoC容器创建bean的流程!)
如果使用如上配置,Spring Web MVC框架将加载“classpath:spring-servlet-config.xml”来进行初始化上下文而不是“/WEB-INF/[servlet名字]-servlet.xml”。
基于Spring Boot
Configuring the DispatcherServlet
yourself is unusual but if you really need to do it, a @Bean
of type DispatcherServletPath
must be provided as well to provide the path of your custom DispatcherServlet
.
2、初始化流程
HttpServletBean继承HttpServlet,因此在Web容器启动时将调用它的init方法:
// 这个方法会在容器初始化每个Servlet的时候被调用一次。方法是在GenericServlet中有一个空定义
public final void init() throws ServletException {
// 将Servlet初始化参数设置到该组件上
PropertyValues pvs = new HttpServletBean.ServletConfigPropertyValues(this.getServletConfig(), this.requiredProperties);
if (!pvs.isEmpty()) {
try {
// 通过BeanWrapper简化设值过程,方便后续使用
BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
ResourceLoader resourceLoader = new ServletContextResourceLoader(this.getServletContext());
bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, this.getEnvironment()));
this.initBeanWrapper(bw);
bw.setPropertyValues(pvs, true);
}...
}
// 提供给子类初始化扩展点,该方法由FrameworkServlet覆盖。
this.initServletBean();
}
FrameworkServlet继承HttpServletBean:
// 进行Web上下文初始化
protected final void initServletBean() throws ServletException {
....
long startTime = System.currentTimeMillis();
try {
// 初始化web上下文
this.webApplicationContext = this.initWebApplicationContext();
this.initFrameworkServlet();
}
...
}
protected WebApplicationContext initWebApplicationContext() {
// ROOT上下文
// this.getServletContext():
// GenericServlet(this.getServletConfig().getServletContext();)
// getWebApplicationContext():
// getWebApplicationContext(sc, WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE)
// 就是这里得到了DispatcherServlet的上下文!
WebApplicationContext rootContext = WebApplicationContextUtils.getWebApplicationContext(this.getServletContext());
WebApplicationContext wac = null;
if (this.webApplicationContext != null) {
// 1、在创建该Servlet时注入的上下文
wac = this.webApplicationContext;
if (wac instanceof ConfigurableWebApplicationContext) {
ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext)wac;
if (!cwac.isActive()) {
if (cwac.getParent() == null) {
cwac.setParent(rootContext);
}
this.configureAndRefreshWebApplicationContext(cwac);
}
}
}
// 2、查找已经绑定的上下文
if (wac == null) {
wac = this.findWebApplicationContext();
}
// 3、如果没有找到相应的上下文,创建并指定父亲为根上下文
if (wac == null) {
wac = this.createWebApplicationContext(rootContext);
}
if (!this.refreshEventReceived) {
synchronized(this.onRefreshMonitor) {
// 4、刷新上下文(执行一些初始化)
// 提供给子类初始化扩展点
this.onRefresh(wac);
}
}
if (this.publishContext) {
String attrName = this.getServletContextAttributeName();
this.getServletContext().setAttribute(attrName, wac);
}
return wac;
}
DispatcherServlet继承FrameworkServlet:
protected void onRefresh(ApplicationContext context) {
this.initStrategies(context);
}
// 初始化默认的Spring Web MVC框架使用的策略
protected void initStrategies(ApplicationContext context) {
this.initMultipartResolver(context);
this.initLocaleResolver(context);
this.initThemeResolver(context);
this.initHandlerMappings(context);
this.initHandlerAdapters(context);
this.initHandlerExceptionResolvers(context);
this.initRequestToViewNameTranslator(context);
this.initViewResolvers(context);
this.initFlashMapManager(context);
}
总结:
整个DispatcherServlet初始化的过程具体主要做了如下两件事情:
1、初始化Spring Web MVC使用的Web上下文,并且可能指定父容器为根上下文;
2、初始化DispatcherServlet使用的策略,如HandlerMapping、HandlerAdapter等。
HandlerMapping初始化
SimpleUrlHandlerMapping
是Spring MVC中适用性最强的HandlerMapping类,允许明确指定URL模式和Handler的映射关系。
private void initHandlerMappings(ApplicationContext context) {
this.handlerMappings = null;
// 优先判断detectAllHandlerMappings的值。
if (this.detectAllHandlerMappings) {
// 默认情况下,Spring MVC会加载在当前系统中所有实现了HandlerMapping接口的bean
// 再进行按优先级排序。(优先级通过Ordered接口设定)
// Find all HandlerMappings in the ApplicationContext,
// including ancestor contexts.
Map<String, HandlerMapping> matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
if (!matchingBeans.isEmpty()) {
this.handlerMappings = new ArrayList(matchingBeans.values());
// We keep HandlerMappings in sorted order.
AnnotationAwareOrderComparator.sort(this.handlerMappings);
}
} else {
try {
// detectAllHandlerMappings值设置为false
// Spring MVC就只会查找名为“handlerMapping”的bean
// 并作为当前系统的唯一的HandlerMapping
HandlerMapping hm = (HandlerMapping)context.getBean("handlerMapping", HandlerMapping.class);
this.handlerMappings = Collections.singletonList(hm);
} catch (NoSuchBeanDefinitionException var3) {
}
}
// Ensure we have at least one HandlerMapping, by registering
// a default HandlerMapping if no other mappings are found.
if (this.handlerMappings == null) {
// 没有定义HandlerMapping的话
// 按照DispatcherServlet.properties所定义的内容来加载默认的HandlerMapping。
this.handlerMappings = this.getDefaultStrategies(context, HandlerMapping.class);
...
}
}
其中,defaultStrategies从静态代码段中获得:
ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, DispatcherServlet.class);
defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
HandlerAdapter初始化
HandlerAdapter可以帮助自定义各种Handler。具体实现同上。
明显是适配器模式。也就是说被适配者是handler。而适配器都实现了HandlerAdapter接口。
private void initHandlerAdapters(ApplicationContext context) {
this.handlerAdapters = null;
if (this.detectAllHandlerAdapters) {
Map<String, HandlerAdapter> matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerAdapter.class, true, false);
if (!matchingBeans.isEmpty()) {
this.handlerAdapters = new ArrayList(matchingBeans.values());
AnnotationAwareOrderComparator.sort(this.handlerAdapters);
}
} else {
try {
HandlerAdapter ha = (HandlerAdapter)context.getBean("handlerAdapter", HandlerAdapter.class);
this.handlerAdapters = Collections.singletonList(ha);
} catch (NoSuchBeanDefinitionException var3) {
}
}
if (this.handlerAdapters == null) {
this.handlerAdapters = this.getDefaultStrategies(context, HandlerAdapter.class);
...
}
}
抽象处理器方法适配器(AbstractHandlerMethodAdapter)
-
afterPropertiesSet方法注入
ArgumentResolvers
和ReturnValueHandlers
到Spring容器。 -
ServletInvocableHandlerMethod
调用invokeAndHandle方法。 -
两大接口:HandlerMethodArgumentResolver、HandlerMethodReturnValueHandler。
使用
HandlerMethodReturnValueComposite
,使用组合模式,放入HandlerMethodReturnValueHandler
的list;同理,HandlerMethodArgumentResolverComposite
使用组合模式,放入HandlerMethodArgumentResolver
的list。 -
RequestResponseBodyMethodProcessor
负责解析Controller里@RequestBody
,支持响应类型是@ResponseBody
。RequestParamMethodArgumentResolver
负责解析Controller里@RequestParam
。这两个都是resolver!都实现了两大接口!
// RequestMappingHandlerAdapter
@Nullable
protected ModelAndView invokeHandlerMethod(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
...
try {
...
ServletInvocableHandlerMethod invocableMethod = this.createInvocableHandlerMethod(handlerMethod);
if (this.argumentResolvers != null) {
invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
}
if (this.returnValueHandlers != null) {
invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
}
...
// ★
invocableMethod.invokeAndHandle(webRequest, mavContainer, new Object[0]);
...
}...
}
// ServletInvocableHandlerMethod
public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {
// ★方法参数处理,见下
Object returnValue = this.invokeForRequest(webRequest, mavContainer, providedArgs);
this.setResponseStatus(webRequest);
if (returnValue == null) {
if (this.isRequestNotModified(webRequest) || this.getResponseStatus() != null || mavContainer.isRequestHandled()) {
this.disableContentCachingIfNecessary(webRequest);
mavContainer.setRequestHandled(true);
return;
}
} else if (StringUtils.hasText(this.getResponseStatusReason())) {
mavContainer.setRequestHandled(true);
return;
}
...
try {
// 返回值的处理 ★ 里面是用的组合,不同子类实现不同
this.returnValueHandlers.handleReturnValue(returnValue, this.getReturnValueType(returnValue), mavContainer, webRequest);
}...
}
// InvocableHandlerMethod
@Nullable
public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {
// 解析请求参数
Object[] args = this.getMethodArgumentValues(request, mavContainer, providedArgs);
...
// 调用Controller中的请求方法
return this.doInvoke(args);
}
HTTP请求处理器适配器(HttpRequestHandlerAdapter)
仅支持对HTTP请求处理器的适配。
Handler需实现HttpRequestHandler接口,并实现其void handlerRequest(HttpRequest,HttpResponse)
方法。
简单控制器处理器适配器(SimpleControllerHandlerAdapter)
将HTTP请求适配到一个控制器的实现进行处理。这里的控制器的实现是一个简单的控制器接口的实现。客户化的业务逻辑通常是在控制器接口的实现类中实现的。
Handler需实现Controller接口,并实现其ModelAndView handler(HttpRequest,HttpResponse,handler)
方法。
ViewResolver初始化
具体实现同上。
private void initViewResolvers(ApplicationContext context) {
this.viewResolvers = null;
if (this.detectAllViewResolvers) {
Map<String, ViewResolver> matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, ViewResolver.class, true, false);
if (!matchingBeans.isEmpty()) {
this.viewResolvers = new ArrayList(matchingBeans.values());
AnnotationAwareOrderComparator.sort(this.viewResolvers);
}
} else {
try {
ViewResolver vr = (ViewResolver)context.getBean("viewResolver", ViewResolver.class);
this.viewResolvers = Collections.singletonList(vr);
} catch (NoSuchBeanDefinitionException var3) {
}
}
if (this.viewResolvers == null) {
this.viewResolvers = this.getDefaultStrategies(context, ViewResolver.class);
...
}
}
DispatcherServlet分派
// DispatcherServlet
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
try {
ModelAndView mv = null;
Object dispatchException = null;
try {
// 检查请求是否是multipart(如文件上传),如果是将通过MultipartResolver解析
processedRequest = this.checkMultipart(request);
multipartRequestParsed = processedRequest != request;
// 2、请求到处理器(页面控制器)的映射,通过HandlerMapping进行映射
// HandlerMapping将会把请求映射为HandlerExecutionChain对象
// 包含一个Handler处理器、多个HandlerInterceptor拦截器
mappedHandler = this.getHandler(processedRequest);
if (mappedHandler == null) {
this.noHandlerFound(processedRequest, response);
return;
}
// 3、处理器适配,即将处理器包装成相应的适配器(从而支持多种类型的处理器)
// 会轮询适配器模,查找能够处理当前请求的处理器的实现
HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler());
// 304 Not Modified缓存支持
...
// 执行处理器相关的拦截器的预处理(HandlerInterceptor.preHandle)
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// 4、由适配器执行处理器(调用处理器相应功能处理方法),返回一个ModelAndView对象
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
this.applyDefaultViewName(processedRequest, mv);
// 执行处理器相关的拦截器的后处理(HandlerInterceptor.postHandle)
mappedHandler.applyPostHandle(processedRequest, response, mv);
}...
// 5&6、解析视图并进行视图的渲染
this.processDispatchResult(processedRequest, response, mappedHandler, mv, (Exception)dispatchException);
}...
} finally {
if (asyncManager.isConcurrentHandlingStarted()) {
if (mappedHandler != null) {
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
}
} else if (multipartRequestParsed) {
// Clean up any resources used by a multipart request.
this.cleanupMultipart(processedRequest);
}
}
}
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response, @Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv, @Nullable Exception exception) throws Exception {
boolean errorView = false;
if (exception != null) {
...
}
// 5、由ViewResolver解析View
// viewName不为空:viewResolver.resolveViewName(viewName, locale)
// viewName为空:mv.getView()
// 6、视图在渲染时会把Model传入
// view.render(mv.getModelInternal(), request, response);这是个接口方法
// 接口名为View:具体实现有StaticView、ThymeleafView、HtmlResourceView和AbstractView等
if (mv != null && !mv.wasCleared()) {
// ViewResolver将把逻辑视图名解析为具体的View(很容易更换其他视图技术)
// View会根据传进来的Model模型数据进行渲染
this.render(mv, request, response);
if (errorView) {
WebUtils.clearErrorRequestAttributes(request);
}
}....
if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
if (mappedHandler != null) {
mappedHandler.triggerAfterCompletion(request, response, (Exception)null);
}
}
}
本文来自博客园,作者:白芷呀,转载请注明原文链接:https://www.cnblogs.com/angelica-duhurica/p/11248607.html