【问题标题】:Orika wrong classloader used in case of using Embedded tomcat在使用嵌入式 tomcat 的情况下使用 Orika 错误的类加载器
【发布时间】:2018-09-13 20:53:45
【问题描述】:

在将我们的 Spring Boot 应用程序从嵌入式码头移动到嵌入式 tomcat 后,我​​们遇到了与类加载器和 orika 相关的问题。 这里有两个类:

@Getter
@Builder
public class SettingsModel {
    public final Boolean useSelfSignUp;
    public final Boolean approve;
    public final Boolean verifyData;
    public final Boolean collectMid;
    public final Boolean flowEnabled;
    public final String  partnerName;
    public final String  networkType;
    public final String upc;
}

@Getter
@Setter
public class SettingsDto {
    private Boolean useSelfSignUp;
    private Boolean approve;
    private Boolean verifyData;
    private Boolean collectMid;
    private String  partnerName;
    private String  networkType;
    private Boolean flowEnabled;
    private String  upc;
}

和映射代码:

private final MapperFacade mapper;
...
mapper.map(settingsDto, SettingsModel.class)

迁移到嵌入式 tomcat 映射后抛出异常

Caused by: java.lang.IllegalAccessError: tried to access method 
onboarding.data.models.SettingsModel.<init>(Ljava/lang/Boolean;Ljava/lang/Boolean;Ljava/lang/Boolean;Ljava/lang/Boolean;Ljava/lang/Boolean;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V from class onboarding.data.models.SettingsModel_SettingsDto_ObjectFactory1006013014242721698432955$9

我发现 orika 使用具有下一个代码的 JavassistCompilerStrategy

Class<?> compiledClass = byteCodeClass.toClass(Thread.currentThread().getContextClassLoader(), this.getClass().getProtectionDomain());

当我们使用嵌入式码头 Thread.currentThread().getContextClassLoader() - 返回 sun.misc.Launcher$AppClassLoader 并且一切都按预期工作,但是在移动到嵌入式 tomcat 后它返回 TomcatEmbeddedWebappClassLoader 并且映射抛出异常。

看起来两个类加载器在工作 sun.misc.Launcher$AppClassLoader 和 TomcatEmbeddedWebappClassLoader,而这个 tomcat 类加载器在 SettingsModel 中找不到所有具有默认访问修饰符(由 lombok 生成)的 args 构造器。

jar包用于app。

我不确定这个问题是否与 Orika 或 spring boot 有关。

我也发现了类似的问题https://gitter.im/spring-projects/spring-boot/archives/2016/01/15,但不确定是同样的问题还是其他问题,并且无法应用那里提供的修复,因为这些类在 spring boot 2.0.3.RELEASE 版本中不可用。

我尝试对 Orika 使用 EclipseJdtCompilerStrategy 而不是 JavassistCompilerStrategy,但没有帮助

春季启动版本 - 2.0.3.RELEASE

orika 版本 - 1.5.2

【问题讨论】:

  • 像这样的类加载器问题很难仅从描述中诊断出来。你能提供一个minimal, complete, and verifiable example吗?
  • @AndyWilkinson 这里是示例项目github.com/AlexSylka/spring-boot-class-loader-issue
  • 我发现在这种情况下使用 Lombok 是一个障碍。它使调试变得更加困难,以获得最小的收益。如果我将 Lombok 从示例项目中剥离出来,则不再出现问题。您能否通过删除 Lombok 并将其替换为仍会触发问题的必要代码来使示例更简洁?
  • 我已经删除了 lombok 并且问题仍然可以重现。如果您将 SettingsModel 构造函数设置为“公共”代码将起作用,但这并不意味着类加载器没有问题。理论上它可以出现在其他地方。
  • @AndyWilkinson 我认为这个问题可能与这个github.com/spring-projects/spring-boot/issues/2308 有关。试图找到一些解决方法。目前我只看到一个:公开构造函数。

标签: java spring-boot classloader embedded-tomcat-8 orika


【解决方案1】:

问题似乎是由于 Orika 的限制。它或您的配置似乎无法处理当线程上下文类加载器是加载目标类的类加载器的子类时调用MapperFacade.map 的情况。这种类加载器安排并不特定于 Spring Boot。我相信在 Tomcat 的shared/lib 目录中的目标类部署到 Tomcat 的非 Spring Boot 应用程序中也会出现同样的问题。

您可以通过在调用映射器之前更改线程上下文类加载器然后在之后恢复它来解决此限制:

@GetMapping("/test-mapping")
@ResponseStatus(HttpStatus.OK)
public void test() {
    SettingsDto settingsDto = new SettingsDto();
    ClassLoader previous = Thread.currentThread().getContextClassLoader();
    try {
        Thread.currentThread().setContextClassLoader(TestController.class.getClassLoader());
        SettingsModel model = mapper.map(settingsDto, SettingsModel.class);
    }
    finally {
        Thread.currentThread().setContextClassLoader(previous);
    }
}

进行此更改后,对 /test-mapping 的调用会产生 200 响应。

【讨论】:

  • 感谢安迪的回复。您认为在这里github.com/orika-mapper/orika/issues 发布问题有什么意义?您还知道如何在应用级别为所有此类映射应用此解决方法,而不是为每个映射单独添加此解决方法吗?
  • 是的,我会打开一个 Orika 问题。您可以使用装饰器模式将MapperContext 包装为执行必要的类加载器操作。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-01-07
  • 1970-01-01
  • 2016-11-15
  • 1970-01-01
  • 2017-01-26
  • 2018-03-03
相关资源
最近更新 更多