Spring Web之Servlet启动一

来源:互联网 发布:装修监理 知乎 编辑:程序博客网 时间:2024/06/06 05:21

前言

Web.xml在初始化web程序的时候会按照一定的顺序加载里面定义的各项配置。在listenner、filter、servlet中,则是按照listener -> filter -> servlet的顺序。可看http://blog.csdn.net/wanghuan203/article/details/7329835。

Spring的bean初始化就是依赖于ContextLoaderListener,此处有对于Spring启动的完整介绍:

http://blog.csdn.net/qyp199312/article/details/38321717

http://blog.csdn.net/qyp199312/article/details/38322209

http://blog.csdn.net/qyp199312/article/details/38334631

使用Spring MVC自定义servlet,配置为:

<servlet><servlet-name>dispatcher</servlet-name><servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class><init-param><param-name>contextConfigLocation</param-name><param-value>/WEB-INF/dispatcher-servlet.xml</param-value></init-param><load-on-startup>1</load-on-startup></servlet>

接下来的内容为通过源码和介绍细讲Spring Web容器初始化相关。



DispatcherServlet启动

入口

毫无疑问,init()方法。且看层次结构:

 org.springframework.web.servletClass DispatcherServletjava.lang.Object  extended by javax.servlet.GenericServlet      extended by javax.servlet.http.HttpServlet          extended by org.springframework.web.servlet.HttpServletBean              extended by org.springframework.web.servlet.FrameworkServlet                  extended by org.springframework.web.servlet.DispatcherServlet


入口:

void org.springframework.web.servlet.HttpServletBean.init() throws ServletException


启动过程


找到init方法里面的:
// Let subclasses do whatever initialization they like.initServletBean();
且看:

