Как программно разрешить компоновщик свойств в Spring
В настоящее время я работаю над веб-приложением на основе Spring 3.1.0.M1, на основе аннотаций, и у меня есть проблема с разрешением заполнителей свойств в одном конкретном месте моего приложения.
Вот история.
1) В моем контексте веб-приложения (загружен DispatcherServlet) у меня есть
MVC-config.xml:
<!-- Handles HTTP GET requests for /resources/version/** -->
<resources mapping="/${app.resources.path}/**" location="/static/" cache-period="31556926"/>
...
<!-- Web properties -->
<context:property-placeholder location="
classpath:app.properties
"/>
2) Внутри app.properties есть 2 свойства, среди прочих:
app.properties:
# Properties provided (filtered) by Maven itself
app.version: 0.1-SNAPSHOT
...
# Static resources mapping
app.resources.path: resources/${app.version}
3) У меня есть JSP-пользовательский тег в моих шаблонах JSP 2.1. Этот тег отвечает за полное построение пути ресурса в зависимости от настроек среды, версии приложения, Spring выбора темы и т.д. Специальный класс тега расширяет spring: класс реализации URL, поэтому его можно считать обычным тегом URL, но с некоторыми дополнительными знаниями о правильном пути.
Моя проблема заключается в том, что я не могу правильно получить ${app.resources.path} в моей реализации JSP-пользовательского тега. Пользовательские теги JSP управляются контейнером сервлета, а не Spring, и поэтому не участвуют в DI. Поэтому я не могу просто использовать обычный @Value ( "$ {app.resources.path}" ) и автоматически разрешить его с помощью Spring.
У меня есть экземпляр контекста веб-приложения, поэтому я должен программно разрешить свое свойство.
До сих пор я пытался:
ResourceTag.java:
// returns null
PropertyResolver resolver = getRequestContext().getWebApplicationContext().getBean(PropertyResolver.class);
resolver.getProperty("app.resources.path");
// returns null, its the same web context instance (as expected)
PropertyResolver resolver2 = WebApplicationContextUtils.getRequiredWebApplicationContext(pageContext.getServletContext()).getBean(PropertyResolver.class);
resolver2.getProperty("app.resources.path");
// throws NPE, resolver3 is null as StringValueResolver is not bound
StringValueResolver resolver3 = getRequestContext().getWebApplicationContext().getBean(StringValueResolver.class);
resolver3.resolveStringValue("app.resources.path");
// null, since context: property-placeholder does not register itself as PropertySource
Environment env = getRequestContext().getWebApplicationContext().getEnvironment();
env.getProperty("app.resources.path");
Итак, теперь я немного застрял в этом. Я знаю, что способность разрешать мой заполнитель где-то в контексте, я просто не знаю, как правильно это сделать.
Любая помощь или идеи для проверки высоко оценены.
Ответы
Ответ 1
Я думаю, вместо того, чтобы сосредоточиться на внутренней работе владельца контекста, вы можете просто определить новую утилиту: свойства, подобные этому:
<util:properties id="appProperties" location="classpath:app.properties" />
и в вашем коде, используйте его следующим образом:
Properties props = appContext.getBean("appProperties", Properties.class);
ИЛИ как это, где вы можете сделать DI:
@Value("#{appProperties['app.resources.path']}")
Ответ 2
Так как Spring 3.0.3 есть EmbeddedValueResolverAware, который будет работать так же, как упоминалось другим сообщением, которое использует вызов appContext.getBeanFactory().resolveEmbeddedValue("${prop}")
.
Чтобы решить проблему:
-
Сделайте свой класс для внедрения интерфейса EmbeddedValueResolverAware, и вы получите для него распознавание.
-
Затем вы сможете получить свойства, как показано в фрагменте кода:
String propertyValue = resolver.resolveStringValue("${your.property.name}");
Затем ваш bean не должен зависеть от ApplicationContext для получения необходимых вам свойств.
Ответ 3
Начиная с версии 3.0, Spring хранит список String resolver в beanFactory.
Вы можете использовать его следующим образом:
String value = appContext.getBeanFactory().resolveEmbeddedValue("${prop}");
В javadoc указан этот метод как для разрешения встроенных значений, таких как атрибуты аннотации, поэтому, возможно, мы обходим его использование, но оно работает.
Ответ 4
Один из вариантов - добавить PropertySource
(здесь MapPropertySource
для иллюстрации конфигурации в памяти) в ConfigurableEnvironment
и попросить его разрешить свойства для вас.
public class Foo {
@Autowired
private ConfigurableEnvironment env;
@PostConstruct
public void setup() {
env.getPropertySources()
.addFirst(new MapPropertySource("my-propertysource",
ImmutableMap.<String, Object>of("your.property.name", "the value")));
env.resolvePlaceholders("your.property.name");
}
}
Необязательно аннотировать класс Foo
с помощью @Configuration
, чтобы получить возможность использования программной конфигурации в пользу XML
.
Ответ 5
Существует еще одно возможное решение: создать тег-классы @Configurable через AspectJ и включить либо время компиляции, либо время загрузки. Затем я мог бы использовать обычные аннотации Spring @Value в моих пользовательских тегах.
Но, действительно, я не хочу настраивать ткацкую инфраструктуру только из-за нескольких классов. Все еще ищет способ разрешить местозаполнитель через ApplicationContext.