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文件加載。