SOFABoot 提供了类隔离框架 SOFAArk,弥补了 Spring Boot 在类隔离能力上的缺失,用以解决在实际开发中常见的类冲突、包冲突问题。
更多信息,请参见 SOFAArk 概述。
在 SOFABoot 工程中使用类隔离能力只需两步操作:
- 配置 - sofa-ark-maven-plugin打包插件。
- 引入 - sofa-ark-springboot-starter类隔离框架依赖。
配置 Maven 打包插件
SOFAArk 默认提供了 Maven 插件 sofa-ark-maven-plugin,您只需通过简单的配置,即可将 SpringBoot 工程打包成标准格式规范的可执行 Ark 包,插件坐标为:
<plugin>
   <groupId>com.alipay.sofa</groupId>
   <artifactId>sofa-ark-maven-plugin</artifactId>
</plugin>配置模板如下:
<build>
    <plugins>
        <plugin>
            <groupId>com.alipay.sofa</groupId>
            <artifactId>sofa-ark-maven-plugin</artifactId>
            <executions>
                <execution>
                    <id>default-cli</id>
                    <!--goal executed to generate executable-ark-jar -->
                    <goals>
                        <goal>repackage</goal>
                    </goals>
                    <configuration>
                        <!--specify destination where executable-ark-jar will be saved, default saved to ${project.build.directory}-->
                        <outputDirectory>./target</outputDirectory>
                        <!--default none-->
                        <arkClassifier>executable-ark</arkClassifier>
                        <!-- all class exported by ark plugin would be resolved by ark biz in default, if
                        configure denyImportClasses, then it would prefer to load them by ark biz itself -->
                        <denyImportClasses>
                            <class>com.alipay.sofa.SampleClass1</class>
                            <class>com.alipay.sofa.SampleClass2</class>
                        </denyImportClasses>
                        <!-- Corresponding to denyImportClasses, denyImportPackages is package-level -->
                        <denyImportPackages>
                            <package>com.alipay.sofa</package>
                            <package>org.springframework</package>
                        </denyImportPackages>
                        <!-- denyImportResources can prevent resource exported by ark plugin with accurate
                        name to be resolved -->
                        <denyImportResources>
                            <resource>META-INF/spring/test1.xml</resource>
                            <resource>META-INF/spring/test2.xml</resource>
                        </denyImportResources>
                    </configuration>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>插件配置项说明:
- outputDirectory:执行- mvn package命令后,指定生成的 Ark 包存放目录。默认存放至- ${project.build.directory}。
- arkClassifier:执行- mvn depleoy命令后,指定发布到仓库的 Ark 包的 Maven 坐标的- classifer值,默认为空。- 推荐配置此项,用于和普通的 Fat Jar 在名字上进行区别。 
- denyImportClasses:默认情况下,应用会优先加载 Ark plugin 导出的类。使用该配置项可以禁止应用从 Ark plugin 加载其导出类。
- denyImportPackages:对应上述的- denyImportClasses,提供包级别的禁止导入。
- denyImportResources:默认情况下,应用会优先加载 Ark plugin 导出的资源。使用该配置项可以禁止应用从 Ark plugin 加载其导出资源。
添加类隔离框架依赖
在实际开发中,为了在运行测试用例时使用 SOFABoot 类隔离能力,需要在 SOFABoot 工程中添加如下依赖:
<dependency>
     <groupId>com.alipay.sofa</groupId>
     <artifactId>sofa-ark-springboot-starter</artifactId>
</dependency>根据 SpringBoot 依赖即服务的原则,添加该依赖之后,在应用启动前,会优先启动 SOFABoot 类隔离容器。
SOFABoot 的类隔离框架会自动检测应用中是否有引入 Ark Plugin(即需要被隔离的 JAR 包,详情请参见 SOFAArk 概述),并隔离加载。例如为了避免 SOFABoot 官方提供的 SOFARPC 组件和应用产生依赖冲突,SOFABoot 提供了 SOFARPC 组件对应的 Ark Plugin 版。如果您需要隔离 SOFARPC,只需要添加如下组件:
<dependency>
     <groupId>com.alipay.sofa</groupId>
     <artifactId>rpc-sofa-boot-plugin</artifactId>
</dependency>添加之后,应用在运行时,SOFABoot 的类隔离容器会自动隔离 SOFARPC 和其他应用依赖,避免可能存在的包冲突。当然开发者也可以独立开发自身所需 Ark Plugin 包,详情请参见 SOFAArk。
运行
在 IDE 中运行
直接通过main函数启动即可,可以看到类似如下的启动成功日志:
2018-04-0710:51:59,646 INFO  main                             -Begin to start ArkServiceContainer
2018-04-0710:52:00,550 INFO  main                             -InitService: com.alipay.sofa.ark.container.service.plugin.PluginDeployServiceImpl
2018-04-0710:52:00,551 INFO  main                             -InitService: com.alipay.sofa.ark.container.service.classloader.ClassloaderServiceImpl
2018-04-0710:52:00,557 INFO  main                             -Finish to start ArkServiceContainer
2018-04-0710:52:00,571 INFO  main                             -Start to process pipeline stage: com.alipay.sofa.ark.container.pipeline.HandleArchiveStage
2018-04-0710:52:00,624 INFO  main                             -Finish to process pipeline stage: com.alipay.sofa.ark.container.pipeline.HandleArchiveStage
2018-04-0710:52:00,624 INFO  main                             -Start to process pipeline stage: com.alipay.sofa.ark.container.pipeline.DeployPluginStage
2018-04-0710:52:00,626 INFO  main                             -Start to deploy plugin: sample-ark-plugin
starting in sample ark plugin activator
2018-04-0710:52:00,645 INFO  main                             -Service: com.alipay.sofa.ark.sample.facade.SamplePluginService publish by:PluginServiceProvider{pluginName='sample-ark-plugin', priority=1000} succeed
2018-04-0710:52:00,647 INFO  main                             -Finish to deploy plugin: sample-ark-plugin
2018-04-0710:52:00,647 INFO  main                             -Finish to process pipeline stage: com.alipay.sofa.ark.container.pipeline.DeployPluginStage
2018-04-0710:52:00,647 INFO  main                             -Start to process pipeline stage: com.alipay.sofa.ark.container.pipeline.DeployBizStage
2018-04-0710:52:00,650 INFO  main                             -Begin to start biz:StartupIn IDE打包运行
使用 mvn clean install 将工程打包具体类隔离能力的 Fat Jar。打包之后的文件格式类似如下:
.
├── classes
│    └── com
│         └── alipay
│              └── sofa
│                   └── boot
│                        └── examples
│                             └── demo
│                                  └── isolation
│                                       └──SofaBootClassIsolationDemoApplication.class
├── generated-sources
│    └── annotations
├── maven-archiver
│    └── pom.properties
├── maven-status
│    └── maven-compiler-plugin
│         ├── compile
│         │    └──default-compile
│         │        ├── createdFiles.lst
│         │        └── inputFiles.lst
│         └── testCompile
│              └──default-testCompile
│                  └── inputFiles.lst
├── sofaboot-sample-with-isolation-2.4.0-ark-biz.jar
├── sofaboot-sample-with-isolation-2.4.0-executable-ark.jar
└── sofaboot-sample-with-isolation-2.4.0.jar其中sofaboot-sample-with-isolation-2.4.0-executable-ark就是一个具备类隔离能力的 Fat JAR,可以直接通过java -jar的方式将其启动。在服务器或者控制台上执行java -jar sofaboot-sample-with-isolation-2.4.0-executable-ark可以看到类似如下的启动成功日志。
2018-04-0710:57:48.033  INFO 8488---[           main] s.b.c.e.t.TomcatEmbeddedServletContainer:Tomcat started on port(s):8080(http)
2018-04-0710:57:48.042  INFO 8488---[           main].i.SofaBootClassIsolationDemoApplication:StartedSofaBootClassIsolationDemoApplicationin7.432 seconds (JVM running for8.99)
Ark container started in8797 ms.测试
为了在运行测试用例时使用 SOFABoot 类隔离能力,需要额外引入 SOFABoot 官方提供的test-sofa-boot-starter依赖,使用SofaBootRunner和SofaJUnit4Runner编写集成测试和单元测试,这也是 SOFABoot 推荐测试用例的编写方式。
使用SofaBootRunner和SofaJUnit4Runner的好处是会自动感知应用是否使用了类隔离能力,开发者只需添加或者删除 SOFABoot 类隔离依赖包即可完成和 SOFABoot 类隔离能力的集成和剥离,:
<dependency>
<groupId>com.alipay.sofa</groupId>
<artifactId>sofa-ark-springboot-starter</artifactId>
</dependency>使用详情如下:
- SofaBootRunner - 集成测试示例代码: - @RunWith(SofaBootRunner.class) @SpringBootTest(classes =SofaBootClassIsolationDemoApplication.class) public class IntegrationTestCase{ @Autowired private SampleService sampleService; @Test public void test(){ Assert.assertTrue("service".equals(sampleService.service())); } }- SofaBootRunner会检测应用是否引入如下依赖:- <dependency> <groupId>com.alipay.sofa</groupId> <artifactId>sofa-ark-springboot-starter</artifactId> </dependency>- 根据 Spring Boot 依赖即服务的原则,如果检测到 - sofa-ark-springboot-starter依赖,- SofaBootRunner会使用 SOFABoot 类隔离能力,否则和原生的- SpringRunner无异。
- SofaJUnit4Runner - 单元测试示例代码: - @RunWith(SofaJUnit4Runner.class) public class UnitTestCase{ @Test public void test(){ Assert.assertTrue(true); } }- SofaJUnit4Runner同样会检测应用是否引入- sofa-ark-springboot-starter依赖。根据 Spring Boot 依赖即服务的原则,如果检测到- sofa-ark-springboot-starter依赖,- SofaJUnit4Runner会使用 SOFABoot 类隔离能力,否则和原生的- JUnit4无异。
- 自定义 Runner - 在编写测试用例时,有时需要指定特殊的 Runner,为了统一编码风格,您可以借助注解 - @DelegateToRunner配合- SofaBootRunner和- SofaJUnit4Runner使用。示例代码如下:- @RunWith(SofaJUnit4Runner.class) @DelegateToRunner(BlockJUnit4ClassRunner.class) public class UnitTestCaseWithoutArk{ @Test public void test(){ Assert.assertFalse(true); } }- 相当于如下使用方法: - @RunWith(BlockJUnit4ClassRunner.class) public class UnitTestCaseWithoutArk{ @Test public void test(){ Assert.assertFalse(true); } }