Spring Boot自定义Bean加载器

7

我同时使用JDBI和Spring Boot。我遵循了这个指南,结果需要创建一个名为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生态系统可能更容易。 - Nico Van Belle
1个回答

4
扫描类路径寻找dao接口,然后将它们注册为bean是一种策略。
我们需要BeanDefinitionRegistryPostProcessor来注册额外的bean定义和一个FactoryBean来创建jdbi dao bean实例。
在你的dao接口上标记@JdbiDao
@JdbiDao
public interface SomeDao {
}

定义一个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);
    }
}

扫描类路径以查找带有@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
@JDrost1818,虽然已经很晚了,但很高兴能够帮到你。 - Mạnh Quyết Nguyễn

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接