Use case: I have my container configured via classpath scanning @ComponentScan. For my test configuration I need the ability to mock specific beans.
Due to the order of loading, beans loaded via classpath scan are not overriding properly when using @Configuration. The following code samples demonstrate the problem. BaseExample.java shows how it is possible to override beans via configuration. ScanExample.java shows that overriding a bean that was loaded via @ComponentScan is skipped (see final note).
Output:Code:// BaseExample.java package com.glassworks.mock; import org.mockito.Mockito; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import com.google.common.base.Joiner; public class BaseExample { private static final Logger log = LoggerFactory.getLogger(BaseExample.class); private static AnnotationConfigApplicationContext ctx; public static void main(String args[]) { ctx = new AnnotationConfigApplicationContext(Config.class, OverrideConfig.class); String beans[] = ctx.getBeanDefinitionNames(); log.info("{} beans found: {}", beans.length, Joiner.on(",").join(beans)); for(String bean : beans) { log.info("{}: {}", bean, ctx.getBean(bean)); } } @Configuration public static class Config { @Bean public AccountDao accountDao() { log.debug("Creating accountDao [Config]"); return new AccountDao(); } } @Configuration public static class OverrideConfig { @Bean public Object accountDao() { log.debug("Creating accountDao [OverrideConfig]"); return Mockito.mock(AccountDao.class); } } }
21:05 INFO | com.glassworks.mock.BaseExample | accountDao: Mock for AccountDao, hashCode: 666537607
Output:Code:// ScanExample.java package com.glassworks.mock; import org.mockito.Mockito; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import com.google.common.base.Joiner; public class ScanExample { private static final Logger log = LoggerFactory.getLogger(ScanExample.class); private static AnnotationConfigApplicationContext ctx; public static void main(String args[]) { ctx = new AnnotationConfigApplicationContext(Config.class, OverrideConfig.class); String beans[] = ctx.getBeanDefinitionNames(); log.info("{} beans found: {}", beans.length, Joiner.on(",").join(beans)); for(String bean : beans) { log.info("{}: {}", bean, ctx.getBean(bean)); } } @Configuration @ComponentScan("com.glassworks.services") public static class Config { } @Configuration public static class OverrideConfig { @Bean public AccountDao accountDao() { log.debug("Creating accountDao [OverrideConfig]"); return Mockito.mock(AccountDao.class); } } }
21:08 INFO | com.glassworks.mock.ScanExample | accountDao: com.glassworks.services.AccountDao@48805ebb
NoteCode:// AccountDao.java package com.glassworks.services; import org.springframework.stereotype.Repository; @Repository public class AccountDao { }
Its worth noting on that with logging set to debug, Spring indicates that it is skipping over the definition. This appears to be a bug.
21:09 DEBUG | o.s.c.a.ConfigurationClassBeanDefinitionReader | Skipping loading bean definition for [BeanMethod:name=accountDao,declaringClass=com.glas sworks.mock.ScanExample$OverrideConfig]: a definition for bean 'accountDao' already exists. This is likely due to an override in XML.


Reply With Quote
