web-dev-qa-db-fra.com

Comment résoudre par programme un espace réservé de propriété au printemps

Je travaille actuellement sur une application Web basée sur Spring 3.1.0.M1, basée sur des annotations, et j'ai un problème avec la résolution des espaces réservés de propriété à un endroit spécifique de mon application.

Voici l'histoire.

1) Dans mon contexte d'application web (chargé par DispatcherServlet), j'ai

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) Dans app.properties, il y a 2 propriétés, entre autres:

app.properties:

# Properties provided (filtered) by Maven itself
app.version: 0.1-SNAPSHOT
...

# Static resources mapping
app.resources.path: resources/${app.version}

3) J'ai une balise personnalisée JSP dans mes modèles JSP 2.1. Cette balise est responsable de la construction du chemin d'accès complet aux ressources en fonction des paramètres d'environnement, de la version de l'application, de la sélection du thème de printemps, etc.

Mon problème est que je ne parviens pas à résoudre correctement $ {app.resources.path} dans mon implémentation de balise personnalisée JSP. Les balises personnalisées JSP sont gérées par le conteneur de servlet, pas Spring, et ne participent donc pas à DI. Je ne peux donc pas simplement utiliser @Value ("$ {app.resources.path}") habituel et le résoudre automatiquement par Spring.

Tout ce que j'ai, c'est l'instance de contexte d'application Web, je dois donc résoudre ma propriété par programme.

Jusqu'à présent, j'ai essayé:

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");

Alors maintenant, je suis un peu coincé avec ça. Je sais que la capacité de résoudre mon espace réservé se situe quelque part dans le contexte, je ne sais tout simplement pas la bonne façon de le faire.
Toute aide ou idée à vérifier est très appréciée.

36
Max Alexejev

Je pense que plutôt que de se concentrer sur le fonctionnement interne de l'espace réservé au contexte, vous pouvez simplement définir un nouvel util: des propriétés comme celle-ci:

<util:properties id="appProperties" location="classpath:app.properties" />

et dans votre code, utilisez-le comme ceci:

Properties props = appContext.getBean("appProperties", Properties.class);

OU comme ça partout où vous pouvez faire DI:

@Value("#{appProperties['app.resources.path']}")
15
Ritesh

Depuis Spring 3.0.3 il y a EmbeddedValueResolverAware qui fonctionnera de la même manière que mentionné par un autre article qui utilise l'appel appContext.getBeanFactory().resolveEmbeddedValue("${prop}").

Résoudre le problème:

  1. Faites votre classe pour implémenter l'interface EmbeddedValueResolverAware et vous obtiendrez le résolveur injecté pour vous

  2. Ensuite, où vous pourrez récupérer des propriétés comme illustré dans un extrait de code:

    String propertyValue = resolver.resolveStringValue("${your.property.name}");
    

Ensuite, votre bean n'a pas besoin de dépendre d'ApplicationContext pour récupérer les propriétés dont vous avez besoin.

36
Eds

Depuis la version 3.0, Spring conserve une liste de résolveurs de chaînes dans le beanFactory. Vous pouvez l'utiliser comme ceci:

String value = appContext.getBeanFactory().resolveEmbeddedValue("${prop}");

Le javadoc indique cette méthode comme pour résoudre valeurs incorporées telles que les attributs d'annotation alors peut-être que nous contournons son utilisation, mais cela fonctionne.

23
thibr

Une option consiste à ajouter un PropertySource (ici MapPropertySource pour illustrer une configuration en mémoire) à un ConfigurableEnvironment et lui demander de résoudre les propriétés pour vous.

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");
    }
}

Annotez éventuellement la classe Foo avec @Configuration pour profiter de la puissance de la configuration programmatique en faveur de XML.

5
Johan Sjöberg

Il existe une autre solution possible: rendre les classes de balises @Configurable via AspectJ et activer le tissage à la compilation ou au chargement. Ensuite, je pouvais utiliser les annotations Spring @Value habituelles dans mes balises personnalisées. Mais, vraiment, je ne veux pas mettre en place une infrastructure de tissage juste à cause de quelques classes. Toujours à la recherche d'un moyen de résoudre l'espace réservé via ApplicationContext.

1
Max Alexejev