三、SpringMVC终结篇(补充中...)
2020-12-25 00:28
标签:obj intercept 组件 保存 process manual 异常 orb jar 《Spring in Action》上给了一张 Spring MVC 最最基本大致处理流程图 解释: ① DispatcherServlet 是 SpringMVC 中的核心控制器,负责接收 Request 并将 Request 转发给对应的处理组件 ② HanlerMapping 是 SpringMVC 中 完 成 url 到 Controller 映 射 的 组 件 。DispatcherServlet 接 收 Request, 然 后 从HandlerMapping 查 找 处 理 Request 的Controller ③ Controller 处理 Request,并返回 ModelAndView 对象,Controller 是 SpringMVC中负责处理 Request 的组件,ModelAndView 是封装结果视图的组件 ④、⑤、⑥视图解析器解析 ModelAndView 对象并返回对应的视图给客户端 根据 Spring MVC 工作机制,从三个部分来分析 Spring MVC 的源代码。 附上全文源码神图一张 打开DispatcherServlet类继承图 DispatcherServlet继承自HttpServlet,它的本质就是一个Servlet,这就是为什么需要在web.xml通过url-mapping为DispatcherServlet配置映射请求的原因。 Servlet初始化必然是看init方法,在父类HttpServletBean中 创建了ServletConfigPropertyValues,负责取到web.xml中contextConfigLocation,并addPropertyValue(),在PropertyValues可以看到取到的值 还用this创建了BeanWrapper,一个实体包装类,简单地说,BeanWrapper提供分析和操作JavaBean的方案,如值的set/get方法、描述的set/get方法以及属性的可读可写性 ResourceLoader读取到servletContext和classLoader,servletContext装载了我们刚才的dispatcher-servlet.xml,classLoader找到我们的字节码文件并追踪到我们的jar包路径,还有很多属性不一一介绍 最后关键是initServletBean,在FrameworkServlet中 核心代码: 主要就是获取初始化 IOC 容器,最终会调用 refresh()方法 ,refresh流程就不说了,然后会调用onRefresh,在DisptcherServlet 中实现了 到这一步就完成了Spring MVC的九大组件(九大组件最后附上)的初始化,其中就包含了url和Controller的 关 系 建 立 目前主流的三种mapping 如下: 1.SimpleUrlHandlerMapping:基于手动配置 url 与control 映射 2.BeanNameUrlHandlerMapping: 基于ioc name 中已 "/" 开头的Bean时行 注册至映射. 3.RequestMappingHandlerMapping:基于@RequestMapping注解配置对应映射 现在基本都是用的@RequestMapping,对应处理类是RequestMappingHandlerMapping 1.父类实现了InitializingBean接口,导致创建RequestMappingHandlerMapping到容器回调初始化方法afterPropertiesSet(populateBean之后有个initializeBean) 2.afterPropertiesSet中调用了initHandlerMethods解析,这个就是解析url与controller映射 3.initHandlerMethods=》processCandidateBean=》detectHandlerMethods (调用链),逻辑依次 3.1基本获取容器所有Bean, 3.2筛选是否isHandler(根据是否有Controller和RequestMapping注解), 3.3注册到MappingRegistry(运用读写锁,加到mappingLookup、urlLookup等等,都是map,第一个路径与method关系,第二个单路径(可以正则)) this.urlLookup.add(url, mapping);//kv:字符串、RequestMappingInfo数组(更详细的请求信息包括get post各种) this.mappingLookup.put(mapping, handlerMethod);//kv: RequestMappingInfo、处理方法handlerMethod DispatcherServlet是个Servlet 核心处理方法即是doService() 开始,主要逻辑是doDispatch 其中getHandler(processedRequest)方法实际上就是从 HandlerMapping 中找到 request url 和Controller 的对应关系。也就是 Map dispatch后面就是找到处理器对应正确的适配器、执行拦截器pre方法,再就是真正执行,通过适配器的handle方法,内部反射获取处理器方法上的注解和参数,解析方法和参数上的注解,然后反射调用方法获取 ModelAndView 结果视图 。 最后一步: 实际上拿到的适配器是RequestMappingHandlerAdapter ,打开handle方法源码: 核心处理逻辑在适配器的handleInternal=》invokeHandlerMethod=》invokeAndHandle=》invokeForRequest方法中: 首先,完成 Request 中的参数和方法参数上数据的绑定。Spring MVC 中提供两种 Request 参数到方法中参数的绑 最后,运用反射把绑定好的参数,传进去调用Controller的方法了doInvoke(args)。 三、SpringMVC终结篇(补充中...) 标签:obj intercept 组件 保存 process manual 异常 orb jar 原文地址:https://www.cnblogs.com/chz-blogs/p/13208625.html一、基本流程与总览
一,初始化。包括ApplicationContext 初始化时用 Map 保存所有 url 和 Controller 类的对应关系;
二,查找。根据请求 url 找到对应的 Controller,并从 Controller 中找到处理请求的方法;
三,处理返回。Request 参数绑定到方法的形参,执行方法处理请求,并返回结果视图。二、初始化
父类HttpServletBean的init方法
public final void init() throws ServletException {
// Set bean properties from init parameters.
PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
if (!pvs.isEmpty()) {
try {
BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
initBeanWrapper(bw);
bw.setPropertyValues(pvs, true);
}
catch (BeansException ex) {
//....
}
}
// Let subclasses do whatever initialization they like.
initServletBean();
}
FrameworkServlet的initServletBean
if (wac == null) {
// No context instance is defined for this servlet -> create a local one
wac = createWebApplicationContext(rootContext);
}
if (!this.refreshEventReceived) {
// Either the context is not a ConfigurableApplicationContext with refresh
// support or the context injected at construction time had already been
// refreshed -> trigger initial onRefresh manually here.
onRefresh(wac);
}
DisptcherServlet的onRefresh
@Override
protected void onRefresh(ApplicationContext context) {
initStrategies(context);
}
//初始化策略
protected void initStrategies(ApplicationContext context) {
//多文件上传的组件
initMultipartResolver(context);
//初始化本地语言环境
initLocaleResolver(context);
//初始化模板处理器
initThemeResolver(context);
//handlerMapping
initHandlerMappings(context);
//初始化参数适配器
initHandlerAdapters(context);
//初始化异常拦截器
initHandlerExceptionResolvers(context);
//初始化视图预处理器
initRequestToViewNameTranslator(context);
//初始化视图转换器
initViewResolvers(context);
//FlashMap 管理器
initFlashMapManager(context);
}
Url与Controller关系映射RequestMappingHandlerMapping
三、查找处理方法handlerMethod
/**
* //处理实际调度处理器
* //处理程序将通过按顺序的servlet的处理器映射器获得。
* //处理器适配器将通过查询servlet的安装的处理器适配器来获得
* //找到支持处理程序类的第一个。
* //所有HTTP方法都由此方法处理。 这取决于处理器适配器或处理程序
* //自己决定哪些方法是可以接受的。
* @param request//请求当前HTTP请求
* @param response//响应当前的HTTP响应
* @throws Exception //任何类型的处理失败的例外
*/
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request; //processedRequest是经过checkMultipart方法处理过的request请求
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
ModelAndView mv = null;
Exception dispatchException = null;
try {
//1、文件上传解析,如果请求类型是multipart将通过MultipartResolver进行文件上传解析;
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);
//2.通过HandlerMapping,将请求映射到处理器(返回一个HandlerExecutionChain,它包括一个处理器、多个HandlerInterceptor拦截器);
// 确定当前请求的处理程序。
mappedHandler = getHandler(processedRequest); //解析第一个方法
if (mappedHandler == null || mappedHandler.getHandler() == null) {
noHandlerFound(processedRequest, response);
return;
}
//3、通过HandlerAdapter支持多种类型的处理器(HandlerExecutionChain中的处理器);
// 确定当前请求的处理程序适配器。
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler()); //解析第二个方法
// 如果处理程序支持,则处理最后修改的头文件。
String method = request.getMethod(); //得到当前的http方法。
boolean isGet = "GET".equals(method);
if (isGet || "HEAD".equals(method)) { //处理http的head方法。这种方法应该很少用
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (logger.isDebugEnabled()) {
logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
}
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}
//4.1调用HandlerExecutionChain的interceptor
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
//4.2执行解析handler中的args,调用(invoke) controller的方法。得到视图
// Actually invoke the handler. 实际上调用处理程序。
mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); //解析第三个方法
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
//4.3调用HandlerExecutionChain的interceptor
applyDefaultViewName(processedRequest, mv);
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
dispatchException = ex;
}
//5.解析视图、处理异常
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
catch (Error err) {
//......
}
}
四、handle开始处理
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler){
return handleInternal(request, response, (HandlerMethod) handler);
}
public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
if (logger.isTraceEnabled()) {
logger.trace("Arguments: " + Arrays.toString(args));
}
return doInvoke(args);
}
定方式:
1、通过注解进行绑定,@RequestParam。
2、通过参数名称进行绑定。
使用注解进行绑定,我们只要在方法参数前面声明@RequestParam("name"),就可以将 request 中参数 name 的值绑定到方法的该参数上。使用参数名称进行绑定的前提是必须要获取方法中参数的名称,Java 反射只提供了获取方法的参数的类型,并没有提供获取参数名称的方法。SpringMVC 解决这个问题的方法是用 asm 框架读取字节码文件,来获取方法的参数名称。asm 框架是一个字节码操作框架 .
文章标题:三、SpringMVC终结篇(补充中...)
文章链接:http://soscw.com/index.php/essay/38036.html