在liquibase的CustomTaskChange类中使用其他的spring beans
在liquibase的CustomTaskChange类中使用其他的spring beans
我需要进行一些数据迁移,但是在liquibase changeset中做这个操作太复杂了。我们使用spring。
所以我写了一个实现了liquibase.change.custom.CustomTaskChange类的类,并在changeset中引用它。
到这一步都还好。
我的问题是:
在这样的类中是否可以访问其他的spring beans?
当我尝试在这个类中使用一个autowired 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 { ListtitleTypes = 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; } ...
还有一些其他的配置:
这是从changeset中的调用:
问题的原因是在Liquibase的CustomTaskChange类中无法使用其他的Spring beans。解决方法有两种:Solution A和Solution B。
Solution A是在官方的自定义变更示例中,通过使用反射来获取ApplicationContext并调用getBean方法来解决这个问题。
Solution B是通过创建一个自定义的Liquibase配置bean,并在其中创建一个扩展了SpringLiquibase的类BeanAwareSpringLiquibase来解决这个问题。BeanAwareSpringLiquibase类有一个对ApplicationContext的静态引用,可以在Spring Boot启动时自动注入ApplicationContext,从而在CustomTaskChange类中可以使用getBean方法来获取其他的Spring beans。
在CustomTaskChange对象中如何获取BeanAwareSpringLiquibase.getBean(...)呢?只需要直接调用BeanAwareSpringLiquibase.getBean方法即可。
需要注意的是,在使用自定义的Liquibase配置bean时,需要将其包路径添加到@SpringBootApplication注解的scanBasePackages属性中,以确保使用自定义的Liquibase配置而不是默认的LiquibaseAutoConfiguration。
另外,最好在execute方法之前的setUp方法中准备好所有需要使用的beans。
至于关于在创建新的模式(schema)时使用Liquibase的问题,目前还没有找到解决方法。
问题的出现原因是,某些bean(如JPA repositories)依赖于liquibase bean。在SpringLiquibase bean初始化时,liquibase运行changelogs,但整个Spring上下文在此时还没有完全加载。如果尝试自动装配依赖于liquibase的bean,在启动时会抛出异常。
为了解决这个问题,可以通过重写Spring Liquibase配置,并在自定义任务上设置一个静态字段来实现。在配置中设置字段可以确保在changeset运行之前设置该字段。
以下是解决方法的代码示例:
/** 任务具有一个在LiquibaseConfiguration类中设置的静态成员。 */ public class MyCustomTask implements CustomTaskChange { private static MyBean myBean; public static void setMyBean(MyBean myBean) { MyCustomTask.myBean = myBean; } public void execute(Database database) throws CustomChangeException { try { JdbcConnection jdbcConnection = (JdbcConnection) database.getConnection(); // 使用myBean进行操作 } catch (DatabaseException | SQLException e) { throw new CustomChangeException(e); } } } /** 扩展SpringBoot Liquibase自动配置 org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration.LiquibaseConfiguration */ (proxyBeanMethods = false) @SpringLiquibase.class ({DataSourceProperties.class, LiquibaseProperties.class}) public static class MyLiquibaseConfiguration extends LiquibaseAutoConfiguration.LiquibaseConfiguration { /** * 自动装配myBean并将其设置在MyCustomTask上。 * * properties Liquibase的配置属性。 * myBean 自定义的bean。 */ public MigrationLiquibaseConfiguration(LiquibaseProperties properties, MyBean myBean) { super(properties); MyCustomTask.setMyBean(myBean); } }
这种技术比静态地暴露整个应用程序上下文更安全。只有需要的字段会被传递给任务,并且之后这些字段不可公开访问。