会员登录 - 用户注册 - 设为首页 - 加入收藏 - 网站地图 Spring 使用兼并之路(二):山穷水尽,山穷水尽!

Spring 使用兼并之路(二):山穷水尽,山穷水尽

时间:2025-05-22 12:53:05 来源:锐评时讯 作者:社会 阅读:688次

作者:京东。科技。李君。

书接上文,前面在 Spring 使用兼并之路(一):摸石头过河 介绍了几种不成功的阅历,下面继续折腾…。

四、库房兼并,独立容器。

在阅历了上面的测验,在搭档为啥不搞两个独立的容器提示下,决议抛开 Spring Boot 内置的父子容器计划,彻底自己完结父子容器。

怎么加载 web 项目?

现在的难题只要一个:怎么加载 web 项目?加载完结后,怎么继续持有 web 项目?通过考虑后,能够创立一个 boot 项目的 Spring Bean,在该 Bean 中加载并持有 web 项目的容器。因为 Spring Bean 默许是单例的,并且会随同 Spring 容器长时间存活,就能够确保 web 容器耐久存活。结合 Spring 扩展点概览及实践 中介绍的 Spring 扩展点,有两个当地能够使用:

1.能够使用 Appl。ic。ationCon。te。xtAware 获取 boot 容器的 ApplicationContext 实例,这样就能够完结自己完结的父子容器;

2.能够使用 ApplicationListener 获取 ContextRefreshedEvent 事情,该事情表明容器现已完结初始化,能够供给服务。在监听到该事情后,来进行 web 容器的加载。

思路确认后,代码完结就很简略了:

p。ac。kage com.diguage.demo.boot.config;imp。or。t org.slf4j.Logger;import org.slf4j.Logge。rF。actory;import org.springframework.beans.BeansException;import org.springframework.context.ApplicationContext;import org.springframework.context.ApplicationContextAware;import org.springframework.context.ApplicationEvent;import org.springframework.context.ApplicationListener;import org.springframework.context.event.ContextRefreshedEvent;import org.springframework.context.support.ClassPathXmlApplicationContext;import org.springframework.stereotype.Component;/** * author D瓜哥 · https://www.diguage.com */Componentpublic class WebLo。ad。erListener implements ApplicationContextAware,        ApplicationListener。< ApplicationEvent >{    private static final Logger logger = LoggerFactory.getLogger(WebLoaderListener.class);    /**     * 父容器,加载 boot 项目     */    private static ApplicationContext parentContext;    /**     * 子容器,加载 web 项目     */    private static ApplicationContext childContext;    Override    public void setApplicationContext(ApplicationContext ctx) throws BeansException {        WebLoaderListener.parentContext = ctx;    }    Override    public void onApplicationEvent(ApplicationEvent event) {        logger.info("receive application event: {}", event);        if (event instanceof ContextRefreshedEvent) {            WebLoaderListener.childContext = new ClassPathXmlApplicationContext(                    new String[]{"classpath:web/spring-cfg.xml"},                    WebLoaderListener.parentContext);        }    }}。

容器重复加载的问题。

这次自己完结的父子容器,好像想象的那样,没有同名 Bean 的查看,省去了许多费事。可是,调查日志,会发现 com.diguage.demo.boot.config.WebLoaderListener#onApplicationEvent 办法被两次履行,也便是监听到了两次 ContextRefreshedEvent 事情,导致 web 容器会被加载两次。因为项目的 RPC 服务不能重复注册,第2次加载抛出反常,导致发动失利。

开始,怀疑是 web 容器,加载了 WebLoaderListener,可是盯梢代码,没有发现 childContext 容器中有 WebLoaderListener 的相关 Bean。

昨日做了个小试验,又调试了一下 Spring 的源代码,发现了其间的奥妙。直接贴代码吧:

SPRING/spring-context/src/main/。java。/org/springframework/context/support/AbstractApplicationContext.java。

