【问题标题】:Spring Boot Custom Bean LoaderSpring Boot 自定义 Bean 加载器
【发布时间】:2020-08-15 00:30:45
【问题描述】:

我将 JDBI 与 Spring Boot 结合使用。我遵循了这个 guide 导致必须创建一个类:JdbiConfig 在其中,对于应用程序上下文中需要的每个 dao,您必须添加:

@Bean
public SomeDao someDao(Jdbi jdbi) {
    return jdbi.onDemand(SomeDao.class);
}

我想知道 Spring Boot 中是否有某种方法可以创建自定义处理器来创建 bean 并将它们放入应用程序上下文中。我对这如何工作有两个想法:

  1. 使用自定义注解@JdbiDao 对 DAO 进行注解,然后编写一些内容来获取这些内容。我尝试手动将这些注入到应用程序启动中,但问题是它们可能无法及时加载以注入,因为它们在类扫描期间无法识别。
  2. 创建一个类JdbiDao,每个存储库接口都可以扩展。然后用标准@Repository注释接口并创建一个自定义处理器以通过Jdbi#onDemand加载它们

这是我的两个想法,但我不知道有什么方法可以实现。我坚持手动创建一个bean?这个问题之前解决了吗?

【问题讨论】:

  • 您是否已经检查过 spring-data-jdbc 项目并且它没有涵盖您将与 jdbi 一起使用的东西?坚持使用 Spring 生态系统可能更容易(开发+测试方面)。

标签: java spring-boot jdbi spring-autoconfiguration jdbi3


【解决方案1】:

策略是扫描你的类路径寻找 dao 接口,然后将它们注册为 bean。

我们需要:BeanDefinitionRegistryPostProcessor 来注册额外的 bean 定义和 FactoryBean 来创建 jdbi dao bean 实例。

  1. @JdbiDao标记你的dao接口
@JdbiDao
public interface SomeDao {
}
  1. 定义一个FactoryBean来创建jdbi dao
public class JdbiDaoBeanFactory implements FactoryBean<Object>, InitializingBean {

    private final Jdbi jdbi;
    private final Class<?> jdbiDaoClass;
    private volatile Object jdbiDaoBean;

    public JdbiDaoBeanFactory(Jdbi jdbi, Class<?> jdbiDaoClass) {
        this.jdbi = jdbi;
        this.jdbiDaoClass = jdbiDaoClass;
    }

    @Override
    public Object getObject() throws Exception {
        return jdbiDaoBean;
    }

    @Override
    public Class<?> getObjectType() {
        return jdbiDaoClass;
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        jdbiDaoBean = jdbi.onDemand(jdbiDaoClass);
    }
}
  1. 扫描 @JdbiDao 注释接口的类路径:
public class JdbiBeanFactoryPostProcessor
        implements BeanDefinitionRegistryPostProcessor, ResourceLoaderAware, EnvironmentAware, BeanClassLoaderAware, BeanFactoryAware {

    private BeanFactory beanFactory;
    private ResourceLoader resourceLoader;
    private Environment environment;
    private ClassLoader classLoader;

    @Override
    public void setResourceLoader(ResourceLoader resourceLoader) {
        this.resourceLoader = resourceLoader;
    }

    @Override
    public void setEnvironment(Environment environment) {
        this.environment = environment;
    }

    @Override
    public void setBeanClassLoader(ClassLoader classLoader) {
        this.classLoader = classLoader;
    }

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        this.beanFactory = beanFactory;
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {

    }

    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
        ClassPathScanningCandidateComponentProvider scanner = new ClassPathScanningCandidateComponentProvider(false) {
            @Override
            protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
                // By default, scanner does not accept regular interface without @Lookup method, bypass this
                return true;
            }
        };
        scanner.setEnvironment(environment);
        scanner.setResourceLoader(resourceLoader);
        scanner.addIncludeFilter(new AnnotationTypeFilter(JdbiDao.class));
        List<String> basePackages = AutoConfigurationPackages.get(beanFactory);
        basePackages.stream()
                .map(scanner::findCandidateComponents)
                .flatMap(Collection::stream)
                .forEach(bd -> registerJdbiDaoBeanFactory(registry, bd));
    }

    private void registerJdbiDaoBeanFactory(BeanDefinitionRegistry registry, BeanDefinition bd) {
        GenericBeanDefinition beanDefinition = (GenericBeanDefinition) bd;
        Class<?> jdbiDaoClass;
        try {
            jdbiDaoClass = beanDefinition.resolveBeanClass(classLoader);
        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);
        }
        beanDefinition.setBeanClass(JdbiDaoBeanFactory.class);
        // Add dependency to your `Jdbi` bean by name
        beanDefinition.getConstructorArgumentValues().addGenericArgumentValue(new RuntimeBeanReference("jdbi"));
        beanDefinition.getConstructorArgumentValues().addGenericArgumentValue(Objects.requireNonNull(jdbiDaoClass));

        registry.registerBeanDefinition(jdbiDaoClass.getName(), beanDefinition);
    }
}
  1. 导入我们的JdbiBeanFactoryPostProcessor
@SpringBootApplication
@Import(JdbiBeanFactoryPostProcessor.class)
public class Application {
}

【讨论】:

  • 这行得通!我正在缩小范围,但不知道如何在工厂注册中添加 JDBI bean 作为参数。谢谢!
  • @JDrost1818 来晚了,但很高兴它有帮助
猜你喜欢
  • 2021-07-30
  • 2016-09-10
  • 1970-01-01
  • 2018-10-22
  • 2021-09-02
  • 1970-01-01
  • 2017-06-21
  • 1970-01-01
  • 2014-06-09
相关资源
最近更新 更多