1、注解
首先我们需要写一个注解:
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface AddGetterSetter {
}
该注解有两个元注解,其中 @Retention(RetentionPolicy.RUNTIME) 指定了该注解在运行时保留,@Target(ElementType.TYPE) 指定该注解只能用于类上。
2、注解处理器
接下来,我们需要一个注解处理器来处理这个注解并自动生成 getter 和 setter 方法
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
public class AddGetterSetterProcessor {
public static void process(Class<?> clazz) {
if (!clazz.isAnnotationPresent(AddGetterSetter.class)) {
return;
}
for (Field field : clazz.getDeclaredFields()) {
String fieldName = field.getName();
String capitalizedFieldName = capitalize(fieldName);
try {
clazz.getMethod("get" + capitalizedFieldName);
} catch (NoSuchMethodException e) {
addGetter(clazz, fieldName);
}
try {
clazz.getMethod("set" + capitalizedFieldName, field.getType());
} catch (NoSuchMethodException e) {
addSetter(clazz, fieldName, field.getType());
}
}
}
private static String capitalize(String s) {
if (s == null || s.isEmpty()) {
return s;
}
return Character.toUpperCase(s.charAt(0)) + s.substring(1);
}
private static void addGetter(Class<?> clazz, String fieldName) {
String capitalizedFieldName = capitalize(fieldName);
String getterName = "get" + capitalizedFieldName;
String getterBody = "return " + fieldName + ";";
String getterCode = "public " + clazz.getSimpleName() + " " + getterName + "() { " + getterBody + " }";
try {
clazz.getDeclaredMethod(getterName);
} catch (NoSuchMethodException e) {
try {
clazz.getDeclaredField(fieldName);
} catch (NoSuchFieldException e1) {
throw new RuntimeException(e1);
}
try {
clazz.getDeclaredMethod(getterName);
} catch (NoSuchMethodException e2) {
addMethod(clazz, getterCode);
}
}
}
private static void addSetter(Class<?> clazz, String fieldName, Class<?> fieldType) {
String capitalizedFieldName = capitalize(fieldName);
String setterName = "set" + capitalizedFieldName;
String setterBody = "this." + fieldName + " = " + fieldName + ";";
String setterCode = "public void " + setterName + "(" + fieldType.getSimpleName() + " " + fieldName + ") { " + setterBody + " }";
try {
clazz.getDeclaredMethod(setterName, fieldType);
} catch (NoSuchMethodException e) {
try {
clazz.getDeclaredField(fieldName);
} catch (NoSuchFieldException e1) {
throw new RuntimeException(e1);
}
try {
clazz.getDeclaredMethod(setterName, fieldType);
} catch (NoSuchMethodException e2) {
addMethod(clazz, setterCode);
}
}
}
private static void addMethod(Class<?> clazz, String code) {
try {
Class<?> compiler = Class.forName("javax.tools.ToolProvider");
Object javac = compiler.getMethod("getSystemJavaCompiler").invoke(null);
Class<?> fileManagerClass = Class.forName("com.sun.tools.javac.api.JavacFileManager");
3、使用
注解有了,注解处理器也有了,那怎么让注解生效呢?
在 Spring Boot 中,注解处理器也是通过注解来定义的。你需要定义一个被 @Component 或者 @Service 等注解修饰的类,然后实现 org.springframework.beans.factory.config.BeanDefinitionRegistryPostProcessor 接口。该接口是 Spring 的扩展点接口,可以在 Spring 应用启动时,动态注册 Bean 定义。
接下来,你需要在应用程序的配置类上,使用 @Import 注解引入你定义的注解处理器类。这样,Spring Boot 在启动时会扫描你的注解处理器类,并自动调用其中的方法。
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.beans.factory.support.BeanNameGenerator;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.type.AnnotationMetadata;
public class AddGetterSetterRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry, BeanNameGenerator importBeanNameGenerator) {
BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(AddGetterSetterProcessor.class);
BeanDefinition beanDefinition = builder.getBeanDefinition();
registry.registerBeanDefinition("addGetterSetterProcessor", beanDefinition);
}
}
在你的应用程序的配置类上,使用 @Import 注解引入这个类即可:
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
@Configuration
@Import(AddGetterSetterRegistrar.class)
public class AppConfig {
//...
}
这样,当你的应用程序启动时,Spring Boot 会自动扫描 AddGetterSetterProcessor 类,并调用其 process 方法来处理使用了 AddGetterSetter 注解的类,为其自动生成 getter 和 setter 方法。