在Spring Boot和Logback中由不同的Classloader加载的类 [英] Classes loaded by different Classloaders in Spring Boot and Logback

查看:103
本文介绍了在Spring Boot和Logback中由不同的Classloader加载的类的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我遇到了一个我想理解的奇怪问题.如果某人有一个很棒的解决方案,但是我实际上正在寻找一个未知的原因:

I ran into a wierd problem that I would like to understand. If someone has a solution that would be great, but I am searching actually for an untderstanding why this is happening:

我写了一个自定义的Logback Layout.我正在扩展 ch.qos.logback.contrib.json.classic.JsonLayout 并覆盖 addCustomDataToJsonMap .如果要在日志记录事件参数列表中找到某种类型的参数,我想在其中添加其他属性:

I wrote a custom Logback Layout. I am extending ch.qos.logback.contrib.json.classic.JsonLayout and overriding addCustomDataToJsonMap. There I would like to add additional attributes if a certain type of argument is found in the logging events arguments list:

  protected void addCustomDataToJsonMap(Map<String, Object> map, ILoggingEvent event) {

    if (event.getArgumentArray() == null) {
      return;
    }

    for (Object argument : event.getArgumentArray()) {

      System.out.println(argument.getClass().getClassLoader()); // 1
      System.out.println(JsonAttribute.class.getClassLoader()); // 2

但是参数列表中的对象的类(1)和静态引用的对象的类(2)由不同的类加载器加载,如输出所示:

But the class of the object in the argument list (1) and the class of the static referenced one (2) are loaded by different classloaders as seen in the output:

org.springframework.boot.devtools.restart.classloader.RestartClassLoader@618157b2
sun.misc.Launcher$AppClassLoader@18b4aac2

由于这个原因,我无法将对象转换为所需的类型并访问其方法.我想到的解决方法是通过Reflecion访问它们,但我宁愿使用实际价值.

Due to this I am not able to cast the object to the disired type and access its methods. The workaround I have in mind would be to access those via Reflecion, but I would rather use the real value.

我想这只是我开发环境中的一个问题,但是正如在第一部分中所述,我真的很想了解正在发生的事情.

I suppose that this is only a problem of my development environment, but as said in the first section I would really like to understand what is happening.

按预期:在没有spring dev工具的情况下以生产"模式运行应用程序时,所有类均由同一类加载器加载.

As expected: When running the appliation in "production"-mode without the spring dev tools, the classes are loaded all by the same classloader.

附录:

此函数的堆栈跟踪:

    at xxx.ExtendableJsonLayout.addCustomDataToJsonMap(ExtendableJsonLayout.java:26)
    at ch.qos.logback.contrib.json.classic.JsonLayout.toJsonMap(Unknown Source)
    at ch.qos.logback.contrib.json.classic.JsonLayout.toJsonMap(Unknown Source)
    at ch.qos.logback.contrib.json.JsonLayoutBase.doLayout(Unknown Source)
    at ch.qos.logback.core.encoder.LayoutWrappingEncoder.encode(LayoutWrappingEncoder.java:115)
    at ch.qos.logback.core.OutputStreamAppender.subAppend(OutputStreamAppender.java:230)
    at ch.qos.logback.core.OutputStreamAppender.append(OutputStreamAppender.java:102)
    at ch.qos.logback.core.UnsynchronizedAppenderBase.doAppend(UnsynchronizedAppenderBase.java:84)
    at ch.qos.logback.core.spi.AppenderAttachableImpl.appendLoopOnAppenders(AppenderAttachableImpl.java:51)
    at ch.qos.logback.classic.Logger.appendLoopOnAppenders(Logger.java:270)
    at ch.qos.logback.classic.Logger.callAppenders(Logger.java:257)
    at ch.qos.logback.classic.Logger.buildLoggingEventAndAppend(Logger.java:421)
    at ch.qos.logback.classic.Logger.filterAndLog_1(Logger.java:398)
    at ch.qos.logback.classic.Logger.error(Logger.java:526)
    at xxx.TestRunner.run(TestRunner.java:23)
    at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:813)
    at org.springframework.boot.SpringApplication.callRunners(SpringApplication.java:797)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:324)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1260)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1248)
    at xxx.TestRunner.main(TestRunner.java:16)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.springframework.boot.devtools.restart.RestartLauncher.run(RestartLauncher.java:49)

我的追加程序的登录配置:

My logback configuration for the appender:

  <appender name="json" class="ch.qos.logback.core.ConsoleAppender">
    <encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
      <layout class="xxx.ExtendableJsonLayout">
        <jsonFormatter
          class="ch.qos.logback.contrib.jackson.JacksonJsonFormatter">
          <prettyPrint>true</prettyPrint>
        </jsonFormatter>
        <timestampFormat>yyyy-MM-dd' 'HH:mm:ss.SSS</timestampFormat>
      </layout>
    </encoder>
  </appender>

JsonAttribute类:

The JsonAttribute class:

@AllArgsConstructor
@Getter
public class JsonAttribute {

  private String key;
  private Object value;

  public static JsonAttribute attr(String key, Object value) {
    return new JsonAttribute(key, value);
  }

  @Override
  public String toString() {
    return key + "=" + value;
  }

推荐答案

我假设您正在使用Spring DevTools.他们创建了一个单独的类加载器,以更快地重新启动应用程序.这个类加载器应该加载您的类,并且在重新启动而不是重新启动整个应用程序时被丢弃.参见 DevTools文档.

I assume you are using Spring DevTools. They create a separate classloader for quicker application restart. This classloader is supposed to load your classes and is thrown away on restart instead of restarting the whole application. See the DevTools docs.

您应该能够对其进行配置,以排除自定义的Logback布局,以便在应用程序重新启动时不会重新加载它(链接文档中的第20.2.6节).

You should be able to configure it to exclude your custom Logback layout so that it is not reloaded on application restart (Sec. 20.2.6 in the linked docs).

这篇关于在Spring Boot和Logback中由不同的Classloader加载的类的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