
>
本篇,我打算从springMVC项目的webxml的配置文件入手,通过部分源码逐步去理解解释三个容器的关系以及调用顺序,因为是基于我个人的理解,可能有所不足。
一般webxml文件里会有如下两段配置信息:
我们先了解下webxml,以下引用自 《webxml文件是什么?有什么用?--详解》 :
然后结合我们上面的webxml中关于spring和spring mvc的配置信息来进入话题:
首先,启动web容器的时,会先生成对应项目的ServelContent对象,这个是每个项目的上下文,这个ServelContent可以管理所有的servlet,并将我们webxml中设置的<context-param>内容作为键值对交给这个对象。
然后加载<listener>标签内容,这个时候就会产生orgspringframeworkwebcontextContextLoaderListener。
spring的这个 ContextLoaderListener 在接下来的过程中很重要,我们来看一下源码
首先,可以看出它继承了ContextLoader类,并实现了ServletContextListener接口。
这里再直接引用他人的结论 《Spring中ContextLoaderListener作用》
好了,人家说法中回到我们的起点了,我们基本都被人告知“ContextLoaderListener的作用是创建并初始化spring容器”
那我们就可以深入进去看看,到底哪里做了这一步:
首先,我们知道了ServletContextListene是ServletContext的监听者,监听器的响应动作就是在服务器启动时contextInitialized会被调用,关闭的时候contextDestroyed被调用,这个好理解,那我们就来看一下ContextLoaderListener重写的contextInitialized方法到底做了什么。
我们再进入观察initWebApplicationContext方法细看
我因为自己消化过一遍,直接给出关键位置的方法说明——
1、首先是278行:创建了WebApplicationContext,我们可以理解为spring容器的壳子有了
2、其次是288和289行:对ApplicationContext加载了配置文件,并设置servletContext为WebApplicationContext的parent,到这一步,可以理解为我们的spring容器也就差不多成型了
3、接下来是294行:把ApplicationContext对象以键值对的形式存到servletContext中,这一步很关键,就是因为servletContext中存在这个键值对,所以其他内部成员可以通过servletContext访问到ApplicationContext,当然也能使用其管理的bean,而spring mvc则没有这样存在servletContext,所以我觉得正是这一步决定了子容器springmvc可以取用父容器内的bean,反着则不然。
接下来直到轮到我们的springmvc容器<servlet>标签内容
会生成控制orgspringframeworkwebservletDispatcherServlet,这是一个前端控制器,主要的内容我之前也有一篇文章做过自我记录
《Spring MVC的工作机制简单理解》
我们可以看到设置的
<load-on-startup>1</load-on-startup>
这个标签大概意思就是:
1、load-on-startup 元素标记容器是否应该在web应用程序启动的时候就加载这个servlet,(实例化并调用其init()方法)。
2、它的值必须是一个整数,表示servlet被加载的先后顺序。
3、如果该元素的值为负数或者没有设置,则容器会当Servlet被请求时再加载。
4、如果值为正整数或者0时,表示容器在应用启动时就加载并初始化这个servlet,值越小,servlet的优先级越高,就越先被加载。值相同时,容器就会自己选择顺序来加载。
在DispatcherServlet的时候就根据springMVC容器容器的配置文件生成。
比如我这边就是
那顺序确定了,我们再看一下spring和spring mvc的父子关系哪里确定:
我们可以从下面3个截图看到dispatcherServlet的继承关系,同时,init方法用的是dispatcherServlet父类的父类的方法。
重点在于initServletBean()方法,经过追踪,我们找到该方法的最终实现又是在dispatcherServlet的父类FrameworkServlet中
其中涉及父子关系的实际是在219行的initWebApplicationContext()方法
initWebApplicationContext()方法主要用于创建或刷新WebApplicationContext实例,并对Servlet功能所使用的变量进行初始化。
从238行源码就可以看到,它获得ContextLoaderListener中初始化的rootContext,
在246行设置了父子关系的引用,也就是从这一点我们看到了spring和springMVC的父子关系!
并且,可以看到这只是一条单向的引用,spring中没有引用直接指向springMVC,也就是子类能找到父类,然而父类都不知道这个子类,父子容器之间内部对象调用关系更明了。
再通过构造函数和Servlet的contextAttribute属性查找ServletContext来进行webApplicationContext实例的初始化,最终。
这个方法内263行源码onRefresh(wac)方法是FrameworkServlet提供的模板方法,在子类,也就是我们的DispatcherServlet的onRefresh()方法中进行了重写。而在onRefresh()方法中调用了initStrategies()方法来完成初始化工作,初始化Spring MVC的9个组件。
1、Tomcat在启动时给每个Web应用创建一个全局的上下文环境,这个上下文就是ServletContext,其为后面的Spring容器提供环境。
2、Tomcat在启动过程中触发容器初始化事件,Spring的ContextLoaderListener会监听到这个事件,它的contextInitialized方法会被调用,在这个方法中,Spring会初始化全局的Spring根容器,这个就是Spring的IoC容器,IoC容器初始化完毕后,Spring将其存储到ServletContext中,便于以后来获取。
3、Tomcat在启动过程中还会扫描Servlet,一个Web应用中的Servlet可以有多个,以SpringMVC中的DispatcherServlet为例,这个Servlet实际上是一个标准的前端控制器,用以转发、匹配、处理每个Servlet请求。
4、Servlet会在容器启动时加载或延迟加载(根据启动级别设置数字)。延迟加载时,当第一个请求达到时,serlet容器发现对应Servlet还没有被实例化,就调用Servlet的init方法。
在spring MVC里
DispatcherServlet在初始化的时候会建立自己的容器,叫做SpringMVC 容器,用来持有Spring MVC相关的Bean。同时,Spring MVC还会通过ServletContext拿到Spring根容器,并将Spring根容器设为SpringMVC容器的父容器,请注意,Spring MVC容器可以访问父容器中的Bean,但是父容器不能访问子容器的Bean, 也就是说Spring根容器不能访问SpringMVC容器里的Bean。
说的通俗点就是,在Controller里可以访问Service对象,但是在Service里不可以访问Controller对象。
1通过>@RequestMapping(value="/index1")
public String helloaction1(>}
2通过参数名获得:
@RequestMapping(value="/index1")public String helloaction1(String nnn){ //这里名字要与前端元素名字一致才能获得
Systemoutprintln(nnn);
return "index";
}
3通过@RequestParam注解获得:
@RequestMapping(value="/index")public String helloaction(@RequestParam(value="nnn",required=false)String nnn1, Model model){ //nnn要与前端一致,在此处可以理解为参数nnn1的别名
Systemoutprintln(nnn1);
modeladdAttribute("hello", "这是用action传过来的值:"+nnn1);
return "index";
}
4SpringMvc还能通过将vo作为参数获得vo的各个属性:
@RequestMapping(value="/index2")public String helloaction2(User user){
Systemoutprintln(usergetAccount());
Systemoutprintln(usergetPassword());
return "index";
}
Spring的框架中,orgspringframeworkbeansfactoryconfigPropertyPlaceholderConfigurer类可以将properties(key/value形式)文件中一些动态设定的值(value),在XML中替换为占位该键($key$)的值,properties文件可以根据客户需求,自定义一些相关的参数,这样的设计可提供程序的灵活性。
<bean id="propertyConfigurer" class="orgspringframeworkbeansfactoryconfigPropertyPlaceholderConfigurer" lazy-init="false">
<property name="locations">
<list>
<value>classpath:conf/probizproperties</value>
<value>classpath:conf/mail_configproperties</value>
</list>
</property>
<property name="ignoreResourceNotFound" value="true" />
<property name="ignoreUnresolvablePlaceholders" value="true" />
</bean>
Spring Cloud提供了两套方便我们编写网关的中间件,分别是zuul和Spring GateWay,在zuul1的IO模型是使用BIO(图1-1)。而zuul2对IO模型使用NIO进行了重构(图1-2)。而Spring GateWay的IO模型是使用NIO。而在Netflix发布zuul2的时候Spring Cloud已经开始不集成到Spring Cloud中,因为Spring Cloud 等着zuul2集成太久,才有了Spring Gateway。Spring GateWay的架构是基于Spring webflux的基础上开发的。而对webflux的RP中涉及的Back Pressure、Stream、asynchronous好处不多说哈哈。
在Spring mvc是通过HandlerMapping解析请求链接,然后根据请求链接找到执行这个请求Controller类 。而在Spring GateWay中也是使用HandlerMapping对请求的链接进行解析匹配对应的Route进行代理转发到对应的服务。图2-1为整个请求的流程,用户请求先通过DispatcherHandler找到对应GateWwayHandlerMapping,再通过GateWwayHandlerMapping解析匹配到对应的Handler。Handler处理完后,再经过Filter,最终到Proxied Service
1请求先由DispatcherHanlder进行处理,DispatcherHanlder初始化的时候会从IOC中查找实现HandlerMapping接口的实现类。然后保存到内部变量handlerMappings中,DispatcerHandler调用Handler方法迭代handler
Mappings中的HandlerMapping,
2这里只讲解RoutePredicateHandlerMapping,因此然后调用RoutePredicateHandlerMapping中的获取路由的方法,当RoutePredicateHandlerMapping获取到对应的路由的时候会将Route存储到ServerWebExchanges的属性中,然后返回实现了WebHandler接口的FilteringWebHandler。FilteringWebHandler是一个存放过滤器的Handler。
3最后DispatcherHanlder通过SimpleHandlerAdapter适配器的方式调用FilteringWebHandler的handler方法,FilteringWebHandler调用所有的过滤器,包括proxy filter。通过proxyFilter请求被代理的服务。处理完毕后,并将Response响应回去。
图3-1为handler类关系图。这里主要涉及到Spring GateWay相关类的探讨。如:Spring Webflux使用到的RouteFuntionMapping和SimpleUrlHandlerMapping等不做探讨。
HandlerMapping和Ordered接口主要定义了获取getHandler和当前hanler加载顺序。AbstractHandlerMapping在getHanlder封装了CORS处理。因为所有Handler都可能会涉及到CORS的处理,抽象到AbstractHandlerMapping处理,再提供了getHandlerInternal让子类实现具体的查找Handler的方法。
RoutePredicateHandlerMapping是处理获取路由的hanlder。Route
PredicateHandlerMapping中的RouteLocator是存储了我们启动的时候加载的路由对象信息。获取路由的时候,调用RoutePredicateHanlderMapping的getHandlerInternal方法从RouteLocator获取路由存放在ServerWebExchange中,返回webFilter。
RouteLocator主要作用是提供获取路由的类型。我们在分析Route
PredicateHandlerMapping的时候,知道RoutePredicateHandlerMapping获取路由是通过RouteLocator进行获取的。那么我们这里分析下RouteLocator加载路由。
Route主要为三部分:
最总的 RouteLocator是CachingRoutelocator。加载过程是自上而下进行创建。
第二种方式是通过Properties文件进行创建路由。Properties路由的创建包括:PropertiesRouteDefinitionLocator和DiscoveryClientRouteDefinitionLocator
第三种方式是通过MYSQL或者Reids、内存(InMemoryRouteDefinitionRepository)方式创建路由。实现RouteDefinitionRepository接口实现接口中的方式。InMemoryRouteDefinitionRepository为默认方式。
Filter我们区分为全局Filter和RouteFilter
在转发过程分析中我们知道最终的代理请求是通过一个Proxy Filter进行请求Proxy Service,那么这个Proxy Filter就是NettyRoutingFilter。通过下面的源码我们可以看到在 proxyRequestsendHeaders() send(requestgetBody()map(dataBuffer -> ((NettyDataBuffer) dataBuffer)getNativeBuffer())); 中请求Proxy Service
只要问及Spring MVC框架,这一题基本80%的概率会被问及,大致如下:
从webxml中 servlet的配置开始, 根据servlet拦截的url-parttern,来进行请求转发,大致处理流程如下:
A用户向服务器发送请求,请求被 springMVC前端控制器(DispatchServlet)捕获
B前端控制器对请求URL进行解析,得到请求资源标识符(URL)然后根据该URL调用页面处理器(HandlerMapping)获得该Handler配置的所有相关对象(包括Handler对象以及Handler对象对应的拦截器),最后以HandlerExecutionChain对象的形式返回;
C前端控制器根据获得 Handler 选择一个合适的 HandlerAdapter 适配器处理
D Handler 对数据处理完成以后将返回一个 ModelAndView()对象给前端控制器
E Handler 返回的 ModelAndView()只是一个逻辑视图并不是一个正式的视图,前端控制器通过ViewResolver 试图解析器将逻辑视图转化为真正的视图 View;
F DispatcherServle 通过 model 解析出 ModelAndView()中的参数进行解析最终展现出完整的 view 并返回给客户端;
@requestMapping:用于请求 url 映射。
@RequestBody: 注解实现接收 >
springmvc请求参数获取的几种方法
1、直接把表单的参数写在Controller相应的方法的形参中,适用于get方式提交,不适用于post方式提交。
/
1直接把表单的参数写在Controller相应的方法的形参中
@param username
@param password
@return
/
@RequestMapping("/addUser1")
public String addUser1(String username,String password) {
Systemoutprintln("username is:"+username);
Systemoutprintln("password is:"+password);
return "demo/index";
}
url形式:/SSMDemo/demo/addUser1username=lixiaoxi&password=111111 提交的参数需要和Controller方法中的入参名称一致。
2、通过>
以上就是关于springmvc中怎么获取url上的参数全部的内容,包括:springmvc中怎么获取url上的参数、springMVC的注解@RequestParam与@RequestBody、从源码理解总结web容器、spring容器、spring mvc容器三者关系等相关内容解答,如果想了解更多相关内容,可以关注我们,你们的支持是我们更新的动力!
欢迎分享,转载请注明来源:内存溢出
微信扫一扫
支付宝扫一扫
评论列表(0条)