moduleLoader实际上就是模块的“加载器”
我们从load方法开始
public Module load(ModuleConfig moduleConfig) {
if (LOGGER.isInfoEnabled()) {
LOGGER.info("Loading module: {}", moduleConfig);
}
List<String> tempFileJarURLs = moduleConfig.getModuleUrlPath();
if (LOGGER.isInfoEnabled()) {
LOGGER.info("Local jars: {}", tempFileJarURLs);
}
ConfigurableApplicationContext moduleApplicationContext = loadModuleApplication(moduleConfig, tempFileJarURLs);
if (LOGGER.isInfoEnabled()) {
LOGGER.info("Loading module complete:{}", moduleConfig);
}
return new SpringModule(moduleConfig, moduleConfig.getVersion(), moduleConfig.getName(),
moduleApplicationContext);
}
我们可以看到moduleLoader首先调用了loadModuleApplication方法,下面我们继续看此方法
private ConfigurableApplicationContext loadModuleApplication(ModuleConfig moduleConfig, List<String>
tempFileJarURLs) {
ClassLoader currentClassLoader = Thread.currentThread().getContextClassLoader();
//获取模块的ClassLoader
ClassLoader moduleClassLoader = new ModuleClassLoader(moduleConfig.getModuleUrl(), applicationContext
.getClassLoader(), getOverridePackages(moduleConfig));
try {
//把当前线程的ClassLoader切换成模块的
Thread.currentThread().setContextClassLoader(moduleClassLoader);
ConfigurableApplicationContext context;
Properties properties = getProperties(moduleConfig);
Set<String> scanBase = moduleConfig.getScanPackages();
//注解方式加载bean
if (!scanBase.isEmpty()) {
ModuleAnnotationApplicationContext annotationConfigApplicationContext = new
ModuleAnnotationApplicationContext(properties);
annotationConfigApplicationContext.scan(scanBase.toArray(new String[0]));
context = annotationConfigApplicationContext;
} else {
//XML方式加载bean
ModuleXmlApplicationContext moduleApplicationContext = new ModuleXmlApplicationContext();
moduleApplicationContext.setProperties(properties);
moduleApplicationContext.setConfigLocations(findSpringConfigs(tempFileJarURLs, moduleClassLoader,
getExclusionConfigeNameList(properties)));
context = moduleApplicationContext;
}
context.setParent(applicationContext);
if (LOGGER.isInfoEnabled()) {
LOGGER.info("module {}:{} allow current process to override bean in module", moduleConfig.getName(),
moduleConfig.getVersion());
}
((DefaultResourceLoader) context).setClassLoader(moduleClassLoader);
context.refresh();
return context;
} catch (Throwable e) {
CachedIntrospectionResults.clearClassLoader(moduleClassLoader);
throw Throwables.propagate(e);
} finally {
//还原当前线程的ClassLoader
Thread.currentThread().setContextClassLoader(currentClassLoader);
}
}
上面这段代码可以说是jarslink的核心部分,此方法初始化了模块自己的ClassLoader,初始化Spring Application Context,同时要设置当前线程上下文的ClassLoader为模块的ClassLoader,这样就保证了实例隔离以及类隔离。
初始化上下文的时候,jarslink只能采用注解加载或者xml方式加载,不过在实际工程中,我们可以使用@ImportResource等注解引入我们的xml,值得注意的是,springboot中使用的yml以及propertise方式初始化在jarslink中是无法使用的,需要修改为bean加载或者xml文件加载。