我在重构我的代码生成工具,之前有如下代码:
1
2
3
4
5
6
|
tools:
template-directory: 'classpath:templates/'
enum-comment-pattern: '^([A-Za-z\u4e00-\u9fa5 ]{1,})((([A-Za-z0-9-]+:[\u4e00-\u9fa5A-Za-z0-9-]{1,},?)+))$'
number-pattern: '^[0-9]*$'
|
我的Properties类是这样写的:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
@Data
public class ToolsProperties {
private String templateDirectory;
private String numberPattern;
public Path getTemplateDirectory(){
// 做一些处理将String转换成Path
}
public Pattern getNumberPattern(){
// 做一些处理将String转换成Pattern
}
}
|
我觉得这样的写法非常的不优雅,我无法接受,我想要如下的写法:
1
2
3
4
5
6
7
|
@Data
public class ToolsProperties {
private Path templateDirectory;
private Pattern numberPattern;
}
|
在我开始编码前,我以为我会使用Convert完成该工作,但是我发现SpringBoot支持自动将application.yml中的字符串转换成Path和Pattern,于是我就使用了该特性。
Convert的开发
我还是要写一份我开发Convert(毕竟这份代码没啥用了,我准备删除了):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
public class PathConverter implements Converter<String, Path> {
@Override
public Path convert(String templateDirectory) {
try {
Path templateDirectoryPath = templateDirectory.startsWith("classpath:") ?
ResourceUtils.getFile(templateDirectory).toPath() :
Paths.get(templateDirectory);
if (!Files.exists(templateDirectoryPath)) {
throw new RuntimeException("TemplateDirectory不存在,请检查配置文件");
}
return templateDirectoryPath;
} catch (FileNotFoundException e) {
throw new RuntimeException("TemplateDirectory不存在,请检查配置文件");
}
}
}
|
这份代码有两个亮点:
- 支持在配置文件中配置
classpath:
。
- 使用了ResourceUtils工具处理classpath。
可惜了,Spring默认支持对Path的转换,导致我这个代码没用到。
SpringBoot对Path的处理
很糟心的是,将ToolsProperties中templateDirectory字段换成Path类型并没有想象中的那么顺利,我一开始将template-directory配置成classpath:templates
,结果提示无法找到该文件,我以为是SpringBoot默认的Path转换器不支持classpath,于是我就想替换掉这个转换器,接下来就是漫长的找这个转换器的过程(这个转换器是不存在的)
具体过程我就不写出来了,总是整个过程我收获了如下技巧:
-
我在ToolsProperties写了一个Setter(手写的,非Lombok),然后断点在这个setter中,最终SpringBoot调到了这个方法,我从而可以查看调用栈,从而快速找到我想观察的方法。(貌似可以直接断点到字段上,我没有试过)
-
我一开始不知道Idea可以断点拉姆达表达式,遇到拉姆达表达式,我就一层一层的去找这个拉姆达表达式是在哪实现的,然后在拉姆达表达式中进行断点。实际上不需要这么做,我可以直接断点在函数式接口的方法上,Idea会自动帮我找到这个方法的定义。(虽然如此,还是一层一层的找更快乐)
-
对Path、Pattern的处理,SpringBoot并没有采用Convert,而是一系列非常复杂的机制,有兴趣的自己去看看吧,我目前还没有完全消化。
-
如果我们用Path接受配置文件中的配置,Path指向一个文件夹的时候配置的值一定要一个反斜杠结尾。
分享一些快乐
- 我是如何知道SpringBoot支持
classpath:
的,如下,我在断点调试值看到了如下的代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
|
@Override
public void setAsText(String text) throws IllegalArgumentException {
boolean nioPathCandidate = !text.startsWith(ResourceUtils.CLASSPATH_URL_PREFIX);
if (nioPathCandidate && !text.startsWith("/")) {
try {
URI uri = new URI(text);
if (uri.getScheme() != null) {
nioPathCandidate = false;
// Let's try NIO file system providers via Paths.get(URI)
setValue(Paths.get(uri).normalize());
return;
}
}
catch (URISyntaxException ex) {
// Not a valid URI; potentially a Windows-style path after
// a file prefix (let's try as Spring resource location)
nioPathCandidate = !text.startsWith(ResourceUtils.FILE_URL_PREFIX);
}
catch (FileSystemNotFoundException ex) {
// URI scheme not registered for NIO (let's try URL
// protocol handlers via Spring's resource mechanism).
}
}
this.resourceEditor.setAsText(text);
Resource resource = (Resource) this.resourceEditor.getValue();
if (resource == null) {
setValue(null);
}
else if (nioPathCandidate && !resource.exists()) {
setValue(Paths.get(text).normalize());
}
else {
try {
setValue(resource.getFile().toPath());
}
catch (IOException ex) {
throw new IllegalArgumentException("Failed to retrieve file for " + resource, ex);
}
}
}
|
- 我最后是如何知道path的值要配置成
classpath:templates/
,没有分号则SpringBoot会提示没有该文件的?
同样是上面的源码呀,哈哈,里面有一行resource.getFile().toPath()
,看到这一行时,我就开始知道是因为我少了一个反斜杠最后导致SpringBoot提示文件夹不存在。
实际上使用Java原生的API是不存在这个问题的,下面两个输出都为true:
1
2
3
4
|
System.out.println(Files.exists(Paths.get("C:\\Users\\wujj\\Desktop\\Tmp5")));
System.out.println(Files.exists(Paths.get("C:\\Users\\wujj\\Desktop\\Tmp5\\")));
|
后续:
一顿分析猛如虎,最后发现根本就不是分析的情况,只因为我的templates文件是空的,Idea没有给我拷贝到target目录下,而Spring的Resource类是判断二进制文件的目录是否存在templates文件夹。
我为什么会分析错呢???因为我第一次猜测是Path只能接受文件,所以我在template目录下创建了一个文件,然后进行测试,发现Path确实能正确的接受,所以我认为Path能接受文件。然后我觉得很奇怪,不可能说只能接受文件不能接受目录,所以我分析是因为我少了最后的分号,这个时候我没有删除这个文件就进行了测试,发现确实可以,所以我认为就是因为少了分号。此时我没有再删除这个文件进行二次测试就下结论了。擦
我找了一圈没有找到很好的方案解决这个问题(我不想再引入一些复杂的东西导致我Maven插件配置的乱七八糟),考虑到这个问题会在启动阶段暴露,所以我们知道有这个问题就好了,尽量不要在resources创建空文件夹,然后在application.yml中配置这个文件夹。