Skip to content

Commit 11b83d9

Browse files
committed
Polish Stage 7 Lesson 3
1 parent 31ee2f0 commit 11b83d9

File tree

13 files changed

+550
-0
lines changed

13 files changed

+550
-0
lines changed

「一入 Java 深似海 」/代码/segmentfault/deep-in-java/stage-7/pom.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
<modules>
1616
<module>stage-7-lesson-1</module>
1717
<module>stage-7-lesson-2</module>
18+
<module>stage-7-lesson-3</module>
1819
</modules>
1920

2021
</project>
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0"
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
5+
<parent>
6+
<artifactId>stage-7</artifactId>
7+
<groupId>com.segmentfault</groupId>
8+
<version>1.0-SNAPSHOT</version>
9+
</parent>
10+
<modelVersion>4.0.0</modelVersion>
11+
12+
<artifactId>stage-7-lesson-3</artifactId>
13+
<name>「一入 Java 深似海 」系列 :: 第七期 :: 第三节</name>
14+
15+
<properties>
16+
<java.version>1.8</java.version>
17+
</properties>
18+
19+
<dependencies>
20+
21+
<dependency>
22+
<groupId>org.springframework</groupId>
23+
<artifactId>spring-context</artifactId>
24+
<version>5.1.10.RELEASE</version>
25+
</dependency>
26+
27+
<dependency>
28+
<groupId>org.springframework</groupId>
29+
<artifactId>spring-context-indexer</artifactId>
30+
<version>5.1.10.RELEASE</version>
31+
<optional>true</optional>
32+
</dependency>
33+
</dependencies>
34+
35+
<build>
36+
<plugins>
37+
<plugin>
38+
<groupId>org.apache.maven.plugins</groupId>
39+
<artifactId>maven-compiler-plugin</artifactId>
40+
<configuration>
41+
<encoding>UTF-8</encoding>
42+
<source>${java.version}</source>
43+
<target>${java.version}</target>
44+
</configuration>
45+
</plugin>
46+
</plugins>
47+
</build>
48+
49+
</project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
package com.segementfalut.deep.in.java.annotation.processing;
2+
3+
import javax.annotation.processing.*;
4+
import javax.lang.model.SourceVersion;
5+
import javax.lang.model.element.Element;
6+
import javax.lang.model.element.ElementKind;
7+
import javax.lang.model.element.Modifier;
8+
import javax.lang.model.element.TypeElement;
9+
import javax.lang.model.type.DeclaredType;
10+
import javax.lang.model.type.TypeMirror;
11+
import javax.lang.model.util.Types;
12+
import javax.tools.FileObject;
13+
import javax.tools.StandardLocation;
14+
import java.io.IOException;
15+
import java.io.Writer;
16+
import java.sql.Wrapper;
17+
import java.util.*;
18+
19+
/**
20+
* 1. 扩展 javax.annotation.processing.AbstractProcessor 抽象类
21+
* 2. 指定需要处理的注解类名(集合)
22+
* 3. 指定支持的 Java 源代码版本
23+
* 4. 指定支持的 Processorcom.segementfalut.deep.in.java.reflection.Repository 参数 Options(可选)
24+
* 5. 利用 Java SPI 配置 javax.annotation.processing.Processor 的实现类
25+
*/
26+
@SupportedAnnotationTypes(RepositoryAnnotationProcessor.REPOSITORY_ANNOTATION_CLASS_NAME)
27+
@SupportedSourceVersion(SourceVersion.RELEASE_8)
28+
public class RepositoryAnnotationProcessor extends AbstractProcessor {
29+
30+
public static final String REPOSITORY_ANNOTATION_CLASS_NAME = "com.segementfalut.deep.in.java.reflection.Repository";
31+
32+
private static final String CRUD_REPOSITORY_INTERFACE_CLASS_NAME = "com.segementfalut.deep.in.java.reflection.CrudRepository";
33+
34+
/**
35+
* Key 为 CrudRepository 接口实现类,Value 为 CrudRepository 接口首个参数类型
36+
*/
37+
private Map<String, String> crudRepositoryParameterizedTypesMapping = new HashMap<>();
38+
39+
@Override
40+
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
41+
// 输出处理的注解类名称
42+
43+
// 第一个阶段:处理阶段
44+
// 获取所有的编译类
45+
roundEnv.getRootElements()
46+
.stream()
47+
.filter(this::isRepositoryAnnotationPresent) // 过滤标注 @com.segementfalut.deep.in.java.reflection.Repository 的元素
48+
.forEach(this::processRepositoryAnnotatedElement); // 处理标注 @com.segementfalut.deep.in.java.reflection.Repository 的元素
49+
50+
51+
// 第二个阶段:完成阶段
52+
if (roundEnv.processingOver()) {
53+
// 将 crudRepositoryParameterizedTypesMapping 输出到新生成的文件
54+
try {
55+
generateCrudRepositoryParameterizedTypesMetadata();
56+
} catch (IOException e) {
57+
throw new RuntimeException(e);
58+
}
59+
}
60+
61+
return false;
62+
}
63+
64+
private void generateCrudRepositoryParameterizedTypesMetadata() throws IOException {
65+
// 找到 ClassPath
66+
Filer filer = processingEnv.getFiler();
67+
String resourceName = "META-INF/crud-repos-mappings.properties";
68+
FileObject fileObject = filer.createResource(StandardLocation.CLASS_OUTPUT, "", resourceName);
69+
70+
try (Writer writer = fileObject.openWriter()) {
71+
Properties properties = new Properties();
72+
properties.putAll(crudRepositoryParameterizedTypesMapping);
73+
properties.store(writer, "Generated by RepositoryAnnotationProcessor");
74+
}
75+
}
76+
77+
78+
private void processRepositoryAnnotatedElement(Element element) {
79+
80+
if (!isConcreteClass(element) || !isCrudRepositoryType(element)) {
81+
return;
82+
}
83+
84+
System.out.println("CrudRepository 实现类为 :" + element.toString());
85+
86+
// CrudRepository 接口类型
87+
TypeMirror crudRepositoryGenericInterfaceType = getGenericInterfaceType(element, CRUD_REPOSITORY_INTERFACE_CLASS_NAME);
88+
89+
System.out.println("CrudRepository 实现泛型接口定义为 :" + crudRepositoryGenericInterfaceType.toString());
90+
91+
// 由于 CrudRepository 是接口类型,可以强制转化为 DeclaredType
92+
DeclaredType declaredType = DeclaredType.class.cast(crudRepositoryGenericInterfaceType);
93+
// 获取泛型参数类型列表
94+
List<? extends TypeMirror> parameterizedTypes = declaredType.getTypeArguments();
95+
// 获取第一个参数类型
96+
TypeMirror firstArgumentType = parameterizedTypes.get(0);
97+
// 必然等于 User 对象
98+
System.out.println("CrudRepository 实现接口的首个泛型参数为 :" + firstArgumentType.toString());
99+
// Key 为 CrudRepository 接口实现类,Value 为 CrudRepository 接口首个参数类型
100+
crudRepositoryParameterizedTypesMapping.put(crudRepositoryGenericInterfaceType.toString(), firstArgumentType.toString());
101+
}
102+
103+
private boolean isConcreteClass(Element element) {
104+
return !element.getModifiers().contains(Modifier.ABSTRACT);
105+
}
106+
107+
private boolean isCrudRepositoryType(Element element) {
108+
return getGenericInterfaceType(element, CRUD_REPOSITORY_INTERFACE_CLASS_NAME) != null;
109+
}
110+
111+
private TypeMirror getGenericInterfaceType(Element element, String interfaceTypeName) {
112+
ElementKind kind = element.getKind();
113+
if (kind.isClass() && element instanceof TypeElement) {
114+
TypeElement typeElement = (TypeElement) element;
115+
return typeElement.getInterfaces().stream() // 发现当前参数类型的所有接口
116+
.filter(interfaceType -> typeEquals(interfaceType, interfaceTypeName))
117+
.findFirst() // 找到第一个符合条件的 CrudRepository 接口
118+
.orElse(null);
119+
}
120+
return null;
121+
}
122+
123+
private boolean typeEquals(TypeMirror type, String typeName) {
124+
Types types = processingEnv.getTypeUtils();
125+
TypeMirror erasedType = types.erasure(type); // 擦写泛型参数
126+
return Objects.equals(typeName, erasedType.toString());
127+
}
128+
129+
private boolean isRepositoryAnnotationPresent(Element element) {
130+
return isAnnotationPresent(element, REPOSITORY_ANNOTATION_CLASS_NAME);
131+
}
132+
133+
private boolean isAnnotationPresent(Element element, String annotationClassName) {
134+
return element.getAnnotationMirrors() // 返回当前元素的所有注解集合
135+
.stream()
136+
.filter(annotation -> Objects.equals(annotationClassName, annotation.getAnnotationType().toString()))
137+
.count() > 0;
138+
}
139+
140+
// @Override
141+
// public SourceVersion getSupportedSourceVersion() {
142+
// return SourceVersion.latest();
143+
// }
144+
//
145+
// @Override
146+
// public Set<String> getSupportedAnnotationTypes() {
147+
// return singleton("com.segementfalut.deep.in.java.reflection.Repository");
148+
// }
149+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package com.segementfalut.deep.in.java.annotation.processing;
2+
3+
import com.segementfalut.deep.in.java.compiler.Compiler;
4+
import org.springframework.stereotype.Component;
5+
6+
import java.io.File;
7+
import java.io.IOException;
8+
9+
import static com.segementfalut.deep.in.java.compiler.CompilerDemo.getClassOutputDirectory;
10+
11+
/**
12+
* 编译 + RepositoryAnnotationProcessor 处理
13+
*/
14+
@Component
15+
public class RepositoryAnnotationProcessorDemo {
16+
17+
public static void main(String[] args) throws IOException {
18+
File sourceDirectory = new File(System.getProperty("user.dir"), "/src/main/java/");
19+
File targetDirectory = getClassOutputDirectory();
20+
// 基于 Compiler
21+
Compiler compiler = new Compiler(sourceDirectory, targetDirectory);
22+
compiler.setProcessors(new RepositoryAnnotationProcessor());
23+
compiler.compile(
24+
"com.segementfalut.deep.in.java.reflection.User",
25+
"com.segementfalut.deep.in.java.reflection.Repository",
26+
"com.segementfalut.deep.in.java.reflection.CrudRepository",
27+
"com.segementfalut.deep.in.java.reflection.UserRepository"
28+
);
29+
}
30+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
package com.segementfalut.deep.in.java.compiler;
2+
3+
import javax.annotation.processing.Processor;
4+
import javax.tools.*;
5+
import java.io.File;
6+
import java.io.IOException;
7+
import java.io.OutputStreamWriter;
8+
import java.util.ArrayList;
9+
import java.util.Arrays;
10+
import java.util.Collections;
11+
import java.util.List;
12+
import java.util.stream.Stream;
13+
14+
import static java.util.Arrays.asList;
15+
import static java.util.stream.Collectors.toList;
16+
17+
public class Compiler {
18+
19+
// 指定 Java Source 文件的根路径 -sourcepath
20+
private final File sourceDirectory;
21+
22+
private final File targetDirectory;
23+
24+
private final JavaCompiler javaCompiler;
25+
26+
private final StandardJavaFileManager fileManager;
27+
28+
private Iterable<? extends Processor> processors;
29+
30+
public Compiler(File sourceDirectory, File targetDirectory) {
31+
this.sourceDirectory = sourceDirectory;
32+
this.targetDirectory = targetDirectory;
33+
this.javaCompiler = ToolProvider.getSystemJavaCompiler();
34+
this.fileManager = javaCompiler.getStandardFileManager(null, null, null);
35+
}
36+
37+
public void setProcessors(Processor... processors) {
38+
this.processors = asList(processors);
39+
}
40+
41+
/**
42+
* @param classNames 类所在的目录相对于 {@link #sourceDirectory}
43+
* @throws IOException
44+
*/
45+
public void compile(String... classNames) throws IOException {
46+
// 指定 Java 新生成的 Class 输出目录(并非编译时依赖的 ClassPath) javac -d
47+
this.fileManager.setLocation(StandardLocation.CLASS_OUTPUT, Collections.singleton(targetDirectory));
48+
49+
List<File> sourceFiles = Stream.of(classNames)
50+
.map(name -> name.replace('.', File.separatorChar).concat(".java")) // 目标类的源文件(相对路径)
51+
.map(name -> sourceDirectory.getAbsolutePath() + File.separatorChar + name) // 目标类的源文件(Maven 路径)
52+
.map(File::new) // 将路径变为文件
53+
.collect(toList());
54+
55+
Iterable<? extends JavaFileObject> compilationUnits =
56+
fileManager.getJavaFileObjectsFromFiles(sourceFiles);
57+
58+
JavaCompiler.CompilationTask compilationTask = javaCompiler.getTask(new OutputStreamWriter(System.out), fileManager, null, null, null, compilationUnits);
59+
// 设置 Processor
60+
compilationTask.setProcessors(processors);
61+
// 执行编译
62+
compilationTask.call();
63+
}
64+
65+
public File getSourceDirectory() {
66+
return sourceDirectory;
67+
}
68+
69+
public File getTargetDirectory() {
70+
return targetDirectory;
71+
}
72+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
package com.segementfalut.deep.in.java.compiler;
2+
3+
import javax.tools.*;
4+
import java.io.File;
5+
import java.io.IOException;
6+
import java.io.OutputStreamWriter;
7+
import java.util.Arrays;
8+
import java.util.Collections;
9+
import java.util.stream.Stream;
10+
11+
public class CompilerDemo {
12+
13+
public static void main(String[] args) throws IOException {
14+
// 获取 JavaCompiler
15+
JavaCompiler javaCompiler = ToolProvider.getSystemJavaCompiler();
16+
StandardJavaFileManager fileManager = javaCompiler.getStandardFileManager(null, null, null);
17+
// 指定 Java Source Code 路径 javac -sourcepath
18+
// 目标类: com.segementfalut.deep.in.java.compiler.CompilerDemo
19+
Class<?> targetClass = CompilerDemo.class;
20+
// 目标类的源文件(相对路径):com/segementfalut/deep/in/java/compiler/CompilerDemo/java
21+
String sourceFileRelativePath = targetClass.getName().replace('.', '/').concat(".java");
22+
// 目标类的源文件(Maven 路径):${user.dir}/src/main/java + 目标类的源文件(相对路径)
23+
String sourceFilePath = System.getProperty("user.dir") + "/src/main/java/" + sourceFileRelativePath;
24+
File sourceFile = new File(sourceFilePath);
25+
// 指定 Java 新生成的 Class 输出目录(并非编译时依赖的 ClassPath) javac -d
26+
fileManager.setLocation(StandardLocation.CLASS_OUTPUT, Collections.singleton(getClassOutputDirectory()));
27+
28+
Iterable<? extends JavaFileObject> compilationUnits =
29+
fileManager.getJavaFileObjectsFromFiles(Arrays.asList(sourceFile));
30+
31+
JavaCompiler.CompilationTask compilationTask = javaCompiler.getTask(new OutputStreamWriter(System.out), fileManager, null, null, null, compilationUnits);
32+
// 执行编译
33+
compilationTask.call();
34+
}
35+
36+
public static File getClassOutputDirectory() {
37+
File currentClassPath = new File(findClassPath());
38+
File targetDirectory = currentClassPath.getParentFile();
39+
File classOutputDirectory = new File(targetDirectory, "new-classes");
40+
// 生成类输出目录
41+
classOutputDirectory.mkdir();
42+
return classOutputDirectory;
43+
}
44+
45+
public static String findClassPath() {
46+
String classPath = System.getProperty("java.class.path");
47+
return Stream.of(classPath.split(File.pathSeparator))
48+
.map(File::new)
49+
.filter(File::isDirectory)
50+
.filter(File::canRead)
51+
.filter(File::canWrite)
52+
.map(File::getAbsolutePath)
53+
.findFirst()
54+
.orElse(null);
55+
}
56+
}

0 commit comments

Comments
 (0)