SOFAArk容器通过监听springboot启动事件来启动自己,启动类SofaArkBootstrap是由AppClassLoader加载的,
然后会创建一个新的ContainerClassLoader来加载容器相关的类。
容器启动过程中会为每一个plugin和biz模块都创建一个对应的PluginClassLoader和BizClassLoader,每一个plugin都有一个独立的PluginClassLoader加载,biz模块也一样。这样就做到了类隔离。
SOFAArk容器中,一个ContainerClassLoader,N个PluginClassLoader和BizClassLoader。
1 官方流程
官方流程:https://www.sofastack.tech/projects/sofa-boot/sofa-ark-startup/

Ark 应用的整体启动流程如下图所述:
image.png
当用 java -jar 启动 Ark 包 或者 在 IDE 中通过 SofaArkBootstrap.launch 启动 Ark 应用时,相应 Launcher 入口会负责启动应用,其中会反射调用 ArkContainer 的入口,初始化 ArkService ,然后依次执行 pipeline,来完成整个 Ark 应用的启动。
ArkService
Ark Serivce 是 Ark 容器中的服务,底层使用 Guice 对服务进行管理。同时针对服务,提供了生命周期接口 com.alipay.sofa.ark.spi.service.ArkService
publicinterfaceArkService {

/*
Ark Service init
@throws ArkException
/
voidinit()throws ArkException;

/*
Ark Service dispose
@throws ArkException
/
voiddispose()throws ArkException;

}

当服务实现了上述接口时,在 Ark Serivce 容器启动时和停止时会调用相应的生命周期接口
Pipeline 服务
Pipeline 也是注册在 Ark Service 容器中的一个服务,服务本身是没有顺序和优先级的,在 Pipeline 中会对服务进行一些组装,同时完成整个 Ark 容器的启动
Archive 解析
在 Pipeline 的最开始,会将运行的 fatjar 进行解析,解析成运行时需要的模型,主要包括 Ark 插件模型和 Ark 业务模型,并将这些模型注册到 Ark Service 中的 PluginManagerService 以及 BizManagerService 中
初始化环境
设置一些运行时需要的默认参数,比如设置 log4j.ignoreTCL 为 true 让 log4j/log4j2 初始化是日志不要从 ThreadContextClassloader 中寻找配置文件(背景)
注册容器服务
在 Ark 容器中会发布一些服务供其它的插件来使用,比如 BizDeployer 来让 SOFAArk 官方插件 sofa-jarslink 来完成 biz 的动态加载/卸载等
部署 Ark 插件
从 PluginManagerService 中获取到所有的 Ark 插件,并按照插件优先级顺序: ClassloaderService 准备插件 export 类的 map 映射 PluginDeployService 启动插件的 com.alipay.sofa.ark.spi.service.PluginActivator
启动 Ark 业务
从 BizManagerService 中获取到所有的 Ark 业务,并执行业务配置在 MANIFEST.MF 属性 Main-Class 中提供的入口 main 函数

2 springboot启动流程:

1 com.alipay.sofa.ark.springboot.listener.ArkApplicationStartListener 监听spring启动事件
@Override
publicvoid onApplicationEvent(SpringApplicationEvent event) {
try {
if (isSpringBoot2()
&& APPLICATION_STARTING_EVENT.equals(event.getClass().getCanonicalName())) {
startUpArk(event);
}

if (isSpringBoot1()
&& APPLICATION_STARTED_EVENT.equals(event.getClass().getCanonicalName())) {
startUpArk(event);
}
} catch (Throwable e) {
thrownew RuntimeException(“Meet exception when determine whether to start SOFAArk!”, e);
}
}

publicvoid startUpArk(SpringApplicationEvent event) {
if (LAUNCH_CLASSLOADER_NAME.equals(this.getClass().getClassLoader().getClass().getName())) {
SofaArkBootstrap.launch(event.getArgs());
}
}

3 利用SofaArkBootstrap启动ark容器);)

SofaArkBootstrap.launch(event.getArgs()););)
publicstaticvoid launch(String[] args) {
try {
if (!isSofaArkStarted()) {
entryMethod = new EntryMethod(Thread.currentThread());
IsolatedThreadGroup threadGroup = new IsolatedThreadGroup(
entryMethod.getDeclaringClassName());
LaunchRunner launchRunner = new LaunchRunner(SofaArkBootstrap.class.getName(),
MAIN_ENTRY_NAME, args);
Thread launchThread = new Thread(threadGroup, launchRunner,
entryMethod.getMethodName());
launchThread.start();
LaunchRunner.join(threadGroup);
threadGroup.rethrowUncaughtException();
System.exit(0);
}
} catch (Throwable e) {
thrownew RuntimeException(e);
}
}

核心过程launchRunner的run 当前这个类LaunchRunner由AppClassLoader加载生成。 反射调用SofaArkBootstrap.remain方法
publicvoid run() {
Thread thread = Thread.currentThread();
ClassLoader classLoader = thread.getContextClassLoader();
try {
Class<?> startClass = classLoader.loadClass(this.startClassName);
Method entryMethod;
try {
entryMethod = startClass.getMethod(startMethodName, String[].class);
} catch (NoSuchMethodException ex) {
entryMethod = startClass.getDeclaredMethod(startMethodName, String[].class);
}
if (!entryMethod.isAccessible()) {
entryMethod.setAccessible(true);
}
entryMethod.invoke(null, new Object[] { this.args });
} catch (NoSuchMethodException ex) {

Exception wrappedEx = new Exception(
String.format(
“The specified entry class:%s doesn’t contain an entry method:%s with appropriate signature.”,
this.startClassName, this.startMethodName), ex);
thread.getThreadGroup().uncaughtException(thread, wrappedEx);
} catch (Throwable ex) {
thread.getThreadGroup().uncaughtException(thread, ex);
}
}

com.alipay.sofa.ark.support.startup.SofaArkBootstrap#remain
privatestaticvoid remain(String[] args) throws Exception {// NOPMD
AssertUtils.assertNotNull(entryMethod, “No Entry Method Found.”);
URL[] urls = getURLClassPath();
new ClasspathLauncher(new ClassPathArchive(entryMethod.getDeclaringClassName(),
entryMethod.getMethodName(), urls)).launch(args, getClasspath(urls),
entryMethod.getMethod());
}

launch
public Object launch(String[] args, String classpath, Method method) throws Exception {
JarFile.registerUrlProtocolHandler();
ClassLoader classLoader = createContainerClassLoader(getContainerArchive());
List attachArgs = new ArrayList<>();
attachArgs.add(String.format(“%s%s=%s”, CommandArgument.ARK_CONTAINER_ARGUMENTS_MARK,
CommandArgument.CLASSPATH_ARGUMENT_KEY, classpath));
attachArgs.add(String.format(“%s%s=%s”, CommandArgument.ARK_BIZ_ARGUMENTS_MARK,
CommandArgument.ENTRY_CLASS_NAME_ARGUMENT_KEY, method.getDeclaringClass().getName()));
attachArgs.add(String.format(“%s%s=%s”, CommandArgument.ARK_BIZ_ARGUMENTS_MARK,
CommandArgument.ENTRY_METHOD_NAME_ARGUMENT_KEY, method.getName()));
attachArgs.addAll(Arrays.asList(args));
return launch(attachArgs.toArray(new String[attachArgs.size()]), getMainClass(),
classLoader);
}

1 创建了一个ContainerClassLoader类加载器(ContainerClassLoader),后面会用来加载SOFAArk相关的类
2 ark的,命令行参数 解析出来
3 再一次调用 launch方法,最终createMainMethodRunner方法返回的是MainMethodRunner这个工具类的实例,MainMethodRunner只是一个执行器,它实际是为了执行ArkContainer类的main方法。

com.alipay.sofa.ark.container.ArkContainer#main方法

  1. try {
  2. LaunchCommand launchCommand = LaunchCommand.parse(args);
  3. if (launchCommand.isExecutedByCommandLine()) {
  4. ExecutableArkBizJar executableArchive;
  5. File rootFile = new File(URLDecoder.decode(launchCommand.getExecutableArkBizJar()
  6. .getFile()));
  7. if (rootFile.isDirectory()) {
  8. executableArchive = new ExecutableArkBizJar(new ExplodedArchive(rootFile));
  9. } else {
  10. executableArchive = new ExecutableArkBizJar(new JarFileArchive(rootFile,
  11. launchCommand.getExecutableArkBizJar()));
  12. }
  13. return new ArkContainer(executableArchive, launchCommand).start();//最主要流程
  14. } else {
  15. ClassPathArchive classPathArchive = new ClassPathArchive(
  16. launchCommand.getEntryClassName(), launchCommand.getEntryMethodName(),
  17. launchCommand.getClasspath());
  18. returnnew ArkContainer(classPathArchive, launchCommand).start();
  19. }
  20. } catch (IOException e) {
  21. thrownew ArkRuntimeException(String.format("SOFAArk startup failed, commandline=%s",
  22. LaunchCommand.toString(args)), e);
  23. }

com.alipay.sofa.ark.container.ArkContainer#start

  1. public Object start() throws ArkRuntimeException {
  2. AssertUtils.assertNotNull(arkServiceContainer, "arkServiceContainer is null !");
  3. if (started.compareAndSet(false, true)) {
  4. Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
  5. @Override
  6. publicvoid run() {
  7. stop();
  8. }
  9. }));
  10. // 1 bootstrap.properties配置文件
  11. prepareArkConfig();
  12. // 2 初始化log相关配置
  13. reInitializeArkLogger();
  14. // 3 启动Ark Service Container
  15. arkServiceContainer.start();
  16. // 4 注冊的Pipeline
  17. Pipeline pipeline = arkServiceContainer.getService(Pipeline.class);
  18. // 5
  19. pipeline.process(pipelineContext);
  20. System.out.println("Ark container started in " + (System.currentTimeMillis() - start) //NOPMD
  21. + " ms.");
  22. }
  23. returnthis;
  24. }
  1. 启动Ark Service Container
  2. com.alipay.sofa.ark.container.service.ArkServiceContainer#start
  3. ClassLoader oldClassLoader = ClassLoaderUtils.pushContextClassLoader(getClass()
  4. .getClassLoader());
  5. try {
  6. LOGGER.info("Begin to start ArkServiceContainer");
  7. injector = Guice.createInjector(findServiceModules());
  8. for (Binding<ArkService> binding : injector
  9. .findBindingsByType(new TypeLiteral<ArkService>() {
  10. })) {
  11. arkServiceList.add(binding.getProvider().get());
  12. }
  13. Collections.sort(arkServiceList, new OrderComparator());
  14. for (ArkService arkService : arkServiceList) {
  15. LOGGER.info(String.format("Init Service: %s", arkService.getClass().getName()));
  16. arkService.init();
  17. }
  18. ArkServiceContainerHolder.setContainer(this);
  19. ArkClient.setBizFactoryService(getService(BizFactoryService.class));
  20. ArkClient.setBizManagerService(getService(BizManagerService.class));
  21. ArkClient.setInjectionService(getService(InjectionService.class));
  22. ArkClient.setEventAdminService(getService(EventAdminService.class));
  23. ArkClient.setArguments(arguments);
  24. LOGGER.info("Finish to start ArkServiceContainer");
  25. } finally {
  26. ClassLoaderUtils.popContextClassLoader(oldClassLoader);
  27. }
  28. }
  29. spi 注册ClassLoaderServiceImpl
  30. protectedvoid configure() {
  31. binder().bind(Pipeline.class).to(StandardPipeline.class);
  32. Multibinder<ArkService> arkServiceMultibinder = Multibinder.newSetBinder(binder(),
  33. ArkService.class);
  34. arkServiceMultibinder.addBinding().to(PluginDeployServiceImpl.class);
  35. arkServiceMultibinder.addBinding().to(BizDeployServiceImpl.class);
  36. arkServiceMultibinder.addBinding().to(ClassLoaderServiceImpl.class);
  37. arkServiceMultibinder.addBinding().to(StandardTelnetServerImpl.class);
  38. binder().bind(PluginManagerService.class).to(PluginManagerServiceImpl.class);
  39. binder().bind(BizManagerService.class).to(BizManagerServiceImpl.class);
  40. binder().bind(ClassLoaderService.class).to(ClassLoaderServiceImpl.class);
  41. binder().bind(PluginDeployService.class).to(PluginDeployServiceImpl.class);
  42. binder().bind(BizDeployService.class).to(BizDeployServiceImpl.class);
  43. binder().bind(RegistryService.class).to(RegistryServiceImpl.class);
  44. binder().bind(InjectionService.class).to(InjectionServiceImpl.class);
  45. binder().bind(TelnetServerService.class).to(StandardTelnetServerImpl.class);
  46. binder().bind(BizFactoryService.class).to(BizFactoryServiceImpl.class);
  47. binder().bind(PluginFactoryService.class).to(PluginFactoryServiceImpl.class);
  48. binder().bind(ExtensionLoaderService.class).to(ExtensionLoaderServiceImpl.class);
  49. binder().bind(EventAdminService.class).to(EventAdminServiceImpl.class);
  50. }

// 4 注冊的Pipeline

image.png

privatevoid initializePipeline() {
addPipelineStage(
ArkServiceContainerHolder.getContainer().getService(HandleArchiveStage.class))
.addPipelineStage(
ArkServiceContainerHolder.getContainer().getService(RegisterServiceStage.class))
.addPipelineStage(
ArkServiceContainerHolder.getContainer().getService(ExtensionLoaderStage.class))
.addPipelineStage(
ArkServiceContainerHolder.getContainer().getService(DeployPluginStage.class))
.addPipelineStage(
ArkServiceContainerHolder.getContainer().getService(DeployBizStage.class))
.addPipelineStage(
ArkServiceContainerHolder.getContainer().getService(FinishStartupStage.class));
}

HandleArchiveStage 注册plugin和biz模块, 仅注册不部署
RegisterServiceStage plugin,模块和事件这些内部服务不可被覆盖, 因此先发布
ExtensionLoaderStage 扩展服务
DeployPluginStage 部署插件
DeployBizStage 部署biz模块
FinishStartupStage 结束, 里面主要发送一个结束事件

3 主要加载过程
0 HandleArchiveStage 动态配置 注册\初始化插件(每一个插件都有一个新的PluginClassLoader类加载器)Activator

publicfinal String SOFA_ARK_CONTAINER = “SOFA-ARK/container/“;

publicfinal String SOFA_ARK_MODULE = “SOFA-ARK/biz/“;

publicfinal String SOFA_ARK_PLUGIN = “SOFA-ARK/plugin/“;

识别插件

publicvoid registerPlugin(Plugin plugin) {
if (plugins.putIfAbsent(plugin.getPluginName(), plugin) != null) {
thrownew ArkRuntimeException(String.format(“duplicate plugin: %s exists.”,
plugin.getPluginName()));
}
}
public Plugin createPlugin(PluginArchive pluginArchive) throws IOException,
IllegalArgumentException {
AssertUtils.isTrue(isArkPlugin(pluginArchive), “Archive must be a ark plugin!”);
PluginModel plugin = new PluginModel();
Attributes manifestMainAttributes = pluginArchive.getManifest().getMainAttributes();
plugin
.setPluginName(manifestMainAttributes.getValue(PLUGIN_NAME_ATTRIBUTE))
.setGroupId(manifestMainAttributes.getValue(GROUP_ID_ATTRIBUTE))
.setArtifactId(manifestMainAttributes.getValue(ARTIFACT_ID_ATTRIBUTE))
.setVersion(manifestMainAttributes.getValue(PLUGIN_VERSION_ATTRIBUTE))
.setPriority(manifestMainAttributes.getValue(PRIORITY_ATTRIBUTE))
.setPluginActivator(manifestMainAttributes.getValue(ACTIVATOR_ATTRIBUTE))
.setClassPath(pluginArchive.getUrls())
.setPluginUrl(pluginArchive.getUrl())
.setExportClasses(manifestMainAttributes.getValue(EXPORT_CLASSES_ATTRIBUTE))
.setExportPackages(manifestMainAttributes.getValue(EXPORT_PACKAGES_ATTRIBUTE))
.setImportClasses(manifestMainAttributes.getValue(IMPORT_CLASSES_ATTRIBUTE))
.setImportPackages(manifestMainAttributes.getValue(IMPORT_PACKAGES_ATTRIBUTE))
.setImportResources(manifestMainAttributes.getValue(IMPORT_RESOURCES_ATTRIBUTE))
.setExportResources(manifestMainAttributes.getValue(EXPORT_RESOURCES_ATTRIBUTE))
.setPluginClassLoader(
new PluginClassLoader(plugin.getPluginName(), plugin.getClassPath()))
.setPluginContext(new PluginContextImpl(plugin));
return plugin;

导出类与类加载器关系

@Override
publicvoid prepareExportClassAndResourceCache() {
for (Plugin plugin : pluginManagerService.getPluginsInOrder()) {
for (String exportIndex : plugin.getExportPackageNodes()) {
exportNodeAndClassLoaderMap.putIfAbsent(exportIndex, plugin.getPluginClassLoader());
}
for (String exportIndex : plugin.getExportPackageStems()) {
exportStemAndClassLoaderMap.putIfAbsent(exportIndex, plugin.getPluginClassLoader());
}
for (String exportIndex : plugin.getExportClasses()) {
exportClassAndClassLoaderMap
.putIfAbsent(exportIndex, plugin.getPluginClassLoader());
}
for (String resource : plugin.getExportResources()) {
exportResourceAndClassLoaderMap.putIfAbsent(resource, new LinkedList<>());
exportResourceAndClassLoaderMap.get(resource).add(plugin.getPluginClassLoader());
}
for (String resource : plugin.getExportPrefixResourceStems()) {
exportPrefixStemResourceAndClassLoaderMap.putIfAbsent(resource, new LinkedList<>());
exportPrefixStemResourceAndClassLoaderMap.get(resource).add(
plugin.getPluginClassLoader());
}
for (String resource : plugin.getExportSuffixResourceStems()) {
exportSuffixStemResourceAndClassLoaderMap.putIfAbsent(resource, new LinkedList<>());
exportSuffixStemResourceAndClassLoaderMap.get(resource).add(
plugin.getPluginClassLoader());
}
}
}

//其他插件导入

protected Class<?> resolveExportClass(String name) {
if (shouldFindExportedClass(name)) {
ClassLoader importClassLoader = classloaderService.findExportClassLoader(name);
if (importClassLoader != null) {
try {
return importClassLoader.loadClass(name);
} catch (ClassNotFoundException e) {
// just log when debug level
if (ArkLoggerFactory.getDefaultLogger().isDebugEnabled()) {
// log debug message
ArkLoggerFactory.getDefaultLogger().debug(
“Fail to load export class “ + name, e);
}
}
}
}

1先部署 DeployPluginStage
com.alipay.sofa.ark.container.model.PluginModel#start

publicvoid start() throws ArkRuntimeException {
if (activator == null || activator.isEmpty()) {
return;
}

EventAdminService eventAdminService = ArkServiceContainerHolder.getContainer().getService(
EventAdminService.class);

ClassLoader oldClassLoader = ClassLoaderUtils
.pushContextClassLoader(this.pluginClassLoader);
try {
eventAdminService.sendEvent(new BeforePluginStartupEvent(this));
// 1使用自己的PluginClassLoader加载插件的启动activator类
pluginActivator = (PluginActivator) pluginClassLoader.loadClass(activator)
.newInstance();
// 2 启动插件activator
pluginActivator.start(pluginContext);
} catch (Throwable ex) {
thrownew ArkRuntimeException(ex.getMessage(), ex);
} finally {
eventAdminService.sendEvent(new AfterPluginStartupEvent(this));
ClassLoaderUtils.popContextClassLoader(oldClassLoader);

}
}

public final static String ACTIVATOR_ATTRIBUTE = “activator”;

com.alipay.sofa.ark.spi.service.PluginActivator WebPluginActivatorZookeeperConfigActivator

2 DeployBizStage

publicvoid deploy(String[] args) throws ArkRuntimeException {
ServiceReference serviceReference = registryService
.referenceService(BizDeployer.class);
bizDeployer = serviceReference.getService();

LOGGER.info(String.format(“BizDeployer=\’%s\’ is starting.”, bizDeployer.getDesc()));

bizDeployer.init(args);
bizDeployer.deploy();
}

进入BizModel.start方法,依然使用了MainMethodRunner这个工具类来启动当前biz模块,参数mainClass是biz模块中有main方法的启动类,是在创建BizModel对象的时候设置的每个biz模块也会由一个独立的BizClassLoader负责加载模块所有的类

https://www.jianshu.com/p/cd5a10a23860