是否可以将包中的所有类注册为Spring bean

时间:2016-05-31 14:22:50

标签: java spring groovy dependency-injection spring-boot

我熟悉基于Springs Java的配置选项,包括使用@Component@Configuration以及@Bean注释来注册Spring bean。

但是,在将一个体面的项目转换为Spring时,系统地触摸项目中的所有类并使用@Configuration @Bean进行更新或使用{{1注释每个类}}。我们有一个很大的Groovy项目要转换,我想简化这个过程。

我的问题: Spring中是否提供了一个工具,允许您告诉Spring在特定包中自动配置所有有效的bean候选类?

如果没有,还有哪些其他选择?

4 个答案:

答案 0 :(得分:8)

ClassPathBeanDefinitionScanner就是你所需要的。

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());
    }
}

如果需要,您可以在包含过滤器中传递自定义逻辑。在当前版本中,提供的包中的每个类都将作为bean包含。

但是不可能在类上自动构建正确的依赖结构,它实际上取决于你想要的范围。你需要亲自动手。

答案 1 :(得分:7)

我做的几乎和Roman一样,只是我在构建时,而不是在运行时,使用代码生成。这里的基本原理是,我非常喜欢魔法在构建时发生在部署时发生的魔法。

在最简单的版本中,编写一个扫描包的main方法(而不是反射api,我正在使用Guava的ClassPath扫描程序)并为它找到的每个类创建一个@Bean方法

对于代码生成,我使用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"));

  }
}

答案 2 :(得分:4)

您可以尝试使用自己的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
  }

}

请参阅https://github.com/sandarkin/so-q37548350

上的示例项目

答案 3 :(得分:1)

冒着听起来很原始的风险,为什么不在你的IDE中做一个简单的查找和替换(例如在一个包中搜索&#34;公共类&#34;用&#34替换; @Component公共类&# 34;)?这应该比以编程方式做任何事情要快得多。