@Overrideprotected final void initServletBean() throws ServletException {getServletContext().log("Initializing Spring FrameworkServlet '" + getServletName() + "'");this.logger.info("FrameworkServlet '" + getServletName() + "': initialization started");try {this.webApplicationContext = initWebApplicationContext();initFrameworkServlet();}            /*省略多部*/if (wac == null) {// No context instance was injected at construction time -> see if one// has been registered in the servlet context. If one exists, it is assumed// that the parent context (if any) has already been set and that the// user has performed any initialization such as setting the context idwac = findWebApplicationContext();}if (wac == null) {// No context instance is defined for this servlet -> create a local one              //实际上一般是执行这段,因为在用这个的时候一般情况下Spring都已经起好了。不过不管之前是否启动,这儿还是要来一遍的wac = createWebApplicationContext(rootContext);}      }


有没有觉得很眼熟的样子?再看这两个:

FrameworkServlet:


org.springframework.web.servletClass FrameworkServletjava.lang.Object  extended by javax.servlet.GenericServlet      extended by javax.servlet.http.HttpServlet          extended by org.springframework.web.servlet.HttpServletBean              extended by org.springframework.web.servlet.FrameworkServlet


ContextLoader:


org.springframework.web.contextClass ContextLoaderjava.lang.Object  extended by org.springframework.web.context.ContextLoader
再看他们的源码:

FrameworkServlet

protected WebApplicationContext initWebApplicationContext() {WebApplicationContext wac = null;if (this.webApplicationContext != null) {// A context instance was injected at construction time -> use itwac = this.webApplicationContext;if (wac instanceof ConfigurableWebApplicationContext) {ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;if (!cwac.isActive()) {// 省略configureAndRefreshWebApplicationContext(cwac);}}}}
ContextLoader
public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {if (servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) {throw new IllegalStateException("Cannot initialize context because there is already a root application context present - " +"check whether you have multiple ContextLoader* definitions in your web.xml!");}servletContext.log("Initializing Spring root WebApplicationContext");try {if (this.context == null) {this.context = createWebApplicationContext(servletContext);}if (this.context instanceof ConfigurableWebApplicationContext) {ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context;if (!cwac.isActive()) {configureAndRefreshWebApplicationContext(cwac, servletContext);}}}//已省略部分}

他们都得要经历这个步骤:

void org.springframework.context.ConfigurableApplicationContext.refresh() throws BeansException,  IllegalStateException

这儿相当于是个保险。这玩意也是可以装载Spring Bean的。接下来的一步就很简单了。Context全局共享,因此拿它来创造自己的WebApplicationContext:

WebApplicationContext  org.springframework.web.servlet.FrameworkServlet.createWebApplicationContext(ApplicationContex t parent)

此处跟装载Spring Bean一样装载各个bean。执行的都是AbstractApplicationContext.fresh()方法。但是他们的侧重点比较不一样,这也是咱们人为的各组件分离的思想。


在此处:

①初始化在xml里面定义的被初始化的内容。

②Url Mapping。


此处再次附上初始化的方法:

synchronized (this.startupShutdownMonitor) {// Prepare this context for refreshing.prepareRefresh();// Tell the subclass to refresh the internal bean factory.load配置ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();// Prepare the bean factory for use in this context.prepareBeanFactory(beanFactory);try {// Allows post-processing of the bean factory in context subclasses.postProcessBeanFactory(beanFactory);// Invoke factory processors registered as beans in the context.invokeBeanFactoryPostProcessors(beanFactory);// Register bean processors that intercept bean creation.registerBeanPostProcessors(beanFactory);// Initialize message source for this context.initMessageSource();// Initialize event multicaster for this context.initApplicationEventMulticaster();// Initialize other special beans in specific context subclasses.onRefresh();// Check for listener beans and register them.registerListeners();// Instantiate all remaining (non-lazy-init) singletons.<span style="font-family: Arial, Helvetica, sans-serif;">初始化每个Bean并Url Mapping</span>finishBeanFactoryInitialization(beanFactory);// Last step: publish corresponding event.finishRefresh();}}

整个流程

①根据<Servlet>标签的先后顺序调用其init()方法。

②DispatcherServlet调HttpServletBean的init()方法。

③HttpServletBean逐级调用AbstractApplicationContext的refresh()方法。

④refresh()方法先扫描context:component-scan下base-package定义的需要被扫描的组件,并初始化并读取起注解进行url Mapping。

refresh()方法还配置加载*-servlet.xml里面定义的各种bean比如MappingJacksonJsonView、InternalResourceViewResolver等等。

请求来了之后先由容器根据servlet-mapping导入到相应的Servlet里面去逐级找到service()方法并执行。

⑦service()方法调用doGet或者其他。找不到就返回比如:

<span style="font-size:14px;">    protected void doGet(HttpServletRequest req, HttpServletResponse resp)        throws ServletException, IOException    {        String protocol = req.getProtocol();        String msg = lStrings.getString("http.method_get_not_supported");        if (protocol.endsWith("1.1")) {            resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, msg);        } else {            resp.sendError(HttpServletResponse.SC_BAD_REQUEST, msg);        }    }</span>
方法不被支持


Url Mapping

入口

refresh:
// Last step: publish corresponding event.finishRefresh();
finishRefresh:
// Instantiate all remaining (non-lazy-init) singletons.beanFactory.preInstantiateSingletons();
preInstantiateSingletons:
getBean(beanName);// initprotected <T> T doGetBean(final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly)

循环遍历找到:
Bean name : requestMappingHandlerMapping
Class:
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping
就是这玩意来实现Url Mapping的

配对方式

AbstractAutowireCapableBeanFactory#invokeInitMethods//里面的语句((InitializingBean) bean).afterPropertiesSet();
可看此结构
org.springframework.web.servlet.mvc.method.annotationClass RequestMappingHandlerMappingjava.lang.Object  extended by org.springframework.context.support.ApplicationObjectSupport      extended by org.springframework.web.context.support.WebApplicationObjectSupport          extended by org.springframework.web.servlet.handler.AbstractHandlerMapping              extended by org.springframework.web.servlet.handler.AbstractHandlerMethodMapping<RequestMappingInfo>                  extended by org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMapping                      extended by org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping

看遍历方法
protected void initHandlerMethods() {<span style="white-space:pre"></span>//省略for (String beanName : beanNames) {if (isHandler(getApplicationContext().getType(beanName))){detectHandlerMethods(beanName);}}handlerMethodsInitialized(getHandlerMethods());}
再看细化:
protected void detectHandlerMethods(final Object handler) {//省略<span style="white-space:pre"></span>//取全部方法Set<Method> methods = HandlerMethodSelector.selectMethods(userType, new MethodFilter() {public boolean matches(Method method) {return getMappingForMethod(method, userType) != null;}});<span style="white-space:pre"></span>//遍历方法并找到包含其注解的方法。for (Method method : methods) {T mapping = getMappingForMethod(method, userType);registerHandlerMethod(handler, method, mapping);}}
再看看存储的数据结构:
protected void registerHandlerMethod(Object handler, Method method, T mapping) {HandlerMethod handlerMethod;if (handler instanceof String) {String beanName = (String) handler;handlerMethod = new HandlerMethod(beanName, getApplicationContext(), method);}<span style="white-space:pre"></span>//存储。包含支持method.header等 private final Map<T, HandlerMethod> handlerMethods = new LinkedHashMap<T, HandlerMethod>();this.handlerMethods.put(mapping, handlerMethod);if (logger.isInfoEnabled()) {logger.info("Mapped \"" + mapping + "\" onto " + handlerMethod);}Set<String> patterns = getMappingPathPatterns(mapping);for (String pattern : patterns) {if (!getPathMatcher().isPattern(pattern)) {this.urlMap.add(pattern, mapping);//private final MultiValueMap<String, T> urlMap = new LinkedMultiValueMap<String, T>();}}}

以上到此处很明确的看到了Spring通过反射将扫描到的Bean如何初始化再加载其各项配置到自己的存储里面来。
其中RequestMappingHandlerMapping自动搜索@RequestMapping并加载到自己的数据结构中来。

等到请求来了之后调用相关的方法即可。


后记

Spring MVC使用servlet作为入口,来引导解析已定义的bean并初始化并按照自己的规则将定义了的requestMapping映射为一个个的资源地址。请求来了之后直接取资源所在的方法即可。

下一节学习笔记为requestBody和responseBody








0 0
原创粉丝点击