You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring中Log4j自定义Appender读取静态Map为空的原因

问题分析与解决方案

这个问题的核心原因是Log4j2自定义Appender的加载机制和Spring上下文完全隔离,导致你的静态类A被加载了两次,产生了两个独立的静态变量副本。

为什么会出现这种情况?

Log4j2的插件(包括你自定义的MapAppender)是在应用启动的极早期阶段,由Log4j2自身的类加载器初始化的——这个时间点甚至早于Spring容器开始创建Bean的时间。而你的服务层BService是由Spring的Web应用类加载器加载和管理的。

如果A类被这两个不同的类加载器分别加载,那么每个类加载器都会创建一个A类的独立副本,对应的静态listMap自然也是完全分离的:

  • 服务层调用A.addMap()时,操作的是Spring类加载器加载的A类的静态Map;
  • 而MapAppender的append()方法中,访问的是Log4j2插件类加载器加载的A类的静态Map,两者完全不是同一个对象,所以会出现“服务层能拿到数据,Appender拿不到”的现象。

另外,你的代码里还有几个小问题需要先修正:

  1. A.getMap()的返回类型错误:listMap.values()返回的是Collection<Session>,不是Map<String>;
  2. A.addMap()方法声明为void但写了return,这会导致编译错误;
  3. 多线程环境下(服务层和Appender可能同时操作Map),HashMap不是线程安全的,建议换成ConcurrentHashMap。

解决方法

最可靠的方式是把静态类A改成Spring管理的单例Bean,让Appender通过Spring上下文获取正确的实例,避免类加载器隔离的问题。

步骤1:将A改为Spring单例Bean

@Component
public class A {
    // 换成线程安全的ConcurrentHashMap
    private final Map<String, Session> listMap = new ConcurrentHashMap<>();

    // 返回不可修改的集合,避免外部直接修改内部Map
    public Collection<Session> getSessionCollection() {
        return Collections.unmodifiableCollection(listMap.values());
    }

    public Session addSession(String name, Session s) {
        return listMap.put(name, s);
    }
}

步骤2:创建Spring上下文持有类

用来在非Spring管理的类(比如你的MapAppender)中获取Spring Bean:

@Component
public class ApplicationContextHolder implements ApplicationContextAware {
    private static ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext ctx) throws BeansException {
        applicationContext = ctx;
    }

    public static <T> T getBean(Class<T> beanClass) {
        return applicationContext.getBean(beanClass);
    }
}

步骤3:在MapAppender中获取Spring管理的A实例

修改append()方法,通过ApplicationContextHolder拿到正确的A Bean:

@Override
public void append(LogEvent event) {
    // 获取Spring管理的A实例
    A aBean = ApplicationContextHolder.getBean(A.class);
    Collection<Session> sessionCollection = aBean.getSessionCollection();
    // 执行你的send逻辑
    send();
}

其他注意事项

  • 确保ApplicationContextHolder被Spring正确扫描到(和其他Bean在同一个包下,或者通过@ComponentScan指定扫描路径);
  • 如果你的应用是Web应用,还要注意Log4j2的初始化时机,确保Spring上下文完全加载后Appender才开始处理日志事件(可以通过配置Log4j2的启动顺序,或者在Appender中添加判断,避免Spring上下文未初始化时调用getBean)。

内容的提问来源于stack exchange,提问作者bearics

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.07 10:12:34