Неправильный загрузчик классов Orika используется в случае использования Embedded tomcat

У нас есть проблема, связанная с загрузчиками классов и orika, после переноса нашего весеннего загрузочного приложения со встроенного причала на встроенный tomcat. Вот два класса:

@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 не может найти все конструкторы аргументов с модификатором доступа по умолчанию (сгенерированным lombok) в SettingsModel.

упаковка jar используется для приложения.

Я не уверен, связана ли эта проблема с Orika или spring boot.

Также я нашел аналогичную проблему https://gitter.im/spring-projects/spring-boot/archives/2016/01/15, но не уверен, что это та же проблема или что-то еще, и я не могу применить исправление, предоставленное там, потому что эти классы недоступны в весенней загрузке 2.0.3.RELEASE версия.

Я пытался использовать EclipseJdtCompilerStrategy вместо JavassistCompilerStrategy для Orika, это не помогло.

весенняя загрузочная версия - 2.0.3.RELEASE

версия орики - 1.5.2


person Alex Sylka    schedule 13.09.2018    source источник
comment
Подобные проблемы с ClassLoader трудно диагностировать только по описанию. Можете ли вы предоставить минимальный, полный и поддающийся проверке пример?   -  person Andy Wilkinson    schedule 14.09.2018
comment
@AndyWilkinson, вот пример проекта github.com/AlexSylka/spring-boot-class- проблема с загрузчиком   -  person Alex Sylka    schedule 14.09.2018
comment
Я считаю использование Ломбока помехой в такой ситуации. Это усложняет отладку с минимальной выгодой. Если я уберу Lombok из примера проекта, проблема больше не возникнет. Можете ли вы сделать образец более минимальным, удалив Lombok и заменив его необходимым кодом, который по-прежнему будет вызывать проблему?   -  person Andy Wilkinson    schedule 14.09.2018
comment
Я удалил ломбок, и проблема все еще может быть воспроизведена. Если вы сделаете конструктор SettingsModel общедоступным, код будет работать, но это не значит, что не будет проблем с загрузчиком классов. Теоретически он может появиться в каком-то другом месте.   -  person Alex Sylka    schedule 14.09.2018
comment
@AndyWilkinson Я думаю, что эта проблема может быть связана с этим github.com/spring- проекты/весенняя загрузка/проблемы/2308. Пытаюсь найти обходной путь. Пока я вижу только один: сделать конструкторы общедоступными.   -  person Alex Sylka    schedule 16.09.2018


Ответы (1)


Проблема, по-видимому, связана с ограничением в Orika. Он или ваша конфигурация не справляются с ситуацией, когда MapperFacade.map вызывается, когда загрузчик класса контекста потока является дочерним по отношению к загрузчику класса, который загрузил целевой класс. Такое расположение загрузчика классов не относится к Spring Boot. Я полагаю, что та же проблема может возникнуть и в приложении, отличном от Spring Boot, развернутом на Tomcat с целевым классом в каталоге Tomcat shared/lib.

Вы можете обойти это ограничение, изменив загрузчик класса контекста потока перед вызовом преобразователя, а затем восстановив его после этого:

@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.

person Andy Wilkinson    schedule 17.09.2018
comment
Спасибо Энди за ваш ответ. Как вы думаете, имеет ли смысл размещать проблему здесь github.com/orika-mapper/orika /проблемы ? Также знаете ли вы, как применить этот обходной путь на уровне приложения для всех таких сопоставлений, а не добавлять этот обходной путь для каждого сопоставления отдельно? - person Alex Sylka; 17.09.2018
comment
Да, я бы открыл вопрос об Орике. Возможно, вы сможете использовать шаблон декоратора, чтобы обернуть MapperContext тем, который выполняет необходимые манипуляции с загрузчиком классов. - person Andy Wilkinson; 17.09.2018