1. 使用ast(抽象语法树)修改代码文件和JSR-269:Pluggable Annotations Processing API(插入式注解处理API)处理注解
2. 创建注解处理程序
package com.spring.ast.processor;
import com.google.auto.service.AutoService;
import com.spring.ast.annotated.Describe;
import com.sun.source.tree.Tree;
import com.sun.tools.javac.api.JavacTrees;
import com.sun.tools.javac.code.Flags;
import com.sun.tools.javac.code.Symbol;
import com.sun.tools.javac.processing.JavacProcessingEnvironment;
import com.sun.tools.javac.tree.JCTree;
import com.sun.tools.javac.tree.TreeMaker;
import com.sun.tools.javac.tree.TreeTranslator;
import com.sun.tools.javac.util.*;
import org.apache.commons.lang3.StringUtils;
import javax.annotation.processing.*;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import java.util.Set;
/**
* @author liuzhiqiang
* @SupportedAnnotationTypes 受支持的注解
* SupportedSourceVersion 支持的java版本
* AutoService 创建 META-INF/services/javax.annotation.processing.Processor 文件
*/
@SupportedAnnotationTypes("com.spring.ast.annotated.Describe")
@SupportedSourceVersion(value = SourceVersion.RELEASE_8)
@AutoService(Processor.class)
public class DescribeAnnotationProcessor extends AbstractProcessor {
/**
* 将Element转换为JCTree的工具,提供了待处理的抽象语法树
*/
private JavacTrees trees;
/**
* 封装了创建AST节点的一些方法
*/
private TreeMaker treeMaker;
/**
* 提供了创建标识符的方法
*/
private Names names;
/**
* 首字母大写
*
* @param str
* @return
*/
private static String getMethodName(String str) {
String[] cs = str.split("");
cs[0] = cs[0].toUpperCase();
return String.join("", cs);
}
@Override
public SourceVersion getSupportedSourceVersion() {
return SourceVersion.latestSupported();
}
@Override
public synchronized void init(ProcessingEnvironment processingEnv) {
super.init(processingEnv);
this.trees = JavacTrees.instance(processingEnv);
Context context = ((JavacProcessingEnvironment) processingEnv).getContext();
this.treeMaker = TreeMaker.instance(context);
this.names = Names.instance(context);
}
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
// 获取被@Getter注解标记的所有元素(这个元素可能是类、变量、方法等等)
Set<? extends Element> set = roundEnv.getElementsAnnotatedWith(Describe.class);
for (Element element : set) {
if (element instanceof Symbol.VarSymbol) {
Symbol symbol = ((Symbol.VarSymbol) element).owner;
for (Tree tree : trees.getPath(symbol)) {
if (tree instanceof JCTree.JCCompilationUnit) {
JCTree.JCCompilationUnit jccu = (JCTree.JCCompilationUnit) tree;
JCTree.JCImport jcImport = treeMaker.Import(memberAccess("com.spring.ast.service.ExchangeUtil"), false);
jccu.defs = jccu.defs.prepend(jcImport);
jccu.accept(new TreeTranslator() {
@Override
public void visitClassDef(JCTree.JCClassDecl jcClassDecl) {
Describe describe = element.getAnnotation(Describe.class);
JCTree.JCMethodDecl jcMethodDecl = generateCodeDescribe(describe, ((Symbol.VarSymbol) element).name);
jcClassDecl.defs = jcClassDecl.defs.prepend(jcMethodDecl);
super.visitClassDef(jcClassDecl);
}
});
}
}
}
}
return true;
}
JCTree.JCMethodDecl generateCodeDescribe(Describe describe, Name name) {
String funName = "get" + getMethodName(name.toString()) + "Describe";
if (!StringUtils.isEmpty(describe.alias())) {
funName = "get" + getMethodName(describe.alias());
}
ListBuffer<JCTree.JCStatement> jcStatements = new ListBuffer<>();
ListBuffer<JCTree.JCExpression> args = new ListBuffer<>();
for (String arg : describe.args()) {
args.append(treeMaker.Literal(arg));
}
JCTree.JCMethodInvocation jcMethodInvocation = treeMaker.Apply(
List.of(memberAccess("java.lang.Object")),
memberAccess("ExchangeUtil.createDescribe"),
List.of(
treeMaker.NewArray(memberAccess("java.lang.String"), List.nil(), args.toList()),
treeMaker.Literal(describe.type()),
treeMaker.Literal(describe.alias())
)
);
JCTree.JCExpressionStatement printLiteral = treeMaker.Exec(treeMaker.Apply(
List.of(memberAccess("java.lang.Object")),
memberAccess("ExchangeUtil.describeHandle"),
List.of(
jcMethodInvocation,
memberAccess("this." + name))
)
);
jcStatements.append(treeMaker.Return(printLiteral.getExpression()));
//设置方法体
JCTree.JCBlock jcBlock = treeMaker.Block(0, jcStatements.toList());
JCTree.JCMethodDecl jcMethodDecl = treeMaker.MethodDef(
treeMaker.Modifiers(Flags.PUBLIC),
getNameFromString(funName),
memberAccess("java.lang.String"),
List.nil(),
List.nil(),
List.nil(),
jcBlock,
null
);
return jcMethodDecl;
}
private JCTree.JCExpression memberAccess(String components) {
String[] componentArray = components.split("\\.");
JCTree.JCExpression expr = treeMaker.Ident(getNameFromString(componentArray[0]));
for (int i = 1; i < componentArray.length; i++) {
expr = treeMaker.Select(expr, getNameFromString(componentArray[i]));
}
return expr;
}
private Name getNameFromString(String s) {
return names.fromString(s);
}
}
3. 创建注解
package com.spring.ast.annotated;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @author liuzhiqiang
*/
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.SOURCE)
public @interface Describe {
/**
* 类型
*
* @return
*/
String type() default "";
/**
* 其他参数
*
* @return
*/
String[] args() default {};
/**
* 别名
* @return
*/
String alias() default "";
}
4. 工具类
package com.spring.ast.service;
import com.spring.ast.annotated.Describe;
import com.spring.ast.annotated.Relation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.lang.annotation.Annotation;
/**
* @author liuzhiqiang
*/
@Component
@Slf4j
public class ExchangeUtil {
private static ExchangeService exchangeService;
@Resource
private ExchangeService service;
public static String describeHandle(Describe describe, Object value) {
try {
if (ExchangeUtil.exchangeService != null) {
return ExchangeUtil.exchangeService.describeHandle(describe, value);
} else {
return "";
}
} catch (Exception e) {
log.error(e.getMessage());
return "";
}
}
public static String relationHandle(Relation relation, Object value) {
try {
if (ExchangeUtil.exchangeService != null) {
return ExchangeUtil.exchangeService.relationHandle(relation, value);
} else {
return "";
}
} catch (Exception e) {
log.error(e.getMessage());
return "";
}
}
@PostConstruct
public void init() {
ExchangeUtil.exchangeService = this.service;
}
public static Relation createRelation(String table,String showField,String targetField,String[] args, String alias) {
return new Relation(){
@Override
public Class<? extends Annotation> annotationType() {
return Relation.class;
}
@Override
public String table() {
return table;
}
@Override
public String showField() {
return showField;
}
@Override
public String targetField() {
return targetField;
}
@Override
public String[] args() {
return args;
}
@Override
public String alias() {
return alias;
}
};
}
public static Describe createDescribe(String[] args, String type, String alias) {
return new Describe(){
@Override
public Class<? extends Annotation> annotationType() {
return Describe.class;
}
@Override
public String type() {
return type;
}
@Override
public String[] args() {
return args;
}
@Override
public String alias() {
return alias;
}
};
}
}
5. service 业务代码实现此接口
package com.spring.ast.service;
import com.spring.ast.annotated.Describe;
import com.spring.ast.annotated.Relation;
/**
* @author liuzhiqiang
*/
public interface ExchangeService {
/**
* 关联关系处理
* @param relation
* @param value
* @return
*/
String relationHandle(Relation relation, Object value);
/**
* 描述字段处理
* @param describe
* @param value
* @return
*/
String describeHandle(Describe describe, Object value);
}
6. 使用
package com.spring.dict.domain;
import com.spring.ast.annotated.Describe;
import com.spring.ast.annotated.Relation;
import lombok.Data;
/**
* @author liuzhiqiang
*/
@Data
public class TestDomain {
@Describe(type = "city", alias = "nameCity")
private String name;
@Relation(table = "customer", showField = "customerName", alias = "customerName")
private String customer;
}
7. 最终生成的class文件代码
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package com.spring.dict.domain;
import com.spring.ast.service.ExchangeUtil;
public class TestDomain {
private String name;
private String customer;
public String getCustomerName() {
return ExchangeUtil.relationHandle(ExchangeUtil.createRelation("customer", "customerName", "id", new String[0], "customerName"), this.customer);
}
public String getNameCity() {
return ExchangeUtil.describeHandle(ExchangeUtil.createDescribe(new String[0], "city", "nameCity"), this.name);
}
public TestDomain() {
}
public String getName() {
return this.name;
}
public String getCustomer() {
return this.customer;
}
public void setName(final String name) {
this.name = name;
}
public void setCustomer(final String customer) {
this.customer = customer;
}
public boolean equals(final Object o) {
if (o == this) {
return true;
} else if (!(o instanceof TestDomain)) {
return false;
} else {
TestDomain other = (TestDomain)o;
if (!other.canEqual(this)) {
return false;
} else {
Object this$name = this.getName();
Object other$name = other.getName();
if (this$name == null) {
if (other$name != null) {
return false;
}
} else if (!this$name.equals(other$name)) {
return false;
}
Object this$customer = this.getCustomer();
Object other$customer = other.getCustomer();
if (this$customer == null) {
if (other$customer != null) {
return false;
}
} else if (!this$customer.equals(other$customer)) {
return false;
}
return true;
}
}
}
protected boolean canEqual(final Object other) {
return other instanceof TestDomain;
}
public int hashCode() {
int PRIME = true;
int result = 1;
Object $name = this.getName();
int result = result * 59 + ($name == null ? 43 : $name.hashCode());
Object $customer = this.getCustomer();
result = result * 59 + ($customer == null ? 43 : $customer.hashCode());
return result;
}
public String toString() {
return "TestDomain(name=" + this.getName() + ", customer=" + this.getCustomer() + ")";
}
}
8. 实现ExchangeService
package com.spring.dict.service;
import com.spring.ast.annotated.Describe;
import com.spring.ast.annotated.Relation;
import com.spring.ast.service.ExchangeService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
/**
* @author liuzhiqiang
*/
@Component
@Slf4j
public class DictImpl implements ExchangeService {
@Override
public String relationHandle(Relation relation, Object value) {
log.info("select " + relation.showField() + " from " + relation.table() + " where " + relation.targetField() + " = '" + value + "'");
return "xxx2";
}
@Override
public String describeHandle(Describe describe, Object value) {
log.info("select label from dict where type = {} and value = '{}'", describe.type(), value);
return "xxx";
}
}
源码:https://gitee.com/lzq199528/springboot/tree/master/spring-ast-annotate