1. 前因
在做公用模块时,遇到某些场景下需要将实体中某些字段加解密后进行数据交互的要求.由于这样的处理与具体业务无关,所以提出来做成公用模块.
需要完成的功能点:
在编写代码时能检查、提醒非字符串类型的变量不正确
能指定解密方式、解密key
将字符串内容还原成明文
2. 思考
在平常工作中,经常会使用到自定义注解.重点在运行期注解,对于 RetentionPolicy.CLASS,RetentionPolicy.SOURCE
类型的注解鲜少涉猎.故对完成上述要求的第二、第三亮点功能很简单.对第一点的完成没有经验.
在日常研发中,经常使用到lombok的注解来简化代码编写,但查看生成的class文件确与正常写的代码殊无二致.仔细查看@Data,@Getter...
等注解,都是RetentionPolicy.SOURCE
在代码编译阶段生效. 由此可见,我们要实现第一点功能.需要借助类似lombok处理注解的方法.
3. 编码实现
对于需要完成的三个功能点中的后两个由于是常用功能,将不详细记录.重点记录实现第一点的细节与原理.
经过多番查询lomok代码,以及搜索资料大致总结出了出了下面几个步骤:
自定义一个注解(如果只是为了满足第一点,则使用
RetentionPolicy.SOURCE
;三个功能点都要满足则还是需要使用RetentionPolicy.RUNTIME
)
@Inherited@Documented@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.FIELD)public @interface MaxEncryptField {/*** 加解密类型* @return*/DecryptType decryptType();/*** 加解密key* @return*/String decryptKey() default "";enum DecryptType{/*** des3 加解密算法*/DES3,/*** rsa 加解密算法*/RSA}}
自定义一个Annotation Processor,需要继承
javax.annotation.processing.AbstractProcessor
,并覆写process方法
@Slf4j@SupportedAnnotationTypes("com.dan.common.encrypt.anno.MaxEncryptField")public class DanEncryptAnnoProcessor extends AbstractProcessor {@Overridepublic boolean process(Set<? extends TypeElement> annotations, RoundEnvironment env) {Messager messager = processingEnv.getMessager();Type eleType = null;for (Element e : env.getElementsAnnotatedWith(MaxEncryptField.class)) {/*** 获取到注解了MaxEncryptField的元素对象类型*/eleType = ((Symbol.VarSymbol)e).type;/*** 元素对象类型如果为AnnotatedType类型,或者为ClassType则进入判断是否为字符串*/if (eleType instanceof Type.AnnotatedType || eleType instanceof Type.ClassType){/*** 获取元素对象类型,并比较是否为字符串类型;不是则提醒*/if (!"java.lang.String".equals(eleType.unannotatedType().toString())){messager.printMessage(Diagnostic.Kind.ERROR,String.format("%s#%s类型不为String,不能解密",/*** 获取到注解的元素对象的类全限定名*/e.getEnclosingElement().asType(),/*** 获取到注解的元素对象的字段、方法等名称(此处为字段名)*/e.getSimpleName()), e);}}else {/*** 元素对象为原生类型*/messager.printMessage(Diagnostic.Kind.ERROR,String.format("%s#%s类型不为String,不能解密",/*** 获取到注解的元素对象的类全列名*/e.getEnclosingElement().asType(),/*** 获取到注解的元素对象的字段、方法等名称(此处为字段名)*/e.getSimpleName()), e);}}return true;}/*** 编译器允许执行的JDK版本;* 等同与在类上注解@SupportedSourceVersion*/@Overridepublic SourceVersion getSupportedSourceVersion() {return SourceVersion.latestSupported();}/*** 该注解处理器是注册到哪些注解上。它的返回值是一个字符串的集合,包含本处理器想要处理的注解类型的合法全称* 等同与在类上注解@SupportedAnnotationTypes,如果是否注解,则为第一步创建的自定义注解的全限定名*/@Overridepublic Set<String> getSupportedAnnotationTypes() {Set<String> annotations = new LinkedHashSet<>();//把我们自己定义的注解添加进去annotations.add(MaxEncryptField.class.getCanonicalName());return annotations;}}
将第二步自定义的Annotation Processor注册成服务
在classpath下新建META-INF/services包;并在其中添加文件javax.annotation.processing.Processor
# javax.annotation.processing.Processor 内容为# 第二步自定义annotations processor的全限定名com.dan.common.encrypt.anno.processor.DanEncryptAnnoProcessor
4. 后续相关
以上步骤完成后,能在代码编译时对注解了MaxEncryptField的字段类型做检验,如果不为字符串则编译失败;也可以在idea、eclipse等ide工具中设置外部processor 校验,能达到在idea中对普通方法注解@Override报错提醒的效果

在idea中设置processor 校验步骤:
项目右键 ---> Properties ---> Java Compiler ---> 1. Annotation Processing ---> Enable project specific settings
2. Annotation Processing ---> Factory Path ---> Enable project specific settings & 添加自定义的 Processor jar




