Drools用户手册看了得有一段时间了,现在开始看源码了,因为每次使用drools都会看见这么一段代码:
代码段1 起手代码
KieServices ks = KieServices.Factory.get();
那我就从这段代码出发开始研究drools的源码吧,这么一小段代码起初我还真没看起它,结果被啪啪打脸了,里面东西可是多的狠啊,这段代码的目的不难看出来,就是获取KieServices的实例的,从代码来看,Factory就是KieSerices的内部类,然后其有一个get方法,用来获取KieServices的实例,源代码如下所示:
代码段2 KieServices类的内部类Factory
public static class Factory {
public Factory() {
}
public static KieServices get() {
return KieServices.Factory.LazyHolder.INSTANCE;
}
private static class LazyHolder {
private static KieServices INSTANCE = (KieServices)ServiceRegistry
.getService(KieServices.class);
private LazyHolder() {
}
}
}
只看get方法,这里直接就是一个返回语句,返回的是LazyHolder,这又是一个内部类,也就是说LazyHolder是内部类的内部类,这个内部类的内部类里面有一个静态属性,叫做INSTANCE(实例),这个INSTANCE是如何获取的?是从ServiceRegistry类的getService方法获取的,因为要获取的就是KieServices的实例,所以将其作为参数传了进去。
这个时候就要研究ServiceRegistry类了,从类名来看,这是一个服务注册类,也就是说在获取KieService之前,这个服务已经在Kie中注册了 ,使用的时候就是直接获取,来看看这个ServiceRegistry的getService方法的源代码:
代码段3 ServiceRegistry的getService方法
static <T> T getService(Class<T> cls) {
return getInstance().get(cls);
}
非常简单的代码,里面又涉及到了两个方法,一个是getInstance方法,一个是get方法。
ServiceRegistry类的getInstance方法
代码段4 ServiceRegistry的getinstance方法
static ServiceRegistry getInstance() {
return ServiceRegistry.ServiceRegistryHolder.serviceRegistry;
}
这个方法涉及到了ServiceRegistry类中的内部类ServiceRegistryHolder的一个属性——serviceRegistry。
代码段5 ServiceRegistry的内部类ServiceRegistryHolder
public static class ServiceRegistryHolder {
private static ServiceRegistry serviceRegistry = ServiceRegistry
.Impl
.getServiceRegistry();
public ServiceRegistryHolder() {
}
}
这是内部类ServiceRegistryHolder的全部源码,因为不多,就全放出来了,也很简单,serviceRegistry属性就是ServiceRegistry的内部类Impl中的getServiceRegistry方法,可以说从一个内部类调用另一个内部类的方法,我也是一时间没想到这么做的意义是什么,但是我之前在敲代码的时候好像也有这么干过,如果之后想起来,我再回来把想法给补上。
那就先来看看这个getServiceRegistry方法吧:
代码段6 ServiceRegistry的内部类Impl类的ServiceRegistry方法
public static ServiceRegistry() {
if (supplier == null) {
supplier = (Supplier)ServiceUtil
.instanceFromNames(
new String[]{"org.drools.dynamic.DynamicServiceRegistrySupplier",
"org.drools.statics.StaticServiceRegistrySupplier"});
}
return (ServiceRegistry)supplier.get();
}
这是将传入的字符串数组进行流化,然后按照下面顺序进行操作:
-
映射:对每一个字符串进行操作,调用ServiceUtil的instance,通过全路径名实例化类,代码很简单,如下所示:
代码段8private static Optional<?> instance(String className) { try { return Optional.of(Class.forName(className).getConstructor().newInstance()); } catch (Exception var2) { return Optional.empty(); } }
-
过滤:判断得到的实例化的类是否为空,如果为空则会被过滤掉
-
映射:将第二步没有过滤掉的实例从Optional中get出来
-
寻找:找到第一个符合条件的类,将其返回
-
异常:如果没有符合条件的类,就抛出异常
按照源码来走,这一步会获得到类DynamicServiceRegistrySupplier,源码如下:
代码段9 DynamicServiceRegistrySupplier源码
public class DynamicServiceRegistrySupplier implements Supplier<ServiceRegistry> {
public DynamicServiceRegistrySupplier() {
}
public ServiceRegistry get() {
return DynamicServiceRegistrySupplier.LazyHolder.INSTANCE;
}
static class LazyHolder {
static final Impl INSTANCE = new Impl();
LazyHolder() {
}
}
}
这一段代码会返回给代码段6,然后会执行get方法,这个方法返回的是ServiceRegistry内部类Impl的实例,这就是getInstance方法最终的结果,就是Impl的实例
内部类Impl的get方法
返回到代码段3中,里面的getInstance方法已经走了一遍,现在开始走这个get方法,其源码如下所示:
代码段10 ServiceRegistry的内部类Impl实现的get方法
public <T> T get(Class<T> cls) {
Iterator var2 = this.getAll(cls).iterator();
Object service;
do {
if (!var2.hasNext()) {
return null;
}
service = var2.next();
} while(!cls.isInstance(service));
return service;
}
这段代码先是调用了一个getAll的方法,看看getAll的源码:
代码段11 ServiceRegistry的内部类Impl的getAll方法
public <T> List<T> getAll(Class<T> cls) {
return (List)this.registry
.getOrDefault(cls.getCanonicalName(), Collections.emptyList());
}
这个代码看似很简单,但是其实大部分的内容都在这里,这段代码是通过传入类的全路径名获取一个列表,如果没有该全路径名,就会返回一个空列表,那这个registry属性究竟是个什么呢?
代码段12 ServiceRegistry的内部类Impl的registry属性
private Map<String, List<Object>> registry =
ServiceDiscoveryImpl.getInstance().getServices();
是一个映射,我们可以看到,之前的操作,可没有对这个映射进行初始化的地方,这个东西又是另外的一个路线了,也就是在前面代码段9,实例化Impl的时候,这里就已经初始化了,为了这一路可以顺利结束,我先跳过这一段,咱们后面再讲,咱们回到代码段10,通过getAll获取了一个服务列表,通过迭代找到KieServices实例,然后返回该实例,这个时候代码段1就完成了调用,获得了一个KieServices。
服务注册与配置
这把我们再来看这个代码段12里面的东西,首先是ServiceDiscoveryImpl类,这个类的描述是这样的:
代码段13
public class ServiceDiscoveryImpl {...}
既不是哪个类的子类,也不是什么类的实现类,他就是一个孤零零的,类。那这就简单多了,我们直奔这个getInstance方法而去:
代码段14 ServiceDiscoveryImpl的getInstance方法和内部类LazyHolder
public static ServiceDiscoveryImpl getInstance() {
return ServiceDiscoveryImpl.LazyHolder.INSTANCE;
}
private static class LazyHolder {
static final ServiceDiscoveryImpl INSTANCE = new ServiceDiscoveryImpl();
private LazyHolder() {
}
}
这个getInstance方法,和之前的的某些代码段用法一致,获取某个内部类的属性,这个属性就是这个孤零零的类的实例,然后再是代码段12里面的getServices方法:
代码段15 ServiceDiscoveryImpl的getServices方法
public synchronized Map<String, List<Object>> getServices() {
if (!this.sealed) {
this.getKieConfs().ifPresent((kieConfs) -> {
Iterator var2 = kieConfs.resources.iterator();
while(var2.hasNext()) {
URL kieConfUrl = (URL)var2.next();
this.registerConfs(kieConfs.classLoader, kieConfUrl);
}
});
//创建一个不可变的映射
this.cachedServices = Collections.unmodifiableMap(this.buildMap());
this.sealed = true;
}
return this.cachedServices;
}
这是一个同步方法,sealed初始默认值是false,所以进入方法后会直接进入到if语句中。先是获取到kie的配置内容,对非空配置进行遍历加载。获取配置是一个大活,操作异常的复杂,咱们就从这个getKieConfs方法开始走起:
代码段16 ServiceDiscoveryImpl的getKieConfs方法
private Optional<ServiceDiscoveryImpl.KieConfs> getKieConfs() {
return Stream.of(this.getClass().getClassLoader(),
Thread.currentThread().getContextClassLoader(),
ClassLoader.getSystemClassLoader())
.map(this::loadKieConfs)
.filter(Objects::nonNull)
.findFirst();
}
这个操作很简单,先是将三个类加载器传入Stream.of中,使其组成一个类加载器数组,然后将其流化。
对每一个类加载器执行lodaKieConfs操作,对map的返回值进行过滤,过滤掉null值,最后拿到第一个符合条件的配置,返回。这一步操作,只有lodaKieConfs是一个方法操作,这个操作的代码如下:
代码段17 ServiceDiscoveryImpl的loadKieConfs方法
private ServiceDiscoveryImpl.KieConfs loadKieConfs(ClassLoader cl) {
if (cl == null) {
return null;
} else {
try {
Collection<URL> resources = findKieConfUrls(cl);
return resources.isEmpty() ? null : new ServiceDiscoveryImpl.KieConfs(cl, resources);
} catch (IOException var3) {
return null;
}
}
}
先是判断传入的类加载器是否为空,如果不为空,会执行findKieConfUrls方法,该方法的代码如下:
代码段18 ServiceDiscoveryImpl的findKieConfUrls方法
private static Collection<URL> findKieConfUrls(ClassLoader cl) throws IOException {
//声明一个空的URL地址列表
List<URL> kieConfsUrls = new ArrayList();
//获取类加载器中所有"Meta-INF/kie"文件夹下的资源的枚举
Enumeration metaInfs = cl.getResources("META-INF/kie");
//遍历枚举值
while(metaInfs.hasMoreElements()) {
//资源路径
URL metaInf = (URL)metaInfs.nextElement();
//如果资源是来自虚拟文件系统,则清空列表,并跳出循环
//vfs是"Virtual File System"(虚拟文件系统)协议.
if (metaInf.getProtocol().startsWith("vfs")) {
((List)kieConfsUrls).clear();
break;
}
//打开资源连接
URLConnection con = metaInf.openConnection();
//判断当前连接类型是Jar还是其他类型
if (con instanceof JarURLConnection) {
//收集JAR中的kie配置地址
collectKieConfsInJar((List)kieConfsUrls, metaInf, (JarURLConnection)con);
} else {
//收集文件中的kie配置地址
collectKieConfsInFile((List)kieConfsUrls, new File(metaInf.getFile()));
}
}
if (((List)kieConfsUrls).isEmpty()) {
//如果经过之前操作,配置URL地址列表为空
kieConfsUrls = (List)getKieConfsFromKnownModules(cl).collect(Collectors.toList());
} else {
//寻找未注册的模块
List<String> notRegisteredModules = (List)((List)kieConfsUrls).stream()
.map(ServiceDiscoveryImpl::getModuleName)
.filter((module) -> {
return Arrays.binarySearch(KIE_MODULES, module) < 0;
}).collect(Collectors.toList());
//如果未注册模块的列表不为空,则抛出异常
if (!notRegisteredModules.isEmpty()) {
throw new IllegalStateException("kie.conf file discovered for modules " + notRegisteredModules + " but not listed among the known modules. This will not work under OSGi or JBoss vfs.");
}
}
//获取类加载器中所有"Meta-INF/kie.conf"文件
Enumeration kieConfEnum = cl.getResources("META-INF/kie.conf");
//直接将该文件的URL添加到URL地址列表中
while(kieConfEnum.hasMoreElements()) {
((List)kieConfsUrls).add((URL)kieConfEnum.nextElement());
}
if (log.isDebugEnabled()) {
log.debug("Discovered kie.conf files: " + kieConfsUrls);
}
//返回配置文件URL地址列表
return (Collection)kieConfsUrls;
}
因为这是一段很长的代码,所以我把解释都放到了注释里面,这里面涉及到的其他方法的源代码如下:
收集JAR中的kie配置地址
代码段19 ServiceDiscoveryImpl的collectKieConfsInJar方法
private static void collectKieConfsInJar(List<URL> kieConfsUrls,
URL metaInf,
JarURLConnection con) throws IOException {
//获取jar文件
JarFile jarFile = con.getJarFile();
//获取该jar里面的资源条目
Enumeration entries = jarFile.entries();
//遍历条目
while(entries.hasMoreElements()) {
JarEntry entry = (JarEntry)entries.nextElement();
//如果条目是以kie.conf为结尾,则将其添加到kie配置地址列表中
if (entry.getName().endsWith("kie.conf")) {
String metaInfString = metaInf.toString();
int confFileFolderLength = metaInfString.endsWith("/") ? "META-INF/kie".length() + 1 : "META-INF/kie".length();
kieConfsUrls.add(new URL(metaInfString.substring(0, metaInfString.length() - confFileFolderLength) + entry.getName()));
}
}
}
收集文件中的kie配置地址
代码段20 ServiceDiscoveryImpl的collectKieConfsInFile方法
private static void collectKieConfsInFile(List<URL> kieConfsUrls, File file) throws IOException {
if (file.isDirectory()) {
//如果文件是一个文件夹,则获取文件夹里面的文件数组
File[] var2 = file.listFiles();
//获取文件数组的大小
int var3 = var2.length;
//遍历文件
for(int var4 = 0; var4 < var3; ++var4) {
File child = var2[var4];
//递归调用
collectKieConfsInFile(kieConfsUrls, child);
}
} else if (file.toString().endsWith("kie.conf")) {
//如果文件是以kie.conf结尾,则将当前文件的URL直接加入到kie配置地址列表中
kieConfsUrls.add(file.toURI().toURL());
}
}
从已知的模块中寻找配置文件地址
代码段21 ServiceDiscoveryImpl的getKieConfsFromKnownModules方法
public static Stream<URL> getKieConfsFromKnownModules(ClassLoader cl) {
return Stream.of(KIE_MODULES).map((module) -> {
return cl.getResource("META-INF/kie/" + module + (module.length() > 0 ? "/" : "") + "kie.conf");
}).filter(Objects::nonNull);
}
这里面主要的就是KIE_MODULES,需要将这里面的东西流化之后找到对应的资源地址
代码段22 ServiceDiscoveryImpl的KIE_MODULES常量
private static final String[] KIE_MODULES = new String[]{
"",
"drools-alphanetwork-compiler",
"drools-beliefs",
"drools-compiler",
"drools-core",
"drools-decisiontables",
"drools-metric",
"drools-model-compiler",
"drools-mvel",
"drools-persistence-jpa",
"drools-ruleunit",
"drools-scorecards",
"drools-serialization-protobuf",
"drools-traits",
"drools-workbench-models-guided-dtable",
"drools-workbench-models-guided-scorecard",
"drools-workbench-models-guided-template",
"jbpm-bpmn2",
"jbpm-case-mgmt-cmmn",
"jbpm-flow", "jbpm-flow-builder",
"jbpm-human-task-jpa", "kie-ci",
"kie-dmn-core", "kie-dmn-jpmml",
"kie-internal", "kie-pmml",
"kie-pmml-evaluator-assembler",
"kie-pmml-evaluator-core",
"kie-server-services-jbpm-cluster"};
注册配置
这个地方需要回到代码段15,在获取到配置文件地址列表之后,对配置进行一个注册
代码段23 ServiceDiscoveryImpl的registerConfs方法
public void registerConfs(ClassLoader classLoader, URL url) {
log.debug("Loading kie.conf from {} in classloader {}", url, classLoader);
try {
BufferedReader br = new BufferedReader(new InputStreamReader(url.openStream()));
try {
for(String line = br.readLine(); line != null; line = br.readLine()) {
if (line.contains("=") && !line.contains("[")) {
String[] entry = line.split("=");
//这里需要再解释一下<----------------<----------------<--------看这里
this.processKieService(classLoader, entry[0].trim(), entry[1].trim());
}
}
} catch (Throwable var7) {
try {
br.close();
} catch (Throwable var6) {
var7.addSuppressed(var6);
}
throw var7;
}
br.close();
} catch (Exception var8) {
throw new RuntimeException("Unable to build kie service url = " + url.toExternalForm(), var8);
}
}
这一段代码没什么好解释的,就是读取文件内容,进行处理,处理的内容需要好好解释一下,也就是代码段23中注释的地方
代码段24 ServiceDiscoveryImpl的processKieService方法
private void processKieService(ClassLoader classLoader, String key, String values) {
//将值通过字符串“,”号分割为字符串数组
String[] var4 = values.split(",");
//获取分割后数量
int var5 = var4.length;
//遍历分割后的数组
for(int var6 = 0; var6 < var5; ++var6) {
String value = var4[var6];
//判断key值是不是以“?”号开头
boolean optional = key.startsWith("?");
//将开头的“?”号去掉,获取service名
String serviceName = optional ? key.substring(1) : key;
try {
if (value.startsWith("+")) {
//如果值是以“+”开头,从childServices映射中获取serviceName对应的列表,
//如果childServices没有serviceName关键字,则直接返回一个新的列表
//将返回的列表中加入实例
((List)this.childServices.computeIfAbsent(serviceName, (k) -> {
return new ArrayList();
}))
.add(this.newInstance(classLoader, value.substring(1)));
log.debug("Added child Service {}", value);
} else {
//通过符号“;”将值分割,如果分割的结果数组数量大于2,则抛出异常
String[] splitValues = value.split(";");
if (splitValues.length > 2) {
throw new RuntimeException("Invalid kie.conf entry: " + value);
}
//如果分割的数组长度是2,则优先级为数组下标为1对应的数,
//如果分割的数组长度是1.也就是没有配置优先级,则优先级为0
int priority = splitValues.length == 2 ?
Integer.parseInt(splitValues[1].trim()) : 0;
//将服务添加到一个优先级映射中
this.services.put(priority, serviceName,
this.newInstance(classLoader, splitValues[0].trim()));
log.debug("Added Service {} with priority {}", value, priority);
}
} catch (RuntimeException var12) {
if (!optional) {
log.error("Loading failed because {}", var12.getMessage());
throw var12;
}
log.info("Cannot load service: {}", serviceName);
}
}
}
总结
最后我们看一下,一小段短短的代码,里面却包含了如此多的工作,先是要实例化服务注册也就是Impl类,在实例化的时候需要通过服务发现类将所有的配置文件获取,建立服务列表,最后通过传入服务类的类名,获取服务类。流程就是这么个流程,你说简单他也简单,你说难我觉得你说的对,最后我也有个地方没有明白,可能是源码看太多脑子浆糊了,代码段15里面,还有一个buildMap的方法,源码如下,谁能给我解释解释,最后那一段代码在做什么?
代码段X ServiceDiscoveryImpl的buildMap方法
private Map<String, List<Object>> buildMap() {
Map<String, List<Object>> servicesMap = new HashMap();
//处理KieService方法里面的优先级映射转成迭代器
Iterator var2 = this.services.entrySet().iterator();
while(true) {
Entry serviceEntry;
List children;
do {
if (!var2.hasNext()) {
//如果优先级映射里面没有下一个元素
if (!this.childServices.isEmpty()) {
//如果子服务列表不空,则抛出异常
throw new RuntimeException("Child services " + this.childServices.keySet() + " have no parent");
}
if (log.isTraceEnabled()) {
//如果日志是可追踪的,则将优先级列表里面的内容重新迭代一遍,用来打印日志
var2 = servicesMap.entrySet().iterator();
while(var2.hasNext()) {
serviceEntry = (Entry)var2.next();
if (((List)serviceEntry.getValue()).size() == 1) {
log.trace("Service {} is implemented by {}", serviceEntry.getKey(), ((List)serviceEntry.getValue()).get(0));
} else {
log.trace("Service {} is implemented (in order of priority) by {}", serviceEntry.getKey(), serviceEntry.getValue());
}
}
}
return servicesMap;
}
//获取服务条目
serviceEntry = (Entry)var2.next();
log.debug("Service {} is implemented by {}", serviceEntry.getKey(), ((List)serviceEntry.getValue()).get(0));
//将服务条目放入到服务映射中
servicesMap.put((String)serviceEntry.getKey(), (List)serviceEntry.getValue());
//移除childService中的已经添加到服务映射中的条目
children = (List)this.childServices.remove(serviceEntry.getKey());
} while(children == null);
//从这开始下面的这段代码我是没太搞懂是要做什么
Iterator var5 = children.iterator();
while(var5.hasNext()) {
Object child = var5.next();
Iterator var7 = ((List)serviceEntry.getValue()).iterator();
while(var7.hasNext()) {
Object service = var7.next();
((Consumer)service).accept(child);
}
}
}
}