Spring Resource: контексты приложения и пути ресурсов, подстановочные знаки в путях ресурсов конструктора контекста приложения

Пути к ресурсам в значениях конструктора контекста приложения могут быть простыми путями (как показано ранее), каждый из которых имеет однозначное сопоставление с целевым ресурсом или, альтернативно, может содержать специальный префикс "classpath*:" или внутреннее регулярное выражение в Ant-стиле (сопоставлены с помощью утилиты Spring PathMatcher). Оба последних по сути являются подстановочными знаками.

Одно из применений этого механизма - когда вам нужно выполнить сборку приложения в компонентном стиле. Все компоненты могут "публиковать" фрагменты определения контекста по хорошо известному пути местоположения, и, когда конечный контекст приложения создается с использованием того же пути с префиксом classpath*:, автоматически выбираются все фрагменты компонентов.

Обратите внимание, что этот подстановочный знак специфичен для использования путей к ресурсам в конструкторах контекста приложения (или когда вы напрямую используете иерархию служебных классов PathMatcher) и разрешается во время построения. Это не имеет ничего общего с самим типом ресурса. Вы не можете использовать префикс classpath*: для создания фактического ресурса, поскольку ресурс указывает только на один ресурс за раз.

Шаблоны в Ant-стиле

Расположение путей может содержать шаблоны в Ant-стиле, как показано в следующем примере:

/WEB-INF/*-context.xml
com/mycompany/**/applicationContext.xml
file:C:/some/path/*-context.xml
classpath:com/mycompany/**/applicationContext.xml

Когда расположение пути содержит шаблон в Ant-стиле, преобразователь следует более сложной процедуре, пытаясь разрешить подстановочный знак. Он создает ресурс для пути до последнего сегмента без подстановочных знаков и получает из него URL. Если этот URL-адрес не является URL-адресом jar: или вариантом для конкретного контейнера (например, zip: в WebLogic, wsjar в WebSphere и т. д.), из него получается файл java.io.File, который используется для устранения подстановочного знака путем обхода файловой системы. В случае URL-адреса jar преобразователь либо получает от него java.net.JarURLConnection, либо вручную анализирует URL-адрес jar, а затем просматривает содержимое файла jar для разрешения подстановочных знаков.

Последствия для переносимости

Если указанный путь уже является URL-адресом файла (либо неявно, потому что базовый ResourceLoader является файловой системой, либо явно), подстановочные знаки гарантированно работают полностью переносимым способом.

Если указанный путь является местоположением пути к классам, преобразователь должен получить последний URL сегмента пути без подстановочного знака, выполнив вызов Classloader.getResource(). Поскольку это всего лишь узел пути (а не файл в конце), на самом деле не определено, какой именно тип URL возвращается в этом случае. На практике это всегда java.io.File, представляющий каталог (где ресурс пути к классам преобразуется в расположение файловой системы) или какой-либо URL-адрес jar (где ресурс пути к классам разрешается в расположение jar). Тем не менее, существует проблема переносимости этой операции.

Если URL-адрес jar получен для последнего сегмента без подстановочного знака, преобразователь должен иметь возможность получить от него java.net.JarURLConnection или вручную проанализировать URL-адрес jar, чтобы иметь возможность просматривать содержимое jar-файла и разрешать подстановочный знак. Это работает в большинстве сред, но не работает в других, и рекомендуется тщательно протестировать разрешение подстановочных знаков ресурсов, поступающих из jar-файлов, в вашей конкретной среде, прежде чем полагаться на него.

classpath*: префикс

При создании контекста приложения на основе XML строка местоположения может использовать специальный префикс classpath*:, как показано в следующем примере:

Java

ApplicationContext ctx =
    new ClassPathXmlApplicationContext("classpath*:conf/appContext.xml");

Kotlin

val ctx = ClassPathXmlApplicationContext("classpath*:conf/appContext.xml")

