简体   繁体   English

在Spring Boot和Logback中由不同的Classloader加载的类

[英]Classes loaded by different Classloaders in Spring Boot and Logback

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: 如果某人有一个很棒的解决方案,但是我实际上正在寻找一个未知的原因:

I wrote a custom Logback Layout. 我写了一个自定义的Logback Layout。 I am extending ch.qos.logback.contrib.json.classic.JsonLayout and overriding addCustomDataToJsonMap . 我正在扩展ch.qos.logback.contrib.json.classic.JsonLayout并覆盖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

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

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

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. 我想到的解决方法是通过Reflecion访问它们,但我宁愿使用实际价值。

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. 我想这只是我的开发环境的问题,但是正如在第一部分中所述,我真的很想了解正在发生的事情。

Edit: 编辑:

As expected: When running the appliation in "production"-mode without the spring dev tools, the classes are loaded all by the same classloader. 不出所料:在没有spring dev工具的情况下以“生产”模式运行应用程序时,所有类均由同一类加载器加载。

Addendum: 附录:

The stacktrace of this function: 该函数的堆栈跟踪:

    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>

The JsonAttribute class: JsonAttribute类:

@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;
  }

I assume you are using Spring DevTools. 我假设您正在使用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 . 请参阅DevTools文档

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM