1、問題
最近在學習契約測試,用到的是spring-cloud-contract,網上有很多教程,便試着照葫蘆畫瓢的方式,來實現一遍。由提供者建立契約、生成存根,然後把存根交給消費方測試時,拋出了一個異常No stubs or contracts were found for [XXX],詳細異常如下:
Caused by: org.eclipse.aether.resolution.ArtifactResolutionException: Could not find artifact com.contract:ContractTest:jar:stubs:0.0.1-SNAPSHOT
at shaded.org.eclipse.aether.internal.impl.DefaultArtifactResolver.resolve(DefaultArtifactResolver.java:423) ~[spring-cloud-contract-shade-2.2.2.RELEASE.jar:2.2.2.RELEASE]
at shaded.org.eclipse.aether.internal.impl.DefaultArtifactResolver.resolveArtifacts(DefaultArtifactResolver.java:225) ~[spring-cloud-contract-shade-2.2.2.RELEASE.jar:2.2.2.RELEASE]
at shaded.org.eclipse.aether.internal.impl.DefaultArtifactResolver.resolveArtifact(DefaultArtifactResolver.java:202) ~[spring-cloud-contract-shade-2.2.2.RELEASE.jar:2.2.2.RELEASE]
at shaded.org.eclipse.aether.internal.impl.DefaultRepositorySystem.resolveArtifact(DefaultRepositorySystem.java:257) ~[spring-cloud-contract-shade-2.2.2.RELEASE.jar:2.2.2.RELEASE]
at org.springframework.cloud.contract.stubrunner.AetherStubDownloader.unpackedJar(AetherStubDownloader.java:185) ~[spring-cloud-contract-stub-runner-2.2.2.RELEASE.jar:2.2.2.RELEASE]
... 78 common frames omitted
Caused by: org.eclipse.aether.transfer.ArtifactNotFoundException: Could not find artifact com.contract:ContractTest:jar:stubs:0.0.1-SNAPSHOT
at shaded.org.eclipse.aether.internal.impl.DefaultArtifactResolver.resolve(DefaultArtifactResolver.java:413) ~[spring-cloud-contract-shade-2.2.2.RELEASE.jar:2.2.2.RELEASE]
... 82 common frames omitted
//省略...
Caused by: java.lang.IllegalArgumentException: No stubs or contracts were found for [com.contract:ContractTest:0.0.1-SNAPSHOT:stubs] and the switch to fail on no stubs was set.
at org.springframework.cloud.contract.stubrunner.CompositeStubDownloader.downloadAndUnpackStubJar(CompositeStubDownloaderBuilder.java:77) ~[spring-cloud-contract-stub-runner-2.2.2.RELEASE.jar:2.2.2.RELEASE]
at org.springframework.cloud.contract.stubrunner.StubRunnerFactory.createStubsFromServiceConfiguration(StubRunnerFactory.java:75) ~[spring-cloud-contract-stub-runner-2.2.2.RELEASE.jar:2.2.2.RELEASE]
at org.springframework.cloud.contract.stubrunner.BatchStubRunnerFactory.buildBatchStubRunner(BatchStubRunnerFactory.java:69) ~[spring-cloud-contract-stub-runner-2.2.2.RELEASE.jar:2.2.2.RELEASE]
at org.springframework.cloud.contract.stubrunner.spring.StubRunnerConfiguration.batchStubRunner(StubRunnerConfiguration.java:87) ~[spring-cloud-contract-stub-runner-2.2.2.RELEASE.jar:2.2.2.RELEASE]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_151]
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_151]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_151]
at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_151]
at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:154) ~[spring-beans-5.2.5.RELEASE.jar:5.2.5.RELEASE]
... 67 common frames omitted
根據異常可知道大致的意思是找不到存根。spring-cloud-contract我用的是2.2.2-RELEASE版本,消費方使用的存根是指向本地的maven倉庫的,代碼如下:
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment=SpringBootTest.WebEnvironment.RANDOM_PORT)
@AutoConfigureMockMvc
@AutoConfigureStubRunner(ids = {"com.contract:ContractTest:0.0.1-SNAPSHOT:stubs:8080"},
stubsMode = StubRunnerProperties.StubsMode.LOCAL)
public class TestConsumer {
}
然後我找了一下本地的倉庫,發現是有ContractTest:0.0.1-SNAPSHOT-stubs.jar這個存根的,但爲啥找不到呢?百思不得其姐…於是網上找答案,也很少相關的答案。於是只能“吃自己”了…
2、源碼跟蹤
網上找不到答案,那隻能搜索一下源碼了,因爲我覺得問題應該是出在查找本地倉庫那塊了。因爲StubsMode有三種情況,現在我用的是LOCAL。
public enum StubsMode {
CLASSPATH,//類路徑
LOCAL,//本地
REMOTE,//遠程
}
於是我查找了StubsMode的引用,一路跟蹤,最終找到了讀取本地庫的位置,就是這個localRepositoryDirectory方法
public static RepositorySystemSession newSession(RepositorySystem system,
boolean workOffline) {
DefaultRepositorySystemSession session = MavenRepositorySystemUtils.newSession();
session.setOffline(workOffline);
if (!workOffline) {
session.setUpdatePolicy(RepositoryPolicy.UPDATE_POLICY_ALWAYS);
}
session.setChecksumPolicy(RepositoryPolicy.CHECKSUM_POLICY_WARN);
String localRepositoryDirectory = localRepositoryDirectory(workOffline);//加載本地倉庫的目錄
if (log.isDebugEnabled()) {
log.debug("Local Repository Directory set to [" + localRepositoryDirectory
+ "]. Work offline: [" + workOffline + "]");
}
LocalRepository localRepo = new LocalRepository(localRepositoryDirectory);
session.setLocalRepositoryManager(
system.newLocalRepositoryManager(session, localRepo));
return session;
}
深入這個方法,最後來到了"罪魁禍首"的地方—userSettings方法,這個方法裏面有個讀取系統變量的過程,就是fromSystemPropOrEnv(MAVEN_USER_SETTINGS_LOCATION),MAVEN_USER_SETTINGS_LOCATION常量值是org.apache.maven.user-settings,由上下文可以得知這個是配置maven倉庫的setting.xml的路徑,如果不設置這個路徑,就會默認找user.home路徑下的那個setting.xml。這我纔想起,我的maven倉庫並不是在user.home下的,而是另外建了個倉庫地址!
private static File userSettings() {
//MAVEN_USER_SETTINGS_LOCATION=org.apache.maven.user-settings
String user = fromSystemPropOrEnv(MAVEN_USER_SETTINGS_LOCATION);
if (user == null) {
return new File(new File(System.getProperty("user.home")).getAbsoluteFile(),
File.separator + ".m2" + File.separator + "settings.xml");
}
return new File(user);
}
3、解決問題
得知了原因,問題就好解決了,只要把系統參數org.apache.maven.user-settings設置爲正確的地址不就可以了?我用的是eclipse,在啓動設置那裏添加環境變量,來設置setting.xml的位置。
重啓消費方,發現問題得解!!!
4、結論
從這個案例來看,我們不由得感嘆閱讀源碼的重要性!源碼不僅可以讓你學習到很多思想,而且可以幫助你解決問題。網上可能有問題的解決方案,但是有時候你只知道怎麼解決問題,但不知道爲啥這樣解決。源碼可以讓你從根本上去認知這套框架、系統,對於問題的解答,你也可以從最根源出着手!