ClassLoader: ресурсы от Callee - PullRequest
       1

ClassLoader: ресурсы от Callee

0 голосов
/ 27 сентября 2019

У меня есть проект, который содержит два подпроекта.

parent
 |
 + - - my-api
 |     |
 |      ` - config.properties
 |
 ` - - my-project
       |
        ` - config.properties

my-api содержит методы, которые загружают ресурсы с помощью

ClassLoader loader = Thread.currentThread().getContextClassLoader();
InputStream input = loader.getResourceAsStream(name);

my-project вызывает метод my-api длязагрузить config.properties, хранящиеся в my-project/src/test/resources

Когда я удаляю my-api/src/test/resources/config.properties, config.properties из my- project загружается правильно.Однако, если я не удаляю его, загружается my-api/src/test/resources/config.properties.

1 Ответ

0 голосов
/ 30 сентября 2019

Поведение, которое вы видите, далеко от неожиданности.

В обычной установке Java почти весь код (кроме базовых низкоуровневых библиотек) использует один и тот же ClassLoader, который находит свои ресурсы с помощью CLASSPATH,Если несколько записей пути к классам содержат ресурс с одинаковым путем, будет возвращен ресурс, принадлежащий записи, которая появляется первой в пути к классам.Из документации URLClassLoader :

URL-адреса будут искать в порядке, указанном для классов и ресурсов, после первого поиска в указанном родительском загрузчике классов.

В вашем случае my-api должен появляться первым на пути к классам, поэтому всякий раз, когда он содержит ресурс config.properties, он будет загружен в предпочтение перед тем, который содержится в my-project.

Вы появляетесьхотеть загрузить config.properties, который находится в той же записи classpath, что и конкретный вызывающий класс.Это возможно , но требует, чтобы вы вручную определили корень записи classpath, в которой находится вызывающий класс. Он включает в себя довольно много кода и очень трудно охватить все возможные случаи загрузки классов (то есть папки классов против jar-файлов против затененных jar-файлов, созданных нижестоящими проектами, против классов, загруженных по сети, против пользовательских загрузчиков классов, используемых downstream, против менеджеров безопасности и т. д.).Итак, я не собираюсь публиковать подробности о том, как это сделать, так как считаю, что это плохая идея.Если вы действительно хотите пойти по этому пути, начните с:

Foo.class.getProtectionDomain().getCodeSource().getLocation()

Итак, какие есть альтернативы?

У вас есть несколько вариантов, но я бы предпочел просто поставитьразличные config.properties файлы в разных пакетах.Общая практика - иметь разные базовые пакеты для каждого проекта, поэтому вы можете сделать что-то вроде этого:

parent
 |
 + - - my-api
 |     |
 |      ` - - com.my.api
 |            |
 |             ` - config.properties
 |
 ` - - my-project
       |
        ` - - com.my.project
              |
               ` - config.properties

Затем у вас есть 2 варианта загрузки этих файлов.Если у вас есть класс, который, как вы знаете, находится в том же пакете, что и каждый файл свойств, вы можете загрузить его, используя этот класс:

void loadConfig(Class<?> sibling) {
    InputStream config = sibling.getResourceAsStream("config.properties");
    //...
}

В качестве альтернативы, вы можете просто передать имя пакета:

void loadConfig(String packageName) {
    String path = "/" + packageName.replace(".", "/") + "/config.properties";
    InputStream config = this.getClass().getClassLoader().getResourceAsStream(path);
    //...
}

Если вы действительно хотите определить пакет от вызываемого абонента, вы можете использовать первый метод вместе с безобразным хаком:

void loadConfig() {
    try {
        StackTraceElement[] stackTrace = new Throwable().getStackTrace();
        Class<?> sibling = Class.forName(stackTrace[1].getClassName());
        InputStream config = sibling.getResourceAsStream("config.properties");
        //...
    } catch (ClassNotFoundException e) {
        // Calling class must have been loaded using a different ClassLoader.
        // There's might be some complex code that could be used to recover here,
        // but this should happen rarely, so maybe just propogate Exception
    }
}

Я не рекомендую этопоследний подход.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...