1、资料
AutoService: https://github.com/google/auto/tree/master/service
javaPoet: https://github.com/square/javapoet
2、介绍
本文主要是利用两者+注解在maven的编译期来生成代码,一个简单demo,也可以用来扩展逻辑来生成自己的代码。
3、工程介绍
3.1 父子工程截图
3.2 父工程testo的pom文件
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<packaging>pom</packaging>
<modules>
<module>auto</module>
<module>test</module>
</modules>
<groupId>com.example</groupId>
<artifactId>testo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>testo</name>
<description>testo</description>
<properties>
<java.version>8</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<auto-service.version>1.0-rc6</auto-service.version>
</properties>
</project>
3.3 子模块test的pom文件
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>testo</artifactId>
<groupId>com.example</groupId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>test</artifactId>
<properties>
<java.version>8</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<auto-service.version>1.0-rc6</auto-service.version>
</properties>
<dependencies>
<dependency>
<groupId>com.example</groupId>
<artifactId>auto</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>2.0.12</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.10.1</version>
<configuration>
<annotationProcessorPaths>
<path>
<groupId>com.example</groupId>
<artifactId>auto</artifactId>
<version>0.0.1-SNAPSHOT</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
</plugins>
</build>
</project>
3.4 子模块test代码截图
3.5 子模块auto的pom文件
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>testo</artifactId>
<groupId>com.example</groupId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>auto</artifactId>
<dependencies>
<dependency>
<groupId>com.google.auto.service</groupId>
<artifactId>auto-service</artifactId>
<version>1.0-rc2</version>
</dependency>
<dependency>
<groupId>com.squareup</groupId>
<artifactId>javapoet</artifactId>
<version>1.13.0</version>
</dependency>
</dependencies>
</project>
3.6 子模块auto的代码截图
3.7 子模块auto代码
package com.example.testo.auto;
import com.squareup.javapoet.JavaFile;
import com.squareup.javapoet.TypeSpec;
import javax.annotation.processing.RoundEnvironment;
import javax.lang.model.element.TypeElement;
import java.io.File;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
public abstract class BaseCodeGenProcessor implements CodeGenProcessor {
public static final String PREFIX = "Base";
@Override
public void generate(TypeElement typeElement, RoundEnvironment roundEnvironment)
throws Exception {
System.out.println(1111);
//添加其他逻辑扩展
}
public void genJavaSourceFile(String packageName, String pathStr, TypeSpec.Builder typeSpecBuilder) {
TypeSpec typeSpec = typeSpecBuilder.build();
JavaFile javaFile = JavaFile.builder(packageName, typeSpec)
.addFileComment("--auto generated by ada--")
.build();
String packagePath = packageName.replace(".", File.separator) + File.separator + typeSpec.name + ".java";
try {
Path path = Paths.get(pathStr);
File file = new File(path.toFile().getAbsolutePath());
if (!file.exists()) {
return;
}
String sourceFileName = path.toFile().getAbsolutePath() + File.separator + packagePath;
File sourceFile = new File(sourceFileName);
if (!sourceFile.exists()) {
javaFile.writeTo(file);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
package com.example.testo.auto;
import javax.annotation.processing.RoundEnvironment;
import javax.lang.model.element.TypeElement;
import java.lang.annotation.Annotation;
/**
* @Author ada
* @Date 2022/9/5 10:37 PM
* @Version 1.0
*/
public interface CodeGenProcessor {
/**
* 需要解析的类上的注解.
* @return
*/
Class<? extends Annotation> getAnnotation();
/**
* 获取生成的包路径.
* @param typeElement
* @return
*/
String generatePackage(TypeElement typeElement);
/**
* 代码生成逻辑.
* @param typeElement
* @param roundEnvironment
* @throws Exception
*/
void generate(TypeElement typeElement, RoundEnvironment roundEnvironment) throws Exception;
}
package com.example.testo.auto;
import com.google.common.collect.Maps;
import java.lang.annotation.Annotation;
import java.util.Iterator;
import java.util.Map;
import java.util.ServiceLoader;
import java.util.Set;
public final class CodeGenProcessorRegistry {
private static Map<String, ? extends CodeGenProcessor> PROCESSORS;
private CodeGenProcessorRegistry() {
throw new UnsupportedOperationException();
}
/**
* 注解处理器要处理的注解集合
*
* @return
*/
public static Set<String> getSupportedAnnotations() {
return PROCESSORS.keySet();
}
public static CodeGenProcessor find(String annotationClassName) {
return PROCESSORS.get(annotationClassName);
}
/**
* spi 加载所有的processor
*
* @return
*/
public static void initProcessors() {
final Map<String, CodeGenProcessor> map = Maps.newLinkedHashMap();
ServiceLoader<CodeGenProcessor> processors = ServiceLoader.load(CodeGenProcessor.class,CodeGenProcessor.class.getClassLoader());
Iterator<CodeGenProcessor> iterator = processors.iterator();
while (iterator.hasNext()) {
CodeGenProcessor next = iterator.next();
Class<? extends Annotation> annotation = next.getAnnotation();
map.put(annotation.getName(), next);
}
PROCESSORS = map;
}
}
package com.example.testo.auto;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface GenService {
String pkgName();
String sourcePath() default "src/main/java";
boolean overrideSource() default false;
}
package com.example.testo.auto;
import com.google.auto.service.AutoService;
import com.squareup.javapoet.JavaFile;
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.TypeSpec;
import javax.annotation.processing.RoundEnvironment;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement;
import java.io.File;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.nio.file.Path;
import java.nio.file.Paths;
/**
* @Author ada
* @Date 2022/9/15 11:39 PM
* @Version 1.0
*/
@AutoService(CodeGenProcessor.class)
public class GenServiceProcessor extends BaseCodeGenProcessor {
@Override
public Class<? extends Annotation> getAnnotation() {
return GenService.class;
}
@Override
public String generatePackage(TypeElement typeElement) {
return typeElement.getAnnotation(GenService.class).pkgName();
}
@Override
public void generate(TypeElement typeElement, RoundEnvironment roundEnvironment) throws Exception {
MethodSpec main = MethodSpec.methodBuilder("main")
.addModifiers(Modifier.PUBLIC, Modifier.STATIC)
.returns(void.class)
.addParameter(String[].class, "args")
.addStatement("$T.out.println($S)", System.class, "Hello, JavaPoet!")
.build();
String className = "HelloWorld";
TypeSpec.Builder helloWorld = TypeSpec.classBuilder(className)
.addModifiers(Modifier.PUBLIC, Modifier.FINAL)
.addMethod(main);
String packageName = "com.example.helloworld";
genJavaSourceFile(packageName, typeElement.getAnnotation(GenService.class).sourcePath(), helloWorld);
}
}
package com.example.testo.auto;
import com.google.auto.service.AutoService;
import com.squareup.javapoet.JavaFile;
import com.squareup.javapoet.TypeSpec;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.Processor;
import javax.annotation.processing.RoundEnvironment;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import javax.lang.model.util.ElementFilter;
import javax.tools.Diagnostic.Kind;
import java.io.File;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Set;
@AutoService(Processor.class)
public class Only4PlayCodeGenProcessor extends AbstractProcessor {
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
annotations.stream().forEach(an -> {
Set<? extends Element> typeElements = roundEnv.getElementsAnnotatedWith(an);
Set<TypeElement> types = ElementFilter.typesIn(typeElements);
for (TypeElement typeElement : types){
CodeGenProcessor codeGenProcessor = CodeGenProcessorRegistry.find(
an.getQualifiedName().toString());
try {
codeGenProcessor.generate(typeElement,roundEnv);
} catch (Exception e) {
ProcessingEnvironmentHolder.getEnvironment().getMessager().printMessage(Kind.ERROR,"代码生成异常:" + e.getMessage());
}
}
});
return false;
}
@Override
public synchronized void init(ProcessingEnvironment processingEnv) {
super.init(processingEnv);
ProcessingEnvironmentHolder.setEnvironment(processingEnv);
CodeGenProcessorRegistry.initProcessors();
}
@Override
public Set<String> getSupportedAnnotationTypes() {
return CodeGenProcessorRegistry.getSupportedAnnotations();
}
@Override
public SourceVersion getSupportedSourceVersion() {
return SourceVersion.latestSupported();
}
}
package com.example.testo.auto;
import javax.annotation.processing.ProcessingEnvironment;
public final class ProcessingEnvironmentHolder {
public static final ThreadLocal<ProcessingEnvironment> environment = new ThreadLocal<>();
public static void setEnvironment(ProcessingEnvironment pe){
environment.set(pe);
}
public static ProcessingEnvironment getEnvironment(){
return environment.get();
}
}
4、调试技巧
1、mvn install auto
2、mvn clean test,然后debug mvn complie test