最近在学习契约测试,用到的是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这个存根的,但为啥找不到呢?百思不得其姐…于是网上找答案,也很少相关的答案。于是只能“吃自己”了…
网上找不到答案,那只能搜索一下源码了,因为我觉得问题应该是出在查找本地仓库那块了。因为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);
}
得知了原因,问题就好解决了,只要把系统参数org.apache.maven.user-settings设置为正确的地址不就可以了?我用的是eclipse,在启动设置那里添加环境变量,来设置setting.xml的位置。
重启消费方,发现问题得解!!!
从这个案例来看,我们不由得感叹阅读源码的重要性!源码不仅可以让你学习到很多思想,而且可以帮助你解决问题。网上可能有问题的解决方案,但是有时候你只知道怎么解决问题,但不知道为啥这样解决。源码可以让你从根本上去认知这套框架、系统,对于问题的解答,你也可以从最根源出着手!