序
上一篇文章里,我们了解了 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 这个类里能找到答案。
public class LocalDataSourceLoader implements InstantiationAwareBeanPostProcessor {
private @Value("${soul.database.init_script:META-INF/schema.h2.sql}") String schemaSQLFile;
@Override
public Object postProcessAfterInitialization(@NonNull final Object bean, final String beanName) throws BeansException {
if (bean instanceof DataSourceProperties) {
this.init((DataSourceProperties) bean);
}
return bean;
}
}
可以看到上面的内容很简单,就实现了一个 InstantiationAwareBeanPostProcessor 接口,然后重写了 postProcessAfterInitialization 这个方法。
看到上面的内容是不是有些熟悉?
没错,在之前的文章里简单介绍过 BeanPostProcessor 这个接口的几个方法,主要作用就是在 Spring Bean 的自定义初始化方法执行完成之后执行。
那么 InstantiationAwareBeanPostProcessor 这个接口跟 BeanPostProcessor 又有什么关系呢?前者继承了后者,也就是说上面的 postProcessAfterInitialization 方法本就是 BeanPostProcessor 接口类里面对应方法的实现。
所以,利用这个 postProcessAfterInitialization 方法就可以实现在程序启动时去先执行数据库的初始化。
那么应该怎么去执行数据库的初始化呢?
- 拿到数据库连接
- 拿到数据库脚本文件数据
- 交给数据库连接去执行
我们可以看 LocalDataSourceLoader 类剩下的代码:
public class LocalDataSourceLoader {
// 拿到初始化数据库的脚本文件
private @Value("${soul.database.init_script:META-INF/schema.h2.sql}") String schemaSQLFile;
protected void init(final DataSourceProperties properties) {
// 从数据源属性类拿到数据库连接
String jdbcUrl = StringUtils.replace(properties.getUrl(), "/soul?", "?");
Connection connection = DriverManager.getConnection(jdbcUrl, properties.getUsername(), properties.getPassword());
this.execute(connection);
}
// 交给数据库连接去执行初始化脚本
private void execute(final Connection conn) throws Exception {
ScriptRunner runner = new ScriptRunner(conn);
// doesn't print logger
runner.setLogWriter(null);
Resources.setCharset(StandardCharsets.UTF_8);
Reader read = Resources.getResourceAsReader(schemaSQLFile);
log.info("execute soul schema sql: {}", schemaSQLFile);
runner.runScript(read);
runner.closeConnection();
conn.close();
}
}
可以看到数据库的脚本文件是从配置文件里面拿到的;数据库连接是从数据源属性里面取出 url 构造出来的;最后的执行就使用读取文件流的方式去执行了。
总结
以上就是 soul-admin 初始化数据库脚本的过程和代码了,总的来说一点也不复杂。
但是一样有几点做的比较巧妙:
- 在 DataSourceProperties Bean 初始化完成之后就开始执行数据库脚本的初始化
- 使用 DataSourceProperties 里面的属性直接就可以拿到数据的连接
下一篇文章里,我们分析 soul-admin 模块里面的数据同步代码。