Java-基于注解修改代码,处理响应前端数据字典及关联对象

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

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值