J'ai un groupe de haricots de printemps qui sont extraits du chemin de classe via des annotations, par exemple.
@Repository("personDao")
public class PersonDaoImpl extends AbstractDaoImpl implements PersonDao {
// Implementation omitted
}
Dans le fichier XML Spring, il existe un PropertyPlaceholderConfigurer défini:
<bean id="propertyConfigurer"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location" value="/WEB-INF/app.properties" />
</bean>
Je souhaite injecter l’une des propriétés d’app.properites dans le haricot présenté ci-dessus. Je ne peux pas simplement faire quelque chose comme
<bean class="com.example.PersonDaoImpl">
<property name="maxResults" value="${results.max}"/>
</bean>
Parce que PersonDaoImpl ne figure pas dans le fichier XML Spring (il est extrait du chemin de classe via des annotations). J'ai aussi loin que ce qui suit:
@Repository("personDao")
public class PersonDaoImpl extends AbstractDaoImpl implements PersonDao {
@Resource(name = "propertyConfigurer")
protected void setProperties(PropertyPlaceholderConfigurer ppc) {
// Now how do I access results.max?
}
}
Mais je ne sais pas comment accéder à la propriété qui m'intéresse de ppc
?
Vous pouvez le faire au printemps 3 en utilisant le support EL. Exemple:
@Value("#{systemProperties.databaseName}")
public void setDatabaseName(String dbName) { ... }
@Value("#{strategyBean.databaseKeyGenerator}")
public void setKeyGenerator(KeyGenerator kg) { ... }
systemProperties
est un objet implicite et strategyBean
est un nom de bean.
Un autre exemple, qui fonctionne lorsque vous souhaitez extraire une propriété d'un objet Properties
. Il montre également que vous pouvez appliquer @Value
aux champs:
@Value("#{myProperties['github.oauth.clientId']}")
private String githubOauthClientId;
Voici un blog post J'ai écrit à ce sujet pour un peu plus d'informations.
Personnellement, j'adore cette nouvelle méthode au printemps 3.0 de la documentation :
private @Value("${propertyName}") String propertyField;
Pas de getters ou setters!
Avec les propriétés en cours de chargement via la config:
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"
p:location="classpath:propertyFile.properties" name="propertiesBean"/>
Pour approfondir ma joie, je peux même contrôler cliquer sur l'expression EL dans IntelliJ et cela m'amène à la définition de la propriété!
Il y a aussi la version totalement non xml :
@PropertySource("classpath:propertyFile.properties")
public class AppConfig {
@Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
Il y a une nouvelle annotation @Value
dans Spring 3.0.0M. @Value
prend en charge non seulement les expressions #{...}
, mais également ${...}
les espaces réservés.
<context:property-placeholder ... />
est l'équivalent XML de PropertyPlaceholderConfigurer.
Exemple: applicationContext.xml
<context:property-placeholder location="classpath:test.properties"/>
Classe de composant
private @Value("${propertyName}") String propertyField;
Une autre alternative consiste à ajouter le bean appProperties ci-dessous:
<bean id="propertyConfigurer"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location" value="/WEB-INF/app.properties" />
</bean>
<bean id="appProperties"
class="org.springframework.beans.factory.config.PropertiesFactoryBean">
<property name="singleton" value="true"/>
<property name="properties">
<props>
<prop key="results.max">${results.max}</prop>
</props>
</property>
</bean>
Une fois récupéré, ce bean peut être converti en un Java.util.Properties
qui contiendra une propriété nommée results.max
dont la valeur est lue à partir de app.properties
. De nouveau, ce bean peut être une dépendance injectée (en tant qu'instance de Java.util.Properties) dans n'importe quelle classe via l'annotation @Resource.
Personnellement, je préfère cette solution (à l'autre que j'ai proposée), car vous pouvez limiter exactement les propriétés exposées par appProperties, sans qu'il soit nécessaire de lire app.properties deux fois.
J'ai besoin de deux fichiers de propriétés, un pour la production et un remplacement pour le développement (qui ne sera pas déployé).
Pour avoir les deux, un Bean Propriétés qui peut être auto-câblé et un PropertyConfigurer, vous pouvez écrire:
<bean id="appProperties" class="org.springframework.beans.factory.config.PropertiesFactoryBean">
<property name="singleton" value="true" />
<property name="ignoreResourceNotFound" value="true" />
<property name="locations">
<list>
<value>classpath:live.properties</value>
<value>classpath:development.properties</value>
</list>
</property>
</bean>
et référencez le bean Properties dans PropertyConfigurer
<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="properties" ref="appProperties" />
</bean>
Avant d’obtenir Spring 3, qui vous permet d’injecter des constantes de propriété directement dans vos beans à l’aide d’annotations, j’ai écrit une sous-classe du bean PropertyPlaceholderConfigurer qui fait la même chose. Ainsi, vous pouvez marquer vos propriétaires et Spring va automatiquement transférer vos propriétés dans vos haricots de la manière suivante:
@Property(key="property.key", defaultValue="default")
public void setProperty(String property) {
this.property = property;
}
L'annotation est la suivante:
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.FIELD})
public @interface Property {
String key();
String defaultValue() default "";
}
Le PropertyAnnotationAndPlaceholderConfigurer est le suivant:
public class PropertyAnnotationAndPlaceholderConfigurer extends PropertyPlaceholderConfigurer {
private static Logger log = Logger.getLogger(PropertyAnnotationAndPlaceholderConfigurer.class);
@Override
protected void processProperties(ConfigurableListableBeanFactory beanFactory, Properties properties) throws BeansException {
super.processProperties(beanFactory, properties);
for (String name : beanFactory.getBeanDefinitionNames()) {
MutablePropertyValues mpv = beanFactory.getBeanDefinition(name).getPropertyValues();
Class clazz = beanFactory.getType(name);
if(log.isDebugEnabled()) log.debug("Configuring properties for bean="+name+"["+clazz+"]");
if(clazz != null) {
for (PropertyDescriptor property : BeanUtils.getPropertyDescriptors(clazz)) {
Method setter = property.getWriteMethod();
Method getter = property.getReadMethod();
Property annotation = null;
if(setter != null && setter.isAnnotationPresent(Property.class)) {
annotation = setter.getAnnotation(Property.class);
} else if(setter != null && getter != null && getter.isAnnotationPresent(Property.class)) {
annotation = getter.getAnnotation(Property.class);
}
if(annotation != null) {
String value = resolvePlaceholder(annotation.key(), properties, SYSTEM_PROPERTIES_MODE_FALLBACK);
if(StringUtils.isEmpty(value)) {
value = annotation.defaultValue();
}
if(StringUtils.isEmpty(value)) {
throw new BeanConfigurationException("No such property=["+annotation.key()+"] found in properties.");
}
if(log.isDebugEnabled()) log.debug("setting property=["+clazz.getName()+"."+property.getName()+"] value=["+annotation.key()+"="+value+"]");
mpv.addPropertyValue(property.getName(), value);
}
}
for(Field field : clazz.getDeclaredFields()) {
if(log.isDebugEnabled()) log.debug("examining field=["+clazz.getName()+"."+field.getName()+"]");
if(field.isAnnotationPresent(Property.class)) {
Property annotation = field.getAnnotation(Property.class);
PropertyDescriptor property = BeanUtils.getPropertyDescriptor(clazz, field.getName());
if(property.getWriteMethod() == null) {
throw new BeanConfigurationException("setter for property=["+clazz.getName()+"."+field.getName()+"] not available.");
}
Object value = resolvePlaceholder(annotation.key(), properties, SYSTEM_PROPERTIES_MODE_FALLBACK);
if(value == null) {
value = annotation.defaultValue();
}
if(value == null) {
throw new BeanConfigurationException("No such property=["+annotation.key()+"] found in properties.");
}
if(log.isDebugEnabled()) log.debug("setting property=["+clazz.getName()+"."+field.getName()+"] value=["+annotation.key()+"="+value+"]");
mpv.addPropertyValue(property.getName(), value);
}
}
}
}
}
}
N'hésitez pas à modifier à votre goût
Chemin de printemps:private @Value("${propertyName}") String propertyField;
est un nouveau moyen d'injecter la valeur à l'aide de la classe "PropertyPlaceholderConfigurer" de Spring. Une autre façon est d'appeler
Java.util.Properties props = System.getProperties().getProperty("propertyName");
Remarque: Pour @Value, vous ne pouvez pas utiliser statique propertyField, il doit être uniquement non statique, sinon il retourne null. Pour résoudre ce problème, un séparateur non statique est créé pour le champ statique et @Value est appliqué au-dessus de celui-ci.
Vous pouvez également annoter votre classe:
@PropertySource("classpath:/com/myProject/config/properties/database.properties")
Et avoir une variable comme celle-ci:
@Autowired
private Environment env;
Maintenant, vous pouvez accéder à toutes vos propriétés de cette manière:
env.getProperty("database.connection.driver")
Comme mentionné précédemment, @Value
fait le travail et il est assez flexible car vous pouvez avoir le printemps EL dedans.
Voici quelques exemples qui pourraient être utiles:
//Build and array from comma separated parameters
//Like currency.codes.list=10,11,12,13
@Value("#{'${currency.codes.list}'.split(',')}")
private List<String> currencyTypes;
Un autre pour obtenir un set
d'un list
//If you have a list of some objects like (List<BranchVO>)
//and the BranchVO has areaCode,cityCode,...
//You can easily make a set or areaCodes as below
@Value("#{BranchList.![areaCode]}")
private Set<String> areas;
Vous pouvez également définir des valeurs pour les types primitifs.
@Value("${amount.limit}")
private int amountLimit;
Vous pouvez appeler des méthodes statiques:
@Value("#{T(foo.bar).isSecurityEnabled()}")
private boolean securityEnabled;
Vous pouvez avoir de la logique
@Value("#{T(foo.bar).isSecurityEnabled() ? '${security.logo.path}' : '${default.logo.path}'}")
private String logoPath;
Une solution possible consiste à déclarer un deuxième bean qui lit dans le même fichier de propriétés:
<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location" value="/WEB-INF/app.properties" />
</bean>
<util:properties id="appProperties" location="classpath:/WEB-INF/app.properties"/>
Le bean nommé 'appProperties' est de type Java.util.Properties et peut être une dépendance injectée à l'aide de la méthode @Resource présentée ci-dessus.
Si vous êtes bloqué avec Spring 2.5, vous pouvez définir un haricot pour chacune de vos propriétés et les injecter à l'aide de qualificateurs. Comme ça:
<bean id="someFile" class="Java.io.File">
<constructor-arg value="${someFile}"/>
</bean>
et
@Service
public class Thing
public Thing(@Qualifier("someFile") File someFile) {
...
Ce n'est pas très lisible, mais le travail est fait.
Pour moi, c'était la réponse de @ Lucky, et plus précisément, la ligne
AutowiredFakaSource fakeDataSource = ctx.getBean(AutowiredFakaSource.class);
à partir de la page Captain Debug
cela a résolu mon problème. J'ai une application basée sur ApplicationContext exécutée à partir de la ligne de commande, et à en juger par un certain nombre de commentaires sur SO, Spring les connecte différemment aux applications basées sur MVC.
Valeurs de propriété de câblage automatique dans beans Spring:
La plupart des gens savent que vous pouvez utiliser @Autowired pour dire à Spring d'injecter un objet dans un autre lors du chargement du contexte de votre application. Un nugget d’information moins connu est que vous pouvez également utiliser l’annotation @Value pour injecter les valeurs d’un fichier de propriétés dans les attributs d’un bean. voir ce post pour plus d'infos ...
nouveautés au printemps 3. || valeurs de bean autowiring || valeurs de propriété autowiring en printemps
Je pense que le moyen le plus pratique d’injecter des propriétés dans Bean est sa méthode de définition.
Exemple:
package org.some.beans;
public class MyBean {
Long id;
String name;
public void setId(Long id) {
this.id = id;
}
public Long getId() {
return id;
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
Définition XML Bean:
<bean id="Bean1" class="org.some.beans.MyBean">
<property name="id" value="1"/>
<property name="name" value="MyBean"/>
</bean>
Pour chaque méthode nommée property
setProperty(value)
sera appelée.
Cette méthode est particulièrement utile si vous avez besoin de plusieurs beans basés sur une implémentation.
Par exemple, si nous définissons un bean supplémentaire dans XML:
<bean id="Bean2" class="org.some.beans.MyBean">
<property name="id" value="2"/>
<property name="name" value="EnotherBean"/>
</bean>
Puis code comme ceci:
MyBean b1 = appContext.getBean("Bean1");
System.out.println("Bean id = " + b1.getId() + " name = " + b1.getName());
MyBean b2 = appContext.getBean("Bean2");
System.out.println("Bean id = " + b2.getId() + " name = " + b2.getName());
Va imprimer
Bean id = 1 name = MyBean
Bean id = 2 name = AnotherBean
Donc, dans votre cas, cela devrait ressembler à ceci:
@Repository("personDao")
public class PersonDaoImpl extends AbstractDaoImpl implements PersonDao {
Long maxResults;
public void setMaxResults(Long maxResults) {
this.maxResults = maxResults;
}
// Now use maxResults value in your code, it will be injected on Bean creation
public void someMethod(Long results) {
if (results < maxResults) {
...
}
}
}
Si vous avez besoin de plus de flexibilité pour les configurations, essayez le Settings4jPlaceholderConfigurer: http://settings4j.sourceforge.net/currentrelease/configSpringPlaceholder.html
Dans notre application, nous utilisons:
L'ordre par défaut pour lequel clé-valeur-Source est vérifiée en premier est décrit dans:
http://settings4j.sourceforge.net/currentrelease/configDefault.html
Il peut être personnalisé avec un settings4j.xml (exact à log4j.xml) dans votre chemin de classe.
Donnez-moi votre opinion: [email protected]
amicalement,
Harald