在液化物 CustomTaskChange 类中使用其他弹簧豆

2022-09-03 07:41:03

我需要做一些数据迁移,这太复杂了,无法在液化基变更集中进行。我们使用弹簧

这就是为什么我写了一个实现liquibase.change.custom.CustomTaskChange类的类。然后,我从变更集中引用它。

到目前为止,一切都很好。

我的问题是:是否有可能从这样的班级中获取其他春豆?

当我尝试在此类中使用自动布线的bean时,它是null,这使我认为自动布线在这一点上根本没有完成?

我还在其他一些线程中读到,Liquibase bean必须在所有其他bean之前初始化,这是正确的吗?

以下是我写的类的一个片段:

@Component
public class UpdateJob2 implements CustomTaskChange {

private String param1;

@Autowired
private SomeBean someBean;

@Override
public void execute(Database database) throws CustomChangeException {
    try {
        List<SomeObject> titleTypes = someBean.getSomeObjects(
                param1
        );
    } catch (Exception e) {         
        throw new CustomChangeException();
    }
...

我得到一个异常,在调试时,我可以看到someBean是空的。

以下是SpringLiquibase的配置:

@Configuration
@EnableTransactionManagement(proxyTargetClass = true)
@ComponentScan({
"xxx.xxx.."})
public class DatabaseConfiguration {

@Bean
public SpringLiquibase springLiquibase() {
    SpringLiquibase liquibase = new SpringLiquibase();
    liquibase.setDataSource(dataSource());
    liquibase.setChangeLog("classpath:liquibase-changelog.xml");
    return liquibase;
}
...

更多配置:

<?xml version="1.0" encoding="UTF-8"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
               xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
               xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog
     http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.1.xsd">

    <includeAll path="dbschema"/>

</databaseChangeLog>

这里是从变更集调用的:

<?xml version="1.0" encoding="UTF-8"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
               xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
               xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog
     http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.1.xsd">

<changeSet id="201509281536" author="sr">
        <customChange class="xxx.xxx.xxx.UpdateJob2">
            <param name="param1" value="2" />
        </customChange>
</changeSet>


答案 1

我目前也在遇到这个问题...经过数小时的挖掘,我发现了2种解决方案,不需要AOP。

液化物版本: 4.1.1


解决方案 A

在官方示例中自定义更改

https://docs.liquibase.com/change-types/community/custom-change.html

在CustomChange.setFileOpener中,ResourceAccessor实际上是一个内部类SpringLiquibase$SpringResourceOpener,它有一个成员'resourceLoader',这确实是一个AppplicationContext。不幸的是,它是私有的,没有可用的getter。

所以这里有一个丑陋的解决方案:使用反射来获取它并调用getBean。


解决方案B(更优雅)

在我们开始之前,让我们看看关于Liquibase的一些基本事实。将Liquibase与Spring Boot集成的官方方法是使用:

org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration$LiquibaseConfiguration

这是一个有条件的内部配置bean,用于仅在SpringLiquibase.class缺失时才创建SpringLiquibase。

@Configuration
@ConditionalOnMissingBean(SpringLiquibase.class)
@EnableConfigurationProperties({ DataSourceProperties.class,
        LiquibaseProperties.class })
@Import(LiquibaseJpaDependencyConfiguration.class)
public static class LiquibaseConfiguration {...}

因此,我们可以通过添加一个 liquibase 配置豆来创建我们自己的 SpringLiquibase。

@Getter
@Configuration
@EnableConfigurationProperties(LiquibaseProperties.class)
public class LiquibaseConfig {

    private DataSource dataSource;

    private LiquibaseProperties properties;

    public LiquibaseConfig(DataSource dataSource, LiquibaseProperties properties) {
        this.dataSource = dataSource;
        this.properties = properties;
    }

    @Bean
    public SpringLiquibase liquibase() {
        SpringLiquibase liquibase = new BeanAwareSpringLiquibase();
        liquibase.setDataSource(dataSource);
        liquibase.setChangeLog(this.properties.getChangeLog());
        liquibase.setContexts(this.properties.getContexts());
        liquibase.setDefaultSchema(this.properties.getDefaultSchema());
        liquibase.setDropFirst(this.properties.isDropFirst());
        liquibase.setShouldRun(this.properties.isEnabled());
        liquibase.setLabels(this.properties.getLabels());
        liquibase.setChangeLogParameters(this.properties.getParameters());
        liquibase.setRollbackFile(this.properties.getRollbackFile());
        return liquibase;
   }
}

在其中,我们更新了SpringLiquibase的扩展类:BeanAwareSpringLiquibase。

public class BeanAwareSpringLiquibase extends SpringLiquibase {
private static ResourceLoader applicationContext;

public BeanAwareSpringLiquibase() {
}

public static final <T> T getBean(Class<T> beanClass) throws Exception {
    if (ApplicationContext.class.isInstance(applicationContext)) {
        return ((ApplicationContext)applicationContext).getBean(beanClass);
    } else {
        throw new Exception("Resource loader is not an instance of ApplicationContext");
    }
}

public static final <T> T getBean(String beanName) throws Exception {
    if (ApplicationContext.class.isInstance(applicationContext)) {
        return ((ApplicationContext)applicationContext).getBean(beanName);
    } else {
        throw new Exception("Resource loader is not an instance of ApplicationContext");
    }
}

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

BeanAwareSpringLiquibase对上述ResourceLoader有一个静态引用。在Spring Bootstartup上,ResourceLoaderAware接口定义的'setResourceLoader'将在初始化Bean接口定义的'afterPropertiesSet'之前自动调用,因此代码执行将如下所示:

  1. Spring Boot invoke setResourceLoader, injecting resourceLoader(applicationContext) to BeanAwareSpringLiquibase.

  2. Spring Boot 在 PropertiesSet 之后调用,执行 Liquibase 更新(包括 customChange),现在您已经拥有了对 applicationContext 的完全访问权限

附言:

  1. 请记住将 Liquibase 配置 bean 包路径添加到@ComponentScan否则它仍然会使用 LiquibaseAutoConfig,而不是我们自己的 LiquibaseConfig。

  2. 在“执行”之前,在“setUp”中准备所需的所有豆子将是一个更好的约定。


答案 2

变更集中引用的类.xml不是由Spring管理的,因此像DI这样的很酷的东西将不起作用。

您可以做的是将Spring Bean注入Non-Spring对象中。看到这个答案:https://stackoverflow.com/a/1377740/4365460


推荐