在liquibase CustomTaskChange类中使用其他spring bean

时间:2015-09-28 15:26:56

标签: java spring liquibase

我需要进行一些数据迁移,这在liquibase变更集中太复杂了。我们用春天

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

到目前为止一切都很好。

我的问题是: 是否可以从这样的类中访问其他spring bean?

当我尝试在这个类中使用自动装配的bean时,它是null,这让我觉得此时根本没有完成自动装配?

我还在其他一些帖子中读过,必须在所有其他bean之前初始化Liquibase 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为null。

以下是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>

2 个答案:

答案 0 :(得分:2)

你的changeset.xml中引用的类不是由Spring管理的,所以像DI这样很酷的东西都行不通。

您可以做的是将Spring bean注入非Spring对象。请参阅此答案:https://stackoverflow.com/a/1377740/4365460

答案 1 :(得分:0)

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

Liquibase版本:4.1.1


解决方案A

在customChange的官方示例中

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

在CustomChange.setFileOpener中,ResourceAccessor实际上是SpringLiquibase $ SpringResourceOpener的内部类,并且它具有成员“ resourceLoader”,而该成员确实是ApplicationContext。不幸的是,它是私人的,没有可用的吸气剂。

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


解决方案B (更优雅)

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

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

这是用于创建SpringLiquibase的条件内部配置Bean 仅当SpringLiquibase.class丢失时

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

因此我们可以通过添加一个liquibase配置bean来创建自己的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'将在由InitializingBean接口定义的'afterPropertiesSet'之前自动调用,因此代码执行如下:

  1. Spring Boot调用setResourceLoader,将resourceLoader(applicationContext)注入BeanAwareSpringLiquibase。

  2. Spring Boot调用afterPropertiesSet,执行包括customChange在内的Liquibase更新,到目前为止,您已经具有对applicationContext的完全访问权限

PS:

  1. 请记住将您的Liquibase配置bean软件包路径添加到@ComponentScan,否则它将仍然使用LiquibaseAutoConfiguration代替我们自己的LiquibaseConfig。

  2. 在“执行”将是更好的约定之前,准备“设置”中需要的所有bean。