Этот специальный префикс указывает, что все ресурсы пути к классам, которые соответствуют заданному имени, должны быть получены (внутренне, по сути, это происходит через вызов ClassLoader.getResources(…)), а затем объединены для формирования окончательного определения контекста приложения.

Путь к классам с подстановочными знаками основан на методе getResources() базового загрузчика классов. Поскольку большинство серверов приложений в настоящее время предоставляют свою собственную реализацию загрузчика классов, поведение может отличаться, особенно при работе с файлами jar. Простой тест, чтобы проверить, работает ли classpath*, - это использовать загрузчик классов для загрузки файла из jar в путь к классам: getClass().getClassLoader().getResources("<someFileInsideTheJar>"). Попробуйте этот тест с файлами с одинаковыми именами, но расположенными в двух разных местах. Если возвращается несоответствующий результат, проверьте в документации сервера приложений параметры, которые могут повлиять на поведение загрузчика классов.

Вы также можете комбинировать префикс classpath*: с шаблоном PathMatcher в остальной части пути расположения (например, classpath*:META-INF/*-beans.xml). В этом случае стратегия разрешения довольно проста: вызов ClassLoader.getResources() используется в последнем сегменте пути без подстановочных знаков, чтобы получить все соответствующие ресурсы в иерархии загрузчика классов, а затем для каждого ресурса такое же разрешение PathMatcher описанная ранее стратегия используется для подпути с подстановочными знаками.

Другие примечания, касающиеся подстановочных знаков

Обратите внимание, что classpath*: в сочетании с шаблонами в Ant-стиле надежно работает только с хотя бы одним корневым каталогом перед запуском шаблона, если только фактические целевые файлы не находятся в файловой системе. Это означает, что такой шаблон, как classpath*:*.xml, может извлекать файлы не из корня файлов jar, а только из корня расширенных каталогов.

Возможность Spring извлекать записи пути к классам исходит из метода JDK ClassLoader.getResources(), который возвращает только местоположения файловой системы для пустой строки (указывающей потенциальные корни для поиска). Spring оценивает конфигурацию среды выполнения URLClassLoader и манифест java.class.path в файлах jar, но это не гарантирует переносимого поведения.

Для сканирования пакетов пути к классам необходимо наличие соответствующих записей каталога в пути к классам. Когда вы создаете JAR с помощью Ant, не активируйте переключатель только файлы в задаче JAR. Кроме того, каталоги пути к классам могут не отображаться в зависимости от политик безопасности в некоторых средах - например, в автономных приложениях на JDK 1.7.0_45 и выше (что требует настройки 'Trusted-Library' в ваших манифестах.).

На пути к модулю JDK 9 (Jigsaw) сканирование пути к классам Spring обычно работает должным образом. Здесь также настоятельно рекомендуется разместить ресурсы в выделенном каталоге, чтобы избежать вышеупомянутых проблем переносимости при поиске на корневом уровне файла jar.

Шаблоны в Ant-стиле с classpath: не гарантируется нахождение подходящих ресурсов, если корневой пакет для поиска доступен в нескольких местах пути к классам. Рассмотрим следующий пример расположения ресурса:

com/mycompany/package1/service-context.xml

Теперь рассмотрим путь в Ant-стиле, который кто-то может использовать, чтобы попытаться найти этот файл:

classpath:com/mycompany/**/service-context.xml

Такой ресурс может находиться только в одном месте, но когда путь, такой как в предыдущем примере, используется для его разрешения, преобразователь обрабатывает (первый) URL-адрес, возвращаемый getResource("com/mycompany");. Если этот базовый узел пакета существует в нескольких местах загрузчика классов, фактического конечного ресурса там может не быть. Следовательно, в таком случае вы должны предпочесть использовать classpath*: с тем же шаблоном в стиле Ant, который ищет все места пути к классам, содержащие корневой пакет.


Читайте также:


Комментарии

Популярные сообщения из этого блога

Методы класса Object в Java

Как получить текущий timestamp в Java

Основные опции JVM для повышения производительности и отладки