设为首页 收藏本站
查看: 845|回复: 0

[经验分享] spring源码:web容器启动(li)

[复制链接]
累计签到:1 天
连续签到:1 天
发表于 2017-3-2 10:10:11 | 显示全部楼层 |阅读模式
    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;
    }
}
  三、

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-349151-1-1.html 上篇帖子: Solr的总结文档 下篇帖子: tcpdump抓取HTTP包【转载】
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表