侧边栏壁纸
  • 累计撰写 57 篇文章
  • 累计创建 98 个标签
  • 累计收到 5 条评论

目 录CONTENT

文章目录

关于Java注解处理器的使用

Sir丶雨轩
2021-06-24 / 0 评论 / 2 点赞 / 690 阅读 / 5738 字

在我们一般的SpringBoot项目中,由于使用了MybatisPlus或者JPA,很多增删改查都是定一个空的Mapper或者Dao就可以实现的,我这里比较懒,参考lombok的原理实现自动生成Dao代码

首先注解处理器的项目必须是单独的一个项目。

我们创建一个Maven项目

添加Google的类库 auto-service

官方示例说明

package foo.bar;

import javax.annotation.processing.Processor;

@AutoService(Processor.class)
final class MyProcessor implements Processor {
  // …
}

AutoService会自动在build/classes输入目录下生成文件META-INF/services/javax.annotation.processing.Processor,文件的内容如下

foo.bar.MyProcessor
在javax.annotation.processing.Processor的情况下,如果一个jar中包含metadata文件,并且在javac的classpath下,javac会自动加载这个jar,同时包含它的普通注解编译环境。
定义一个注解

package com.yuxuan66.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * @author Sir丶雨轩
 * @since 2021/6/24
 */
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.CLASS)
public @interface Dao {

}

定义一个处理器

package com.yuxuan66.processor;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.IoUtil;
import com.google.auto.service.AutoService;
import com.google.common.primitives.Chars;
import com.yuxuan66.annotation.Dao;
import org.beetl.core.Configuration;
import org.beetl.core.GroupTemplate;
import org.beetl.core.Template;
import org.beetl.core.resource.ClasspathResourceLoader;
import org.beetl.core.resource.StringTemplateResourceLoader;
import sun.nio.ch.IOUtil;

import javax.annotation.processing.*;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.TypeElement;
import javax.lang.model.util.Elements;
import javax.lang.model.util.Types;
import javax.tools.Diagnostic;
import javax.tools.JavaFileObject;
import java.io.IOException;
import java.io.InputStream;
import java.io.Writer;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.LinkedHashSet;
import java.util.Objects;
import java.util.Set;

/**
 * @author Sir丶雨轩
 * @since 2021/6/24
 */
@AutoService(Processor.class)
public class DaoProcessor extends AbstractProcessor {

    private Types mTypeUtils;
    private Elements mElementUtils;
    private Filer mFiler;
    private Messager messager;

    @Override
    public Set<String> getSupportedAnnotationTypes() {
        Set<String> annotations = new LinkedHashSet<>();
        //把我们自己定义的注解添加进去
        annotations.add(Dao.class.getCanonicalName());
        return annotations;
    }

    @Override
    public SourceVersion getSupportedSourceVersion() {
        return SourceVersion.latestSupported();
    }


    @Override
    public synchronized void init(ProcessingEnvironment processingEnv) {
        super.init(processingEnv);

        mTypeUtils = processingEnv.getTypeUtils();
        mElementUtils = processingEnv.getElementUtils();
        mFiler = processingEnv.getFiler();
        messager = processingEnv.getMessager();
    }
    private void error(Element e, String msg, Object... args) {
        messager.printMessage(Diagnostic.Kind.ERROR, String.format(msg, args), e);
    }
    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        for (Element annotatedElement : roundEnv.getElementsAnnotatedWith(Dao.class)) {
            if (annotatedElement.getKind() != ElementKind.CLASS) {
                error(annotatedElement, "Only classes can be annotated with @%s", Dao.class.getSimpleName());
                return true;
            }
            // //解析,并生成代码
            try {

                analysisAnnotated(annotatedElement);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return false;
    }

    private void analysisAnnotated(Element classElement) throws IOException {

        Dao annotation = classElement.getAnnotation(Dao.class);

        InputStream inputStream = this.getClass().getResourceAsStream("/template/Dao.btl");

        String daoContent = IoUtil.read(Objects.requireNonNull(inputStream), StandardCharsets.UTF_8);
        // 获取注解所在的包,把Dao生成到原始包中,或者按照需求生成在对应的包中
        String packageName = mElementUtils.getPackageOf(classElement).getQualifiedName().toString();


        daoContent = daoContent.replaceAll("#date", DateUtil.format(new Date(),"yyyy/MM/dd"));
        daoContent = daoContent.replaceAll("#package",packageName);
        daoContent = daoContent.replaceAll("#className",classElement.getSimpleName().toString());

        try {
            JavaFileObject source = mFiler.createSourceFile(packageName + "." + classElement.getSimpleName() + "Dao");
            Writer writer = source.openWriter();
            writer.write(daoContent);
            writer.flush();
            writer.close();
        } catch (IOException ignored) {
        }
    }
}

由于功能比较简单,仅需要简单替换几个字符串即可,所以我们直接字符串串替换就可以了,如果有复杂的需求可以使用模板引擎解析

我们的模板

package #package;

import #package.#className;
import com.yuxuan66.support.basic.jpa.BasicDao;

/**
 * Basic-Admin Automatic Generation
 * @author  Sir丶雨轩
 * @since #date
 */
public interface #classNameDao extends BasicDao<#className> {

}

然后我们需要使用的项目直接在Maven中引用即可,生成出来的Dao、Mapper就像自己创建的一样,也可以被Spring管理,生成的代码会在这里显示
QQ截图20210624150402.png

2

评论区