如何创建自定义注解自动生成Singleton类的样板代码?
自定义编译时注解实现单例类自动生成
需求说明
项目中需要创建大量单例类,希望通过自定义编译时注解,像Lombok的@Getter那样自动生成单例样板代码:包括私有静态final实例、私有构造方法、getInstance()静态方法。
比如只需要写:
@Singleton public class AgeValidation { // 业务代码 }
编译后自动生成以下样板代码:
private static final AgeValidation instance = new AgeValidation(); private AgeValidation() { } public static AgeValidation getInstance() { return instance; }
实现步骤
1. 定义自定义注解@Singleton
创建注解类,指定作用目标为类,保留策略为编译时(SOURCE):
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.SOURCE) public @interface Singleton { }
2. 编写注解处理器
继承AbstractProcessor,实现编译时解析@Singleton注解并生成代码的逻辑:
import javax.annotation.processing.AbstractProcessor; import javax.annotation.processing.RoundEnvironment; import javax.annotation.processing.SupportedAnnotationTypes; import javax.annotation.processing.SupportedSourceVersion; import javax.lang.model.SourceVersion; import javax.lang.model.element.Element; import javax.lang.model.element.TypeElement; import javax.tools.JavaFileObject; import java.io.IOException; import java.io.PrintWriter; import java.util.Set; @SupportedAnnotationTypes("com.yourpackage.Singleton") // 替换为你的注解全类名 @SupportedSourceVersion(SourceVersion.RELEASE_8) // 根据你的JDK版本调整 public class SingletonProcessor extends AbstractProcessor { @Override public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { for (Element element : roundEnv.getElementsAnnotatedWith(Singleton.class)) { if (element instanceof TypeElement) { TypeElement typeElement = (TypeElement) element; String className = typeElement.getSimpleName().toString(); String packageName = processingEnv.getElementUtils().getPackageOf(typeElement).toString(); try { // 创建要生成的Java文件对象 JavaFileObject sourceFile = processingEnv.getFiler().createSourceFile(packageName + "." + className); try (PrintWriter writer = new PrintWriter(sourceFile.openWriter())) { // 写入包声明 writer.println("package " + packageName + ";"); writer.println(); // 写入类定义 writer.println("public class " + className + " {"); // 生成单例实例 writer.println(" private static final " + className + " instance = new " + className + "();"); writer.println(); // 生成私有构造方法 writer.println(" private " + className + "() { }"); writer.println(); // 生成getInstance方法 writer.println(" public static " + className + " getInstance() {"); writer.println(" return instance;"); writer.println(" }"); writer.println("}"); } } catch (IOException e) { e.printStackTrace(); } } } return true; } }
注意:上面的处理器是生成新的类文件,如果需要直接修改原有类(像Lombok那样修改字节码),建议使用JavaPoet或者ASM库来操作语法树或字节码,避免生成重复类。比如用JavaPoet可以更优雅地构建代码,还能保留原有类的成员。
3. 配置注解处理器
Maven配置
在pom.xml中添加注解处理器依赖和编译插件配置:
<dependencies> <!-- 注解API --> <dependency> <groupId>javax.annotation</groupId> <artifactId>javax.annotation-api</artifactId> <version>1.3.2</version> <scope>provided</scope> </dependency> <!-- 可选:用JavaPoet简化代码生成 --> <dependency> <groupId>com.squareup</groupId> <artifactId>javapoet</artifactId> <version>1.13.0</version> <scope>provided</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.8.1</version> <configuration> <source>8</source> <target>8</target> <annotationProcessors> <annotationProcessor>com.yourpackage.SingletonProcessor</annotationProcessor> </annotationProcessors> </configuration> </plugin> </plugins> </build>
自动注册处理器(可选)
使用@AutoService注解自动注册处理器,无需手动配置:
import com.google.auto.service.AutoService; import javax.annotation.processing.Processor; @AutoService(Processor.class) @SupportedAnnotationTypes("com.yourpackage.Singleton") @SupportedSourceVersion(SourceVersion.RELEASE_8) public class SingletonProcessor extends AbstractProcessor { // 处理器逻辑... }
需要添加AutoService依赖:
<dependency> <groupId>com.google.auto.service</groupId> <artifactId>auto-service</artifactId> <version>1.0.1</version> <scope>provided</scope> </dependency>
4. 使用示例
在你的类上添加@Singleton注解即可:
@Singleton public class AgeValidation { // 这里写你的业务逻辑,编译时会自动生成单例相关代码 }
内容的提问来源于stack exchange,提问作者Sagar Kalra
相关产品推荐
相关产品推荐

