Java 注解处理器
Java 注解处理器(Annotation Processor)是 Java 编译器的一部分,用于处理源代码中的注解信息。它可以在编译时扫描和处理注解,并生成额外的代码或者进行其它的操作。注解处理器可以用来自动生成代码、进行代码检查、生成文档等。
默认情况下,非受检异常会回滚,受检异常Exception不会回滚事务:
Spring避坑指南:Spring声明式事务@Transactional避坑 崔认知,公众号:认知科技技术团队Spring避坑指南:Spring声明式事务@Transactional避坑
所以,我们必须在配置:
@Transactional(rollbackFor = Exception.class)
Java 注解处理器自动检查代码
Java 注解处理器(Annotation Processor)由于是在编译期处理注解,处理的注解必须在编译期被保留,即:注解的RetentionPolicy不能是:java.lang.annotation.RetentionPolicy#SOURCE。由于Transactional的RetentionPolicy是
RetentionPolicy.RUNTIME,所以可以使用 Java 注解处理器来处理。
实现Java 注解处理器需要实现javax.annotation.processing.AbstractProcessor,并根据SPI的加载规则需要在文件META-INF/services/javax.annotation.processing.Processor中配置自定义实现的Java 注解处理类。
这里使用auto-service工具来自动实现SPI的文件生成:
<dependency>
<groupId>com.google.auto.service</groupId>
<artifactId>auto-service</artifactId>
<version>1.1.1</version>
</dependency>
检测Spring注解事务没有配置rollbackFor = Exception.class的自定义注解处理器:
package org.renzhikeji;
import com.google.auto.service.AutoService;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.processing.*;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.*;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
/**
* @author 认知科技技术团队
* 微信公众号:认知科技技术团队
*/
@SupportedAnnotationTypes("org.springframework.transaction.annotation.Transactional")
@AutoService(Processor.class)
public class TransactionalProcessor extends AbstractProcessor {
private static ConcurrentHashMap<Element, Element> message = new ConcurrentHashMap<>();
@Override
public SourceVersion getSupportedSourceVersion() {
return SourceVersion.latestSupported();
}
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
System.out.println("=======Transactional rollbackFor=Exception.class== 处理 ");
Set<? extends Element> elementsAnnotatedWith = roundEnv.getElementsAnnotatedWith(Transactional.class);
for (Element element : elementsAnnotatedWith) {
if (!element.getKind().isExecutable()) {
continue;
}
Map<String, Object> keyValues = getAnnotation(element, Transactional.class.getName());
if (keyValues.isEmpty()) {
message.put(element, element);
continue;
}
Set<String> rollbackFor = Optional.ofNullable((List<Object>) keyValues.get("rollbackFor")).stream()
.map(Object::toString).collect(Collectors.toSet());
if ( !rollbackFor.contains("java.lang.Exception.class")) {
message.put(element, element);
}
}
if (!message.isEmpty()) {
throw new RuntimeException("使用spring的事务注解Transactional,请配置rollbackFor=Exception.class,防止事务失效");
}
return true;
}
private static Map<String, Object> getAnnotation(Element element, String name) {
for (AnnotationMirror mirror : element.getAnnotationMirrors()) {
if (mirror.getAnnotationType().toString().equals(name)) {
return annotationKeyValues(mirror);
}
}
throw new IllegalArgumentException(String.format("%s has no annotation %s", element, name));
}
private static Map<String, Object> annotationKeyValues(AnnotationMirror mirror) {
Map<String, Object> result = new LinkedHashMap<>();
for (ExecutableElement key : mirror.getElementValues().keySet()) {
result.put(key.getSimpleName().toString(), mirror.getElementValues().get(key).getValue());
}
return result;
}
}
编译打包后,auto-service工具来自动实现SPI的文件生成:
在项目中maven依赖自定义实现的注解处理器jar包,并测试:
编译时报错:
小结
Java 注解处理器是一种用于处理源代码中注解信息的工具,可以在编译时对代码进行静态分析和修改,实现自动化任务和定制框架行为。比如:代码生成工具框架lombok、SPI文件自动生成工具auto-service、代码检测工具error-prone等。