I'm familiar with Springs Java based configuration options, including the usage of @Component
and @Configuration
in conjunction with @Bean
annotations to register Spring beans.
However, when converting a decent size project to Spring, it can be very labor intensive to systematically touch all classes in the project and update with @Configuration
@Bean
s or annotating each class with @Component
. We have a large Groovy project to be converted and I would like to simplify the process.
My question: Is there a facility provided in Spring that allows you to tell Spring to auto-configure all valid bean candidate classes within a specific package?
If not, what other options are available?
ClassPathBeanDefinitionScanner
is all you need.
public class Main {
public static void main(String[] args) {
GenericApplicationContext context = new GenericApplicationContext();
ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(context, false);
scanner.addIncludeFilter((metadataReader, metadataReaderFactory) -> true);
scanner.scan("net.company.name");
context.refresh();
A a = context.getBean(A.class);
System.out.println(a.toString());
}
}
You can pass custom logic in include filter if you want. In current version every class in the provided package will be included as a bean.
But it is impossible to build a right dependency structure on your classes automagically, it really depends on the scope you want. You need to do it by your hands.
I'd do pretty much the same thing that Roman did, only I'd do it at build time, not at runtime, using code generation. The rationale here is that I strongly prefer magic to happen at build time to magic that happens at deploy time.
In the simplest version, write a main method that scans the package (instead of reflections api, I'm using Guava's ClassPath scanner) and creates a @Bean
method for every class it finds.
For the Code generation, I'd use JCodeModel:
public class PackageBeanGenerator {
public static void main(String[] args) throws Exception {
String packageName = args[0];
JCodeModel codeModel = new JCodeModel();
// create class definition
JDefinedClass springConfig = codeModel._package(packageName)._class("SpringConfig");
springConfig.annotate(Configuration.class);
for (ClassPath.ClassInfo classInfo : ClassPath.from(
PackageBeanGenerator.class.getClassLoader()
).getTopLevelClasses(packageName)) {
Class<?> type = classInfo.load();
String beanName = CaseFormat.UPPER_CAMEL.to(
CaseFormat.LOWER_CAMEL,
type.getSimpleName());
JMethod beanMethod = springConfig.method(JMod.PUBLIC, type, beanName);
beanMethod.annotate(Bean.class);
beanMethod.body()._return(JExpr._new(codeModel._ref(type)));
}
// write class to file
codeModel.build(new File("/path/to/output/folder"));
}
}
You can try to use your own BeanDefinitionRegistryPostProcessor
@Component
public class CustomBeanFactoryPostProcessor implements BeanDefinitionRegistryPostProcessor {
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
Reflections reflections = new Reflections("my.package.prefix", new SubTypesScanner(false));
Set<Class<? extends Object>> allClasses = reflections.getSubTypesOf(Object.class);
for (Class clazz : allClasses) {
GenericBeanDefinition gbd = new GenericBeanDefinition();
gbd.setBeanClass(clazz);
gbd.setAttribute("attributeName", "attributeValue");
registry.registerBeanDefinition(clazz.getSimpleName() + "_Bean", gbd);
}
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
// Custom post process the existing bean definitions
}
}
See sample project at https://github.com/sandarkin/so-q37548350
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With