Issue
I am using spring boot which does autoscan of beans during start up.
Normally i know it scans beans marked with @service
, @repository
and @component
.
Than in the @Configuration
we have @Bean
annotation.
Is there any way I can implement some interface and it will become part of scanning if the package name is different which is provided in
@SpringBootApplication(scanBasePackages = "xyz.yah.*")
Problem is my libraries have different package names and it becomes little cumbersome to change the scanBasePackages property so i was wondering if i my bean can implement some interface through which it immediately becomes part of auto scan. Any solutions ?
Also not sure if i just implement Aware interface will be enough to be eligible for Autoscanning.
Solution
Thinking more on this, this should work for you. However it's additional black magic, and hiding of where/how beans are getting created, naming, scopes, etc. etc. . I'd recommend creating configuration classes etc.
Using https://github.com/ronmamo/reflections,
Reflections reflections = new Reflections("com.mycompany.basepackage");
Set<Class<? extends MyInterface>> classes = reflections.getSubTypesOf(MyInterface.class);
Will give you a set of all classes of your interface. This can be pulled together to dynamically load the beans on startup like this,
@Configuration
public class AwareConfig implements BeanDefinitionRegistryPostProcessor {
private static final Logger LOG = LoggerFactory.getLogger(AwareConfig.class);
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
Reflections reflections = new Reflections(this.getClass().getPackage().getName());
Set<Class<? extends Aware>> classes = reflections.getSubTypesOf(Aware.class);
classes.stream().forEach(clazz -> {
try {
BeanDefinitionBuilder builder = BeanDefinitionBuilder
.genericBeanDefinition(Class.forName(clazz.getName())).setLazyInit(false);
((DefaultListableBeanFactory) beanFactory).registerBeanDefinition(clazz.getName(),
builder.getBeanDefinition());
} catch (ClassNotFoundException e) {
throw new IllegalArgumentException(e);
}
LOG.info("Creating - {} bean", clazz.getName());
});
}
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry arg0) throws BeansException {
//NoOp
}
}
You could use other types of bean post processors depending on usecase. Also doesn't have to be an interface could drive it by annotations, allowing you to specify arguments easier rather than implement them via an interface.
I've created a working example here,
https://github.com/Flaw101/dynamically-load-spring-bean
Bare in mind this is fairly simple and the more complex your beans are the more issues loading and injecting them will be.
I've updated the example to inject the two dynamic beans into a service
class.
Answered By - Darren Forsythe