/** * Publish the given event to all listene。rs。. *。 < p >This is the internal delegate that all other {code publishEvent} * methods refer to. It is not meant to be cal。led。directly but rather serves * as a propaga。ti。on mechanism between application contexts in a hierarchy, * potentially overridden in。 sub。classes for a custom propagation arrangement. * pa。ram。event the event to publish (may be an {link ApplicationEvent} * or a payload object to be turned into a {link PayloadApplicationEvent}) * param typeHint the resolved event type, if known. * The implementation of this method also tolerates a payload type hint for * a payload object to be turned into a {link PayloadApplicationEvent}. * However, the。 recom。mended way is to construct an actual event object via * {link PayloadApplicationEvent#PayloadApplicationEvent(Object, Object, ResolvableType)} * instead for such scenarios. * since 4.2 * see ApplicationEventMulticaster#multicastEvent(ApplicationEvent, ResolvableType) */protected void publishEvent(Object event, Nullable ResolvableType typeHint) {    Assert.notNull(event, "Event must not be null");    ResolvableType eventType = null;    // Decorate event as an ApplicationEvent if necessary    ApplicationEvent applicationEvent;    if (event instanceof ApplicationEvent applEvent) {        applicationEvent = applEvent;        eventType = typeHint;    }    else {        ResolvableType payloadType = null;        if (typeHint != null && ApplicationEvent.class.i。sAs。signableF。rom。(typeHint.toClass())) {            eventType = typeHint;        }        else {            payloadType = typeHint;        }        applicationEvent = new PayloadApplicationEvent。<  >(this, event, payloadType);    }    // Determine event type only once (for multicast and parent publish)    if (eventType == null) {        eventType = ResolvableType.forInstance(applicationEvent);        if (typeHint == null) {            typeHint = eventType;        }    }    // Multicast right now if possible - or lazily once the multicaster is initialized    if (this.earlyApplicationEvents != null) {        this.earlyApplicationEvents.add(applicationEvent);    }    else if (this.applicationEventMulticaster != null) {        this.applicationEventMulticaster.multicastEvent(applicationEvent, eventType);    }    // Publish event via parent context as well...    // 假如有父容器,则也将事情发布给父容器。    if (this.parent != null) {        if (this.parent instanceof AbstractApplicationContext abstractApplicationContext) {            abstractApplicationContext.publishEvent(event, typeHint);        }        else {            this.parent.publishEvent(event);        }    }}。

在 publishEvent 办法的最终,假如父容器不为 null 的情况下,则也会向父容器播送容器的相关事情。

看到这儿就清楚了,不是 web 容器持有了 WebLoaderListener 这个 Bean,而是 web 容器主意向父容器播送了 ContextRefreshedEvent 事情。

容器毁掉。

除了上述问题,还有一个问题需求考虑:怎么毁掉 web 容器?假如不能毁掉容器,会有一些意想不到的问题。比方,注册。中心。的 RPC 供给方不能及时毁掉等等。

这儿的解决计划也比较简略:相同根据事情监听,Spring 容器毁掉会有 ContextClosedEvent 事情,在 WebLoaderListener 中监听该事情,然后调用 AbstractApplicationContext#close 办法就能够完结 Spring 容器的毁掉作业。

父子容器加载及毁掉。

结合上面的一切论说,完好的代码如下:

package com.diguage.demo.boot.config;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.beans.BeansException;import org.springframework.context.ApplicationContext;import org.springframework.context.ApplicationContextAware;import org.springframework.context.ApplicationEvent;import org.springframework.context.ApplicationListener;import org.springframework.context.event.ContextClosedEvent;import org.springframework.context.event.ContextRefreshedEvent;import org.springframework.context.support.AbstractApplicationContext;import org.springframework.context.support.ClassPathXmlApplicationContext;import org.springframework.stereotype.Component;import java.util.Objects;/** * 根据事情监听的 web 项目加载器 * * author D瓜哥 · https://www.diguage.com */Componentpublic class WebLoaderListener implements ApplicationContextAware,        ApplicationListener。< ApplicationEvent >{    private static final Logger logger = LoggerFactory.getLogger(WebLoaderListener.class);    /**     * 父容器,加载 boot 项目     */    private static ApplicationContext parentContext;    /**     * 子容器,加载 web 项目     */    private static ClassPathXmlApplicationContext childContext;    Override    public void setApplicationContext(ApplicationContext ctx) throws BeansException {        WebLoaderListener.parentContext = ctx;    }    /**     * 事情监听     *     * author D瓜哥 · https://www.diguage.com     */    Override    public void onApplicationEvent(ApplicationEvent event) {        logger.info("receive application event: {}", event);        if (event instanceof ContextRefreshedEvent refreshedEvent) {            ApplicationContext context = refreshedEvent.getApplicationContext();            if (Objects.equals(WebLoaderListener.parentContext, context)) {                // 加载 web 容器                WebLoaderListener.childContext = new ClassPathXmlApplicationContext(                        new String[]{"classpath:web/spring-cfg.xml"},                        WebLoaderListener.parentContext);            }        } else if (event instanceof ContextClosedEvent) {            // 处理容器毁掉事情            if (Objects.nonNull(WebLoaderListener.childContext)) {                synchronized (WebLoaderListener.class) {                    if (Objects.nonNull(WebLoaderListener.childContext)) {                        AbstractApplicationContext ctx = WebLoaderListener.childContext;                        WebLoaderListener.childContext = null;                        ctx.close();                    }                }            }        }    }}。

五、。参考资料。

1.Spring 扩展点概览及实践 - "地瓜哥"博客网。

2.Context Hierarchy with the Spring Boot Fluent Builder A。PI。。

3.How to revert initial git commit?

审阅修改 黄宇。

内容来源:https://sh.tanphatexpress.com.vn/app-1/game co tuong hai nguoi choi,https://chatbotjud-hml.saude.mg.gov.br/app-1/ganha-tempo-barueri

(责任编辑:社会)

    系统发生错误

    系统发生错误

    您可以选择 [ 重试 ] [ 返回 ] 或者 [ 回到首页 ]

    [ 错误信息 ]

    页面发生异常错误,系统设置开启调试模式后,刷新本页查看具体错误!