(1). 概述
从前面的学习,大概的了解到,Liquibase会读取XML,并转换成SQL文件,最终会在目标库运行脚本(保证一致性/元子性).
那么,Liquibase是如何解析XML的呢?能否支持扩展呢?答案就在:ChangeLogParserFactory.
(2). Liquibase.getDatabaseChangeLog
public class Liquibase implements AutoCloseable {
public void update(int changesToApply, Contexts contexts, LabelExpression labelExpression)
throws LiquibaseException {
// ... ...
// ************************************************************************
// 解析(XML/JSON/YAML/SQLFile),转换成业务模型:DatabaseChangeLog
// ************************************************************************
getDatabaseChangeLog()
// ... ...
}
public DatabaseChangeLog getDatabaseChangeLog() throws LiquibaseException {
// databaseChangeLog 解析XML/JSON/YML之后的业务模型对象.
// changeLogFile 待解析的数据文件.
if (databaseChangeLog == null && changeLogFile != null) {
// ************************************************************************
// 2. ChangeLogParserFactory为典型的工厂模式+单例模式
// ************************************************************************
// ************************************************************************
// 通过SPI加载:ChangeLogParser类的所有实现类,遍历所有的:ChangeLogParser.supports方法,是否支持解析
// supports比较简单,判断changeLogFile的后缀即可.
// ChangeLogParser的实现类有: YamlChangeLogParser/SqlChangeLogParser/XMLChangeLogSAXParser/...
// ************************************************************************
ChangeLogParser parser = ChangeLogParserFactory.getInstance().getParser(changeLogFile, resourceAccessor);
// ************************************************************************
// 委托给ChangeLogParser.parse方法进行解析(我这里是XML,所以,会委托给:XMLChangeLogSAXParser).
// ************************************************************************
databaseChangeLog = parser.parse(changeLogFile, changeLogParameters, resourceAccessor);
}
return databaseChangeLog;
} //end getDatabaseChangeLog
}
(3). ChangeLogParserFactory.getInstance
public class ChangeLogParserFactory {
private static ChangeLogParserFactory instance;
private List<ChangeLogParser> parsers = new ArrayList<>();
public static synchronized ChangeLogParserFactory getInstance() {
if (instance == null) {
// ******************************************************************
// 1. 典型的单例,构建出:ChangeLogParserFactory
// ******************************************************************
instance = new ChangeLogParserFactory();
}
return instance;
} // end getInstance
private ChangeLogParserFactory() {
try {
// ******************************************************************
// 2. Scope有点类似于Spring中的容器,这里典型的组合模式.
// ******************************************************************
List<ChangeLogParser> parser = Scope.getCurrentScope().getServiceLocator().findInstances(ChangeLogParser.class);
register(parser);
} catch (Exception e) {
throw new UnexpectedLiquibaseException(e);
}
} // end ChangeLogParserFactory
}
(4). Scope
public class Scope {
// 定义有哪些可枚举的类
public enum Attr {
logService,
ui,
resourceAccessor,
classLoader,
database,
quotingStrategy,
changeLogHistoryService,
lockService,
executeMode,
lineSeparator,
serviceLocator,
fileEncoding,
databaseChangeLog,
changeSet,
} // end Attr
private static ScopeManager scopeManager;
// 典型的组合模式,在Java的ClassLoader里有这样的写法,在Spring国际化支持也有这样的写法
// 目的是:Scop是有层级的,各司其职.
private Scope parent;
//
private SmartMap values = new SmartMap();
private String scopeId;
private LiquibaseListener listener;
public static Scope getCurrentScope() {
// 这样初始化:SingletonScopeManager是否会存在并发的问题,不过,Liquibase本身就不支持并发操作.
if (scopeManager == null) {
scopeManager = new SingletonScopeManager();
}
// 可以理解为,这里是Spring的父容器,通过父容器,加载一些常用的类实例.而子容器加载一些业务性质的类实例.
if (scopeManager.getCurrentScope() == null) {
Scope rootScope = new Scope();
scopeManager.setCurrentScope(rootScope);
// rootScope创建logService-->JavaLogService的映射关系
rootScope.values.put(Attr.logService.name(), new JavaLogService());
// rootScope创建resourceAccessor-->ClassLoaderResourceAccessor的映射关系
rootScope.values.put(Attr.resourceAccessor.name(), new ClassLoaderResourceAccessor());
// rootScope创建serviceLocator-->StandardServiceLocator的映射关系
rootScope.values.put(Attr.serviceLocator.name(), new StandardServiceLocator());
// rootScope创建ui-->ConsoleUIService的映射关系
rootScope.values.put(Attr.ui.name(), new ConsoleUIService());
// ***********************************************************************************
// 1. 通过反射创建:LiquibaseConfiguration,并调用:init方法
// ***********************************************************************************
rootScope.getSingleton(LiquibaseConfiguration.class).init(rootScope);
// 重写:LogService
LogService overrideLogService = rootScope.getSingleton(LogServiceFactory.class).getDefaultLogService();
if (overrideLogService == null) {
throw new UnexpectedLiquibaseException("Cannot find default log service");
}
rootScope.values.put(Attr.logService.name(), overrideLogService);
// 设置优先级最高的:ServiceLocator
//check for higher-priority serviceLocator
ServiceLocator serviceLocator = rootScope.getServiceLocator();
for (ServiceLocator possibleLocator : serviceLocator.findInstances(ServiceLocator.class)) {
if (possibleLocator.getPriority() > serviceLocator.getPriority()) {
serviceLocator = possibleLocator;
}
}
rootScope.values.put(Attr.serviceLocator.name(), serviceLocator);
}
return scopeManager.getCurrentScope();
} // end getCurrentScope
public <T extends SingletonObject> T getSingleton(Class<T> type) {
// 优先从父Scope加载
if (getParent() != null) {
return getParent().getSingleton(type);
}
// 父Scope不存在的情况下,自己再进行加载
String key = type.getName();
T singleton = get(key, type);
if (singleton == null) {
try {
try {
// 尝试,通过有参(Scope),构建出Class<T> type,抛出异常的情况下,通过默认构造器,构建出Class<T> type.
Constructor<T> constructor = type.getDeclaredConstructor(Scope.class);
constructor.setAccessible(true);
singleton = constructor.newInstance(this);
} catch (NoSuchMethodException e) { //try without scope
// 通过默认构造器,构建出:Class<T> type对象.
Constructor<T> constructor = type.getDeclaredConstructor();
constructor.setAccessible(true);
singleton = constructor.newInstance();
}
} catch (Exception e) {
throw new UnexpectedLiquibaseException(e);
}
values.put(key, singleton);
}
return singleton;
} // end getSingleton
}
(5). LiquibaseConfiguration.init
public class LiquibaseConfiguration implements SingletonObject {
// init仅仅只是通过SPI加载了类的实现,并没有初始化,在3.6.x里,是调用相应的工厂时,才去加载.
// 现在变成了运行期就去加载.
public void init(Scope scope) {
// 先清一下缓存
configurationValueProviders.clear();
// *******************************************************************
// 1. 从scope里获得ServiceLocator,在这里按着现类为:StandardServiceLocator
// *******************************************************************
ServiceLocator serviceLocator = scope.getServiceLocator();
// *******************************************************************
// 2. 委托给:StandardServiceLocator.findInstances方法,去加载:AutoloadedConfigurations类的子类.
// *******************************************************************
final List<AutoloadedConfigurations> containers = serviceLocator.findInstances(AutoloadedConfigurations.class);
for (AutoloadedConfigurations container : containers) {
// 3. 通过日志记录.
Scope.getCurrentScope().getLog(getClass()).fine("Found ConfigurationDefinitions in " + container.getClass().getName());
}
// 4. 委托给:StandardServiceLocator.findInstances方法,去加载:ConfigurationValueProvider类的子类.
configurationValueProviders.addAll(serviceLocator.findInstances(ConfigurationValueProvider.class));
} //end init
}
(6). StandardServiceLocator.findInstances
public class StandardServiceLocator implements ServiceLocator {
public <T> List<T> findInstances(Class<T> interfaceType) throws ServiceNotFoundException {
// 所有实例的结果集
List<T> allInstances = new ArrayList<>();
// 1. 获得日志服务
final Logger log = Scope.getCurrentScope().getLog(getClass());
// *******************************************************************
// 2. 这里,又把请求委托给了:java.util.ServiceLoader.load方法去加载Class<T>的所有实现类.
// 注意:这里用的是java提供的SPI,所以,只需要找这个文件即可:META-INF/services/{interfaceType}
// 相比,以前的3.6.x版本,提升了不少,感觉已经允许自己进行扩展了.
// *******************************************************************
final Iterator<T> services = ServiceLoader.load(interfaceType, Scope.getCurrentScope().getClassLoader(true)).iterator();
while (services.hasNext()) {
try {
final T service = services.next();
log.fine("Loaded "+interfaceType.getName()+" instance "+service.getClass().getName());
allInstances.add(service);
} catch (Throwable e) {
log.info("Cannot load service: "+e.getMessage());
log.fine(e.getMessage(), e);
}
}
return Collections.unmodifiableList(allInstances);
}// end
}
(7). ChangeLogParser.
(8). 总结
从上面的代码能剖析出:Liquibase对ChangeLogParser进行抽象,它的功能主要是对ChangeLog进行解析,ChangeLogParser实现类有:YamlChangeLogParser/SqlChangeLogParser/XMLChangeLogSAXParser.
Liquibase在3.6.x版本时,SPI是自己写的,而在最新版本的时候(4.5.0),开始进行了优化,把SPI交给了jdk,所以,你想扩展的话,只要符合jdk SPI规范即可!