In follow-up, I have managed to get a solution working. It requires I sacrifice some of the features, but I think it still works alright.
1) One key thing I use with Spring XML is PropertyPlaceHolderConfigurer
Code:
<bean id="properties" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath:MyApp_${context}.props</value>
<value>classpath:Common.props</value>
</list>
</property>
<property name="systemPropertiesModeName">
<value>SYSTEM_PROPERTIES_MODE_OVERRIDE</value>
</property>
</bean>
Because I want it to pick out a certain properties file (dev/test/production), I launch my app with "-Dcontext=production" argument. This doesn't carry over to Spring JavaConfig very handily.
To deal with this, I wrote a static method call that loads all my system properties into a class filled with static attributes. The static function call reads the system property, and then conveniently uses DefaultResourceLoader (from Spring core) to load things up:
Code:
public static void loadProperties() {
if (System.getProperty("context") == null) {
throw new RuntimeException("System property 'context' must be specified. Suggest 'dev', 'test', or 'production'");
}
if (resourceLoader == null) {
resourceLoader = new DefaultResourceLoader();
}
if (props == null) {
props = new Properties();
try {
props.load(resourceLoader.getResource("classpath:MyApp" + "_" + System.getProperty("context") + ".props").getInputStream());
props.load(resourceLoader.getResource("classpath:Common.props").getInputStream());
} catch (IOException e) {
e.printStackTrace();
throw new RuntimeException(e);
}
MyAppProperties.init(props);
}
}
To bring this into Spring JavaConfig, I just created a constructor call that calls this static function, and now I have programmatic access to all of these properties.
Code:
@Configuration
public class AppContextConfig {
public AppContextConfig() {
MyApp.loadProperties();
}
@Bean
public BasicDataSource dataSource() {
BasicDataSource dataSource = new BasicDataSource();
dataSource.setDriverClassName(MyAppProperties.DatabaseDriver);
dataSource.setUrl(MyAppProperties.DatabaseConnection);
dataSource.setUsername(MyAppProperties.DBUserName);
dataSource.setPassword(MyAppProperties.DBPassword);
return dataSource;
}
...
This seems to do the trick for me.
2) Because I have both Spring AOP and AspectJ proxies a la load time weaving, I must still use a wrapping ClassPathXmlApplicationContext in order to define those parts. On the positive side, Spring JavaConfig already documented how to do that. On the negative side, my Java code can't see the nice APIs of JavaConfigApplicationContext.
Code:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd">
<bean class="com.harris.myapp.AppContextConfig"/>
<bean class="org.springframework.config.java.process.ConfigurationPostProcessor"/>
<context:load-time-weaver/>
<bean id="securityAspect" class="com.harris.myapp.aop.SecurityAspect" factory-method="aspectOf">
<property name="securityInterceptor"><ref bean="securityInterceptor"/></property>
</bean>
</beans>
One downside to this is the fact that Spring IDE has a warning where I have securityAspect referring to securityInterceptor, both of which are defined in separate places. However, running the app appears to work correctly.
3) My TestNG/JUnit regression test suite passed with flying colors! That tells me this is usable. Hooray!