spring源码:web容器启动(li)
web项目中可以集成spring的ApplicationContext进行bean的管理,这样使用起来bean更加便捷,能够利用到很多spring的特性。我们比较常用的web容器有jetty,tomcat,jboss等,以jetty为例,我们看一下web容器是如何初始化和启动spring的context。
一、Spring容器的加载
在web工程中都有一个web.xml文件,jetty在启动的时候会加载这个配置文件,并且对文件中的各个listener进行加载。ContextLoaderListener继承了ServletContextListener,ServletContextListener作为ServletContext的监听者,会在ServletContext创建、销毁等过程中监听ServletContextEvent事件,然后进行相应处理。关于这一块可以参考Spring的事件发布和监听机制:(Spring源码中的ApplicationContext的增强功能)中关于ApplicationContext作为事件发布者部分。所有的扩展点都在接受到ServletContextEvent事件时,具体的ContextLoaderListener处理ServletContextEvent代码如下:
/**
* Initialize the root web application context.
*/
public void contextInitialized(ServletContextEvent event) {
this.contextLoader = createContextLoader();
this.contextLoader.initWebApplicationContext(event.getServletContext());
}
这里创建了一个contextLoader对象,ContextLoader顾名思义就是context的加载器,由它来完成context的加载:
public WebApplicationContext initWebApplicationContext(ServletContext servletContext)
throws IllegalStateException, BeansException { 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");
if (logger.isInfoEnabled()) {
logger.info("Root WebApplicationContext: initialization started");
}
long startTime = System.currentTimeMillis(); try {
// Determine parent for root web application context, if any.
ApplicationContext parent = loadParentContext(servletContext); // Store context in local instance variable, to guarantee that
// it is available on ServletContext shutdown.
this.context = createWebApplicationContext(servletContext, parent);
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);
currentContextPerThread.put(Thread.currentThread().getContextClassLoader(), this.context); if (logger.isDebugEnabled()) {
logger.debug("Published root WebApplicationContext as ServletContext attribute with name [" +
WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE + "]");
}
if (logger.isInfoEnabled()) {
long elapsedTime = System.currentTimeMillis() - startTime;
logger.info("Root WebApplicationContext: initialization completed in " + elapsedTime + " ms");
} return this.context;
}
catch (RuntimeException ex) {
logger.error("Context initialization failed", ex);
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, ex);
throw ex;
}
catch (Error err) {
logger.error("Context initialization failed", err);
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, err);
throw err;
}
}
初始化web的context做了两件事情:1.查看是否指定了父容器,如果存在父容器则获取父容器;2.创建webContext,并指定父容器。laputa也使用了父子容器的指派特性,二方库plouto中负责bean加载的context作为父容器,laputa应用自己作为子容器,这样laputa就能够使用到了plouto中声明的bean(如在plouto中声明的widgetagContext,在laputa中的tagList可以顺利完成依赖注入)。之前做一个需求时,为了在tag中暴露cmsTemplateService给组件接入,把cmsTemplateService声明放到了plouto中,这样在laputa中能够更加方便引用。创建的webContext,默认给出的是XmlWebApplicationContext,关于这个类大家肯定不会陌生,学习ApplicationContext的例子中会经常使用这个容器来加载xml形式的bean配置。到此,我们获得了一个ApplicationContext,通过这个context我们可以获取当前容器的bean以及父容器的bean。
二、如何在应用中使用context
上述获取context后进行context存放的代码中有一段非常重要:
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);
currentContextPerThread.put(Thread.currentThread().getContextClassLoader(), this.context);
这两行代码告知了我们如何去获取context:1.从servletContext中去拿;2.从当前的线程Map中去拿。
A.servletContext中获取spring上下文。Spring对这一种获取方式做了封装:WebApplicationContextUtils.getRequiredWebApplicationContext(ServletContext sc)方法来得到WebApplicationContext:
public static WebApplicationContext getWebApplicationContext(ServletContext sc) {
return getWebApplicationContext(sc, WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);
}
————————————————————————————————————————————————————————————————————————————————————————————————————————————
public static WebApplicationContext getWebApplicationContext(ServletContext sc, String attrName) {
Assert.notNull(sc, "ServletContext must not be null");
Object attr = sc.getAttribute(attrName);
if (attr == null) {
return null;
}
if (attr instanceof RuntimeException) {
throw (RuntimeException) attr;
}
if (attr instanceof Error) {
throw (Error) attr;
}
if (attr instanceof Exception) {
IllegalStateException ex = new IllegalStateException();
ex.initCause((Exception) attr);
throw ex;
}
if (!(attr instanceof WebApplicationContext)) {
throw new IllegalStateException("Context attribute is not of type WebApplicationContext: " + attr);
}
return (WebApplicationContext) attr;
}
通过servletContext获取上下文,不足之处在于开发者必须能够先获得servletContext作为入参传入,所以使用起来不是很方便。
从线程Map中获取spring上下文。在ContextLoader中有静态方法来获取spring上下文:
public static WebApplicationContext getCurrentWebApplicationContext() {
return (WebApplicationContext) currentContextPerThread.get(Thread.currentThread().getContextClassLoader());
}
这种方法类比与上述方式更加便捷,不再需要感知到servletcontext的存在。
spring获取上下文方式------实现ApplicationContextAware接口。这种方式最通用,不仅仅局限于web应用。我们仅需要在用到的类中,让其继承ApplicationContextAwar接口,并实现set方法,就能够让容器在启动的时候把上下文注入到当前对象中。举例如流程引擎中的“开始节点”,获取spring容器上下文并存入PE的context中,方便后续节点能够使用spring容器:
public class InitParamNode implements ApplicationContextAware{
private static final String PARAM_PLUGS = "param.plugs";
private static final String PARAM_EXTENDS = "param.extends";
private static final String PARAM_SIGNS = "param.signs";
private static final String SPRING_CONTEXT = "springContext";
private ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext)
throws BeansException {
this.applicationContext=applicationContext;
}
public Map<String,Object> execute(PostParam postParam){
Map<String,Object> context=new HashMap<String,Object>();
context.put(SPRING_CONTEXT, this.applicationContext);
if(postParam.getCommodityExtParam()!=null){
context.put(PARAM_SIGNS, postParam.getCommodityExtParam().getSignParam());
context.put(PARAM_EXTENDS, postParam.getCommodityExtParam().getExtendParam());
context.put(PARAM_PLUGS, postParam.getCommodityExtParam().getPluginParam());
}
return context;
}
}
三、
spring源码:web容器启动(li)的更多相关文章
- Spring源码学习-容器BeanFactory(四) BeanDefinition的创建-自定义标签的解析.md
写在前面 上文Spring源码学习-容器BeanFactory(三) BeanDefinition的创建-解析Spring的默认标签对Spring默认标签的解析做了详解,在xml元素的解析中,Spri ...
- Spring源码学习-容器BeanFactory(三) BeanDefinition的创建-解析Spring的默认标签
写在前面 上文Spring源码学习-容器BeanFactory(二) BeanDefinition的创建-解析前BeanDefinition的前置操作中Spring对XML解析后创建了对应的Docum ...
- Spring源码学习-容器BeanFactory(二) BeanDefinition的创建-解析前BeanDefinition的前置操作
写在前面 上文 Spring源码学习-容器BeanFactory(一) BeanDefinition的创建-解析资源文件主要讲Spring容器创建时通过XmlBeanDefinitionReader读 ...
- Spring源码学习-容器BeanFactory(一) BeanDefinition的创建-解析资源文件
写在前面 从大四实习至今已一年有余,作为一个程序员,一直没有用心去记录自己工作中遇到的问题,甚是惭愧,打算从今日起开始养成写博客的习惯.作为一名java开发人员,Spring是永远绕不过的话题,它的设 ...
- Spring源码系列——容器的启动过程(一)
一. 前言 Spring家族特别庞大,对于开发人员而言,要想全面征服Spring家族,得花费不少的力气.俗话说,打蛇打七寸,那么Spring家族的"七寸"是什么呢?我心目中的答案一 ...
- spring源码:BeanPostProcessor(li)
在spring管理Bean的初始化过程中,除了正常管理bean的实例化(初始化.参数注入等)外,还对外提供了丰富的对Bean操作的扩展.例如自定义初始化操作,自定义容器退出时Bean的销毁操作等等.这 ...
- Spring源码系列 — 容器Extend Point(一)
前言 前文介绍了Spring中的BeanDefinition的细节,随着Spring的启动流程,这节我们介绍Spring的后续处理过程 - Spring的扩展点: BeanFactoryPostPro ...
- Spring源码学习-容器BeanFactory(五) Bean的创建-探寻Bean的新生之路
写在前面 上面四篇文章讲了Spring是如何将配置文件一步一步转化为BeanDefinition的整个流程,下面就到了正式创建Bean对象实例的环节了,我们一起继续学习吧. 2.初始化Bean对象实例 ...
- spring源码-增强容器xml解析-3.1
一.ApplicationContext的xml解析工作是通过ClassPathXmlApplicationContext来实现的,其实看过ClassPathXmlApplicationContext ...
- spring源码:核心组件(li)
一.AOP实现 Spring代理对象的产生:代理的目的是调用目标方法时我们可以转而执行InvocationHandler类的invoke方法,所以如何在InvocationHandler上做文章就是S ...
随机推荐
- Java中常用集合操作
一.Map 名值对存储的. 常用派生类HashMap类 添加: put(key,value)往集合里添加数据 删除: clear()删除所有 remove(key)清除单个,根据k来找 获取: siz ...
- 云计算下PAAS的解析一
云计算下PAAS的解析一 PaaS是Platform-as-a-Service的缩写,意思是平台即服务. 把服务器平台作为一种服务提供的商业模式.通过网络进行程序提供的服务称之为SaaS( ...
- ABP文档翻译--值对象
本人是ABP初学者,在看英文文档和@tkb至简 的ABP框架理论研究总结(典藏版)时,发现大神@tkb至简中少了对Value Objects的翻译,看文档是新的,大神没时间把,小弟给补充上. 介绍 值 ...
- 【干货分享】流程DEMO-事务呈批表
流程名: 事务呈批表 业务描述: 办公采购.会议费用等事务的申请.流程发起时,会检查预算,如果预算不够,将不允许发起费用申请,如果预算够用,将发起流程,同时占用相应金额的预算,但撤销流程会释放相应金 ...
- listview下拉刷新和上拉加载更多的多种实现方案
listview经常结合下来刷新和上拉加载更多使用,本文总结了三种常用到的方案分别作出说明. 方案一:添加头布局和脚布局 android系统为listview提供了addfootview ...
- centos安装nodejs
1.下载安装nodejs wget http://nodejs.org/dist/v0.10.25/node-v0.10.25.tar.gz compat--c++ tar -xf node-v0.1 ...
- 一步步开发自己的博客 .NET版(1、基本显示)
前言 我们每个猿都有一个搭建自己独立博客的梦,我也不例外.以前想 现在想 以后也想.之所以一直迟迟没有着手,是因为难以跨出第一步.每次心里想着,等我以后技术好了再说,然后就没有然后了.以前用过word ...
- SVG:linearGradient渐变在直线上失效的问题解决方案
SVG开发里有个较为少见的问题. 对x1=x2或者y1=y2的直线(line以及path),比如: <path d="M200,10 200,100" stroke=&quo ...
- 图形数据库Neo4J简介
最近我在用图形数据库来完成对一个初创项目的支持.在使用过程中觉得这种图形数据库实际上挺有意思的.因此在这里给大家做一个简单的介绍. NoSQL数据库相信大家都听说过.它们常常可以用来处理传统的关系型数 ...
- Leetcode 笔记 98 - Validate Binary Search Tree
题目链接:Validate Binary Search Tree | LeetCode OJ Given a binary tree, determine if it is a valid binar ...