|
How about using fields?
@Config class MyConfig { String host; int port = 80; @Bean Socket serverSocket() { return new ServerSocket(host,port) } } When instantiating the context you'd need to provide properties that would include host and port (port would be optional) Perhaps @Required can play a role here as well but I'd avoid it if possible. I've added some basic support for this as we work toward M3, using the abstract method style and sourcing values from resource bundles. Using fields is an elegant idea, but I like the fact that dynamic resolution (at runtime) can work naturally with methods.
Usage looks like this: @Config @ResourceBundles("classpath://com/foo/bar/basename") abstract class MyConfig { @Bean Socket serverSocket() { return new ServerSocket(getHost()); } @ExternalValue protected abstract int getHost(); } I will play around with various refinements and possibly using a concrete implementation as a default value. Note: M3 is on the way and should be out this month. I see what you mean, methods are definitely more flexible, on the other hand fields make the code more concise.
Perhaps there is room for both? One nice feature of the proposal in
@Configuration public class MyConfig { @Bean public Server server1( int host, int port ) {...} @Bean public Server server2( int host, int port ) {...} } Has its properties cleanly scoped: server1.host=foo server1.port=123 server2.host=bar server2.port=456 Using @ExternalValues defined at the class level forces us to explicitly scope the property name: @ExternalValue public abstract int getServer1Host(); @ExternalValue public abstract int getServer2Host(); And set properties with names like 'server1Host'. The '.' separated properties feel more natural than cammalCase properties? @ExternalValue could take a name attribute but that adds noise. OTOH if a property is shared across beans then this becomes a bit clunky, we must either duplicate the value in 2 properties (yuck) or decorate both properties to have the same name using the @Name annotation. It feels more common to have 1 unique user of a property rather than using it in many places throughout the config so this might be a reasonable tradeoff. Perhaps properties lacking a bean qualifier are applied to all matching params? For example the property: locale=UK Would be bound to *any* bean factory parameter with the name 'locale' unless an explicit value is provided for that bean? One more tradeoff is how often a property needs to be defaulted, defaults look quite elegant using a non-abstract @ExternalValue method and feel cumbersome having to decorate each param with a @Default(...) annotation, not sure what could be done here to improve this (without native Java support for default paramaters!). Along the same lines, I hope there's an easy way to hook into Spring's registered Property Editors and reflection to inject strings that are converted to more complex objects ... for example:
@Configuration @ResourceBundles("classpath://com/foo/bar/file_with_EnumValue_in_it") public class Config extends ConfigurationSupport { @ExternalValue public abstract MyCustomEnum getEnumValue(); @Bean public MyNewBean newBean() { MyNewBean myNewBean = new MyNewBean(); myNewBean.setEnumValue( getEnumValue() ); .... Here's how I am doing it. I guess, it's better to leave this kind of configuration in the XML file, but I was just too eager to configure my app absolutelly without an XML file :)
private static String SETTINGS_PROPERTIES = "sk/seges/attis/config/dataSourceConfiguration.properties"; private static final Properties settingsProperties; static { InputStream is = null; try { is = ClassLoader.getSystemClassLoader().getResourceAsStream(SETTINGS_PROPERTIES); settingsProperties = new Properties(); settingsProperties.load(is); } catch (Exception e) { throw new ExceptionInInitializerError(e); } finally { if(is != null) { try { is.close(); } catch (Exception e) { throw new ExceptionInInitializerError(e); } } } } @SuppressWarnings("unused") @Bean private DriverManagerDataSource dataSource() { DriverManagerDataSource dataSource = new DriverManagerDataSource(); dataSource.setUrl(settingsProperties.getProperty("db.url")); dataSource.setUsername(settingsProperties.getProperty("db.username")); dataSource.setPassword(settingsProperties.getProperty("db.userpassword")); dataSource.setDriverClassName(settingsProperties.getProperty("db.driver")); return dataSource; } In other words, when you give up the idea of configuring the class's properties using annotations, you can do pretty much you want, like implement your own PropertyPlaceholderConfigurer, since you are in java. IMO, the PropertyPlaceholderConfigurer makes sense only in XML configurations. As mentioned previously,
For those interested in this issue, please take a look at the @PropertiesValueSource / @ExternalValue support coming out shortly in 1.0.0.m4. It's stable in the nightlies as well.
Linking to
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
@Configuration(defaultAutowire = Autowire.BY_NAME)
public abstract class Config extends ConfigurationSupport {
@ExternalBean
public abstract PropertyPlaceholderConfigurer propertyPlaceholderConfigurer();
@Bean(destroyMethodName = "close")
public DataSource dataSource() {
BasicDataSource ds = new BasicDataSource();
ds.setUrl(propertyPlaceholderConfigurer().parseStringValue(
"${jdbc.url}"));
ds.setUsername(propertyPlaceholderConfigurer().parseStringValue(
"${jdbc.username}"));
ds.setPassword(propertyPlaceholderConfigurer().parseStringValue(
"${jdbc.password}"));
ds.setDriverClassName(propertyPlaceholderConfigurer().parseStringValue(
"${jdbc.driverClassName}"));
ds.setPoolPreparedStatements(true);
return ds;
}
}
<bean id="propertyPlaceholderConfigurer"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"
p:location="classpath:applicationContext.properties" />
<bean class="Config" />
<bean class="org.springframework.config.java.process.ConfigurationPostProcessor" />
please notes: method PropertyPlaceholderConfigurer.parseStringValue(String str) hasn't presented yet,I subbmit a issue,please see http://opensource.atlassian.com/projects/spring/browse/SPR-3559