(最新更新日期2021-5-4/23时)
ps:文章主要是自学笔记,可能错误颇多,不喜勿喷,欢迎提出宝贵意见
- 注意HandlerExecutionChain对象(其包含了handler和拦截器
- 注意在doDispatcher方法中从viewResolver获得view;和利用该view进行将model放入request,然后再forward,在某一层调用servlet的service方法进入下一个controller。这两个操作是在doDispatcher方法先后调用的两个方法resolveViewName(根据url返回view)和view.render方法.。二者在doDispatcher方法中是同一层面的,有先后关系,没有包裹(父子调用)关系
- 注意为了更好理解web流程,可以了解Tomcat的基本原理,见简单Tomcat实现
- controller和原来原始的servlet不太一样,原来所有的请求都会到servlet里的doGet方法里调用业务层(一般叫service层含dao)
- 将httpServletRequest req中的数据绑定(填充)到controller中的对应具体方法的参数中去[因为需要知道参数名字[函数参数名没法通过反射获取,只能知道类型,当然类名,类变量名,类方法名可以被获取到;参数名真悲催],最好用注解RequestParam (前面获得了method后,接下来调用method.getAnnotations方法也就得到了参数个数及注解ann,ann.value就获得了参数名,接下来用for循环将req中的与ann.value相同名字的数据按ann在方法中的次序依次放到args[]中),,,之后就调用该controller里的对应该业务方法[实际上是根据前面获得的methodd调用method.invoke (args[])方法,可以看到此时controller里的方法被执行,且参数也传了进去]。详情见SpringMVC中提供两种request参数到方法中参数的绑定方式,中讲解resolveHandlerArguments的部分
- 拦截器interceptor以前是在spring-mvc.xml文件中配置的,spring boot没有了该文件了。自定义的拦截器继承HandlerInterceptor(里面有preHandle,postHandle方法)
由于没有spring-mvc.xml文件了,因而在spring boot像其它配置文件转配置类一样,
继承WebMvcConfigurerAdapter的自定义MVC配置类(用Configuration注解)中,执行addInterceptor()方法将前面自定义的拦截器对象注册到InterceptorRegisteration中的成员变量HandlerInterceptor中去。
而InterceptorRegisteration对象是被加入到InterceptorRegistry中的成员变量 registrations(是个List)中去了
猜测:上面的registrations会根据url在某个方法中传入HandlerMapping的adaptedInterceptors(是个List)中去了(根据url分配到不同handler的handlermapping中)
实际上是某个handlerMapping的具体实现类
如RequestMappingHandlerMapping对象在WebMvcConfigurationSupport.java的
requestMappingHandlerMapping的方法中(其与上面的addInterceptor方法在同一类WebxxxSupport中)
将上面的registrations放入requestxxxmapper.setInterceptors()中的
可以看到此时自定义拦截器类已经到了handlermapping实现类中了,之后会放入handlerExecutionChain中
(注意,上面整个拦截器放入handlermapping的过程应该是在bean初始化的时候进行的,在dispatcher工作之前)
在doDispacter方法中,在执行handler方法(该handler方法就是对应url的controller的对应方法)前遍历该handler的HandlerExecutionchain里的属于自己的interceptor拦截器方法(依次实现比如session验证,语言检查等环节) - 视图解析器viewResolver作用就是将modelandview中的view根据视图解析xml配置文件(比如前缀Web-INF/test,和后缀.jsp,对之进行拼接,使地址最终变成localhost:8080/Test/web-inf/test/ hello.jsp这样的完整地址)进行视图解析
下面看doDispatcher方法里会调用processDispatcherResult方法,该方法里调用了初步的render方法,该render方法里主要进行2个操作:
其一调用resolveViewName根据request里的url解析视图名,返回view对象,在该方法里会出现viewResolver对象进行上面所说的视图解析(拼接地址,新建map)
其二会调用该view的render方法,该view的某实现类比如下面的InternalResourceView的render方法会调用其renderMergedOutputModel方法,在该方法里的exposeModelAsRequestAttributes()方法中,将model(是map〈key,value〉)传入request的各个同名attribute中,然后按需要forward到对应的jsp页面中(此时该jsp页面可以拿到request中的属性及值,然后jsp页面拿到值后填充或者叫渲染页面成html然后放入response返回给浏览器,可以看到response只有最后一刻及jsp渲染好页面才被放入了最主要的html,其余时间是偶尔放一些东西,而到达jsp页面之前都是基本往request里面获取或放入东西,response基本不参与),,,以前都是在servlet的doGet里对直接对request.setAttribute(key,value)放东西然后经过RequestDispatcher.forward(req,res)到具体的jsp页面,现在springMVC要经过视图解析器viewResolver来往里放东西。如何使用? - springMVC的三大重点(重点关注DispatcherServlet里doDispatcher方法)
5.1 建立urlPattern与controller之间的映射map(重点是还要urlPattern与controller里的方法之间的映射,同时需要将controller和其方法上的remapping注解拼接起来重新建立新的map)
5.2 参数数据绑定和拦截器(上面2,3)
5.3 视图解析器功能(主要是拼接jsp前缀和后缀,如localhost:8080/WEB-INF/jsps/hello.jsp + 将model里的数据map放入request的Attributes里,然后forward到已经得到的jsp(见上面3)
注意不要忘记RequestDispatcher.forward的核心转发作用,其可以转发给其它servlet,jsp,html等资源,而转发到其它servlet其实就相当于又一次要访问新的controller,以后还要经过视图解析,而后两者不必,jsp页面渲染变成的html以及本来是html的放入response返回给浏览器)
而forward方法最终经过层层调用,在ApplicationFilterChain.java的internalDoFilter方法中调用了servlet.service(request,response)方法,进入了下一个servlet或者说controller的访问