Spring 3.1 Java Configuration - @Autowired, @Configuration, and @Profile challenge
I'm playing around with Spring 3.1 and Servlet 3.0 and was really intrigued by the new Java configuration option as well as using the Spring profiles and am running into a challenge. Even though all of the examples I've seen online make it look like a class annotated with @Configuration can have other objects @Autowired into them what I'm seeing is that the autowiring doesn't happen until after a few of the beans in the @Configuration class have been generated.
Here's my initializer:
public class SpringMvcInitializer implements WebApplicationInitializer {
private static org.apache.log4j.Logger log= Logger.getLogger(SpringMvcInitializer.class);
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
// Create the 'root' Spring application context
AnnotationConfigWebApplicationContext rootContext = new AnnotationConfigWebApplicationContext();
rootContext.scan("org.jc.config");
servletContext.addListener(new ContextLoaderListener(rootContext));
// Secures the application
servletContext.addFilter("securityFilter", new DelegatingFilterProxy("springSecurityFilterChain"))
.addMappingForUrlPatterns(null, false, "/*");
ServletRegistration.Dynamic appServlet =
servletContext.addServlet("appServlet", new DispatcherServlet(new GenericWebApplicationContext()));
appServlet.setLoadOnStartup(1);
appServlet.addMapping("/");
log.info("Mvc Initializer starting");
}
My Web Config
@Configuration
@EnableWebMvc
@ComponentScan(basePackages="org.jc" )
public class WebConfig extends WebMvcConfigurerAdapter {
@Bean
public InternalResourceViewResolver configureInternalResourceViewResolver() {
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setPrefix("/WEB-INF/");
resolver.setSuffix(".jsp");
return resolver;
}
@Override
public void configureResourceHandling(ResourceConfigurer configurer) {
configurer.addPathMapping("/resources/**");
configurer.addResourceLocation("/resources/");
}
My Configuration Class for JPA
@Configuration
public class JpaConfig {
private static Logger logger = Logger.getLogger(JpaConfig.class);
private AppEnvironmentI appEnvironment;
@Autowired
public void setAppEnvironment(AppEnvironmentI appEnvironment) {
this.appEnvironment = appEnvironment;
checkAppEnv("setAppEnvironment");
}
@Bean
public AppUser globalUser(){
checkAppEnv("globalUser entry");
AppUser appUser = new AppUser();
checkAppEnv("globalUser exit");
return appUser;
}
@Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory(){
checkAppEnv("entityManagerFactory entry");
LocalContainerEntityManagerFactoryBean emf = new LocalContainerEntityManagerFactoryBean ();
emf.setDataSource(dataSource());
emf.setJpaVendorAdapter(this.jpaAdapter());
emf.setPersistenceUnitName("JcEntities2");
checkAppEnv("entityManagerFactory exit");
return emf;
}
@Bean
public DataSource dataSource(){
checkAppEnv("dataSource entry");
DriverManagerDataSource ds = new DriverManagerDataSource();
ds.setDriverClassName("org.apache.derby.jdbc.EmbeddedDriver");
ds.setUrl("jdbc:derby://localhost:1527/JcTestDb");
ds.setUsername("jc");
ds.setPassword("pwd");
checkAppEnv("dataSource exit");
return ds;
}
@Bean
public JpaVendorAdapter jpaAdapter() {
checkAppEnv("jpaAdapter entry");
HibernateJpaVendorAdapter hibernateJpaVendorAdapter = new HibernateJpaVendorAdapter();
hibernateJpaVendorAdapter.setShowSql(true);
hibernateJpaVendorAdapter.setDatabase(Database.DERBY);
checkAppEnv("jpaAdapter exit");
return hibernateJpaVendorAdapter;
}
@Bean
public PlatformTransactionManager transactionManager() {
checkAppEnv("transactionManager entry and exit");
return new JpaTransactionManager( entityManagerFactory().getObject() );
}
private void checkAppEnv(String _method){
if(this.appEnvironment == null){
logger.info(_method + " - App Environment is null!!!!");
}else{
logger.info(_method + " - App Environment JpaConfig = " + appEnvironment.externalPropertiesFile().getPropertyValue("environment"));
}
}
My Profile Class
@Configuration
@Profile("local")
public class AppEnvironmentLocal implements AppEnvironmentI{
private static Logger logger = Logger.getLogger(AppEnvironmentLocal.class);
@Bean
public AppEnvironment externalPropertiesFile(){
logger.info("Fetching properties file for LOCAL environment");
Properties props = FileUtilsJc.getPropertiesFromFlatFile("C:\\AppConf\\JcConf\\JcConfLocal.properties");
AppEnvironment exf = new AppEnvironment(props);
return exf;
}
}
Logger output: It looks like Spring 3.1 is first trying to create the Entity Manager Factory bean before the AppEnvironment is injected into the class. So it creates all of the beans except the Global User (a dummy bean I put in for testing) and Transaction Manager, injects the AppEnvironment, and then开发者_JS百科 creates the global user and transaction manager beans.
Any ideas greatly appreciated!
01-Sep-2011 10:20:55 INFO Mvc Initializer starting
01-Sep-2011 10:20:55 INFO entityManagerFactory entry - App Environment is null!!!!
01-Sep-2011 10:20:55 INFO dataSource entry - App Environment is null!!!!
01-Sep-2011 10:20:55 INFO dataSource exit - App Environment is null!!!!
01-Sep-2011 10:20:55 INFO jpaAdapter entry - App Environment is null!!!!
01-Sep-2011 10:20:55 INFO jpaAdapter exit - App Environment is null!!!!
01-Sep-2011 10:20:55 INFO entityManagerFactory exit - App Environment is null!!!!
01-Sep-2011 10:20:55 INFO Fetching properties file for LOCAL environment
01-Sep-2011 10:20:55 INFO setAppEnvironment - App Environment JpaConfig = LOCAL
Here's the URL for the sample code I used to wire this up:
http://blog.springsource.com/2011/02/14/spring-3-1-m1-introducing-profile/
I found documentation stating that prior to Spring 3.1 it was necessary to add the @AnnotationDrivenConfig annotation to your injectable class in order for @Autowired to work but this was deprecated in Spring 3.1 I found a bunch of posts looking for this annotation but nothing that addressed when a Configuration object will be injected!
As I understand it, @Configuration
beans are constructed before non-config beans and the methods tagged with @Bean
produce objects that are not yet beans at the point that they return; the order in which beans are constructed is otherwise undefined (except for a few special bean types; the standard property file loaders are called very early so that the values they discover can be used to set up a @Configuration
bean). Calling bean methods directly is frowned upon; let Spring do it for you as it wires up the properties after the @Bean
method returns.
精彩评论