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 : requestMappingHandlerMappingClass:
看遍历方法
以上到此处很明确的看到了Spring通过反射将扫描到的Bean如何初始化再加载其各项配置到自己的存储里面来。
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
- Spring Web之Servlet启动一
- spring web.xml之servlet
- Servlet之Tomcat配置与启动(一)
- 复习java web 之 servlet(一)
- spring源码之spring启动一
- spring容器启动之我见(一)
- web启动,quartz 关联的servlet 启动,得到Spring的bean ,servletContext 获取数据源
- Web开发(Servlet)(一)
- Web开发基础之Servlet学习总结(一)
- Java WEB之Servlet学习之路(一)一个最简单的Servlet应用
- web容器启动Spring
- web启动spring过程
- spring mvc 启动时报错Could not open ServletContext resource [/WEB-INF/appServlet-servlet.xml]
- Servlet中配置spring mvc及web.xm各组件的启动顺序
- Java web之Servlet
- Web之Servlet 1
- java web 之servlet
- Web开发之Servlet
- c# 判断当前网络是否可用
- Linux系统启动过程
- CentOS常用命令
- 【Android】Android清除本地数据缓存代码(这些功能很强大不要乱用)
- 线段树
- Spring Web之Servlet启动一
- tomca6,7的catalina.out文件分割
- Android获取手机制作商,系统版本等
- Java mail 实现邮件的发送
- Mahout安装与测试-基于hadoop单结点伪分布式
- 正则表达式-贪婪与非贪婪匹配
- get请求中文乱码
- git修改服务器上前一个提交未merge的代码片段
- Lucene的中文分词器IKAnalyzer