上一篇文章里,我们了解了 soul-admin 的功能和数据库表结构,今天开始,我们就从源码上分析 soul-admin 启动之后的事情吧。

在前面的文章里只提到了 soul-admin 会启动 web socket 服务用来给真实的服务和 soul 网关同步数据,但这只是 soul-admin 启动之后做的一部分事情。

接下来我们看看源码部分吧。

初始化数据库

soul-admin 模块是有数据库参与的,那么也就需要提供数据库的支持,包括数据库的连接和脚本的初始化。

我们就先从这一部分开始了解吧。

从配置文件里可以看到 soul-admin 默认提供了 MySQL 和 H2 两种数据库的支持,在 /resources/META-INF 目录下有 MySQL 和 H2 数据库的初始化脚本文件。

找到了初始化数据库的脚本文件,接下来就是去找程序会在哪里去执行这个脚本了。

我们在 org.dromara.soul.admin.service.init.LocalDataSourceLoader.java 这个类里能找到答案。

  1. public class LocalDataSourceLoader implements InstantiationAwareBeanPostProcessor {
  2. private @Value("${soul.database.init_script:META-INF/schema.h2.sql}") String schemaSQLFile;
  3. @Override
  4. public Object postProcessAfterInitialization(@NonNull final Object bean, final String beanName) throws BeansException {
  5. if (bean instanceof DataSourceProperties) {
  6. this.init((DataSourceProperties) bean);
  7. }
  8. return bean;
  9. }
  10. }

可以看到上面的内容很简单,就实现了一个 InstantiationAwareBeanPostProcessor 接口,然后重写了 postProcessAfterInitialization 这个方法。

看到上面的内容是不是有些熟悉?

没错,在之前的文章里简单介绍过 BeanPostProcessor 这个接口的几个方法,主要作用就是在 Spring Bean 的自定义初始化方法执行完成之后执行。

那么 InstantiationAwareBeanPostProcessor 这个接口跟 BeanPostProcessor 又有什么关系呢?前者继承了后者,也就是说上面的 postProcessAfterInitialization 方法本就是 BeanPostProcessor 接口类里面对应方法的实现。

所以,利用这个 postProcessAfterInitialization 方法就可以实现在程序启动时去先执行数据库的初始化。

那么应该怎么去执行数据库的初始化呢?

  1. 拿到数据库连接
  2. 拿到数据库脚本文件数据
  3. 交给数据库连接去执行

我们可以看 LocalDataSourceLoader 类剩下的代码:

  1. public class LocalDataSourceLoader {
  2. // 拿到初始化数据库的脚本文件
  3. private @Value("${soul.database.init_script:META-INF/schema.h2.sql}") String schemaSQLFile;
  4. protected void init(final DataSourceProperties properties) {
  5. // 从数据源属性类拿到数据库连接
  6. String jdbcUrl = StringUtils.replace(properties.getUrl(), "/soul?", "?");
  7. Connection connection = DriverManager.getConnection(jdbcUrl, properties.getUsername(), properties.getPassword());
  8. this.execute(connection);
  9. }
  10. // 交给数据库连接去执行初始化脚本
  11. private void execute(final Connection conn) throws Exception {
  12. ScriptRunner runner = new ScriptRunner(conn);
  13. // doesn't print logger
  14. runner.setLogWriter(null);
  15. Resources.setCharset(StandardCharsets.UTF_8);
  16. Reader read = Resources.getResourceAsReader(schemaSQLFile);
  17. log.info("execute soul schema sql: {}", schemaSQLFile);
  18. runner.runScript(read);
  19. runner.closeConnection();
  20. conn.close();
  21. }
  22. }

可以看到数据库的脚本文件是从配置文件里面拿到的;数据库连接是从数据源属性里面取出 url 构造出来的;最后的执行就使用读取文件流的方式去执行了。

总结

以上就是 soul-admin 初始化数据库脚本的过程和代码了,总的来说一点也不复杂。

但是一样有几点做的比较巧妙:

  1. 在 DataSourceProperties Bean 初始化完成之后就开始执行数据库脚本的初始化
  2. 使用 DataSourceProperties 里面的属性直接就可以拿到数据的连接

下一篇文章里,我们分析 soul-admin 模块里面的数据同步代码。