前面说完我们如何从github上面去取数据,这里说说server端剩余的类。ConfigServerEncryptionConfiguration类。从类的名字我们可以看出主要是加解密相关的配置类,进入类中可以看到定义了EncryptionController encryptionController()这个的bean,直接进入到EncryptionController类,controller类的访问路径可以通过spring.cloud.config.server.prefix属性进行配置,在类中我们可以看到一系列的加解密相关操作的端点,这里就不在具体介绍,可以参考代码查看。需要注意的是使用的是加密算法是AES256加密,由于jdk默认提供的是AES128的方式,所以需要去http://www.oracle.com/technetwork/java/javase/downloads/jce8-download-2133166.html下载相关jar包,这里指的是jdk1.8,下载解压后覆盖\jre\lib\security路径下的解压后得到的jar包就可以了。
在看下EncryptionAutoConfiguration类,进入该类,我们可以了解到这里定义了2个静态内部类和2个一般内部类,分别是EncryptorConfiguration、KeyStoreConfiguration、SingleTextEncryptorConfiguration、DefaultTextEncryptorConfiguration,
在EncryptorConfiguration类中定义了在prefix是spring.cloud.config.server.encrypt.enabled
为true的条件下才能有效注入加解密的代码;KeyStoreConfiguration这个类主要是用于对称加密的存储;SingleTextEncryptorConfiguration这个类有效的情况是存在TextEncryptor的bean和不存在TextEncryptorLocator的bean时,这个时候才会初始化里面的bean;DefaultTextEncryptorConfiguration这个类有效的情况是不存在TextEncryptor的bean才会初始化里面的bean。所以整体上来说EncryptionAutoConfiguration类主要是为加解密初始化bean的操作。
说完server端的一些内部使用,但是我们外部如何来调用,接下来我们看看EnvironmentController和ResourceController这2个controller类的使用。
首先说说ResourceController类,进入到这个controller可以看到它是一个restful controller类,入口地址可以通过prefix为spring.cloud.config.server.prefix来定义总的入口,类中定义了3个RequestMapping
1.@RequestMapping("/{name}/{profile}/{label}/**")
@RequestMapping("/{name}/{profile}/{label}/**") public String retrieve(@PathVariable String name, @PathVariable String profile, @PathVariable String label, HttpServletRequest request, @RequestParam(defaultValue = "true") boolean resolvePlaceholders) throws IOException { String path = getFilePath(request, name, profile, label); return retrieve(name, profile, label, path, resolvePlaceholders); }
从路径上看它必须匹配有3个及以上的参数,返回类型是String字符串,在getFilePath()方法里面可以看到一系列的替换操作,这里不贴出代码了,进入到retrieve()方法
synchronized String retrieve(String name, String profile, String label, String path, boolean resolvePlaceholders) throws IOException { if (name != null && name.contains("(_)")) { // "(_)" is uncommon in a git repo name, but "/" cannot be matched // by Spring MVC name = name.replace("(_)", "/"); } if (label != null && label.contains("(_)")) { // "(_)" is uncommon in a git branch name, but "/" cannot be matched // by Spring MVC label = label.replace("(_)", "/"); } // ensure InputStream will be closed to prevent file locks on Windows try (InputStream is = this.resourceRepository.findOne(name, profile, label, path) .getInputStream()) { String text = StreamUtils.copyToString(is, Charset.forName("UTF-8")); if (resolvePlaceholders) { Environment environment = this.environmentRepository.findOne(name, profile, label); text = resolvePlaceholders(prepareEnvironment(environment), text); } return text; } }
该方法是一个synchronized方法,首先做的是一些替换操作,然后进入到ResourceRepository接口的实现类GenericResourceRepository中的findOne()方法
public synchronized Resource findOne(String application, String profile, String label, String path) { String[] locations = this.service.getLocations(application, profile, label).getLocations(); try { for (int i = locations.length; i-- > 0;) { String location = locations[i]; for (String local : getProfilePaths(profile, path)) { Resource file = this.resourceLoader.getResource(location) .createRelative(local); if (file.exists() && file.isReadable()) { return file; } } } } catch (IOException e) { throw new NoSuchResourceException( "Error : " + path + ". (" + e.getMessage() + ")"); } throw new NoSuchResourceException("Not found: " + path); }
private Collection<String> getProfilePaths(String profiles, String path) { Set<String> paths = new LinkedHashSet<>(); for (String profile : StringUtils.commaDelimitedListToSet(profiles)) { if (!StringUtils.hasText(profile) || "default".equals(profile)) { paths.add(path); } else { String ext = StringUtils.getFilenameExtension(path); String file = path; if (ext != null) { ext = "." + ext; file = StringUtils.stripFilenameExtension(path); } else { ext = ""; } paths.add(file + "-" + profile + ext); } } paths.add(path); return paths; }
从实现上可以发现它也是一个synchronized方法,它首先会根据初始化的配置文件去SearchPathLocator接口的实现类去数组locations,它主要就是从配置来源做一些获取配置文件到本地的操作,可以通过前面介绍的git相关类参考,然后根据路径返回配置文件信息,返回到retrieve()方法中后,去除一些不必要的信息以及清除占位符之类的后返回String格式的text。
2.@RequestMapping(value = "/{name}/{profile}/**", params = "useDefaultLabel")
@RequestMapping(value = "/{name}/{profile}/**", params = "useDefaultLabel") public String retrieve(@PathVariable String name, @PathVariable String profile, HttpServletRequest request, @RequestParam(defaultValue = "true") boolean resolvePlaceholders) throws IOException { String path = getFilePath(request, name, profile, null); return retrieve(name, profile, null, path, resolvePlaceholders); }
这个请求基本和上面的请求差不多,范围稍微大一些,但是必须包含特定参数useDefaultLabel,其他处理过程一样。
3.@RequestMapping(value = "/{name}/{profile}/{label}/**", produces = MediaType.APPLICATION_OCTET_STREAM_VALUE)
@RequestMapping(value = "/{name}/{profile}/{label}/**", produces = MediaType.APPLICATION_OCTET_STREAM_VALUE) public synchronized byte[] binary(@PathVariable String name, @PathVariable String profile, @PathVariable String label, HttpServletRequest request) throws IOException { String path = getFilePath(request, name, profile, label); return binary(name, profile, label, path); }
这个请求类的路径也是和前面的差不多,但是必须包含返回的类型produces参数为MediaType.APPLICATION_OCTET_STREAM_VALUE的数据,在binary()方法中,其基本方法与前面的mapping路径一样,唯一不同就是把获取到的文件信息直接转为byte[]数组。
我们接下来看看EnvironmentController类,进入到这个controller可以看到它是一个restful controller类,入口地址可以通过prefix为spring.cloud.config.server.prefix来定义总的入口,类中定义了8个RequestMapping。
1.@RequestMapping("/{name}/{profiles:.*[^-].*}")
@RequestMapping("/{name}/{profiles:.*[^-].*}") public Environment defaultLabel(@PathVariable String name, @PathVariable String profiles) { return labelled(name, profiles, null); }
通过mapping可以看到它的请求路径是不含label且不能带有-的路径,而labelled()方法在下面的mapping中介绍,返回类型是一个Environment对象。
2.@RequestMapping("/{name}/{profiles}/{label:.*}")
@RequestMapping("/{name}/{profiles}/{label:.*}") public Environment labelled(@PathVariable String name, @PathVariable String profiles, @PathVariable String label) { if (name != null && name.contains("(_)")) { // "(_)" is uncommon in a git repo name, but "/" cannot be matched // by Spring MVC name = name.replace("(_)", "/"); } if (label != null && label.contains("(_)")) { // "(_)" is uncommon in a git branch name, but "/" cannot be matched // by Spring MVC label = label.replace("(_)", "/"); } Environment environment = this.repository.findOne(name, profiles, label); if(!acceptEmpty && (environment == null || environment.getPropertySources().isEmpty())){ throw new EnvironmentNotFoundException("Profile Not found"); } return environment; }
这个路径将接收全部类型的后缀包含路径为name,profiles和label的路径参数,方法中首先做了一些替换操作,然后通过调用具体的EnvironmentRepository接口的实现类方法findOne()去查找配置文件,具体可以参考前面提到的MultipleJGitEnvironmentRepository的实现,获取到Environment后就返回。在client端,我们就会调用这个接口方法进行远程配置数据的更新操作。
3.@RequestMapping("/{name}-{profiles}.properties")
@RequestMapping("/{name}-{profiles}.properties") public ResponseEntity<String> properties(@PathVariable String name, @PathVariable String profiles, @RequestParam(defaultValue = "true") boolean resolvePlaceholders) throws IOException { return labelledProperties(name, profiles, null, resolvePlaceholders); }
这个路径是直接获取没有label且配置文件后缀为properties的信息,labelledProperties()方法将在下面介绍。
4.@RequestMapping("/{label}/{name}-{profiles}.properties")
@RequestMapping("/{label}/{name}-{profiles}.properties") public ResponseEntity<String> labelledProperties(@PathVariable String name, @PathVariable String profiles, @PathVariable String label, @RequestParam(defaultValue = "true") boolean resolvePlaceholders) throws IOException { validateProfiles(profiles); Environment environment = labelled(name, profiles, label); Map<String, Object> properties = convertToProperties(environment); String propertiesString = getPropertiesString(properties); if (resolvePlaceholders) { propertiesString = resolvePlaceholders(prepareEnvironment(environment), propertiesString); } return getSuccess(propertiesString); }
这个路径是直接获取包含label且配置文件后缀为properties的信息,首先校验了profiles路径是否包含了-,如果包含了直接返回错误,然后直接去labelled()方法获取Environment对象,就是第二个请求路径的方法,最后就是一系列的组装解析数据的过程。
5.@RequestMapping("{name}-{profiles}.json")
@RequestMapping("{name}-{profiles}.json") public ResponseEntity<String> jsonProperties(@PathVariable String name, @PathVariable String profiles, @RequestParam(defaultValue = "true") boolean resolvePlaceholders) throws Exception { return labelledJsonProperties(name, profiles, null, resolvePlaceholders); }
这个路径是直接获取没有label且配置文件后缀为json的信息,labelledJsonProperties()方法将在下面介绍。
6.@RequestMapping("/{label}/{name}-{profiles}.json")
@RequestMapping("/{label}/{name}-{profiles}.json") public ResponseEntity<String> labelledJsonProperties(@PathVariable String name, @PathVariable String profiles, @PathVariable String label, @RequestParam(defaultValue = "true") boolean resolvePlaceholders) throws Exception { validateProfiles(profiles); Environment environment = labelled(name, profiles, label); Map<String, Object> properties = convertToMap(environment); String json = this.objectMapper.writeValueAsString(properties); if (resolvePlaceholders) { json = resolvePlaceholders(prepareEnvironment(environment), json); } return getSuccess(json, MediaType.APPLICATION_JSON); }
从方法实现上看validateProfiles()方法和labelled()方法和前面一样,然后将Environment对象进行map转换,然后通过ObjectMapper进行转换,之后去除掉不必要的参数,用MediaType.APPLICATION_JSON的方式返回。
7.@RequestMapping({ "/{name}-{profiles}.yml", "/{name}-{profiles}.yaml" })
@RequestMapping({ "/{name}-{profiles}.yml", "/{name}-{profiles}.yaml" }) public ResponseEntity<String> yaml(@PathVariable String name, @PathVariable String profiles, @RequestParam(defaultValue = "true") boolean resolvePlaceholders) throws Exception { return labelledYaml(name, profiles, null, resolvePlaceholders); }
这个路径对应两种配置文件路径且不包含label,labelledYaml()方法将在下面介绍。
8.@RequestMapping({ "/{label}/{name}-{profiles}.yml",
"/{label}/{name}-{profiles}.yaml" })
@RequestMapping({ "/{label}/{name}-{profiles}.yml", "/{label}/{name}-{profiles}.yaml" }) public ResponseEntity<String> labelledYaml(@PathVariable String name, @PathVariable String profiles, @PathVariable String label, @RequestParam(defaultValue = "true") boolean resolvePlaceholders) throws Exception { validateProfiles(profiles); Environment environment = labelled(name, profiles, label); Map<String, Object> result = convertToMap(environment); if (this.stripDocument && result.size() == 1 && result.keySet().iterator().next().equals("document")) { Object value = result.get("document"); if (value instanceof Collection) { return getSuccess(new Yaml().dumpAs(value, Tag.SEQ, FlowStyle.BLOCK)); } else { return getSuccess(new Yaml().dumpAs(value, Tag.STR, FlowStyle.BLOCK)); } } String yaml = new Yaml().dumpAsMap(result); if (resolvePlaceholders) { yaml = resolvePlaceholders(prepareEnvironment(environment), yaml); } return getSuccess(yaml); }
从方法实现上看validateProfiles()方法和labelled()方法和前面一样,然后就是就行yml相关的操作,这里不详述了。
以上就主要介绍完了spring-cloud-config-server端的源码。