From 8e3f5c37c1f3d58abb64c6396e70a02e15d0cafd Mon Sep 17 00:00:00 2001
From: ACANX <245818784@qq.com>
Date: Mon, 10 Nov 2025 10:15:18 +0800
Subject: [PATCH 1/3] =?UTF-8?q?add:=E8=87=AA=E5=AE=9A=E4=B9=89=E6=B3=A8?=
=?UTF-8?q?=E8=A7=A3V1?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
autil-incubator/incubator-annotation/pom.xml | 34 +-
.../incubator/annotation/ObjectCopier.java | 47 +
.../annotation/ObjectCopyProcessor.java | 896 +++++++++---------
.../incubator/annotation/ann/CopyTest.java | 42 +
.../annotation/ann/ObjectCopierProcessor.java | 355 +++++++
.../src/main/java/module-info.java | 7 +
.../javax.annotation.processing.Processor | 3 +-
.../java/com/acanx/meta/model/AppTest.java | 17 +-
autil-incubator/pom.xml | 6 +-
autil-test/pom.xml | 36 +
.../com/acanx/util/annotation/CopyTest.java | 42 +
pom.xml | 6 +-
12 files changed, 1020 insertions(+), 471 deletions(-)
create mode 100644 autil-incubator/incubator-annotation/src/main/java/com/acanx/util/incubator/annotation/ObjectCopier.java
create mode 100644 autil-incubator/incubator-annotation/src/main/java/com/acanx/util/incubator/annotation/ann/CopyTest.java
create mode 100644 autil-incubator/incubator-annotation/src/main/java/com/acanx/util/incubator/annotation/ann/ObjectCopierProcessor.java
create mode 100644 autil-incubator/incubator-annotation/src/main/java/module-info.java
create mode 100644 autil-test/src/test/java/com/acanx/util/annotation/CopyTest.java
diff --git a/autil-incubator/incubator-annotation/pom.xml b/autil-incubator/incubator-annotation/pom.xml
index d4aa8ae..1ef87bd 100644
--- a/autil-incubator/incubator-annotation/pom.xml
+++ b/autil-incubator/incubator-annotation/pom.xml
@@ -9,17 +9,18 @@
com.acanx.util
incubator-annotation
- 0.2.0.0-SNAPSHOT
+ 0.4.3-SNAPSHOT
jar
Incubator-Annotation
AUtil Incubator Annotation Module
https://acanx.com
- 11
+ 25
+
UTF-8
- 11
- 3.14.0
+ false
+ 3.14.1
@@ -64,6 +65,12 @@
${junit-jupiter.version}
test
+
+ com.acanx.meta.model
+ model-test
+ 0.5.7-SNAPSHOT
+ compile
+
@@ -81,12 +88,27 @@
${java.version}
${java.version}
+ UTF-8
+ false
+
+ --add-exports
+ jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED
+ --add-exports
+ jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED
+ --add-exports
+ jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED
+ --add-exports
+ jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED
+ --add-exports
+ jdk.compiler/com.sun.tools.javac.model=ALL-UNNAMED
+ --add-exports
+ jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED
+
-
com.acanx.util
incubator-annotation
- 0.2.0.0-SNAPSHOT
+ ${revision}
diff --git a/autil-incubator/incubator-annotation/src/main/java/com/acanx/util/incubator/annotation/ObjectCopier.java b/autil-incubator/incubator-annotation/src/main/java/com/acanx/util/incubator/annotation/ObjectCopier.java
new file mode 100644
index 0000000..2a2439b
--- /dev/null
+++ b/autil-incubator/incubator-annotation/src/main/java/com/acanx/util/incubator/annotation/ObjectCopier.java
@@ -0,0 +1,47 @@
+package com.acanx.util.incubator.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * 对象拷贝注解,用于标记需要进行编译期增强的对象拷贝方法
+ * 被注解的方法会在编译期被增强,自动实现从第一个参数到第二个参数的对象拷贝
+ */
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.SOURCE)
+public @interface ObjectCopier {
+
+ /**
+ * 拷贝策略,默认为浅拷贝
+ */
+ CopyStrategy strategy() default CopyStrategy.SHALLOW;
+
+ /**
+ * 是否忽略空值,如果为true则源对象为null的字段不会覆盖目标对象的字段
+ */
+ boolean ignoreNull() default false;
+
+ /**
+ * 需要排除的字段名列表
+ */
+ String[] exclude() default {};
+
+ /**
+ * 仅包含的字段名列表,如果设置了则只拷贝这些字段
+ */
+ String[] include() default {};
+
+ /**
+ * 拷贝策略枚举
+ */
+ enum CopyStrategy {
+ /** 浅拷贝,直接赋值 */
+ SHALLOW,
+ /** 深拷贝,对引用类型创建新对象 */
+ DEEP
+ }
+
+
+}
diff --git a/autil-incubator/incubator-annotation/src/main/java/com/acanx/util/incubator/annotation/ObjectCopyProcessor.java b/autil-incubator/incubator-annotation/src/main/java/com/acanx/util/incubator/annotation/ObjectCopyProcessor.java
index 2b8647e..0f600c8 100644
--- a/autil-incubator/incubator-annotation/src/main/java/com/acanx/util/incubator/annotation/ObjectCopyProcessor.java
+++ b/autil-incubator/incubator-annotation/src/main/java/com/acanx/util/incubator/annotation/ObjectCopyProcessor.java
@@ -1,448 +1,448 @@
-package com.acanx.util.incubator.annotation;
-
-import com.acanx.annotation.ObjectCopy;
-import javax.annotation.processing.AbstractProcessor;
-import javax.annotation.processing.Filer;
-import javax.annotation.processing.Messager;
-import javax.annotation.processing.ProcessingEnvironment;
-import javax.annotation.processing.RoundEnvironment;
-import javax.annotation.processing.SupportedAnnotationTypes;
-import javax.annotation.processing.SupportedOptions;
-import javax.annotation.processing.SupportedSourceVersion;
-import javax.lang.model.SourceVersion;
-import javax.lang.model.element.Element;
-import javax.lang.model.element.ElementKind;
-import javax.lang.model.element.ExecutableElement;
-import javax.lang.model.element.Modifier;
-import javax.lang.model.element.TypeElement;
-import javax.lang.model.element.VariableElement;
-import javax.lang.model.type.ArrayType;
-import javax.lang.model.type.TypeKind;
-import javax.lang.model.type.TypeMirror;
-import javax.lang.model.util.Elements;
-import javax.tools.Diagnostic;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.stream.Collectors;
-import javax.lang.model.util.Types;
-
-import com.squareup.javapoet.CodeBlock;
-import com.squareup.javapoet.JavaFile;
-import com.squareup.javapoet.MethodSpec;
-import com.squareup.javapoet.TypeName;
-import com.squareup.javapoet.TypeSpec;
-
-/**
- * ObjectCopyProcessor
- *
- * @author ACANX
- * @since 202506
- */
-@SupportedAnnotationTypes("com.acanx.annotation.ObjectCopy")
-@SupportedSourceVersion(SourceVersion.RELEASE_11)
-@SupportedOptions({"incremental"}) // 支持增量编译
-public class ObjectCopyProcessor extends AbstractProcessor {
-
- public static final String SUFFIX = "Copier";
- private Messager messager;
- private Filer filer;
- private Elements elementUtils;
- private Types typeUtils;
-
- // 全局跟踪已生成的文件
- private static final Set generatedFiles = new HashSet<>();
-
- // 收集使用说明
- private static final Map> usageInstructions = new HashMap<>();
-
- // 基本类型与包装类型映射
- private static final Map primitiveToWrapper = Map.of(
- "int", "java.lang.Integer",
- "long", "java.lang.Long",
- "double", "java.lang.Double",
- "float", "java.lang.Float",
- "boolean", "java.lang.Boolean",
- "char", "java.lang.Character",
- "byte", "java.lang.Byte",
- "short", "java.lang.Short"
- );
-
- // 兼容类型映射
- private static final Map> compatibleTypes = Map.of(
- "java.lang.String", Set.of("java.lang.CharSequence"),
- "java.lang.CharSequence", Set.of("java.lang.String"),
- "java.util.Date", Set.of("java.time.Instant", "java.time.LocalDateTime", "java.time.ZonedDateTime"),
- "java.time.Instant", Set.of("java.util.Date", "java.time.LocalDateTime", "java.time.ZonedDateTime"),
- "java.time.LocalDateTime", Set.of("java.util.Date", "java.time.Instant", "java.time.ZonedDateTime"),
- "java.time.ZonedDateTime", Set.of("java.util.Date", "java.time.Instant", "java.time.LocalDateTime")
- );
-
- @Override
- public synchronized void init(ProcessingEnvironment processingEnv) {
- super.init(processingEnv);
- messager = processingEnv.getMessager();
- filer = processingEnv.getFiler();
- elementUtils = processingEnv.getElementUtils();
- typeUtils = processingEnv.getTypeUtils();
- }
-
- @Override
- public boolean process(Set extends TypeElement> annotations, RoundEnvironment roundEnv) {
- try {
- if (roundEnv.processingOver()) {
- printUsageInstructions();
- return false;
- }
-
- Set extends Element> elements = roundEnv.getElementsAnnotatedWith(ObjectCopy.class);
- List methods = elements.stream()
- .filter(e -> e.getKind() == ElementKind.METHOD)
- .map(e -> (ExecutableElement) e)
- .collect(Collectors.toList());
-
- if (methods.isEmpty()) return false;
-
- Map> methodsByClass = methods.stream()
- .collect(Collectors.groupingBy(
- method -> (TypeElement) method.getEnclosingElement()
- ));
-
- for (Map.Entry> entry : methodsByClass.entrySet()) {
- TypeElement enclosingClass = entry.getKey();
- List classMethods = entry.getValue();
- String helperClassName = enclosingClass.getSimpleName().toString() + SUFFIX;
-
- createCopierClass(enclosingClass, classMethods, helperClassName);
- collectUsageInstructions(enclosingClass, classMethods, helperClassName);
- }
-
- return true;
- } catch (Exception e) {
- messager.printMessage(Diagnostic.Kind.ERROR, "处理器异常: " + e.getMessage());
- return false;
- }
- }
-
- private void createCopierClass(TypeElement enclosingClass,
- List methods,
- String helperClassName) {
- try {
- String packageName = elementUtils.getPackageOf(enclosingClass).toString();
- String fullClassName = packageName + "." + helperClassName;
-
- if (generatedFiles.contains(fullClassName)) return;
-
- TypeSpec.Builder helperClassBuilder = TypeSpec.classBuilder(helperClassName)
- .addModifiers(Modifier.PUBLIC, Modifier.FINAL)
- .addJavadoc("Incubator-Annotation生成的对象拷贝Copier类\n\n")
- .addJavadoc("支持功能:\n")
- .addJavadoc("- 同名同类型字段自动复制\n")
- .addJavadoc("- 基本类型与包装类型兼容\n")
- .addJavadoc("- 常用类型自动转换\n")
- .addJavadoc("- 字段映射配置\n\n")
- .addJavadoc("方法列表:\n");
-
- for (ExecutableElement method : methods) {
- String methodName = method.getSimpleName().toString();
- String helperMethodName = methodName;
- ObjectCopy annotation = method.getAnnotation(ObjectCopy.class);
-
- helperClassBuilder.addJavadoc("- $L()\n", helperMethodName);
-
- MethodSpec helperMethod = generateCopyMethod(method, helperMethodName, annotation);
- if (helperMethod != null) {
- helperClassBuilder.addMethod(helperMethod);
- }
- }
-
- JavaFile javaFile = JavaFile.builder(packageName, helperClassBuilder.build())
- .indent(" ")
- .build();
-
- javaFile.writeTo(filer);
- generatedFiles.add(fullClassName);
-
- } catch (Exception e) {
- messager.printMessage(Diagnostic.Kind.ERROR, "创建Copier类失败: " + e.getMessage(), enclosingClass);
- }
- }
-
- private MethodSpec generateCopyMethod(ExecutableElement method,
- String helperMethodName,
- ObjectCopy annotation) {
- List extends VariableElement> parameters = method.getParameters();
-
- if (parameters.size() != 2) {
- messager.printMessage(Diagnostic.Kind.ERROR, "@ObjectCopy 方法必须有两个参数", method);
- return null;
- }
-
- VariableElement sourceParam = parameters.get(0);
- VariableElement targetParam = parameters.get(1);
- TypeElement sourceType = (TypeElement) typeUtils.asElement(sourceParam.asType());
- TypeElement targetType = (TypeElement) typeUtils.asElement(targetParam.asType());
-
- if (sourceType == null || targetType == null) {
- messager.printMessage(Diagnostic.Kind.ERROR, "无法解析源或目标类型", method);
- return null;
- }
-
- // 添加方法Javadoc
- CodeBlock.Builder javadocBuilder = CodeBlock.builder();
- javadocBuilder.add("Incubator-Annotation生成的拷贝方法\n用于替换 {@link $T#$L($T, $T)}\n\n",
- method.getEnclosingElement(),
- method.getSimpleName().toString(),
- sourceParam.asType(),
- targetParam.asType());
-
- javadocBuilder.add("配置信息:\n");
- javadocBuilder.add("- copyNulls: $L\n", annotation.copyNulls());
- javadocBuilder.add("- 忽略字段: $L\n", Arrays.toString(annotation.ignoreFields()));
-
- if (annotation.fieldMappings().length > 0) {
- javadocBuilder.add("- 字段映射:\n");
- for (ObjectCopy.FieldMapping mapping : annotation.fieldMappings()) {
- javadocBuilder.add(" - $L → $L\n", mapping.s(), mapping.t());
- }
- }
-
- // 添加参数文档
- javadocBuilder.add("\n@param source 源对象,包含待拷贝的数据\n");
- javadocBuilder.add("@param target 目标对象,接收拷贝后的数据\n");
-
- MethodSpec.Builder builder = MethodSpec.methodBuilder(helperMethodName)
- .addModifiers(Modifier.PUBLIC, Modifier.STATIC)
- .returns(void.class)
- .addJavadoc(javadocBuilder.build())
- .addParameter(TypeName.get(sourceParam.asType()), "source")
- .addParameter(TypeName.get(targetParam.asType()), "target");
-
- addFieldCopyStatements(builder, annotation, sourceType, targetType);
- return builder.build();
- }
-
- private void addFieldCopyStatements(MethodSpec.Builder builder,
- ObjectCopy config,
- TypeElement sourceTypeElement,
- TypeElement targetTypeElement) {
-
- boolean copyNulls = config.copyNulls();
- Set ignoredFields = new HashSet<>(Arrays.asList(config.ignoreFields()));
-
- // 1. 准备字段映射
- Map fieldMappings = new HashMap<>();
- for (ObjectCopy.FieldMapping mapping : config.fieldMappings()) {
- fieldMappings.put(mapping.s(), mapping.t());
- }
-
- // 2. 收集源类字段
- Map sourceFields = new HashMap<>();
- for (Element element : sourceTypeElement.getEnclosedElements()) {
- if (element.getKind() == ElementKind.FIELD) {
- VariableElement field = (VariableElement) element;
- sourceFields.put(field.getSimpleName().toString(), field);
- }
- }
-
- // 3. 收集目标类字段
- Map targetFields = new HashMap<>();
- for (Element element : targetTypeElement.getEnclosedElements()) {
- if (element.getKind() == ElementKind.FIELD) {
- VariableElement field = (VariableElement) element;
- targetFields.put(field.getSimpleName().toString(), field);
- }
- }
-
- // 4. 使用代码块构建拷贝逻辑
- CodeBlock.Builder codeBlock = CodeBlock.builder();
- codeBlock.add("if (null != source && null != target) {\n");
-
- // 5. 处理自动映射的同名字段
- for (String sourceFieldName : sourceFields.keySet()) {
- if (ignoredFields.contains(sourceFieldName)) continue;
- if (fieldMappings.containsKey(sourceFieldName)) continue;
-
- VariableElement sourceField = sourceFields.get(sourceFieldName);
- VariableElement targetField = targetFields.get(sourceFieldName);
-
- if (targetField != null) {
- if (isTypeCompatible(sourceField.asType(), targetField.asType())) {
- String sourceGetter = getAccessorName(sourceField, true);
- String targetSetter = getAccessorName(targetField, false);
-
- if (copyNulls) {
- codeBlock.add(" target.$L(source.$L());\n", targetSetter, sourceGetter);
- } else {
- codeBlock.add(" if (null != source.$L()) {\n", sourceGetter);
- codeBlock.add(" target.$L(source.$L());\n", targetSetter, sourceGetter);
- codeBlock.add(" }\n");
- }
- }
- }
- }
-
- // 6. 处理显式配置的字段映射
- for (Map.Entry mapping : fieldMappings.entrySet()) {
- String sourceFieldName = mapping.getKey();
- String targetFieldName = mapping.getValue();
-
- if (ignoredFields.contains(sourceFieldName)) continue;
-
- VariableElement sourceField = sourceFields.get(sourceFieldName);
- VariableElement targetField = targetFields.get(targetFieldName);
-
- if (sourceField == null) {
- messager.printMessage(Diagnostic.Kind.WARNING, "源字段不存在: " + sourceFieldName, sourceTypeElement);
- continue;
- }
-
- if (targetField == null) {
- messager.printMessage(Diagnostic.Kind.WARNING, "目标字段不存在: " + targetFieldName, targetTypeElement);
- continue;
- }
-
- if (!isTypeCompatible(sourceField.asType(), targetField.asType())) {
- messager.printMessage(Diagnostic.Kind.WARNING, "字段类型不兼容: " + sourceFieldName + " -> " + targetFieldName, sourceTypeElement);
- continue;
- }
-
- String sourceGetter = getAccessorName(sourceField, true);
- String targetSetter = getAccessorName(targetField, false);
- if (copyNulls) {
- codeBlock.add(" target.$L(source.$L());\n", targetSetter, sourceGetter);
- } else {
- codeBlock.add(" if (null != source.$L()) {\n", sourceGetter);
- codeBlock.add(" target.$L(source.$L());\n", targetSetter, sourceGetter);
- codeBlock.add(" }\n");
- }
- }
- codeBlock.add("}");
- builder.addCode(codeBlock.build());
- }
-
- /**
- * 检查类型兼容性:
- * 1. 完全相同类型
- * 2. 基本类型与对应包装类型
- * 3. 兼容类型映射(如String和CharSequence)
- * 4. 子类到父类的赋值
- */
- private boolean isTypeCompatible(TypeMirror sourceType, TypeMirror targetType) {
- // 1. 类型完全相同
- if (typeUtils.isSameType(sourceType, targetType)) {
- return true;
- }
- // 2. 基本类型与包装类型的兼容
- String sourceName = sourceType.toString();
- String targetName = targetType.toString();
- // 基本类型到包装类型
- if (primitiveToWrapper.containsKey(sourceName) &&
- primitiveToWrapper.get(sourceName).equals(targetName)) {
- return true;
- }
- // 包装类型到基本类型
- if (primitiveToWrapper.containsKey(targetName) &&
- primitiveToWrapper.get(targetName).equals(sourceName)) {
- return true;
- }
- // 3. 兼容类型映射
- Set sourceCompatible = compatibleTypes.get(sourceName);
- if (sourceCompatible != null && sourceCompatible.contains(targetName)) {
- return true;
- }
- Set targetCompatible = compatibleTypes.get(targetName);
- if (targetCompatible != null && targetCompatible.contains(sourceName)) {
- return true;
- }
- // 4. 子类到父类的赋值
- if (typeUtils.isAssignable(sourceType, targetType)) {
- return true;
- }
- // 5. 数组类型兼容(长度相同的基本类型数组)
- if (sourceType.getKind() == TypeKind.ARRAY &&
- targetType.getKind() == TypeKind.ARRAY) {
- TypeMirror sourceComponent = ((ArrayType) sourceType).getComponentType();
- TypeMirror targetComponent = ((ArrayType) targetType).getComponentType();
- return isTypeCompatible(sourceComponent, targetComponent);
- }
- return false;
- }
-
- /**
- * 获取字段的访问器方法名
- * @param field 字段元素
- * @param isGetter true: getter方法, false: setter方法
- */
- private String getAccessorName(VariableElement field, boolean isGetter) {
- String fieldName = field.getSimpleName().toString();
- String prefix = isGetter ? (field.asType().getKind() == TypeKind.BOOLEAN ? "is" : "get") : "set";
- return prefix + capitalize(fieldName);
- }
-
- private String capitalize(String str) {
- return Character.toUpperCase(str.charAt(0)) + str.substring(1);
- }
-
- private void collectUsageInstructions(TypeElement enclosingClass,
- List methods,
- String helperClassName) {
- String className = enclosingClass.getQualifiedName().toString();
- List instructions = new ArrayList<>();
-
- instructions.add("自动生成的对象拷贝Copier类: " + helperClassName);
- instructions.add("支持自动映射:");
- instructions.add(" - 同名同类型字段");
- instructions.add(" - 基本类型与包装类型");
- instructions.add(" - 常用类型转换(String/CharSequence, Date/时间类等)");
- instructions.add("请在以下方法中调用对应的Helper方法:");
-
- for (ExecutableElement method : methods) {
- String methodName = method.getSimpleName().toString();
- String helperMethodName = methodName;
-
- instructions.add(String.format(" - 方法 %s::%s 替换为: %s.%s(source, target)",
- className,
- methodName,
- helperClassName,
- helperMethodName
- ));
- }
-
- usageInstructions.put(className, instructions);
- }
-
- private void printUsageInstructions() {
- if (usageInstructions.isEmpty()) return;
-
- messager.printMessage(Diagnostic.Kind.NOTE, "============================================================");
- messager.printMessage(Diagnostic.Kind.NOTE, " 增强版对象拷贝助手使用说明");
- messager.printMessage(Diagnostic.Kind.NOTE, "============================================================");
- messager.printMessage(Diagnostic.Kind.NOTE, "支持特性:");
- messager.printMessage(Diagnostic.Kind.NOTE, "1. 同名同类型字段自动复制");
- messager.printMessage(Diagnostic.Kind.NOTE, "2. 基本类型与包装类型自动兼容");
- messager.printMessage(Diagnostic.Kind.NOTE, "3. 常用类型自动转换:");
- messager.printMessage(Diagnostic.Kind.NOTE, " - String ↔ CharSequence");
- messager.printMessage(Diagnostic.Kind.NOTE, " - java.util.Date ↔ java.time.*");
- messager.printMessage(Diagnostic.Kind.NOTE, " - 子类到父类的赋值");
- messager.printMessage(Diagnostic.Kind.NOTE, "4. 显式字段映射配置");
- messager.printMessage(Diagnostic.Kind.NOTE, "5. 空值处理控制");
- messager.printMessage(Diagnostic.Kind.NOTE, "------------------------------------------------------------");
- for (Map.Entry> entry : usageInstructions.entrySet()) {
- messager.printMessage(Diagnostic.Kind.NOTE, "处理类: " + entry.getKey());
- for (String instruction : entry.getValue()) {
- messager.printMessage(Diagnostic.Kind.NOTE, instruction);
- }
- messager.printMessage(Diagnostic.Kind.NOTE, "");
- }
- messager.printMessage(Diagnostic.Kind.NOTE, "操作步骤:");
- messager.printMessage(Diagnostic.Kind.NOTE, "1. 打开被@ObjectCopy注解的方法");
- messager.printMessage(Diagnostic.Kind.NOTE, "2. 将方法体替换为对应的Helper方法调用");
- messager.printMessage(Diagnostic.Kind.NOTE, "3. 保存并重新编译");
- messager.printMessage(Diagnostic.Kind.NOTE, "============================================================");
- }
-}
+//package com.acanx.util.incubator.annotation;
+//
+//import com.acanx.annotation.ObjectCopy;
+//import javax.annotation.processing.AbstractProcessor;
+//import javax.annotation.processing.Filer;
+//import javax.annotation.processing.Messager;
+//import javax.annotation.processing.ProcessingEnvironment;
+//import javax.annotation.processing.RoundEnvironment;
+//import javax.annotation.processing.SupportedAnnotationTypes;
+//import javax.annotation.processing.SupportedOptions;
+//import javax.annotation.processing.SupportedSourceVersion;
+//import javax.lang.model.SourceVersion;
+//import javax.lang.model.element.Element;
+//import javax.lang.model.element.ElementKind;
+//import javax.lang.model.element.ExecutableElement;
+//import javax.lang.model.element.Modifier;
+//import javax.lang.model.element.TypeElement;
+//import javax.lang.model.element.VariableElement;
+//import javax.lang.model.type.ArrayType;
+//import javax.lang.model.type.TypeKind;
+//import javax.lang.model.type.TypeMirror;
+//import javax.lang.model.util.Elements;
+//import javax.tools.Diagnostic;
+//import java.util.ArrayList;
+//import java.util.Arrays;
+//import java.util.HashMap;
+//import java.util.HashSet;
+//import java.util.List;
+//import java.util.Map;
+//import java.util.Set;
+//import java.util.stream.Collectors;
+//import javax.lang.model.util.Types;
+//
+//import com.squareup.javapoet.CodeBlock;
+//import com.squareup.javapoet.JavaFile;
+//import com.squareup.javapoet.MethodSpec;
+//import com.squareup.javapoet.TypeName;
+//import com.squareup.javapoet.TypeSpec;
+//
+///**
+// * ObjectCopyProcessor
+// *
+// * @author ACANX
+// * @since 202506
+// */
+//@SupportedAnnotationTypes("com.acanx.annotation.ObjectCopy")
+//@SupportedSourceVersion(SourceVersion.RELEASE_25)
+//@SupportedOptions({"incremental"}) // 支持增量编译
+//public class ObjectCopyProcessor extends AbstractProcessor {
+//
+// public static final String SUFFIX = "Copier";
+// private Messager messager;
+// private Filer filer;
+// private Elements elementUtils;
+// private Types typeUtils;
+//
+// // 全局跟踪已生成的文件
+// private static final Set generatedFiles = new HashSet<>();
+//
+// // 收集使用说明
+// private static final Map> usageInstructions = new HashMap<>();
+//
+// // 基本类型与包装类型映射
+// private static final Map primitiveToWrapper = Map.of(
+// "int", "java.lang.Integer",
+// "long", "java.lang.Long",
+// "double", "java.lang.Double",
+// "float", "java.lang.Float",
+// "boolean", "java.lang.Boolean",
+// "char", "java.lang.Character",
+// "byte", "java.lang.Byte",
+// "short", "java.lang.Short"
+// );
+//
+// // 兼容类型映射
+// private static final Map> compatibleTypes = Map.of(
+// "java.lang.String", Set.of("java.lang.CharSequence"),
+// "java.lang.CharSequence", Set.of("java.lang.String"),
+// "java.util.Date", Set.of("java.time.Instant", "java.time.LocalDateTime", "java.time.ZonedDateTime"),
+// "java.time.Instant", Set.of("java.util.Date", "java.time.LocalDateTime", "java.time.ZonedDateTime"),
+// "java.time.LocalDateTime", Set.of("java.util.Date", "java.time.Instant", "java.time.ZonedDateTime"),
+// "java.time.ZonedDateTime", Set.of("java.util.Date", "java.time.Instant", "java.time.LocalDateTime")
+// );
+//
+// @Override
+// public synchronized void init(ProcessingEnvironment processingEnv) {
+// super.init(processingEnv);
+// messager = processingEnv.getMessager();
+// filer = processingEnv.getFiler();
+// elementUtils = processingEnv.getElementUtils();
+// typeUtils = processingEnv.getTypeUtils();
+// }
+//
+// @Override
+// public boolean process(Set extends TypeElement> annotations, RoundEnvironment roundEnv) {
+// try {
+// if (roundEnv.processingOver()) {
+// printUsageInstructions();
+// return false;
+// }
+//
+// Set extends Element> elements = roundEnv.getElementsAnnotatedWith(ObjectCopy.class);
+// List methods = elements.stream()
+// .filter(e -> e.getKind() == ElementKind.METHOD)
+// .map(e -> (ExecutableElement) e)
+// .collect(Collectors.toList());
+//
+// if (methods.isEmpty()) return false;
+//
+// Map> methodsByClass = methods.stream()
+// .collect(Collectors.groupingBy(
+// method -> (TypeElement) method.getEnclosingElement()
+// ));
+//
+// for (Map.Entry> entry : methodsByClass.entrySet()) {
+// TypeElement enclosingClass = entry.getKey();
+// List classMethods = entry.getValue();
+// String helperClassName = enclosingClass.getSimpleName().toString() + SUFFIX;
+//
+// createCopierClass(enclosingClass, classMethods, helperClassName);
+// collectUsageInstructions(enclosingClass, classMethods, helperClassName);
+// }
+//
+// return true;
+// } catch (Exception e) {
+// messager.printMessage(Diagnostic.Kind.ERROR, "处理器异常: " + e.getMessage());
+// return false;
+// }
+// }
+//
+// private void createCopierClass(TypeElement enclosingClass,
+// List methods,
+// String helperClassName) {
+// try {
+// String packageName = elementUtils.getPackageOf(enclosingClass).toString();
+// String fullClassName = packageName + "." + helperClassName;
+//
+// if (generatedFiles.contains(fullClassName)) return;
+//
+// TypeSpec.Builder helperClassBuilder = TypeSpec.classBuilder(helperClassName)
+// .addModifiers(Modifier.PUBLIC, Modifier.FINAL)
+// .addJavadoc("Incubator-Annotation生成的对象拷贝Copier类\n\n")
+// .addJavadoc("支持功能:\n")
+// .addJavadoc("- 同名同类型字段自动复制\n")
+// .addJavadoc("- 基本类型与包装类型兼容\n")
+// .addJavadoc("- 常用类型自动转换\n")
+// .addJavadoc("- 字段映射配置\n\n")
+// .addJavadoc("方法列表:\n");
+//
+// for (ExecutableElement method : methods) {
+// String methodName = method.getSimpleName().toString();
+// String helperMethodName = methodName;
+// ObjectCopy annotation = method.getAnnotation(ObjectCopy.class);
+//
+// helperClassBuilder.addJavadoc("- $L()\n", helperMethodName);
+//
+// MethodSpec helperMethod = generateCopyMethod(method, helperMethodName, annotation);
+// if (helperMethod != null) {
+// helperClassBuilder.addMethod(helperMethod);
+// }
+// }
+//
+// JavaFile javaFile = JavaFile.builder(packageName, helperClassBuilder.build())
+// .indent(" ")
+// .build();
+//
+// javaFile.writeTo(filer);
+// generatedFiles.add(fullClassName);
+//
+// } catch (Exception e) {
+// messager.printMessage(Diagnostic.Kind.ERROR, "创建Copier类失败: " + e.getMessage(), enclosingClass);
+// }
+// }
+//
+// private MethodSpec generateCopyMethod(ExecutableElement method,
+// String helperMethodName,
+// ObjectCopy annotation) {
+// List extends VariableElement> parameters = method.getParameters();
+//
+// if (parameters.size() != 2) {
+// messager.printMessage(Diagnostic.Kind.ERROR, "@ObjectCopy 方法必须有两个参数", method);
+// return null;
+// }
+//
+// VariableElement sourceParam = parameters.get(0);
+// VariableElement targetParam = parameters.get(1);
+// TypeElement sourceType = (TypeElement) typeUtils.asElement(sourceParam.asType());
+// TypeElement targetType = (TypeElement) typeUtils.asElement(targetParam.asType());
+//
+// if (sourceType == null || targetType == null) {
+// messager.printMessage(Diagnostic.Kind.ERROR, "无法解析源或目标类型", method);
+// return null;
+// }
+//
+// // 添加方法Javadoc
+// CodeBlock.Builder javadocBuilder = CodeBlock.builder();
+// javadocBuilder.add("Incubator-Annotation生成的拷贝方法\n用于替换 {@link $T#$L($T, $T)}\n\n",
+// method.getEnclosingElement(),
+// method.getSimpleName().toString(),
+// sourceParam.asType(),
+// targetParam.asType());
+//
+// javadocBuilder.add("配置信息:\n");
+// javadocBuilder.add("- copyNulls: $L\n", annotation.copyNulls());
+// javadocBuilder.add("- 忽略字段: $L\n", Arrays.toString(annotation.ignoreFields()));
+//
+// if (annotation.fieldMappings().length > 0) {
+// javadocBuilder.add("- 字段映射:\n");
+// for (ObjectCopy.FieldMapping mapping : annotation.fieldMappings()) {
+// javadocBuilder.add(" - $L → $L\n", mapping.s(), mapping.t());
+// }
+// }
+//
+// // 添加参数文档
+// javadocBuilder.add("\n@param source 源对象,包含待拷贝的数据\n");
+// javadocBuilder.add("@param target 目标对象,接收拷贝后的数据\n");
+//
+// MethodSpec.Builder builder = MethodSpec.methodBuilder(helperMethodName)
+// .addModifiers(Modifier.PUBLIC, Modifier.STATIC)
+// .returns(void.class)
+// .addJavadoc(javadocBuilder.build())
+// .addParameter(TypeName.get(sourceParam.asType()), "source")
+// .addParameter(TypeName.get(targetParam.asType()), "target");
+//
+// addFieldCopyStatements(builder, annotation, sourceType, targetType);
+// return builder.build();
+// }
+//
+// private void addFieldCopyStatements(MethodSpec.Builder builder,
+// ObjectCopy config,
+// TypeElement sourceTypeElement,
+// TypeElement targetTypeElement) {
+//
+// boolean copyNulls = config.copyNulls();
+// Set ignoredFields = new HashSet<>(Arrays.asList(config.ignoreFields()));
+//
+// // 1. 准备字段映射
+// Map fieldMappings = new HashMap<>();
+// for (ObjectCopy.FieldMapping mapping : config.fieldMappings()) {
+// fieldMappings.put(mapping.s(), mapping.t());
+// }
+//
+// // 2. 收集源类字段
+// Map sourceFields = new HashMap<>();
+// for (Element element : sourceTypeElement.getEnclosedElements()) {
+// if (element.getKind() == ElementKind.FIELD) {
+// VariableElement field = (VariableElement) element;
+// sourceFields.put(field.getSimpleName().toString(), field);
+// }
+// }
+//
+// // 3. 收集目标类字段
+// Map targetFields = new HashMap<>();
+// for (Element element : targetTypeElement.getEnclosedElements()) {
+// if (element.getKind() == ElementKind.FIELD) {
+// VariableElement field = (VariableElement) element;
+// targetFields.put(field.getSimpleName().toString(), field);
+// }
+// }
+//
+// // 4. 使用代码块构建拷贝逻辑
+// CodeBlock.Builder codeBlock = CodeBlock.builder();
+// codeBlock.add("if (null != source && null != target) {\n");
+//
+// // 5. 处理自动映射的同名字段
+// for (String sourceFieldName : sourceFields.keySet()) {
+// if (ignoredFields.contains(sourceFieldName)) continue;
+// if (fieldMappings.containsKey(sourceFieldName)) continue;
+//
+// VariableElement sourceField = sourceFields.get(sourceFieldName);
+// VariableElement targetField = targetFields.get(sourceFieldName);
+//
+// if (targetField != null) {
+// if (isTypeCompatible(sourceField.asType(), targetField.asType())) {
+// String sourceGetter = getAccessorName(sourceField, true);
+// String targetSetter = getAccessorName(targetField, false);
+//
+// if (copyNulls) {
+// codeBlock.add(" target.$L(source.$L());\n", targetSetter, sourceGetter);
+// } else {
+// codeBlock.add(" if (null != source.$L()) {\n", sourceGetter);
+// codeBlock.add(" target.$L(source.$L());\n", targetSetter, sourceGetter);
+// codeBlock.add(" }\n");
+// }
+// }
+// }
+// }
+//
+// // 6. 处理显式配置的字段映射
+// for (Map.Entry mapping : fieldMappings.entrySet()) {
+// String sourceFieldName = mapping.getKey();
+// String targetFieldName = mapping.getValue();
+//
+// if (ignoredFields.contains(sourceFieldName)) continue;
+//
+// VariableElement sourceField = sourceFields.get(sourceFieldName);
+// VariableElement targetField = targetFields.get(targetFieldName);
+//
+// if (sourceField == null) {
+// messager.printMessage(Diagnostic.Kind.WARNING, "源字段不存在: " + sourceFieldName, sourceTypeElement);
+// continue;
+// }
+//
+// if (targetField == null) {
+// messager.printMessage(Diagnostic.Kind.WARNING, "目标字段不存在: " + targetFieldName, targetTypeElement);
+// continue;
+// }
+//
+// if (!isTypeCompatible(sourceField.asType(), targetField.asType())) {
+// messager.printMessage(Diagnostic.Kind.WARNING, "字段类型不兼容: " + sourceFieldName + " -> " + targetFieldName, sourceTypeElement);
+// continue;
+// }
+//
+// String sourceGetter = getAccessorName(sourceField, true);
+// String targetSetter = getAccessorName(targetField, false);
+// if (copyNulls) {
+// codeBlock.add(" target.$L(source.$L());\n", targetSetter, sourceGetter);
+// } else {
+// codeBlock.add(" if (null != source.$L()) {\n", sourceGetter);
+// codeBlock.add(" target.$L(source.$L());\n", targetSetter, sourceGetter);
+// codeBlock.add(" }\n");
+// }
+// }
+// codeBlock.add("}");
+// builder.addCode(codeBlock.build());
+// }
+//
+// /**
+// * 检查类型兼容性:
+// * 1. 完全相同类型
+// * 2. 基本类型与对应包装类型
+// * 3. 兼容类型映射(如String和CharSequence)
+// * 4. 子类到父类的赋值
+// */
+// private boolean isTypeCompatible(TypeMirror sourceType, TypeMirror targetType) {
+// // 1. 类型完全相同
+// if (typeUtils.isSameType(sourceType, targetType)) {
+// return true;
+// }
+// // 2. 基本类型与包装类型的兼容
+// String sourceName = sourceType.toString();
+// String targetName = targetType.toString();
+// // 基本类型到包装类型
+// if (primitiveToWrapper.containsKey(sourceName) &&
+// primitiveToWrapper.get(sourceName).equals(targetName)) {
+// return true;
+// }
+// // 包装类型到基本类型
+// if (primitiveToWrapper.containsKey(targetName) &&
+// primitiveToWrapper.get(targetName).equals(sourceName)) {
+// return true;
+// }
+// // 3. 兼容类型映射
+// Set sourceCompatible = compatibleTypes.get(sourceName);
+// if (sourceCompatible != null && sourceCompatible.contains(targetName)) {
+// return true;
+// }
+// Set targetCompatible = compatibleTypes.get(targetName);
+// if (targetCompatible != null && targetCompatible.contains(sourceName)) {
+// return true;
+// }
+// // 4. 子类到父类的赋值
+// if (typeUtils.isAssignable(sourceType, targetType)) {
+// return true;
+// }
+// // 5. 数组类型兼容(长度相同的基本类型数组)
+// if (sourceType.getKind() == TypeKind.ARRAY &&
+// targetType.getKind() == TypeKind.ARRAY) {
+// TypeMirror sourceComponent = ((ArrayType) sourceType).getComponentType();
+// TypeMirror targetComponent = ((ArrayType) targetType).getComponentType();
+// return isTypeCompatible(sourceComponent, targetComponent);
+// }
+// return false;
+// }
+//
+// /**
+// * 获取字段的访问器方法名
+// * @param field 字段元素
+// * @param isGetter true: getter方法, false: setter方法
+// */
+// private String getAccessorName(VariableElement field, boolean isGetter) {
+// String fieldName = field.getSimpleName().toString();
+// String prefix = isGetter ? (field.asType().getKind() == TypeKind.BOOLEAN ? "is" : "get") : "set";
+// return prefix + capitalize(fieldName);
+// }
+//
+// private String capitalize(String str) {
+// return Character.toUpperCase(str.charAt(0)) + str.substring(1);
+// }
+//
+// private void collectUsageInstructions(TypeElement enclosingClass,
+// List methods,
+// String helperClassName) {
+// String className = enclosingClass.getQualifiedName().toString();
+// List instructions = new ArrayList<>();
+//
+// instructions.add("自动生成的对象拷贝Copier类: " + helperClassName);
+// instructions.add("支持自动映射:");
+// instructions.add(" - 同名同类型字段");
+// instructions.add(" - 基本类型与包装类型");
+// instructions.add(" - 常用类型转换(String/CharSequence, Date/时间类等)");
+// instructions.add("请在以下方法中调用对应的Helper方法:");
+//
+// for (ExecutableElement method : methods) {
+// String methodName = method.getSimpleName().toString();
+// String helperMethodName = methodName;
+//
+// instructions.add(String.format(" - 方法 %s::%s 替换为: %s.%s(source, target)",
+// className,
+// methodName,
+// helperClassName,
+// helperMethodName
+// ));
+// }
+//
+// usageInstructions.put(className, instructions);
+// }
+//
+// private void printUsageInstructions() {
+// if (usageInstructions.isEmpty()) return;
+//
+// messager.printMessage(Diagnostic.Kind.NOTE, "============================================================");
+// messager.printMessage(Diagnostic.Kind.NOTE, " 增强版对象拷贝助手使用说明");
+// messager.printMessage(Diagnostic.Kind.NOTE, "============================================================");
+// messager.printMessage(Diagnostic.Kind.NOTE, "支持特性:");
+// messager.printMessage(Diagnostic.Kind.NOTE, "1. 同名同类型字段自动复制");
+// messager.printMessage(Diagnostic.Kind.NOTE, "2. 基本类型与包装类型自动兼容");
+// messager.printMessage(Diagnostic.Kind.NOTE, "3. 常用类型自动转换:");
+// messager.printMessage(Diagnostic.Kind.NOTE, " - String ↔ CharSequence");
+// messager.printMessage(Diagnostic.Kind.NOTE, " - java.util.Date ↔ java.time.*");
+// messager.printMessage(Diagnostic.Kind.NOTE, " - 子类到父类的赋值");
+// messager.printMessage(Diagnostic.Kind.NOTE, "4. 显式字段映射配置");
+// messager.printMessage(Diagnostic.Kind.NOTE, "5. 空值处理控制");
+// messager.printMessage(Diagnostic.Kind.NOTE, "------------------------------------------------------------");
+// for (Map.Entry> entry : usageInstructions.entrySet()) {
+// messager.printMessage(Diagnostic.Kind.NOTE, "处理类: " + entry.getKey());
+// for (String instruction : entry.getValue()) {
+// messager.printMessage(Diagnostic.Kind.NOTE, instruction);
+// }
+// messager.printMessage(Diagnostic.Kind.NOTE, "");
+// }
+// messager.printMessage(Diagnostic.Kind.NOTE, "操作步骤:");
+// messager.printMessage(Diagnostic.Kind.NOTE, "1. 打开被@ObjectCopy注解的方法");
+// messager.printMessage(Diagnostic.Kind.NOTE, "2. 将方法体替换为对应的Helper方法调用");
+// messager.printMessage(Diagnostic.Kind.NOTE, "3. 保存并重新编译");
+// messager.printMessage(Diagnostic.Kind.NOTE, "============================================================");
+// }
+//}
diff --git a/autil-incubator/incubator-annotation/src/main/java/com/acanx/util/incubator/annotation/ann/CopyTest.java b/autil-incubator/incubator-annotation/src/main/java/com/acanx/util/incubator/annotation/ann/CopyTest.java
new file mode 100644
index 0000000..58a3bf9
--- /dev/null
+++ b/autil-incubator/incubator-annotation/src/main/java/com/acanx/util/incubator/annotation/ann/CopyTest.java
@@ -0,0 +1,42 @@
+package com.acanx.util.incubator.annotation.ann;
+
+import com.acanx.meta.model.test.annotation.model.MessageFlex;
+import com.acanx.meta.model.test.annotation.model.MessageStable;
+import com.acanx.util.incubator.annotation.ObjectCopier;
+
+/**
+ * CopyTest
+ *
+ * @author ACANX
+ * @since 20251110
+ */
+public class CopyTest {
+
+
+
+ @ObjectCopier
+ private void convert(MessageFlex flex, MessageStable stable) {};
+
+
+ @ObjectCopier(ignoreNull = true, exclude = {"internalId"})
+ private void convert1(MessageFlex flex, MessageStable stable) {
+ // 编译后这个方法会被增强为:
+ // if (flex == null) throw new IllegalArgumentException("Source object cannot be null");
+ // if (stable == null) throw new IllegalArgumentException("Target object cannot be null");
+ // stable.content = flex.content != null ? flex.content : stable.content;
+ // stable.priority = flex.priority != null ? flex.priority : stable.priority;
+ // stable.urgent = flex.urgent != null ? flex.urgent : stable.urgent;
+ // stable.tags = flex.tags != null ? flex.tags : stable.tags;
+ }
+
+ @ObjectCopier(strategy = ObjectCopier.CopyStrategy.DEEP)
+ private void deepConvert(MessageFlex flex, MessageStable stable) {
+ // 深拷贝版本的转换
+ }
+
+ public void processConversion(MessageFlex flex, MessageStable stable) {
+ convert(flex, stable);
+ // 其他业务逻辑
+ }
+
+}
diff --git a/autil-incubator/incubator-annotation/src/main/java/com/acanx/util/incubator/annotation/ann/ObjectCopierProcessor.java b/autil-incubator/incubator-annotation/src/main/java/com/acanx/util/incubator/annotation/ann/ObjectCopierProcessor.java
new file mode 100644
index 0000000..8cbb61b
--- /dev/null
+++ b/autil-incubator/incubator-annotation/src/main/java/com/acanx/util/incubator/annotation/ann/ObjectCopierProcessor.java
@@ -0,0 +1,355 @@
+package com.acanx.util.incubator.annotation.ann;
+
+/**
+ * ObjectCopierProcessor
+ *
+ * @author ACANX
+ * @since 20251110
+ */
+
+
+import com.acanx.util.incubator.annotation.ObjectCopier;
+import com.sun.source.tree.MethodTree;
+import com.sun.source.util.JavacTask;
+import com.sun.source.util.Trees;
+import com.sun.tools.javac.tree.JCTree;
+import com.sun.tools.javac.tree.TreeMaker;
+import com.sun.tools.javac.util.Context;
+import com.sun.tools.javac.util.List;
+import com.sun.tools.javac.util.ListBuffer;
+import com.sun.tools.javac.util.Names;
+
+
+import javax.annotation.processing.AbstractProcessor;
+import javax.annotation.processing.ProcessingEnvironment;
+import javax.annotation.processing.RoundEnvironment;
+import javax.annotation.processing.SupportedAnnotationTypes;
+import javax.annotation.processing.SupportedSourceVersion;
+import javax.lang.model.SourceVersion;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ElementKind;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.TypeMirror;
+import javax.tools.Diagnostic;
+import java.lang.reflect.Method;
+import java.util.Set;
+
+/**
+ * 对象拷贝注解处理器
+ * 通过修改AST在编译期增强被注解的方法
+ */
+@SupportedAnnotationTypes("com.acanx.util.incubator.annotation.ObjectCopier")
+@SupportedSourceVersion(SourceVersion.RELEASE_25)
+public class ObjectCopierProcessor extends AbstractProcessor {
+
+ private Trees trees;
+ private TreeMaker treeMaker;
+ private Names names;
+
+ @Override
+ public synchronized void init(ProcessingEnvironment processingEnv) {
+ super.init(processingEnv);
+ this.trees = Trees.instance(processingEnv);
+ try {
+ // 使用反射获取Context
+ Context context = getContext(processingEnv);
+ this.treeMaker = TreeMaker.instance(context);
+ this.names = Names.instance(context);
+ } catch (Exception e) {
+ processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR,
+ "Failed to initialize compiler internals: " + e.getMessage()
+ );
+ }
+ }
+
+
+
+ @Override
+ public boolean process(Set extends TypeElement> annotations, RoundEnvironment roundEnv) {
+ if (roundEnv.processingOver()) {
+ return false;
+ }
+ for (Element element : roundEnv.getElementsAnnotatedWith(ObjectCopier.class)) {
+ if (element.getKind() != ElementKind.METHOD) {
+ processingEnv.getMessager().printMessage(
+ Diagnostic.Kind.ERROR,
+ "@ObjectCopier can only be applied to methods",
+ element
+ );
+ continue;
+ }
+ ExecutableElement methodElement = (ExecutableElement) element;
+ // 验证方法参数
+ if (!validateMethod(methodElement)) {
+ continue;
+ }
+ // 获取方法对应的AST
+ MethodTree methodTree = trees.getTree(methodElement);
+ if (methodTree instanceof JCTree.JCMethodDecl) {
+ enhanceMethod((JCTree.JCMethodDecl) methodTree, methodElement);
+ }
+ }
+ return true;
+ }
+
+ /**
+ * 通过反射获取Compiler Context
+ */
+ private Context getContext(ProcessingEnvironment processingEnv) throws Exception {
+ // 方法1: 通过JavacTask获取Context
+ if (processingEnv instanceof JavacTask) {
+ JavacTask javacTask = (JavacTask) processingEnv;
+ // 尝试通过getContext方法获取
+ try {
+ Method getContextMethod = javacTask.getClass().getMethod("getContext");
+ return (Context) getContextMethod.invoke(javacTask);
+ } catch (NoSuchMethodException e) {
+ // 如果getContext方法不存在,尝试其他方式
+ processingEnv.getMessager().printMessage(
+ Diagnostic.Kind.NOTE,
+ "getContext() method not found, trying alternative approach"
+ );
+ }
+ }
+ // 方法2: 通过Trees获取Context
+ try {
+ Method getContextMethod = trees.getClass().getMethod("getContext");
+ return (Context) getContextMethod.invoke(trees);
+ } catch (NoSuchMethodException e) {
+ processingEnv.getMessager().printMessage(
+ Diagnostic.Kind.NOTE,
+ "Trees.getContext() not available, trying field access"
+ );
+ }
+ // 方法3: 通过字段访问(最后的手段)
+ return getContextViaFieldAccess(processingEnv);
+ }
+
+ /**
+ * 在类层次结构中查找字段
+ */
+ private java.lang.reflect.Field findField(Class> clazz, String fieldName) {
+ while (clazz != null) {
+ try {
+ java.lang.reflect.Field field = clazz.getDeclaredField(fieldName);
+ return field;
+ } catch (NoSuchFieldException e) {
+ clazz = clazz.getSuperclass();
+ }
+ }
+ return null;
+ }
+
+ /**
+ * 通过字段反射获取Context
+ */
+ private Context getContextViaFieldAccess(ProcessingEnvironment processingEnv) throws Exception {
+ // 获取JavacTask实例
+ if (processingEnv instanceof JavacTask) {
+ JavacTask javacTask = (JavacTask) processingEnv;
+ // 尝试访问context字段
+ try {
+ java.lang.reflect.Field contextField = findField(javacTask.getClass(), "context");
+ if (contextField != null) {
+ contextField.setAccessible(true);
+ return (Context) contextField.get(javacTask);
+ }
+ } catch (Exception e) {
+ processingEnv.getMessager().printMessage(
+ Diagnostic.Kind.WARNING,
+ "Field access failed: " + e.getMessage()
+ );
+ }
+ }
+ throw new IllegalStateException("Unable to access compiler Context");
+ }
+
+
+ /**
+ * 验证方法是否符合要求
+ */
+ private boolean validateMethod(ExecutableElement method) {
+ // 验证参数数量
+ if (method.getParameters().size() != 2) {
+ processingEnv.getMessager().printMessage(
+ Diagnostic.Kind.ERROR,
+ "@ObjectCopier method must have exactly 2 parameters",
+ method
+ );
+ return false;
+ }
+ // 验证方法可见性
+ if (!method.getModifiers().contains(javax.lang.model.element.Modifier.PRIVATE)) {
+ processingEnv.getMessager().printMessage(
+ Diagnostic.Kind.WARNING,
+ "@ObjectCopier is recommended to be used on private methods for better encapsulation",
+ method
+ );
+ }
+ // 验证返回类型为void
+ TypeMirror returnType = method.getReturnType();
+ if (!returnType.toString().equals("void")) {
+ processingEnv.getMessager().printMessage(
+ Diagnostic.Kind.ERROR,
+ "@ObjectCopier method must have void return type",
+ method
+ );
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * 增强方法,添加对象拷贝逻辑
+ */
+ private void enhanceMethod(JCTree.JCMethodDecl methodTree, ExecutableElement methodElement) {
+ ObjectCopier annotation = methodElement.getAnnotation(ObjectCopier.class);
+ // 获取源参数和目标参数
+ JCTree.JCVariableDecl sourceParam = methodTree.getParameters().get(0);
+ JCTree.JCVariableDecl targetParam = methodTree.getParameters().get(1);
+ // 创建拷贝语句
+ List copyStatements = createCopyStatements(
+ sourceParam,
+ targetParam,
+ annotation,
+ methodElement
+ );
+ // 替换方法体
+ JCTree.JCBlock newBody = treeMaker.Block(0, copyStatements);
+ methodTree.body = newBody;
+ processingEnv.getMessager().printMessage(
+ Diagnostic.Kind.NOTE,
+ "Enhanced @ObjectCopier method: " + methodElement.getSimpleName(),
+ methodElement
+ );
+ }
+
+ /**
+ * 创建拷贝语句
+ */
+ private List createCopyStatements(
+ JCTree.JCVariableDecl sourceParam,
+ JCTree.JCVariableDecl targetParam,
+ ObjectCopier annotation,
+ ExecutableElement methodElement) {
+ ListBuffer statements = new ListBuffer<>();
+ // 添加空值检查
+ statements.append(createNullCheckStatement(sourceParam, "Source object cannot be null"));
+ statements.append(createNullCheckStatement(targetParam, "Target object cannot be null"));
+ // 获取源对象类型的所有字段
+ TypeMirror sourceType = methodElement.getParameters().get(0).asType();
+ TypeElement sourceTypeElement = (TypeElement) ((DeclaredType) sourceType).asElement();
+ // 为每个字段生成赋值语句
+ for (Element field : sourceTypeElement.getEnclosedElements()) {
+ if (field.getKind() == ElementKind.FIELD &&
+ !field.getModifiers().contains(javax.lang.model.element.Modifier.STATIC)) {
+ JCTree.JCStatement assignment = createFieldAssignment(
+ sourceParam,
+ targetParam,
+ field,
+ annotation
+ );
+ if (assignment != null) {
+ statements.append(assignment);
+ }
+ }
+ }
+ return statements.toList();
+ }
+
+ /**
+ * 创建空值检查语句
+ */
+ private JCTree.JCStatement createNullCheckStatement(JCTree.JCVariableDecl param, String message) {
+ // 创建条件表达式: param == null
+ JCTree.JCExpression nullCheck = treeMaker.Binary(
+ JCTree.Tag.EQ,
+ treeMaker.Ident(param.name),
+ treeMaker.Literal(null)
+ );
+ // 创建异常抛出语句
+ JCTree.JCExpression exceptionType = treeMaker.Ident(names.fromString("IllegalArgumentException"));
+ JCTree.JCNewClass newException = treeMaker.NewClass(
+ null,
+ List.nil(),
+ exceptionType,
+ List.of(treeMaker.Literal(message)),
+ null
+ );
+ JCTree.JCThrow throwStatement = treeMaker.Throw(newException);
+ // 创建if语句
+ return treeMaker.If(nullCheck, throwStatement, null);
+ }
+
+ /**
+ * 创建字段赋值语句
+ */
+ private JCTree.JCStatement createFieldAssignment(
+ JCTree.JCVariableDecl sourceParam,
+ JCTree.JCVariableDecl targetParam,
+ Element field,
+ ObjectCopier annotation) {
+ String fieldName = field.getSimpleName().toString();
+ // 检查字段是否在排除列表中
+ if (isFieldExcluded(fieldName, annotation)) {
+ return null;
+ }
+ // 检查字段是否在包含列表中(如果设置了include)
+ if (annotation.include().length > 0 && !isFieldIncluded(fieldName, annotation)) {
+ return null;
+ }
+ // 创建字段访问表达式: target.field
+ JCTree.JCFieldAccess targetFieldAccess = treeMaker.Select(
+ treeMaker.Ident(targetParam.name),
+ names.fromString(fieldName)
+ );
+ // 创建字段访问表达式: source.field
+ JCTree.JCFieldAccess sourceFieldAccess = treeMaker.Select(
+ treeMaker.Ident(sourceParam.name),
+ names.fromString(fieldName)
+ );
+ JCTree.JCExpression assignmentExpression;
+ if (annotation.ignoreNull()) {
+ // 如果忽略空值,创建条件赋值: source.field != null ? source.field : target.field
+ JCTree.JCExpression nullCheck = treeMaker.Binary(
+ JCTree.Tag.NE,
+ sourceFieldAccess,
+ treeMaker.Literal(null)
+ );
+ assignmentExpression = treeMaker.Conditional(
+ nullCheck,
+ sourceFieldAccess,
+ treeMaker.Ident(targetParam.name) // 保持原值
+ );
+ } else {
+ // 直接赋值
+ assignmentExpression = sourceFieldAccess;
+ }
+ // 创建赋值语句: target.field = assignmentExpression
+ return treeMaker.Exec(
+ treeMaker.Assign(targetFieldAccess, assignmentExpression)
+ );
+ }
+
+ private boolean isFieldExcluded(String fieldName, ObjectCopier annotation) {
+ for (String excluded : annotation.exclude()) {
+ if (excluded.equals(fieldName)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private boolean isFieldIncluded(String fieldName, ObjectCopier annotation) {
+ for (String included : annotation.include()) {
+ if (included.equals(fieldName)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+
+}
\ No newline at end of file
diff --git a/autil-incubator/incubator-annotation/src/main/java/module-info.java b/autil-incubator/incubator-annotation/src/main/java/module-info.java
new file mode 100644
index 0000000..e2bec23
--- /dev/null
+++ b/autil-incubator/incubator-annotation/src/main/java/module-info.java
@@ -0,0 +1,7 @@
+module com.acanx.util.incubator.annotation {
+ requires java.compiler; // 官方注解处理API
+ requires jdk.compiler;
+ requires com.squareup.javapoet;
+ requires model.test; // 内部模块
+
+}
\ No newline at end of file
diff --git a/autil-incubator/incubator-annotation/src/main/resources/META-INF/services/javax.annotation.processing.Processor b/autil-incubator/incubator-annotation/src/main/resources/META-INF/services/javax.annotation.processing.Processor
index affd371..22f25e6 100644
--- a/autil-incubator/incubator-annotation/src/main/resources/META-INF/services/javax.annotation.processing.Processor
+++ b/autil-incubator/incubator-annotation/src/main/resources/META-INF/services/javax.annotation.processing.Processor
@@ -1 +1,2 @@
-com.acanx.util.incubator.annotation.ObjectCopyProcessor
+#com.acanx.util.incubator.annotation.ObjectCopyProcessor
+com.acanx.util.incubator.annotation.ann.ObjectCopierProcessor
diff --git a/autil-incubator/incubator-annotation/src/test/java/com/acanx/meta/model/AppTest.java b/autil-incubator/incubator-annotation/src/test/java/com/acanx/meta/model/AppTest.java
index 3b4b535..5a07649 100644
--- a/autil-incubator/incubator-annotation/src/test/java/com/acanx/meta/model/AppTest.java
+++ b/autil-incubator/incubator-annotation/src/test/java/com/acanx/meta/model/AppTest.java
@@ -1,20 +1,17 @@
package com.acanx.meta.model;
-import org.junit.jupiter.api.Assertions;
-import org.junit.jupiter.api.Test;
-
/**
* Unit test for simple App.
*/
public class AppTest {
- /**
- * Rigorous Test :-)
- */
- @Test
- public void shouldAnswerWithTrue() {
- Assertions.assertTrue(true);
- }
+// /**
+// * Rigorous Test :-)
+// */
+// @Test
+// public void shouldAnswerWithTrue() {
+// Assertions.assertTrue(true);
+// }
}
\ No newline at end of file
diff --git a/autil-incubator/pom.xml b/autil-incubator/pom.xml
index 092ec27..a5e35d2 100644
--- a/autil-incubator/pom.xml
+++ b/autil-incubator/pom.xml
@@ -16,14 +16,14 @@
https://github.com/ACANX/AUtil
- 11
+ 25
UTF-8
- 11
+
3.14.1
incubator-core
-
+ incubator-annotation
diff --git a/autil-test/pom.xml b/autil-test/pom.xml
index f5e6816..acc9ff0 100644
--- a/autil-test/pom.xml
+++ b/autil-test/pom.xml
@@ -36,6 +36,20 @@
${revision}
test
+
+
+ com.acanx.util
+ incubator-annotation
+ ${revision}
+ test
+
+
+ com.acanx.meta.model
+ model-test
+ 0.5.7-SNAPSHOT
+ test
+
+
@@ -47,7 +61,29 @@
${java.version}
${java.version}
+
+ --add-exports
+ jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED
+ --add-exports
+ jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED
+ --add-exports
+ jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED
+ --add-exports
+ jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED
+ --add-exports
+ jdk.compiler/com.sun.tools.javac.model=ALL-UNNAMED
+ --add-exports
+ jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED
+
+
+
+ com.acanx.util
+ incubator-annotation
+ ${revision}
+
+
+
diff --git a/autil-test/src/test/java/com/acanx/util/annotation/CopyTest.java b/autil-test/src/test/java/com/acanx/util/annotation/CopyTest.java
new file mode 100644
index 0000000..11dd2a4
--- /dev/null
+++ b/autil-test/src/test/java/com/acanx/util/annotation/CopyTest.java
@@ -0,0 +1,42 @@
+package com.acanx.util.annotation;
+
+import com.acanx.meta.model.test.annotation.model.MessageFlex;
+import com.acanx.meta.model.test.annotation.model.MessageStable;
+import com.acanx.util.incubator.annotation.ObjectCopier;
+
+/**
+ * CopyTest
+ *
+ * @author ACANX
+ * @since 20251110
+ */
+public class CopyTest {
+
+
+
+ @ObjectCopier
+ private void convert(MessageFlex flex, MessageStable stable) {};
+
+
+ @ObjectCopier(ignoreNull = true, exclude = {"internalId"})
+ private void convert1(MessageFlex flex, MessageStable stable) {
+ // 编译后这个方法会被增强为:
+ // if (flex == null) throw new IllegalArgumentException("Source object cannot be null");
+ // if (stable == null) throw new IllegalArgumentException("Target object cannot be null");
+ // stable.content = flex.content != null ? flex.content : stable.content;
+ // stable.priority = flex.priority != null ? flex.priority : stable.priority;
+ // stable.urgent = flex.urgent != null ? flex.urgent : stable.urgent;
+ // stable.tags = flex.tags != null ? flex.tags : stable.tags;
+ }
+
+ @ObjectCopier(strategy = ObjectCopier.CopyStrategy.DEEP)
+ private void deepConvert(MessageFlex flex, MessageStable stable) {
+ // 深拷贝版本的转换
+ }
+
+ public void processConversion(MessageFlex flex, MessageStable stable) {
+ convert(flex, stable);
+ // 其他业务逻辑
+ }
+
+}
diff --git a/pom.xml b/pom.xml
index 210e416..c6a4cc6 100644
--- a/pom.xml
+++ b/pom.xml
@@ -36,9 +36,9 @@
0.4.3-SNAPSHOT
UTF-8
UTF-8
- 21
- 21
- 21
+ 25
+ 25
+ 25
3.14.1
3.1.2
3.5.0
From 26d13ebc8e8599e90a9860ba047421f70c031cef Mon Sep 17 00:00:00 2001
From: ACANX <245818784@qq.com>
Date: Mon, 10 Nov 2025 15:54:37 +0800
Subject: [PATCH 2/3] =?UTF-8?q?=E6=94=AF=E6=8C=81=E7=94=9F=E6=88=90?=
=?UTF-8?q?=E5=8F=AA=E6=9C=89=E4=B8=80=E4=B8=AA=E7=B1=BB=E4=B8=AD=E5=8F=AA?=
=?UTF-8?q?=E6=9C=89=E4=B8=80=E4=B8=AA@ObjectCopier=E6=B3=A8=E8=A7=A3?=
=?UTF-8?q?=E7=9A=84=E6=8B=B7=E8=B4=9D=E6=96=B9=E6=B3=95?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
autil-incubator/incubator-annotation/pom.xml | 9 +
.../incubator/annotation/ObjectCopier.java | 7 +
.../incubator/annotation/ann/CopyTest.java | 42 --
.../annotation/ann/ObjectCopierProcessor.java | 575 +++++++++---------
.../src/main/java/module-info.java | 18 +-
.../java/com/acanx/meta/model/CopyTest.java | 42 ++
autil-test/pom.xml | 11 +-
.../main/java/com/acanx/util/enums/Copy2.java | 27 +
.../com/acanx/util/annotation/CopyTest.java | 30 +-
9 files changed, 424 insertions(+), 337 deletions(-)
delete mode 100644 autil-incubator/incubator-annotation/src/main/java/com/acanx/util/incubator/annotation/ann/CopyTest.java
create mode 100644 autil-incubator/incubator-annotation/src/test/java/com/acanx/meta/model/CopyTest.java
create mode 100644 autil-test/src/main/java/com/acanx/util/enums/Copy2.java
diff --git a/autil-incubator/incubator-annotation/pom.xml b/autil-incubator/incubator-annotation/pom.xml
index 1ef87bd..1eef2e9 100644
--- a/autil-incubator/incubator-annotation/pom.xml
+++ b/autil-incubator/incubator-annotation/pom.xml
@@ -103,6 +103,15 @@
jdk.compiler/com.sun.tools.javac.model=ALL-UNNAMED
--add-exports
jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED
+
+
+ --add-opens=jdk.compiler/com.sun.tools.javac.util=com.acanx.util.incubator.annotation
+
+ -J--add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED
+ -J--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED
+ -J--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED
+ -J--add-exports=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED
+ -J--add-opens=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED
diff --git a/autil-incubator/incubator-annotation/src/main/java/com/acanx/util/incubator/annotation/ObjectCopier.java b/autil-incubator/incubator-annotation/src/main/java/com/acanx/util/incubator/annotation/ObjectCopier.java
index 2a2439b..114dfba 100644
--- a/autil-incubator/incubator-annotation/src/main/java/com/acanx/util/incubator/annotation/ObjectCopier.java
+++ b/autil-incubator/incubator-annotation/src/main/java/com/acanx/util/incubator/annotation/ObjectCopier.java
@@ -33,6 +33,13 @@
*/
String[] include() default {};
+ /**
+ * 是否使用getter/setter方法进行拷贝
+ *
+ * @return
+ */
+ boolean useAccessors () default true;
+
/**
* 拷贝策略枚举
*/
diff --git a/autil-incubator/incubator-annotation/src/main/java/com/acanx/util/incubator/annotation/ann/CopyTest.java b/autil-incubator/incubator-annotation/src/main/java/com/acanx/util/incubator/annotation/ann/CopyTest.java
deleted file mode 100644
index 58a3bf9..0000000
--- a/autil-incubator/incubator-annotation/src/main/java/com/acanx/util/incubator/annotation/ann/CopyTest.java
+++ /dev/null
@@ -1,42 +0,0 @@
-package com.acanx.util.incubator.annotation.ann;
-
-import com.acanx.meta.model.test.annotation.model.MessageFlex;
-import com.acanx.meta.model.test.annotation.model.MessageStable;
-import com.acanx.util.incubator.annotation.ObjectCopier;
-
-/**
- * CopyTest
- *
- * @author ACANX
- * @since 20251110
- */
-public class CopyTest {
-
-
-
- @ObjectCopier
- private void convert(MessageFlex flex, MessageStable stable) {};
-
-
- @ObjectCopier(ignoreNull = true, exclude = {"internalId"})
- private void convert1(MessageFlex flex, MessageStable stable) {
- // 编译后这个方法会被增强为:
- // if (flex == null) throw new IllegalArgumentException("Source object cannot be null");
- // if (stable == null) throw new IllegalArgumentException("Target object cannot be null");
- // stable.content = flex.content != null ? flex.content : stable.content;
- // stable.priority = flex.priority != null ? flex.priority : stable.priority;
- // stable.urgent = flex.urgent != null ? flex.urgent : stable.urgent;
- // stable.tags = flex.tags != null ? flex.tags : stable.tags;
- }
-
- @ObjectCopier(strategy = ObjectCopier.CopyStrategy.DEEP)
- private void deepConvert(MessageFlex flex, MessageStable stable) {
- // 深拷贝版本的转换
- }
-
- public void processConversion(MessageFlex flex, MessageStable stable) {
- convert(flex, stable);
- // 其他业务逻辑
- }
-
-}
diff --git a/autil-incubator/incubator-annotation/src/main/java/com/acanx/util/incubator/annotation/ann/ObjectCopierProcessor.java b/autil-incubator/incubator-annotation/src/main/java/com/acanx/util/incubator/annotation/ann/ObjectCopierProcessor.java
index 8cbb61b..f235677 100644
--- a/autil-incubator/incubator-annotation/src/main/java/com/acanx/util/incubator/annotation/ann/ObjectCopierProcessor.java
+++ b/autil-incubator/incubator-annotation/src/main/java/com/acanx/util/incubator/annotation/ann/ObjectCopierProcessor.java
@@ -1,355 +1,380 @@
package com.acanx.util.incubator.annotation.ann;
-/**
- * ObjectCopierProcessor
- *
- * @author ACANX
- * @since 20251110
- */
-
-
import com.acanx.util.incubator.annotation.ObjectCopier;
-import com.sun.source.tree.MethodTree;
-import com.sun.source.util.JavacTask;
-import com.sun.source.util.Trees;
-import com.sun.tools.javac.tree.JCTree;
-import com.sun.tools.javac.tree.TreeMaker;
-import com.sun.tools.javac.util.Context;
-import com.sun.tools.javac.util.List;
-import com.sun.tools.javac.util.ListBuffer;
-import com.sun.tools.javac.util.Names;
-
-
import javax.annotation.processing.AbstractProcessor;
-import javax.annotation.processing.ProcessingEnvironment;
+import javax.annotation.processing.Filer;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.annotation.processing.SupportedSourceVersion;
import javax.lang.model.SourceVersion;
+import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement;
+import javax.lang.model.element.VariableElement;
import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;
+import javax.lang.model.util.Elements;
+import javax.lang.model.util.Types;
import javax.tools.Diagnostic;
-import java.lang.reflect.Method;
+import javax.tools.JavaFileObject;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
import java.util.Set;
-/**
- * 对象拷贝注解处理器
- * 通过修改AST在编译期增强被注解的方法
- */
@SupportedAnnotationTypes("com.acanx.util.incubator.annotation.ObjectCopier")
-@SupportedSourceVersion(SourceVersion.RELEASE_25)
+@SupportedSourceVersion(SourceVersion.RELEASE_17)
public class ObjectCopierProcessor extends AbstractProcessor {
- private Trees trees;
- private TreeMaker treeMaker;
- private Names names;
+ private Types typeUtils;
+ private Elements elementUtils;
+ private Filer filer;
@Override
- public synchronized void init(ProcessingEnvironment processingEnv) {
+ public synchronized void init(javax.annotation.processing.ProcessingEnvironment processingEnv) {
super.init(processingEnv);
- this.trees = Trees.instance(processingEnv);
- try {
- // 使用反射获取Context
- Context context = getContext(processingEnv);
- this.treeMaker = TreeMaker.instance(context);
- this.names = Names.instance(context);
- } catch (Exception e) {
- processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR,
- "Failed to initialize compiler internals: " + e.getMessage()
- );
- }
+ typeUtils = processingEnv.getTypeUtils();
+ elementUtils = processingEnv.getElementUtils();
+ filer = processingEnv.getFiler();
}
-
-
@Override
public boolean process(Set extends TypeElement> annotations, RoundEnvironment roundEnv) {
if (roundEnv.processingOver()) {
return false;
}
+
+ note("开始处理 @ObjectCopier 注解", null);
+
+ Map> classMethodsMap = new HashMap<>();
for (Element element : roundEnv.getElementsAnnotatedWith(ObjectCopier.class)) {
if (element.getKind() != ElementKind.METHOD) {
- processingEnv.getMessager().printMessage(
- Diagnostic.Kind.ERROR,
- "@ObjectCopier can only be applied to methods",
- element
- );
- continue;
- }
- ExecutableElement methodElement = (ExecutableElement) element;
- // 验证方法参数
- if (!validateMethod(methodElement)) {
+ error("注解@ObjectCopier只能应用于方法", element, getAnnotationMirror(element));
continue;
}
- // 获取方法对应的AST
- MethodTree methodTree = trees.getTree(methodElement);
- if (methodTree instanceof JCTree.JCMethodDecl) {
- enhanceMethod((JCTree.JCMethodDecl) methodTree, methodElement);
+ ExecutableElement method = (ExecutableElement) element;
+ TypeElement enclosingClass = (TypeElement) method.getEnclosingElement();
+ classMethodsMap.computeIfAbsent(enclosingClass, k -> new ArrayList<>()).add(method);
+
+ note("找到被注解的方法: " + enclosingClass.getSimpleName() + "." + method.getSimpleName(), method);
+ }
+
+ for (Map.Entry> entry : classMethodsMap.entrySet()) {
+ TypeElement enclosingClass = entry.getKey();
+ List methods = entry.getValue();
+ for (ExecutableElement method : methods) {
+ try {
+ generateCopierClass(method);
+ } catch (Exception e) {
+ error("生成拷贝类时出错: " + e.getMessage(), method, getAnnotationMirror(method));
+ }
}
}
+
+ note("注解处理完成", null);
return true;
}
- /**
- * 通过反射获取Compiler Context
- */
- private Context getContext(ProcessingEnvironment processingEnv) throws Exception {
- // 方法1: 通过JavacTask获取Context
- if (processingEnv instanceof JavacTask) {
- JavacTask javacTask = (JavacTask) processingEnv;
- // 尝试通过getContext方法获取
- try {
- Method getContextMethod = javacTask.getClass().getMethod("getContext");
- return (Context) getContextMethod.invoke(javacTask);
- } catch (NoSuchMethodException e) {
- // 如果getContext方法不存在,尝试其他方式
- processingEnv.getMessager().printMessage(
- Diagnostic.Kind.NOTE,
- "getContext() method not found, trying alternative approach"
- );
+ private void generateCopierClass(ExecutableElement methodElement) throws IOException {
+ if (!validateMethod(methodElement)) {
+ return;
+ }
+
+ VariableElement sourceParam = methodElement.getParameters().get(0);
+ VariableElement targetParam = methodElement.getParameters().get(1);
+ TypeMirror sourceType = sourceParam.asType();
+ TypeMirror targetType = targetParam.asType();
+
+ if (!(sourceType instanceof DeclaredType) || !(targetType instanceof DeclaredType)) {
+ error("源类型和目标类型必须是类类型", methodElement, getAnnotationMirror(methodElement));
+ return;
+ }
+
+ TypeElement sourceElement = (TypeElement) ((DeclaredType) sourceType).asElement();
+ TypeElement targetElement = (TypeElement) ((DeclaredType) targetType).asElement();
+ TypeElement enclosingClass = (TypeElement) methodElement.getEnclosingElement();
+
+ // 生成类名:使用被注解方法所在类的类名 + "Copier"
+ String enclosingClassName = enclosingClass.getSimpleName().toString();
+ String copierClassName = enclosingClassName + "Copier";
+
+ // 获取包名
+ String packageName = elementUtils.getPackageOf(enclosingClass).getQualifiedName().toString();
+
+ // 创建源文件
+ JavaFileObject builderFile = filer.createSourceFile(
+ packageName + "." + copierClassName,
+ methodElement
+ );
+
+ note("生成拷贝类: " + packageName + "." + copierClassName, methodElement);
+
+ try (PrintWriter out = new PrintWriter(builderFile.openWriter())) {
+ writeCopierClass(out, packageName, copierClassName, sourceElement, targetElement, methodElement, enclosingClass);
+ }
+ }
+
+ private void writeCopierClass(PrintWriter out, String packageName, String className,
+ TypeElement sourceElement, TypeElement targetElement,
+ ExecutableElement methodElement, TypeElement enclosingClass) {
+
+ String sourceTypeName = sourceElement.getSimpleName().toString();
+ String targetTypeName = targetElement.getSimpleName().toString();
+ String sourceQualifiedName = sourceElement.getQualifiedName().toString();
+ String targetQualifiedName = targetElement.getQualifiedName().toString();
+ String enclosingClassQualifiedName = enclosingClass.getQualifiedName().toString();
+ String methodName = methodElement.getSimpleName().toString();
+
+ // 包声明
+ out.println("package " + packageName + ";");
+ out.println();
+
+ // 导入语句
+ out.println("import " + sourceQualifiedName + ";");
+ out.println("import " + targetQualifiedName + ";");
+ out.println();
+
+ // 类注释
+ out.println("/**");
+ out.println(" * 自动生成的拷贝类");
+ out.println(" * 将 " + sourceTypeName + " 对象的属性拷贝到 " + targetTypeName + " 对象");
+ out.println(" * 由 {@link " + enclosingClassQualifiedName + "#" + methodName + "(" + sourceTypeName + ", " + targetTypeName + ")} 方法生成");
+ out.println(" */");
+
+ // 类声明
+ out.println("public class " + className + " {");
+ out.println();
+
+ // 拷贝方法
+ out.println(" /**");
+ out.println(" * 执行属性拷贝");
+ out.println(" * 由 {@link " + enclosingClassQualifiedName + "#" + methodName + "(" + sourceTypeName + ", " + targetTypeName + ")} 方法生成");
+ out.println(" * @param source 源对象");
+ out.println(" * @param target 目标对象");
+ out.println(" */");
+ out.println(" public static void " + methodName + "(" + sourceTypeName + " source, " + targetTypeName + " target) {");
+ out.println(" if (source == null || target == null) {");
+ out.println(" return;");
+ out.println(" }");
+ out.println();
+ // 生成属性拷贝代码
+ List mappings = findPropertyMappings(sourceElement, targetElement);
+ if (mappings.isEmpty()) {
+ out.println(" // 未找到可拷贝的属性");
+ } else {
+ out.println(" // 属性拷贝");
+ for (PropertyMapping mapping : mappings) {
+ out.println(" target." + mapping.setterCall.replace("(value)", "(" + "source." + mapping.getterCall + ")" + ";"));
}
}
- // 方法2: 通过Trees获取Context
- try {
- Method getContextMethod = trees.getClass().getMethod("getContext");
- return (Context) getContextMethod.invoke(trees);
- } catch (NoSuchMethodException e) {
- processingEnv.getMessager().printMessage(
- Diagnostic.Kind.NOTE,
- "Trees.getContext() not available, trying field access"
- );
+ out.println(" }");
+ out.println("}");
+ }
+
+ private List findPropertyMappings(TypeElement sourceElement, TypeElement targetElement) {
+ List mappings = new ArrayList<>();
+ List sourceGetters = findGetterMethods(sourceElement);
+ List targetSetters = findSetterMethods(targetElement);
+
+ note("在 " + sourceElement.getSimpleName() + " 中找到 " + sourceGetters.size() + " 个getter方法", sourceElement);
+ note("在 " + targetElement.getSimpleName() + " 中找到 " + targetSetters.size() + " 个setter方法", targetElement);
+
+ for (GetterMethod getter : sourceGetters) {
+ SetterMethod setter = findMatchingSetter(targetSetters, getter.propertyName, getter.returnType);
+ if (setter != null) {
+ mappings.add(new PropertyMapping(getter.propertyName, getter.methodCall, setter.methodCall));
+ note("属性映射: " + getter.propertyName + " (" + getSimpleTypeName(getter.returnType) + ")", sourceElement);
+ }
}
- // 方法3: 通过字段访问(最后的手段)
- return getContextViaFieldAccess(processingEnv);
+
+ note("总共建立 " + mappings.size() + " 个属性映射", sourceElement);
+ return mappings;
}
- /**
- * 在类层次结构中查找字段
- */
- private java.lang.reflect.Field findField(Class> clazz, String fieldName) {
- while (clazz != null) {
- try {
- java.lang.reflect.Field field = clazz.getDeclaredField(fieldName);
- return field;
- } catch (NoSuchFieldException e) {
- clazz = clazz.getSuperclass();
+ private String extractPropertyName(String methodName) {
+ if (methodName.startsWith("get") || methodName.startsWith("set")) {
+ String baseName = methodName.substring(3);
+ if (!baseName.isEmpty()) {
+ return Character.toLowerCase(baseName.charAt(0)) + baseName.substring(1);
+ }
+ } else if (methodName.startsWith("is")) {
+ String baseName = methodName.substring(2);
+ if (!baseName.isEmpty()) {
+ return Character.toLowerCase(baseName.charAt(0)) + baseName.substring(1);
}
}
- return null;
+ return methodName;
}
- /**
- * 通过字段反射获取Context
- */
- private Context getContextViaFieldAccess(ProcessingEnvironment processingEnv) throws Exception {
- // 获取JavacTask实例
- if (processingEnv instanceof JavacTask) {
- JavacTask javacTask = (JavacTask) processingEnv;
- // 尝试访问context字段
- try {
- java.lang.reflect.Field contextField = findField(javacTask.getClass(), "context");
- if (contextField != null) {
- contextField.setAccessible(true);
- return (Context) contextField.get(javacTask);
+ private List findGetterMethods(TypeElement element) {
+ List getters = new ArrayList<>();
+ TypeMirror currentType = element.asType();
+
+ while (currentType.getKind() == TypeKind.DECLARED) {
+ DeclaredType declaredType = (DeclaredType) currentType;
+ TypeElement currentElement = (TypeElement) declaredType.asElement();
+
+ for (Element enclosed : currentElement.getEnclosedElements()) {
+ if (enclosed.getKind() == ElementKind.METHOD && enclosed instanceof ExecutableElement) {
+ ExecutableElement method = (ExecutableElement) enclosed;
+ String methodName = method.getSimpleName().toString();
+
+ if (isGetterMethod(method, methodName)) {
+ String propertyName = extractPropertyName(methodName);
+ String methodCall = methodName + "()";
+ getters.add(new GetterMethod(propertyName, methodCall, method.getReturnType()));
+ }
}
- } catch (Exception e) {
- processingEnv.getMessager().printMessage(
- Diagnostic.Kind.WARNING,
- "Field access failed: " + e.getMessage()
- );
+ }
+
+ currentType = currentElement.getSuperclass();
+ if (currentType.getKind() == TypeKind.NONE) {
+ break;
}
}
- throw new IllegalStateException("Unable to access compiler Context");
+ return getters;
+ }
+
+ private boolean isGetterMethod(ExecutableElement method, String methodName) {
+ if (method.getModifiers().contains(Modifier.STATIC)) return false;
+ if (!method.getParameters().isEmpty()) return false;
+
+ if (methodName.startsWith("get") && methodName.length() > 3) return true;
+ return methodName.startsWith("is") && methodName.length() > 2 &&
+ method.getReturnType().getKind() == TypeKind.BOOLEAN;
}
+ private List findSetterMethods(TypeElement element) {
+ List setters = new ArrayList<>();
+ TypeMirror currentType = element.asType();
+
+ while (currentType.getKind() == TypeKind.DECLARED) {
+ DeclaredType declaredType = (DeclaredType) currentType;
+ TypeElement currentElement = (TypeElement) declaredType.asElement();
+
+ for (Element enclosed : currentElement.getEnclosedElements()) {
+ if (enclosed.getKind() == ElementKind.METHOD && enclosed instanceof ExecutableElement) {
+ ExecutableElement method = (ExecutableElement) enclosed;
+ String methodName = method.getSimpleName().toString();
+
+ if (isSetterMethod(method, methodName)) {
+ String propertyName = extractPropertyName(methodName);
+ String methodCall = methodName + "(value)";
+ setters.add(new SetterMethod(propertyName, methodCall, method.getParameters().get(0).asType()));
+ }
+ }
+ }
+
+ currentType = currentElement.getSuperclass();
+ if (currentType.getKind() == TypeKind.NONE) {
+ break;
+ }
+ }
+ return setters;
+ }
+
+ private boolean isSetterMethod(ExecutableElement method, String methodName) {
+ if (method.getModifiers().contains(Modifier.STATIC)) return false;
+ return methodName.startsWith("set") && methodName.length() > 3 &&
+ method.getParameters().size() == 1;
+ }
+
+ private SetterMethod findMatchingSetter(List setters, String propertyName, TypeMirror getterType) {
+ for (SetterMethod setter : setters) {
+ if (setter.propertyName.equals(propertyName) && isTypeCompatible(getterType, setter.paramType)) {
+ return setter;
+ }
+ }
+ return null;
+ }
+
+ private boolean isTypeCompatible(TypeMirror sourceType, TypeMirror targetType) {
+ return typeUtils.isAssignable(sourceType, targetType) ||
+ typeUtils.isAssignable(targetType, sourceType);
+ }
- /**
- * 验证方法是否符合要求
- */
private boolean validateMethod(ExecutableElement method) {
- // 验证参数数量
if (method.getParameters().size() != 2) {
- processingEnv.getMessager().printMessage(
- Diagnostic.Kind.ERROR,
- "@ObjectCopier method must have exactly 2 parameters",
- method
- );
+ error("@ObjectCopier注解的方法必须恰好有2个参数", method, getAnnotationMirror(method));
return false;
}
- // 验证方法可见性
- if (!method.getModifiers().contains(javax.lang.model.element.Modifier.PRIVATE)) {
- processingEnv.getMessager().printMessage(
- Diagnostic.Kind.WARNING,
- "@ObjectCopier is recommended to be used on private methods for better encapsulation",
- method
- );
- }
- // 验证返回类型为void
- TypeMirror returnType = method.getReturnType();
- if (!returnType.toString().equals("void")) {
- processingEnv.getMessager().printMessage(
- Diagnostic.Kind.ERROR,
- "@ObjectCopier method must have void return type",
- method
- );
+ if (!method.getReturnType().getKind().equals(TypeKind.VOID)) {
+ error("@ObjectCopier注解的方法必须返回void", method, getAnnotationMirror(method));
return false;
}
return true;
}
- /**
- * 增强方法,添加对象拷贝逻辑
- */
- private void enhanceMethod(JCTree.JCMethodDecl methodTree, ExecutableElement methodElement) {
- ObjectCopier annotation = methodElement.getAnnotation(ObjectCopier.class);
- // 获取源参数和目标参数
- JCTree.JCVariableDecl sourceParam = methodTree.getParameters().get(0);
- JCTree.JCVariableDecl targetParam = methodTree.getParameters().get(1);
- // 创建拷贝语句
- List copyStatements = createCopyStatements(
- sourceParam,
- targetParam,
- annotation,
- methodElement
- );
- // 替换方法体
- JCTree.JCBlock newBody = treeMaker.Block(0, copyStatements);
- methodTree.body = newBody;
- processingEnv.getMessager().printMessage(
- Diagnostic.Kind.NOTE,
- "Enhanced @ObjectCopier method: " + methodElement.getSimpleName(),
- methodElement
- );
+ private String getSimpleTypeName(TypeMirror typeMirror) {
+ if (typeMirror instanceof DeclaredType) {
+ TypeElement typeElement = (TypeElement) ((DeclaredType) typeMirror).asElement();
+ return typeElement.getSimpleName().toString();
+ }
+ return typeMirror.toString();
}
- /**
- * 创建拷贝语句
- */
- private List createCopyStatements(
- JCTree.JCVariableDecl sourceParam,
- JCTree.JCVariableDecl targetParam,
- ObjectCopier annotation,
- ExecutableElement methodElement) {
- ListBuffer statements = new ListBuffer<>();
- // 添加空值检查
- statements.append(createNullCheckStatement(sourceParam, "Source object cannot be null"));
- statements.append(createNullCheckStatement(targetParam, "Target object cannot be null"));
- // 获取源对象类型的所有字段
- TypeMirror sourceType = methodElement.getParameters().get(0).asType();
- TypeElement sourceTypeElement = (TypeElement) ((DeclaredType) sourceType).asElement();
- // 为每个字段生成赋值语句
- for (Element field : sourceTypeElement.getEnclosedElements()) {
- if (field.getKind() == ElementKind.FIELD &&
- !field.getModifiers().contains(javax.lang.model.element.Modifier.STATIC)) {
- JCTree.JCStatement assignment = createFieldAssignment(
- sourceParam,
- targetParam,
- field,
- annotation
- );
- if (assignment != null) {
- statements.append(assignment);
- }
- }
- }
- return statements.toList();
+ private void error(String msg, Element element, AnnotationMirror annotationMirror) {
+ processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, msg, element, annotationMirror);
}
- /**
- * 创建空值检查语句
- */
- private JCTree.JCStatement createNullCheckStatement(JCTree.JCVariableDecl param, String message) {
- // 创建条件表达式: param == null
- JCTree.JCExpression nullCheck = treeMaker.Binary(
- JCTree.Tag.EQ,
- treeMaker.Ident(param.name),
- treeMaker.Literal(null)
- );
- // 创建异常抛出语句
- JCTree.JCExpression exceptionType = treeMaker.Ident(names.fromString("IllegalArgumentException"));
- JCTree.JCNewClass newException = treeMaker.NewClass(
- null,
- List.nil(),
- exceptionType,
- List.of(treeMaker.Literal(message)),
- null
- );
- JCTree.JCThrow throwStatement = treeMaker.Throw(newException);
- // 创建if语句
- return treeMaker.If(nullCheck, throwStatement, null);
+ private void warn(String msg, Element element) {
+ processingEnv.getMessager().printMessage(Diagnostic.Kind.WARNING, msg, element);
}
- /**
- * 创建字段赋值语句
- */
- private JCTree.JCStatement createFieldAssignment(
- JCTree.JCVariableDecl sourceParam,
- JCTree.JCVariableDecl targetParam,
- Element field,
- ObjectCopier annotation) {
- String fieldName = field.getSimpleName().toString();
- // 检查字段是否在排除列表中
- if (isFieldExcluded(fieldName, annotation)) {
- return null;
- }
- // 检查字段是否在包含列表中(如果设置了include)
- if (annotation.include().length > 0 && !isFieldIncluded(fieldName, annotation)) {
- return null;
- }
- // 创建字段访问表达式: target.field
- JCTree.JCFieldAccess targetFieldAccess = treeMaker.Select(
- treeMaker.Ident(targetParam.name),
- names.fromString(fieldName)
- );
- // 创建字段访问表达式: source.field
- JCTree.JCFieldAccess sourceFieldAccess = treeMaker.Select(
- treeMaker.Ident(sourceParam.name),
- names.fromString(fieldName)
- );
- JCTree.JCExpression assignmentExpression;
- if (annotation.ignoreNull()) {
- // 如果忽略空值,创建条件赋值: source.field != null ? source.field : target.field
- JCTree.JCExpression nullCheck = treeMaker.Binary(
- JCTree.Tag.NE,
- sourceFieldAccess,
- treeMaker.Literal(null)
- );
- assignmentExpression = treeMaker.Conditional(
- nullCheck,
- sourceFieldAccess,
- treeMaker.Ident(targetParam.name) // 保持原值
- );
- } else {
- // 直接赋值
- assignmentExpression = sourceFieldAccess;
- }
- // 创建赋值语句: target.field = assignmentExpression
- return treeMaker.Exec(
- treeMaker.Assign(targetFieldAccess, assignmentExpression)
- );
+ private void note(String msg, Element element) {
+ processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE, msg, element);
}
- private boolean isFieldExcluded(String fieldName, ObjectCopier annotation) {
- for (String excluded : annotation.exclude()) {
- if (excluded.equals(fieldName)) {
- return true;
+ private AnnotationMirror getAnnotationMirror(Element element) {
+ for (AnnotationMirror mirror : element.getAnnotationMirrors()) {
+ if (mirror.getAnnotationType().toString().equals(ObjectCopier.class.getName())) {
+ return mirror;
}
}
- return false;
+ return null;
}
- private boolean isFieldIncluded(String fieldName, ObjectCopier annotation) {
- for (String included : annotation.include()) {
- if (included.equals(fieldName)) {
- return true;
- }
+ // 内部类保持不变...
+ private static class PropertyMapping {
+ final String propertyName;
+ final String getterCall;
+ final String setterCall;
+
+ PropertyMapping(String propertyName, String getterCall, String setterCall) {
+ this.propertyName = propertyName;
+ this.getterCall = getterCall;
+ this.setterCall = setterCall;
}
- return false;
}
+ private static class GetterMethod {
+ final String propertyName;
+ final String methodCall;
+ final TypeMirror returnType;
+ GetterMethod(String propertyName, String methodCall, TypeMirror returnType) {
+ this.propertyName = propertyName;
+ this.methodCall = methodCall;
+ this.returnType = returnType;
+ }
+ }
+
+ private static class SetterMethod {
+ final String propertyName;
+ final String methodCall;
+ final TypeMirror paramType;
+
+ SetterMethod(String propertyName, String methodCall, TypeMirror paramType) {
+ this.propertyName = propertyName;
+ this.methodCall = methodCall;
+ this.paramType = paramType;
+ }
+ }
}
\ No newline at end of file
diff --git a/autil-incubator/incubator-annotation/src/main/java/module-info.java b/autil-incubator/incubator-annotation/src/main/java/module-info.java
index e2bec23..cfb6849 100644
--- a/autil-incubator/incubator-annotation/src/main/java/module-info.java
+++ b/autil-incubator/incubator-annotation/src/main/java/module-info.java
@@ -1,7 +1,17 @@
module com.acanx.util.incubator.annotation {
- requires java.compiler; // 官方注解处理API
- requires jdk.compiler;
- requires com.squareup.javapoet;
- requires model.test; // 内部模块
+
+ requires java.compiler;
+ requires static jdk.compiler;
+ requires model.test;
+
+
+ // 导出注解处理器所在的包
+ exports com.acanx.util.incubator.annotation;
+ exports com.acanx.util.incubator.annotation.ann;
+ // 关键修正:打开包到jdk.compiler模块 允许反射访问
+ opens com.acanx.util.incubator.annotation.ann to jdk.compiler;
+
+ provides javax.annotation.processing.Processor
+ with com.acanx.util.incubator.annotation.ann.ObjectCopierProcessor;
}
\ No newline at end of file
diff --git a/autil-incubator/incubator-annotation/src/test/java/com/acanx/meta/model/CopyTest.java b/autil-incubator/incubator-annotation/src/test/java/com/acanx/meta/model/CopyTest.java
new file mode 100644
index 0000000..a790dfd
--- /dev/null
+++ b/autil-incubator/incubator-annotation/src/test/java/com/acanx/meta/model/CopyTest.java
@@ -0,0 +1,42 @@
+package com.acanx.meta.model;
+
+import com.acanx.meta.model.test.annotation.model.MessageFlex;
+import com.acanx.meta.model.test.annotation.model.MessageStable;
+import com.acanx.util.incubator.annotation.ObjectCopier;
+
+/**
+ * CopyTest
+ *
+ * @author ACANX
+ * @since 20251110
+ */
+public class CopyTest {
+
+
+
+ @ObjectCopier
+ private void convert(MessageFlex flex, MessageStable stable) {};
+
+
+// @ObjectCopier(ignoreNull = true, exclude = {"internalId"})
+// private void convert1(MessageFlex flex, MessageStable stable) {
+// // 编译后这个方法会被增强为:
+// // if (flex == null) throw new IllegalArgumentException("Source object cannot be null");
+// // if (stable == null) throw new IllegalArgumentException("Target object cannot be null");
+// // stable.content = flex.content != null ? flex.content : stable.content;
+// // stable.priority = flex.priority != null ? flex.priority : stable.priority;
+// // stable.urgent = flex.urgent != null ? flex.urgent : stable.urgent;
+// // stable.tags = flex.tags != null ? flex.tags : stable.tags;
+// }
+
+// @ObjectCopier(strategy = ObjectCopier.CopyStrategy.DEEP)
+// private void deepConvert(MessageFlex flex, MessageStable stable) {
+// // 深拷贝版本的转换
+// }
+
+ public void processConversion(MessageFlex flex, MessageStable stable) {
+ convert(flex, stable);
+ // 其他业务逻辑
+ }
+
+}
diff --git a/autil-test/pom.xml b/autil-test/pom.xml
index acc9ff0..762be52 100644
--- a/autil-test/pom.xml
+++ b/autil-test/pom.xml
@@ -47,7 +47,12 @@
com.acanx.meta.model
model-test
0.5.7-SNAPSHOT
- test
+
+
+ com.acanx.util
+ incubator-annotation
+ 0.4.3-SNAPSHOT
+ compile
@@ -75,6 +80,10 @@
--add-exports
jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED
+
+ com.acanx.util.incubator.annotation.ann.ObjectCopierProcessor
+
+ ${project.build.directory}/generated-sources/annotations
com.acanx.util
diff --git a/autil-test/src/main/java/com/acanx/util/enums/Copy2.java b/autil-test/src/main/java/com/acanx/util/enums/Copy2.java
new file mode 100644
index 0000000..9b46a0e
--- /dev/null
+++ b/autil-test/src/main/java/com/acanx/util/enums/Copy2.java
@@ -0,0 +1,27 @@
+package com.acanx.util.enums;
+
+
+import com.acanx.meta.model.test.annotation.model.MessageFlex;
+import com.acanx.meta.model.test.annotation.model.MessageStable;
+import com.acanx.util.incubator.annotation.ObjectCopier;
+
+/**
+ * CopyTest
+ *
+ * @author ACANX
+ * @since 20251110
+ */
+public class Copy2 {
+
+
+
+ @ObjectCopier
+ private void flexToStable(MessageFlex flex, MessageStable stable) {};
+
+
+ @ObjectCopier
+ private void flex2ToStable(MessageFlex flex, MessageStable stable) {};
+
+
+
+}
diff --git a/autil-test/src/test/java/com/acanx/util/annotation/CopyTest.java b/autil-test/src/test/java/com/acanx/util/annotation/CopyTest.java
index 11dd2a4..946bdaa 100644
--- a/autil-test/src/test/java/com/acanx/util/annotation/CopyTest.java
+++ b/autil-test/src/test/java/com/acanx/util/annotation/CopyTest.java
@@ -18,21 +18,21 @@ public class CopyTest {
private void convert(MessageFlex flex, MessageStable stable) {};
- @ObjectCopier(ignoreNull = true, exclude = {"internalId"})
- private void convert1(MessageFlex flex, MessageStable stable) {
- // 编译后这个方法会被增强为:
- // if (flex == null) throw new IllegalArgumentException("Source object cannot be null");
- // if (stable == null) throw new IllegalArgumentException("Target object cannot be null");
- // stable.content = flex.content != null ? flex.content : stable.content;
- // stable.priority = flex.priority != null ? flex.priority : stable.priority;
- // stable.urgent = flex.urgent != null ? flex.urgent : stable.urgent;
- // stable.tags = flex.tags != null ? flex.tags : stable.tags;
- }
-
- @ObjectCopier(strategy = ObjectCopier.CopyStrategy.DEEP)
- private void deepConvert(MessageFlex flex, MessageStable stable) {
- // 深拷贝版本的转换
- }
+// @ObjectCopier(ignoreNull = true, exclude = {"internalId"})
+// private void convert1(MessageFlex flex, MessageStable stable) {
+// // 编译后这个方法会被增强为:
+// // if (flex == null) throw new IllegalArgumentException("Source object cannot be null");
+// // if (stable == null) throw new IllegalArgumentException("Target object cannot be null");
+// // stable.content = flex.content != null ? flex.content : stable.content;
+// // stable.priority = flex.priority != null ? flex.priority : stable.priority;
+// // stable.urgent = flex.urgent != null ? flex.urgent : stable.urgent;
+// // stable.tags = flex.tags != null ? flex.tags : stable.tags;
+// }
+
+// @ObjectCopier(strategy = ObjectCopier.CopyStrategy.DEEP)
+// private void deepConvert(MessageFlex flex, MessageStable stable) {
+// // 深拷贝版本的转换
+// }
public void processConversion(MessageFlex flex, MessageStable stable) {
convert(flex, stable);
From 143569f10b369cd05805ff48e2396a3957ea9831 Mon Sep 17 00:00:00 2001
From: ACANX <245818784@qq.com>
Date: Mon, 10 Nov 2025 17:09:37 +0800
Subject: [PATCH 3/3] =?UTF-8?q?fix:=E7=BC=96=E8=AF=91=E9=97=AE=E9=A2=98?=
=?UTF-8?q?=E4=BF=AE=E5=A4=8D?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
autil-incubator/incubator-annotation/pom.xml | 54 +--
.../util/incubator/annotation/ASTScanner.java | 301 ----------------
.../acanx/util/incubator/annotation/App.java | 12 -
.../{ObjectCopier.java => Copier.java} | 2 +-
...ierProcessor.java => CopierProcessor.java} | 132 ++++---
.../annotation/MetaObjectCopyPlugin.java | 93 -----
.../annotation/MetaObjectCopyProcessor.java | 334 ------------------
.../annotation/ObjectCopyProcessor.java | 2 +-
.../annotation/PluginLoaderTest.java | 46 ---
.../src/main/java/module-info.java | 10 +-
.../javax.annotation.processing.Processor | 3 +-
.../java/com/acanx/meta/model/CopyTest.java | 82 ++---
autil-incubator/pom.xml | 3 +-
autil-test/pom.xml | 2 +-
.../main/java/com/acanx/util/enums/Copy2.java | 6 +-
.../com/acanx/util/annotation/CopyTest.java | 34 +-
pom.xml | 34 +-
17 files changed, 186 insertions(+), 964 deletions(-)
delete mode 100644 autil-incubator/incubator-annotation/src/main/java/com/acanx/util/incubator/annotation/ASTScanner.java
delete mode 100644 autil-incubator/incubator-annotation/src/main/java/com/acanx/util/incubator/annotation/App.java
rename autil-incubator/incubator-annotation/src/main/java/com/acanx/util/incubator/annotation/{ObjectCopier.java => Copier.java} (97%)
rename autil-incubator/incubator-annotation/src/main/java/com/acanx/util/incubator/annotation/{ann/ObjectCopierProcessor.java => CopierProcessor.java} (81%)
delete mode 100644 autil-incubator/incubator-annotation/src/main/java/com/acanx/util/incubator/annotation/MetaObjectCopyPlugin.java
delete mode 100644 autil-incubator/incubator-annotation/src/main/java/com/acanx/util/incubator/annotation/MetaObjectCopyProcessor.java
delete mode 100644 autil-incubator/incubator-annotation/src/main/java/com/acanx/util/incubator/annotation/PluginLoaderTest.java
diff --git a/autil-incubator/incubator-annotation/pom.xml b/autil-incubator/incubator-annotation/pom.xml
index 1eef2e9..f0c7a04 100644
--- a/autil-incubator/incubator-annotation/pom.xml
+++ b/autil-incubator/incubator-annotation/pom.xml
@@ -16,8 +16,7 @@
https://acanx.com
- 25
-
+ 21
UTF-8
false
3.14.1
@@ -29,48 +28,23 @@
javapoet
1.13.0
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
com.acanx.util
autil-core
${revision}
-
-
-
-
-
-
org.junit.jupiter
junit-jupiter-api
${junit-jupiter.version}
test
-
- com.acanx.meta.model
- model-test
- 0.5.7-SNAPSHOT
- compile
-
+
+
+
+
+
+
@@ -113,13 +87,13 @@
-J--add-exports=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED
-J--add-opens=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED
-
-
- com.acanx.util
- incubator-annotation
- ${revision}
-
-
+
+
+
+
+
+
+
diff --git a/autil-incubator/incubator-annotation/src/main/java/com/acanx/util/incubator/annotation/ASTScanner.java b/autil-incubator/incubator-annotation/src/main/java/com/acanx/util/incubator/annotation/ASTScanner.java
deleted file mode 100644
index f44eba7..0000000
--- a/autil-incubator/incubator-annotation/src/main/java/com/acanx/util/incubator/annotation/ASTScanner.java
+++ /dev/null
@@ -1,301 +0,0 @@
-package com.acanx.util.incubator.annotation;//package com.acanx.util.object.copy;
-//
-//import com.sun.tools.javac.code.Symbol;
-//import com.sun.tools.javac.code.Type;
-//import com.sun.tools.javac.model.JavacElements;
-//import com.sun.tools.javac.tree.JCTree;
-//import com.sun.tools.javac.tree.TreeMaker;
-//import com.sun.tools.javac.tree.TreeScanner;
-//import com.sun.tools.javac.util.List;
-//import com.sun.tools.javac.util.Names;
-//
-//import javax.lang.model.element.ElementKind;
-//import java.util.Arrays;
-//import java.util.HashMap;
-//import java.util.HashSet;
-//import java.util.Map;
-//import java.util.Set;
-//
-///**
-// * ASTScanner
-// *
-// * @author ACANX
-// * @date 2025-06-14
-// * @since 202506
-// */
-//public class ASTScanner extends TreeScanner {
-// private final TreeMaker maker;
-// private final JavacElements elements;
-// private final Names names;
-//
-// ASTScanner(TreeMaker maker, JavacElements elements, Names names) {
-// this.maker = maker;
-// this.elements = elements;
-// this.names = names;
-// }
-// // JDK 8 兼容方法
-// @SuppressWarnings("unused")
-// public void scan(JCTree tree, Void ignored) {
-// if (tree != null) {
-// tree.accept(this);
-// }
-// }
-//
-// // JDK 11 兼容的扫描方法
-// @Override
-// public void visitTopLevel(JCTree.JCCompilationUnit tree) {
-// super.visitTopLevel(tree);
-// // 扫描顶级元素
-// for (JCTree def : tree.defs) {
-// if (def instanceof JCTree.JCClassDecl) {
-// visitClassDef((JCTree.JCClassDecl) def);
-// }
-// }
-// }
-//
-// @Override
-// public void visitClassDef(JCTree.JCClassDecl tree) {
-// super.visitClassDef(tree);
-// // 扫描类中的所有方法
-// for (JCTree member : tree.defs) {
-// if (member instanceof JCTree.JCMethodDecl) {
-// processMethod((JCTree.JCMethodDecl) member);
-// }
-// }
-// }
-//
-// private void processMethod(JCTree.JCMethodDecl method) {
-// // 获取方法上的所有MetaObjectCopy注解
-// List annotations = getMetaObjectCopyAnnotations(method);
-// if (!annotations.isEmpty()) {
-// // 合并所有注解配置
-// boolean copyNulls = annotations.stream().anyMatch(MetaObjectCopy::copyNulls);
-// Set ignoreFields = new HashSet<>();
-// Map fieldMappings = new HashMap<>();
-// for (MetaObjectCopy ann : annotations) {
-// ignoreFields.addAll(Arrays.asList(ann.ignoreFields()));
-// for (MetaObjectCopy.FieldMapping mapping : ann.fieldMappings()) {
-// fieldMappings.put(mapping.target(), mapping.source());
-// }
-// }
-// // 替换方法体
-// method.body = createMethodBody(
-// method.params.get(0),
-// method.params.get(1),
-// copyNulls,
-// ignoreFields,
-// fieldMappings
-// );
-// }
-// }
-//
-// private List getMetaObjectCopyAnnotations(JCTree.JCMethodDecl method) {
-// List annotations = List.nil();
-// for (JCTree.JCAnnotation ann : method.mods.annotations) {
-// Type type = ann.type;
-// if (type != null) {
-// String annotationName = type.toString();
-// if (annotationName.equals(MetaObjectCopy.class.getName())) {
-// // 解析注解属性
-// annotations.add(parseAnnotation(ann));
-// } else if (annotationName.equals(MetaObjectCopy.List.class.getName())) {
-// // 处理多个注解
-// for (JCTree.JCExpression expr : ann.args) {
-// if (expr instanceof JCTree.JCAnnotation) {
-// annotations.add(parseAnnotation((JCTree.JCAnnotation) expr));
-// }
-// }
-// }
-// }
-// }
-// return annotations;
-// }
-//
-// private MetaObjectCopy parseAnnotation(JCTree.JCAnnotation ann) {
-// // 简化处理 - 实际应解析所有属性
-// return new MetaObjectCopy() {
-// @Override
-// public boolean copyNulls() {
-// return false; // 简化实现
-// }
-//
-// @Override
-// public String[] ignoreFields() {
-// return new String[0]; // 简化实现
-// }
-//
-// @Override
-// public FieldMapping[] fieldMappings() {
-// return new FieldMapping[0]; // 简化实现
-// }
-//
-// @Override
-// public Class extends java.lang.annotation.Annotation> annotationType() {
-// return MetaObjectCopy.class;
-// }
-// };
-// }
-//
-// private JCTree.JCBlock createMethodBody(
-// JCTree.JCVariableDecl sourceParam,
-// JCTree.JCVariableDecl targetParam,
-// boolean copyNulls,
-// Set ignoreFields,
-// Map fieldMappings
-// ) {
-// List statements = com.sun.tools.javac.util.List.nil();
-//
-// // 1. 空值检查
-// statements = statements.append(createNullCheck(sourceParam, targetParam));
-// // 2. 获取源和目标类型
-// Symbol.ClassSymbol sourceType = (Symbol.ClassSymbol) sourceParam.vartype.type.tsym;
-// Symbol.ClassSymbol targetType = (Symbol.ClassSymbol) targetParam.vartype.type.tsym;
-// // 3. 生成字段拷贝语句
-// statements = statements.appendList(createFieldCopyStatements(
-// sourceParam,
-// targetParam,
-// sourceType,
-// targetType,
-// copyNulls,
-// ignoreFields,
-// fieldMappings
-// ));
-// return maker.Block(0, statements);
-// }
-//
-// private JCTree.JCStatement createNullCheck(JCTree.JCVariableDecl source, JCTree.JCVariableDecl target) {
-// // if (source == null || target == null) return;
-// return maker.If(
-// maker.Binary(
-// JCTree.Tag.OR,
-// maker.Binary(
-// JCTree.Tag.EQ,
-// maker.Ident(source.name),
-// maker.Literal(null)
-// ),
-// maker.Binary(
-// JCTree.Tag.EQ,
-// maker.Ident(target.name),
-// maker.Literal(null)
-// )
-// ),
-// maker.Block(0, List.of(maker.Return(null))),
-// null
-// );
-// }
-//
-// private List createFieldCopyStatements(
-// JCTree.JCVariableDecl sourceParam,
-// JCTree.JCVariableDecl targetParam,
-// Symbol.ClassSymbol sourceType,
-// Symbol.ClassSymbol targetType,
-// boolean copyNulls,
-// Set ignoreFields,
-// Map fieldMappings) {
-// List statements = com.sun.tools.javac.util.List.nil();
-// // 获取目标字段列表
-// Map targetFields = getAccessibleFields(targetType);
-// for (Map.Entry entry : targetFields.entrySet()) {
-// String targetFieldName = entry.getKey();
-// Symbol.VarSymbol targetField = entry.getValue();
-// // 检查是否忽略该字段
-// if (ignoreFields.contains(targetFieldName)) {
-// continue;
-// }
-// // 获取源字段名(考虑自定义映射)
-// String sourceFieldName = fieldMappings.getOrDefault(targetFieldName, targetFieldName);
-// // 获取源字段
-// Symbol.VarSymbol sourceField = getField(sourceType, sourceFieldName);
-// if (sourceField == null) {
-// continue; // 源对象没有该字段
-// }
-// // 生成字段拷贝语句
-// JCTree.JCStatement copyStmt = createSingleFieldCopy(
-// sourceParam,
-// targetParam,
-// sourceField,
-// targetField,
-// copyNulls
-// );
-// if (copyStmt != null) {
-// statements = statements.append(copyStmt);
-// }
-// }
-// return statements;
-// }
-//
-// private Map getAccessibleFields(Symbol.ClassSymbol clazz) {
-// Map fields = new HashMap<>();
-// // 获取所有字段(包括继承的)
-// for (Symbol member : elements.getAllMembers(clazz)) {
-// if (member.getKind().equals(ElementKind.FIELD)){
-// Symbol.VarSymbol field = (Symbol.VarSymbol) member;
-// fields.put(field.getSimpleName().toString(), field);
-// }
-// }
-// return fields;
-// }
-//
-// private Symbol.VarSymbol getField(Symbol.ClassSymbol clazz, String fieldName) {
-// for (Symbol member : elements.getAllMembers(clazz)) {
-// if (member.getKind().equals(ElementKind.FIELD) &&
-// member.getSimpleName().toString().equals(fieldName)) {
-// return (Symbol.VarSymbol) member;
-// }
-// }
-// return null;
-// }
-//
-// private JCTree.JCStatement createSingleFieldCopy(
-// JCTree.JCVariableDecl sourceParam,
-// JCTree.JCVariableDecl targetParam,
-// Symbol.VarSymbol sourceField,
-// Symbol.VarSymbol targetField,
-// boolean copyNulls) {
-// try {
-// // 1. 构建getter调用
-// String getterName = getAccessorName(sourceField.getSimpleName().toString(), "get");
-// JCTree.JCExpression getterCall = maker.Apply(
-// com.sun.tools.javac.util.List.nil(),
-// maker.Select(maker.Ident(sourceParam.name), names.fromString(getterName)),
-// com.sun.tools.javac.util.List.nil()
-// );
-// // 2. 构建setter调用
-// String setterName = getAccessorName(targetField.getSimpleName().toString(), "set");
-// JCTree.JCExpression setterCall = maker.Apply(
-// com.sun.tools.javac.util.List.nil(),
-// maker.Select(maker.Ident(targetParam.name), names.fromString(setterName)),
-// List.of(getterCall)
-// );
-// // 3. 基本类型直接赋值
-// if (targetField.type.isPrimitive()) {
-// return maker.Exec(setterCall);
-// }
-// // 4. 非基本类型处理
-// if (copyNulls) {
-// // 允许空值 - 直接赋值
-// return maker.Exec(setterCall);
-// } else {
-// // 不允许空值 - 添加空值检查
-// JCTree.JCExpression notNullCheck = maker.Binary(
-// JCTree.Tag.NE,
-// getterCall,
-// maker.Literal(null)
-// );
-// return maker.If(
-// notNullCheck,
-// maker.Exec(setterCall),
-// null
-// );
-// }
-// } catch (Exception e) {
-// // getter/setter不存在
-// return null;
-// }
-// }
-//
-// private String getAccessorName(String fieldName, String prefix) {
-// return prefix + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
-// }
-//}
-//
diff --git a/autil-incubator/incubator-annotation/src/main/java/com/acanx/util/incubator/annotation/App.java b/autil-incubator/incubator-annotation/src/main/java/com/acanx/util/incubator/annotation/App.java
deleted file mode 100644
index 3586920..0000000
--- a/autil-incubator/incubator-annotation/src/main/java/com/acanx/util/incubator/annotation/App.java
+++ /dev/null
@@ -1,12 +0,0 @@
-package com.acanx.util.incubator.annotation;
-
-/**
- *
- * App
- *
- */
-public class App {
- public static void main(String[] args) {
- System.out.println("Hello World!");
- }
-}
diff --git a/autil-incubator/incubator-annotation/src/main/java/com/acanx/util/incubator/annotation/ObjectCopier.java b/autil-incubator/incubator-annotation/src/main/java/com/acanx/util/incubator/annotation/Copier.java
similarity index 97%
rename from autil-incubator/incubator-annotation/src/main/java/com/acanx/util/incubator/annotation/ObjectCopier.java
rename to autil-incubator/incubator-annotation/src/main/java/com/acanx/util/incubator/annotation/Copier.java
index 114dfba..6f5b1f1 100644
--- a/autil-incubator/incubator-annotation/src/main/java/com/acanx/util/incubator/annotation/ObjectCopier.java
+++ b/autil-incubator/incubator-annotation/src/main/java/com/acanx/util/incubator/annotation/Copier.java
@@ -11,7 +11,7 @@
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
-public @interface ObjectCopier {
+public @interface Copier {
/**
* 拷贝策略,默认为浅拷贝
diff --git a/autil-incubator/incubator-annotation/src/main/java/com/acanx/util/incubator/annotation/ann/ObjectCopierProcessor.java b/autil-incubator/incubator-annotation/src/main/java/com/acanx/util/incubator/annotation/CopierProcessor.java
similarity index 81%
rename from autil-incubator/incubator-annotation/src/main/java/com/acanx/util/incubator/annotation/ann/ObjectCopierProcessor.java
rename to autil-incubator/incubator-annotation/src/main/java/com/acanx/util/incubator/annotation/CopierProcessor.java
index f235677..91fc03a 100644
--- a/autil-incubator/incubator-annotation/src/main/java/com/acanx/util/incubator/annotation/ann/ObjectCopierProcessor.java
+++ b/autil-incubator/incubator-annotation/src/main/java/com/acanx/util/incubator/annotation/CopierProcessor.java
@@ -1,6 +1,5 @@
-package com.acanx.util.incubator.annotation.ann;
+package com.acanx.util.incubator.annotation;
-import com.acanx.util.incubator.annotation.ObjectCopier;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.Filer;
import javax.annotation.processing.RoundEnvironment;
@@ -29,9 +28,9 @@
import java.util.Map;
import java.util.Set;
-@SupportedAnnotationTypes("com.acanx.util.incubator.annotation.ObjectCopier")
+@SupportedAnnotationTypes("com.acanx.util.incubator.annotation.Copier")
@SupportedSourceVersion(SourceVersion.RELEASE_17)
-public class ObjectCopierProcessor extends AbstractProcessor {
+public class CopierProcessor extends AbstractProcessor {
private Types typeUtils;
private Elements elementUtils;
@@ -53,8 +52,9 @@ public boolean process(Set extends TypeElement> annotations, RoundEnvironment
note("开始处理 @ObjectCopier 注解", null);
+ // 按被注解方法所在的类进行分组
Map> classMethodsMap = new HashMap<>();
- for (Element element : roundEnv.getElementsAnnotatedWith(ObjectCopier.class)) {
+ for (Element element : roundEnv.getElementsAnnotatedWith(Copier.class)) {
if (element.getKind() != ElementKind.METHOD) {
error("注解@ObjectCopier只能应用于方法", element, getAnnotationMirror(element));
continue;
@@ -66,15 +66,14 @@ public boolean process(Set extends TypeElement> annotations, RoundEnvironment
note("找到被注解的方法: " + enclosingClass.getSimpleName() + "." + method.getSimpleName(), method);
}
+ // 为每个包含被注解方法的类生成一个拷贝类
for (Map.Entry> entry : classMethodsMap.entrySet()) {
TypeElement enclosingClass = entry.getKey();
List methods = entry.getValue();
- for (ExecutableElement method : methods) {
- try {
- generateCopierClass(method);
- } catch (Exception e) {
- error("生成拷贝类时出错: " + e.getMessage(), method, getAnnotationMirror(method));
- }
+ try {
+ generateCopierClass(enclosingClass, methods);
+ } catch (Exception e) {
+ error("生成拷贝类时出错: " + e.getMessage(), enclosingClass, null);
}
}
@@ -82,25 +81,19 @@ public boolean process(Set extends TypeElement> annotations, RoundEnvironment
return true;
}
- private void generateCopierClass(ExecutableElement methodElement) throws IOException {
- if (!validateMethod(methodElement)) {
- return;
+ private void generateCopierClass(TypeElement enclosingClass, List methods) throws IOException {
+ // 验证所有方法
+ List validMethods = new ArrayList<>();
+ for (ExecutableElement method : methods) {
+ if (validateMethod(method)) {
+ validMethods.add(method);
+ }
}
- VariableElement sourceParam = methodElement.getParameters().get(0);
- VariableElement targetParam = methodElement.getParameters().get(1);
- TypeMirror sourceType = sourceParam.asType();
- TypeMirror targetType = targetParam.asType();
-
- if (!(sourceType instanceof DeclaredType) || !(targetType instanceof DeclaredType)) {
- error("源类型和目标类型必须是类类型", methodElement, getAnnotationMirror(methodElement));
+ if (validMethods.isEmpty()) {
return;
}
- TypeElement sourceElement = (TypeElement) ((DeclaredType) sourceType).asElement();
- TypeElement targetElement = (TypeElement) ((DeclaredType) targetType).asElement();
- TypeElement enclosingClass = (TypeElement) methodElement.getEnclosingElement();
-
// 生成类名:使用被注解方法所在类的类名 + "Copier"
String enclosingClassName = enclosingClass.getSimpleName().toString();
String copierClassName = enclosingClassName + "Copier";
@@ -111,59 +104,108 @@ private void generateCopierClass(ExecutableElement methodElement) throws IOExcep
// 创建源文件
JavaFileObject builderFile = filer.createSourceFile(
packageName + "." + copierClassName,
- methodElement
+ enclosingClass
);
- note("生成拷贝类: " + packageName + "." + copierClassName, methodElement);
+ note("生成拷贝类: " + packageName + "." + copierClassName + ",包含 " + validMethods.size() + " 个方法", enclosingClass);
try (PrintWriter out = new PrintWriter(builderFile.openWriter())) {
- writeCopierClass(out, packageName, copierClassName, sourceElement, targetElement, methodElement, enclosingClass);
+ writeCopierClass(out, packageName, copierClassName, validMethods, enclosingClass);
}
}
private void writeCopierClass(PrintWriter out, String packageName, String className,
- TypeElement sourceElement, TypeElement targetElement,
- ExecutableElement methodElement, TypeElement enclosingClass) {
+ List methods, TypeElement enclosingClass) {
- String sourceTypeName = sourceElement.getSimpleName().toString();
- String targetTypeName = targetElement.getSimpleName().toString();
- String sourceQualifiedName = sourceElement.getQualifiedName().toString();
- String targetQualifiedName = targetElement.getQualifiedName().toString();
String enclosingClassQualifiedName = enclosingClass.getQualifiedName().toString();
- String methodName = methodElement.getSimpleName().toString();
// 包声明
out.println("package " + packageName + ";");
out.println();
- // 导入语句
- out.println("import " + sourceQualifiedName + ";");
- out.println("import " + targetQualifiedName + ";");
+ // 收集所有需要导入的类型
+ Set imports = collectImports(methods);
+ for (String importLine : imports) {
+ out.println("import " + importLine + ";");
+ }
out.println();
// 类注释
out.println("/**");
out.println(" * 自动生成的拷贝类");
- out.println(" * 将 " + sourceTypeName + " 对象的属性拷贝到 " + targetTypeName + " 对象");
- out.println(" * 由 {@link " + enclosingClassQualifiedName + "#" + methodName + "(" + sourceTypeName + ", " + targetTypeName + ")} 方法生成");
+ out.println(" * 包含 " + methods.size() + " 个拷贝方法");
+ out.println(" * 由 {@link " + enclosingClassQualifiedName + "} 中的方法生成");
out.println(" */");
// 类声明
out.println("public class " + className + " {");
out.println();
- // 拷贝方法
+ // 为每个方法生成拷贝方法
+ for (ExecutableElement method : methods) {
+ writeCopierMethod(out, method, enclosingClass);
+ out.println();
+ }
+
+ out.println("}");
+ }
+
+ private Set collectImports(List methods) {
+ Set imports = new java.util.HashSet<>();
+ for (ExecutableElement method : methods) {
+ List extends VariableElement> parameters = method.getParameters();
+ if (parameters.size() >= 2) {
+ TypeMirror sourceType = parameters.get(0).asType();
+ TypeMirror targetType = parameters.get(1).asType();
+
+ if (sourceType instanceof DeclaredType) {
+ TypeElement sourceElement = (TypeElement) ((DeclaredType) sourceType).asElement();
+ imports.add(sourceElement.getQualifiedName().toString());
+ }
+
+ if (targetType instanceof DeclaredType) {
+ TypeElement targetElement = (TypeElement) ((DeclaredType) targetType).asElement();
+ imports.add(targetElement.getQualifiedName().toString());
+ }
+ }
+ }
+ return imports;
+ }
+
+ private void writeCopierMethod(PrintWriter out, ExecutableElement methodElement, TypeElement enclosingClass) {
+ VariableElement sourceParam = methodElement.getParameters().get(0);
+ VariableElement targetParam = methodElement.getParameters().get(1);
+ TypeMirror sourceType = sourceParam.asType();
+ TypeMirror targetType = targetParam.asType();
+
+ if (!(sourceType instanceof DeclaredType) || !(targetType instanceof DeclaredType)) {
+ return;
+ }
+
+ TypeElement sourceElement = (TypeElement) ((DeclaredType) sourceType).asElement();
+ TypeElement targetElement = (TypeElement) ((DeclaredType) targetType).asElement();
+
+ String sourceTypeName = sourceElement.getSimpleName().toString();
+ String targetTypeName = targetElement.getSimpleName().toString();
+ String enclosingClassQualifiedName = enclosingClass.getQualifiedName().toString();
+ String methodName = methodElement.getSimpleName().toString();
+
+ // 方法注释
out.println(" /**");
out.println(" * 执行属性拷贝");
+ out.println(" * 将 " + sourceTypeName + " 对象的属性拷贝到 " + targetTypeName + " 对象");
out.println(" * 由 {@link " + enclosingClassQualifiedName + "#" + methodName + "(" + sourceTypeName + ", " + targetTypeName + ")} 方法生成");
out.println(" * @param source 源对象");
out.println(" * @param target 目标对象");
out.println(" */");
+
+ // 方法声明
out.println(" public static void " + methodName + "(" + sourceTypeName + " source, " + targetTypeName + " target) {");
out.println(" if (source == null || target == null) {");
out.println(" return;");
out.println(" }");
out.println();
+
// 生成属性拷贝代码
List mappings = findPropertyMappings(sourceElement, targetElement);
if (mappings.isEmpty()) {
@@ -175,7 +217,6 @@ private void writeCopierClass(PrintWriter out, String packageName, String classN
}
}
out.println(" }");
- out.println("}");
}
private List findPropertyMappings(TypeElement sourceElement, TypeElement targetElement) {
@@ -183,18 +224,13 @@ private List findPropertyMappings(TypeElement sourceElement, Ty
List sourceGetters = findGetterMethods(sourceElement);
List targetSetters = findSetterMethods(targetElement);
- note("在 " + sourceElement.getSimpleName() + " 中找到 " + sourceGetters.size() + " 个getter方法", sourceElement);
- note("在 " + targetElement.getSimpleName() + " 中找到 " + targetSetters.size() + " 个setter方法", targetElement);
-
for (GetterMethod getter : sourceGetters) {
SetterMethod setter = findMatchingSetter(targetSetters, getter.propertyName, getter.returnType);
if (setter != null) {
mappings.add(new PropertyMapping(getter.propertyName, getter.methodCall, setter.methodCall));
- note("属性映射: " + getter.propertyName + " (" + getSimpleTypeName(getter.returnType) + ")", sourceElement);
}
}
- note("总共建立 " + mappings.size() + " 个属性映射", sourceElement);
return mappings;
}
@@ -334,7 +370,7 @@ private void note(String msg, Element element) {
private AnnotationMirror getAnnotationMirror(Element element) {
for (AnnotationMirror mirror : element.getAnnotationMirrors()) {
- if (mirror.getAnnotationType().toString().equals(ObjectCopier.class.getName())) {
+ if (mirror.getAnnotationType().toString().equals(Copier.class.getName())) {
return mirror;
}
}
diff --git a/autil-incubator/incubator-annotation/src/main/java/com/acanx/util/incubator/annotation/MetaObjectCopyPlugin.java b/autil-incubator/incubator-annotation/src/main/java/com/acanx/util/incubator/annotation/MetaObjectCopyPlugin.java
deleted file mode 100644
index 7b6d097..0000000
--- a/autil-incubator/incubator-annotation/src/main/java/com/acanx/util/incubator/annotation/MetaObjectCopyPlugin.java
+++ /dev/null
@@ -1,93 +0,0 @@
-package com.acanx.util.incubator.annotation;//package com.acanx.util.object.copy;
-//
-//import com.sun.source.util.JavacTask;
-//import com.sun.source.util.Plugin;
-//import com.sun.source.util.TaskEvent;
-//import com.sun.source.util.TaskListener;
-//
-//import javax.annotation.processing.SupportedAnnotationTypes;
-//import javax.annotation.processing.SupportedSourceVersion;
-//import javax.lang.model.SourceVersion;
-//import java.io.InputStream;
-//import java.nio.charset.StandardCharsets;
-//
-//import com.sun.tools.javac.api.BasicJavacTask;
-//import com.sun.tools.javac.model.JavacElements;
-//import com.sun.tools.javac.tree.JCTree;
-//import com.sun.tools.javac.tree.TreeMaker;
-//import com.sun.tools.javac.util.Context;
-//import com.sun.tools.javac.util.Names;
-//
-//
-///**
-// * MetaObjectCopyPlugin
-// *
-// * @author ACANX
-// * @since 202506
-// */
-//
-//@SupportedAnnotationTypes("MetaObjectCopy")
-//@SupportedSourceVersion(SourceVersion.RELEASE_11)
-//public class MetaObjectCopyPlugin implements Plugin {
-//
-// static {
-// System.out.println("=== 插件初始化开始 ===");
-// String serviceFile = "META-INF/services/com.sun.source.util.Plugin";
-// try (InputStream is = MetaObjectCopyPlugin.class.getClassLoader()
-// .getResourceAsStream(serviceFile)) {
-// if (is == null) {
-// System.err.println("❌ 服务文件未找到: " + serviceFile);
-// } else {
-// String content = new String(is.readAllBytes(), StandardCharsets.UTF_8);
-// System.out.println("✅ 服务文件内容: " + content);
-//
-// if (!content.contains("com.acanx.util.object.copy.MetaObjectCopyPlugin")) {
-// System.err.println("❌ 服务文件不包含本插件类名");
-// }
-// }
-// } catch (Exception e) {
-// System.err.println("❌ 读取服务文件失败: " + e.getMessage());
-// }
-// System.out.println("=== 插件初始化结束 ===");
-// }
-//
-//
-//
-// @Override
-// public String getName() {
-// return "MetaObjectCopy";
-// }
-//
-// @Override
-// public void init(JavacTask task, String... args) {
-// // JDK 版本检测
-// String javaVersion = System.getProperty("java.version");
-// Context context = ((BasicJavacTask) task).getContext();
-// JavacElements elements = JavacElements.instance(context);
-// TreeMaker maker = TreeMaker.instance(context);
-// Names names = Names.instance(context);
-//
-// task.addTaskListener(new TaskListener() {
-// @Override
-// public void started(TaskEvent e) {}
-//
-// @Override
-// public void finished(TaskEvent e) {
-// if (e.getKind() == TaskEvent.Kind.ANALYZE) {
-// // JDK 11 兼容的扫描方式
-// JCTree.JCCompilationUnit compUnit = (JCTree.JCCompilationUnit) e.getCompilationUnit();
-//
-// if (javaVersion.startsWith("1.8")) {
-// // JDK 8 兼容方式 (使用已添加的 scan(JCTree, Void) 方法)
-//// new ASTScanner(maker, elements, names).scan(compUnit, null);
-// } else {
-// // JDK 11+ 标准方式
-//// new ASTScanner(maker, elements, names).scan(compUnit);
-// }
-// }
-// }
-// });
-// }
-//
-//
-//}
diff --git a/autil-incubator/incubator-annotation/src/main/java/com/acanx/util/incubator/annotation/MetaObjectCopyProcessor.java b/autil-incubator/incubator-annotation/src/main/java/com/acanx/util/incubator/annotation/MetaObjectCopyProcessor.java
deleted file mode 100644
index 3be80b1..0000000
--- a/autil-incubator/incubator-annotation/src/main/java/com/acanx/util/incubator/annotation/MetaObjectCopyProcessor.java
+++ /dev/null
@@ -1,334 +0,0 @@
-//package com.acanx.util.incubator.annotation;
-//
-//
-//import com.acanx.annotation.ObjectCopy;
-//import com.google.auto.service.AutoService;
-//import org.objectweb.asm.ClassReader;
-//import org.objectweb.asm.ClassVisitor;
-//import org.objectweb.asm.ClassWriter;
-//import org.objectweb.asm.Label;
-//import org.objectweb.asm.MethodVisitor;
-//import org.objectweb.asm.Opcodes;
-//import org.objectweb.asm.Type;
-//
-//import javax.annotation.processing.AbstractProcessor;
-//import javax.annotation.processing.Filer;
-//import javax.annotation.processing.Processor;
-//import javax.annotation.processing.RoundEnvironment;
-//import javax.annotation.processing.SupportedAnnotationTypes;
-//import javax.annotation.processing.SupportedSourceVersion;
-//import javax.lang.model.SourceVersion;
-//import javax.lang.model.element.Element;
-//import javax.lang.model.element.ElementKind;
-//import javax.lang.model.element.ExecutableElement;
-//import javax.lang.model.element.TypeElement;
-//import javax.lang.model.type.ArrayType;
-//import javax.lang.model.type.DeclaredType;
-//import javax.lang.model.type.TypeMirror;
-//import javax.tools.Diagnostic;
-//import javax.tools.FileObject;
-//import javax.tools.StandardLocation;
-//import java.io.IOException;
-//import java.io.InputStream;
-//import java.io.PrintWriter;
-//import java.io.StringWriter;
-//import java.net.URL;
-//import java.nio.file.Files;
-//import java.nio.file.Path;
-//import java.nio.file.Paths;
-//import java.nio.file.StandardOpenOption;
-//import java.util.Arrays;
-//import java.util.HashMap;
-//import java.util.Map;
-//import java.util.Set;
-//
-///**
-// * ObjectValueCopyProcessor
-// *
-// * @author ACANX
-// * @since 202506
-// */
-//@SupportedAnnotationTypes("com.acanx.annotation.ObjectCopy")
-//@SupportedSourceVersion(SourceVersion.RELEASE_11)
-//@AutoService(Processor.class)
-//public class MetaObjectCopyProcessor extends AbstractProcessor {
-//
-// @Override
-// public boolean process(Set extends TypeElement> annotations, RoundEnvironment roundEnv) {
-// for (Element element : roundEnv.getElementsAnnotatedWith(ObjectCopy.class)) {
-// if (element.getKind() == ElementKind.METHOD) {
-// ExecutableElement method = (ExecutableElement) element;
-// processMethod(method);
-// }
-// }
-// return true;
-// }
-//
-// private void processMethod(ExecutableElement method) {
-// try {
-// // 获取类名和方法信息
-// TypeElement classElement = (TypeElement) method.getEnclosingElement();
-// String className = classElement.getQualifiedName().toString();
-// String methodName = method.getSimpleName().toString();
-//
-// // 正确获取方法参数类型
-// Type originAsmType = getAsmType(method.getParameters().get(0).asType());
-// Type targetAsmType = getAsmType(method.getParameters().get(1).asType());
-//
-// // 正确获取方法描述符
-// String methodDesc = Type.getMethodDescriptor(Type.VOID_TYPE, originAsmType, targetAsmType);
-//
-// // 获取注解配置
-// ObjectCopy annotation = method.getAnnotation(ObjectCopy.class);
-// boolean copyNulls = annotation.copyNulls();
-// String[] ignoreFields = annotation.ignoreFields();
-// ObjectCopy.FieldMapping[] fieldMappings = annotation.fieldMappings();
-//
-// // 读取原始类字节码
-// String binaryName = processingEnv.getElementUtils().getBinaryName(classElement).toString();
-// String relativePath = binaryName.replace('.', '/')+ ".class";
-// processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE, "RelativePath:"+relativePath);
-// processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE, "Class:" + getClass().getName());
-//
-//
-// // 4. 使用 Filer 获取资源
-// Filer filer = processingEnv.getFiler();
-// FileObject fileObject = filer.getResource(
-// StandardLocation.CLASS_OUTPUT, "", relativePath);
-//
-// processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE, "Path:" + fileObject.toUri().toURL());
-// InputStream input = fileObject.openInputStream();
-// if (input == null) {
-// processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "Class file not found: " + binaryName);
-// return;
-// } else {
-//
-// }
-// ClassReader cr = new ClassReader(input);
-// ClassWriter cw = new ClassWriter(cr, ClassWriter.COMPUTE_MAXS);
-//
-// // 创建ClassVisitor修改方法
-// cr.accept(new ClassVisitor(Opcodes.ASM7, cw) {
-// @Override
-// public MethodVisitor visitMethod(int access, String name, String desc,
-// String signature, String[] exceptions) {
-// if (name.equals(methodName) && desc.equals(methodDesc)) {
-// return new MethodVisitor(Opcodes.ASM7,
-// super.visitMethod(access, name, desc, signature, exceptions)) {
-//
-// @Override
-// public void visitCode() {
-// // 完全替换方法体
-// generateNewMethodBody(this, method, annotation);
-// super.visitMaxs(0, 0); // 自动计算max stack/locals
-// }
-//
-// @Override
-// public void visitEnd() {
-// // 不调用super,避免原始方法体被保留
-// }
-// };
-// }
-// return super.visitMethod(access, name, desc, signature, exceptions);
-// }
-// }, ClassReader.EXPAND_FRAMES);
-//
-// // 将修改后的字节码写回文件系统
-// writeModifiedClass(className, cw.toByteArray());
-//
-// } catch (Exception e) {
-// StringWriter sw = new StringWriter();
-// e.printStackTrace(new PrintWriter(sw));
-// processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR,
-// "Failed to process @MetaObjectCopy: " + e.getMessage() + "\n" + sw.toString());
-// }
-// }
-//
-// /**
-// * 根据完整类名获取 .class 文件路径
-// * @param className 完整类名(如 com.example.MyClass)
-// * @return .class 文件路径,如果找不到返回 null
-// */
-// public static String findClassFilePath(String className) {
-// try {
-// // 将类名转换为资源路径
-// String resourcePath = className.replace('.', '/') + ".class";
-// // 获取系统类加载器
-// ClassLoader classLoader = ClassLoader.getSystemClassLoader();
-// // 获取资源 URL
-// URL url = classLoader.getResource(resourcePath);
-// if (url != null) {
-// // 处理特殊字符和编码
-// String decodedPath = java.net.URLDecoder.decode(url.getPath(), "UTF-8");
-// // 处理 JAR 文件路径
-// if (decodedPath.contains("!")) {
-// return decodedPath.substring(0, decodedPath.indexOf("!"));
-// }
-// return decodedPath;
-// }
-// return null;
-// } catch (Exception e) {
-// e.printStackTrace();
-// return null;
-// }
-// }
-//
-// private void generateNewMethodBody(MethodVisitor mv, ExecutableElement method, ObjectCopy annotation) {
-// // 获取方法参数类型
-// TypeMirror originType = method.getParameters().get(0).asType();
-// TypeMirror targetType = method.getParameters().get(1).asType();
-//
-// boolean copyNulls = annotation.copyNulls();
-// String[] ignoreFields = annotation.ignoreFields();
-// ObjectCopy.FieldMapping[] fieldMappings = annotation.fieldMappings();
-//
-// // === 开始生成新方法体 ===
-// Label startLabel = new Label();
-// Label endLabel = new Label();
-// Label returnLabel = new Label();
-//
-// mv.visitLabel(startLabel);
-//
-// // 空值检查:if (origin == null || target == null) return;
-// mv.visitVarInsn(Opcodes.ALOAD, 1); // 加载origin
-// mv.visitJumpInsn(Opcodes.IFNULL, returnLabel);
-// mv.visitVarInsn(Opcodes.ALOAD, 2); // 加载target
-// mv.visitJumpInsn(Opcodes.IFNULL, returnLabel);
-//
-// // 创建字段映射表
-// Map fieldMap = new HashMap<>();
-// for (ObjectCopy.FieldMapping mapping : fieldMappings) {
-// fieldMap.put(mapping.target(), mapping.source());
-// }
-//
-// // 获取目标类字段
-// TypeElement targetElement = (TypeElement) ((DeclaredType) targetType).asElement();
-// for (Element field : targetElement.getEnclosedElements()) {
-// if (field.getKind() != ElementKind.FIELD) continue;
-//
-// String targetField = field.getSimpleName().toString();
-//
-// // 检查忽略字段
-// if (Arrays.asList(ignoreFields).contains(targetField)) continue;
-//
-// // 获取源字段名
-// String sourceField = fieldMap.getOrDefault(targetField, targetField);
-//
-// // 生成字段拷贝逻辑
-// generateFieldCopy(mv, originType, targetType, sourceField, targetField,
-// field.asType(), copyNulls);
-// }
-//
-// mv.visitJumpInsn(Opcodes.GOTO, returnLabel);
-// mv.visitLabel(endLabel);
-//
-// // 异常处理块(空实现)
-// mv.visitLabel(returnLabel);
-// mv.visitInsn(Opcodes.RETURN);
-//
-// // 局部变量表(调试信息)
-// mv.visitLocalVariable("origin", Type.getDescriptor(Object.class), null, startLabel, endLabel, 1);
-// mv.visitLocalVariable("target", Type.getDescriptor(Object.class), null, startLabel, endLabel, 2);
-// mv.visitMaxs(3, 3); // 自动计算
-// }
-//
-// private void generateFieldCopy(MethodVisitor mv, TypeMirror originType, TypeMirror targetType,
-// String sourceField, String targetField, TypeMirror fieldType,
-// boolean copyNulls) {
-// String getter = "get" + capitalize(sourceField);
-// String setter = "set" + capitalize(targetField);
-//
-// // 使用新的类型转换方法
-// Type asmFieldType = getAsmType(fieldType);
-// String fieldDesc = asmFieldType.getDescriptor();
-//
-// // 获取内部类名
-// String originInternal = getAsmType(originType).getInternalName();
-// String targetInternal = getAsmType(targetType).getInternalName();
-//
-//
-// Label afterSet = new Label();
-//
-// // 加载target对象
-// mv.visitVarInsn(Opcodes.ALOAD, 2);
-//
-// // 加载origin对象
-// mv.visitVarInsn(Opcodes.ALOAD, 1);
-//
-// // 调用origin.getter()
-// mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, originInternal, getter, "()" + fieldDesc, false);
-//
-// // 空值检查
-// if (!copyNulls) {
-// mv.visitInsn(Opcodes.DUP); // 复制返回值
-// mv.visitJumpInsn(Opcodes.IFNULL, afterSet); // 如果为null则跳过setter
-// }
-//
-// // 调用target.setter()
-// mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, targetInternal, setter, "(" + fieldDesc + ")V", false);
-//
-// if (!copyNulls) {
-// mv.visitLabel(afterSet);
-// mv.visitInsn(Opcodes.POP); // 弹出多余的null值
-// }
-// }
-//
-// // 工具方法保持不变
-// private String capitalize(String s) {
-// return s.substring(0, 1).toUpperCase() + s.substring(1);
-// }
-//
-// private String getInternalName(TypeMirror type) {
-// return getAsmType(type).getInternalName();
-// }
-//
-// private String getTypeDescriptor(TypeMirror type) {
-// switch (type.getKind()) {
-// case BOOLEAN: return "Z";
-// case BYTE: return "B";
-// case CHAR: return "C";
-// case SHORT: return "S";
-// case INT: return "I";
-// case LONG: return "J";
-// case FLOAT: return "F";
-// case DOUBLE: return "D";
-// case VOID: return "V";
-// default: return "L" + getInternalName(type) + ";";
-// }
-// }
-//
-// private void writeModifiedClass(String className, byte[] bytecode) throws IOException {
-// String outputPath = processingEnv.getOptions().get("outputDirectory");
-// if (outputPath == null) {
-// outputPath = "out/production/classes"; // 默认输出目录
-// }
-//
-// Path classFile = Paths.get(outputPath, className + ".class");
-// Files.createDirectories(classFile.getParent());
-// Files.write(classFile, bytecode, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);
-// }
-//
-//
-// private Type getAsmType(TypeMirror typeMirror) {
-// switch (typeMirror.getKind()) {
-// case BOOLEAN: return Type.BOOLEAN_TYPE;
-// case BYTE: return Type.BYTE_TYPE;
-// case CHAR: return Type.CHAR_TYPE;
-// case SHORT: return Type.SHORT_TYPE;
-// case INT: return Type.INT_TYPE;
-// case LONG: return Type.LONG_TYPE;
-// case FLOAT: return Type.FLOAT_TYPE;
-// case DOUBLE: return Type.DOUBLE_TYPE;
-// case VOID: return Type.VOID_TYPE;
-// case ARRAY:
-// ArrayType arrayType = (ArrayType) typeMirror;
-// return Type.getType("[" + getAsmType(arrayType.getComponentType()).getDescriptor());
-// case DECLARED:
-// DeclaredType declaredType = (DeclaredType) typeMirror;
-// TypeElement typeElement = (TypeElement) declaredType.asElement();
-// return Type.getObjectType(typeElement.getQualifiedName().toString().replace('.', '/'));
-// default:
-// return Type.getType(Object.class);
-// }
-// }
-//}
diff --git a/autil-incubator/incubator-annotation/src/main/java/com/acanx/util/incubator/annotation/ObjectCopyProcessor.java b/autil-incubator/incubator-annotation/src/main/java/com/acanx/util/incubator/annotation/ObjectCopyProcessor.java
index 0f600c8..f844dbb 100644
--- a/autil-incubator/incubator-annotation/src/main/java/com/acanx/util/incubator/annotation/ObjectCopyProcessor.java
+++ b/autil-incubator/incubator-annotation/src/main/java/com/acanx/util/incubator/annotation/ObjectCopyProcessor.java
@@ -44,7 +44,7 @@
// * @since 202506
// */
//@SupportedAnnotationTypes("com.acanx.annotation.ObjectCopy")
-//@SupportedSourceVersion(SourceVersion.RELEASE_25)
+//@SupportedSourceVersion(SourceVersion.RELEASE_17)
//@SupportedOptions({"incremental"}) // 支持增量编译
//public class ObjectCopyProcessor extends AbstractProcessor {
//
diff --git a/autil-incubator/incubator-annotation/src/main/java/com/acanx/util/incubator/annotation/PluginLoaderTest.java b/autil-incubator/incubator-annotation/src/main/java/com/acanx/util/incubator/annotation/PluginLoaderTest.java
deleted file mode 100644
index 76f601a..0000000
--- a/autil-incubator/incubator-annotation/src/main/java/com/acanx/util/incubator/annotation/PluginLoaderTest.java
+++ /dev/null
@@ -1,46 +0,0 @@
-package com.acanx.util.incubator.annotation;//package com.acanx.util.object.copy;
-//
-//import com.sun.source.util.Plugin;
-//
-//import java.util.ServiceLoader;
-//
-///**
-// * PluginLoaderTest
-// *
-// * @author ACANX
-// * @date 2025-06-14
-// * @since 202506
-// */
-//public class PluginLoaderTest {
-// public static void main(String[] args) {
-// System.out.println("=== 开始加载插件 ===");
-// System.out.println("类路径: " + System.getProperty("java.class.path"));
-//
-// ServiceLoader loader = ServiceLoader.load(Plugin.class);
-// int count = 0;
-//
-// for (Plugin plugin : loader) {
-// System.out.println("✅ 找到插件: " + plugin.getClass().getName());
-// System.out.println(" 插件名称: " + plugin.getName());
-// count++;
-// }
-//
-// if (count == 0) {
-// System.err.println("❌ 未找到任何插件");
-//
-// // 尝试直接加载类
-// try {
-// Class> clazz = Class.forName("com.acanx.util.object.copy.MetaObjectCopyPlugin");
-// System.out.println("ℹ️ 类存在: " + clazz);
-//
-// // 尝试实例化
-// Plugin plugin = (Plugin) clazz.getDeclaredConstructor().newInstance();
-// System.out.println("✅ 手动创建插件成功: " + plugin.getName());
-// } catch (ClassNotFoundException e) {
-// System.err.println("❌ 类未找到: com.acanx.util.object.copy.MetaObjectCopyPlugin");
-// } catch (Exception e) {
-// System.err.println("❌ 创建实例失败: " + e.getMessage());
-// }
-// }
-// }
-//}
diff --git a/autil-incubator/incubator-annotation/src/main/java/module-info.java b/autil-incubator/incubator-annotation/src/main/java/module-info.java
index cfb6849..e8f79f2 100644
--- a/autil-incubator/incubator-annotation/src/main/java/module-info.java
+++ b/autil-incubator/incubator-annotation/src/main/java/module-info.java
@@ -1,17 +1,17 @@
+import com.acanx.util.incubator.annotation.CopierProcessor;
+
module com.acanx.util.incubator.annotation {
requires java.compiler;
requires static jdk.compiler;
- requires model.test;
+// requires model.test;
// 导出注解处理器所在的包
exports com.acanx.util.incubator.annotation;
- exports com.acanx.util.incubator.annotation.ann;
// 关键修正:打开包到jdk.compiler模块 允许反射访问
- opens com.acanx.util.incubator.annotation.ann to jdk.compiler;
+ opens com.acanx.util.incubator.annotation to jdk.compiler;
- provides javax.annotation.processing.Processor
- with com.acanx.util.incubator.annotation.ann.ObjectCopierProcessor;
+ provides javax.annotation.processing.Processor with CopierProcessor;
}
\ No newline at end of file
diff --git a/autil-incubator/incubator-annotation/src/main/resources/META-INF/services/javax.annotation.processing.Processor b/autil-incubator/incubator-annotation/src/main/resources/META-INF/services/javax.annotation.processing.Processor
index 22f25e6..bb7a6e7 100644
--- a/autil-incubator/incubator-annotation/src/main/resources/META-INF/services/javax.annotation.processing.Processor
+++ b/autil-incubator/incubator-annotation/src/main/resources/META-INF/services/javax.annotation.processing.Processor
@@ -1,2 +1 @@
-#com.acanx.util.incubator.annotation.ObjectCopyProcessor
-com.acanx.util.incubator.annotation.ann.ObjectCopierProcessor
+com.acanx.util.incubator.annotation.CopierProcessor
diff --git a/autil-incubator/incubator-annotation/src/test/java/com/acanx/meta/model/CopyTest.java b/autil-incubator/incubator-annotation/src/test/java/com/acanx/meta/model/CopyTest.java
index a790dfd..04b77b0 100644
--- a/autil-incubator/incubator-annotation/src/test/java/com/acanx/meta/model/CopyTest.java
+++ b/autil-incubator/incubator-annotation/src/test/java/com/acanx/meta/model/CopyTest.java
@@ -1,42 +1,42 @@
-package com.acanx.meta.model;
-
-import com.acanx.meta.model.test.annotation.model.MessageFlex;
-import com.acanx.meta.model.test.annotation.model.MessageStable;
-import com.acanx.util.incubator.annotation.ObjectCopier;
-
-/**
- * CopyTest
- *
- * @author ACANX
- * @since 20251110
- */
-public class CopyTest {
-
-
-
- @ObjectCopier
- private void convert(MessageFlex flex, MessageStable stable) {};
-
-
-// @ObjectCopier(ignoreNull = true, exclude = {"internalId"})
-// private void convert1(MessageFlex flex, MessageStable stable) {
-// // 编译后这个方法会被增强为:
-// // if (flex == null) throw new IllegalArgumentException("Source object cannot be null");
-// // if (stable == null) throw new IllegalArgumentException("Target object cannot be null");
-// // stable.content = flex.content != null ? flex.content : stable.content;
-// // stable.priority = flex.priority != null ? flex.priority : stable.priority;
-// // stable.urgent = flex.urgent != null ? flex.urgent : stable.urgent;
-// // stable.tags = flex.tags != null ? flex.tags : stable.tags;
+//package com.acanx.meta.model;
+//
+//import com.acanx.meta.model.test.annotation.model.MessageFlex;
+//import com.acanx.meta.model.test.annotation.model.MessageStable;
+//import com.acanx.util.incubator.annotation.Copier;
+//
+///**
+// * CopyTest
+// *
+// * @author ACANX
+// * @since 20251110
+// */
+//public class CopyTest {
+//
+//
+//
+// @Copier
+// private void convert(MessageFlex flex, MessageStable stable) {};
+//
+//
+//// @ObjectCopier(ignoreNull = true, exclude = {"internalId"})
+//// private void convert1(MessageFlex flex, MessageStable stable) {
+//// // 编译后这个方法会被增强为:
+//// // if (flex == null) throw new IllegalArgumentException("Source object cannot be null");
+//// // if (stable == null) throw new IllegalArgumentException("Target object cannot be null");
+//// // stable.content = flex.content != null ? flex.content : stable.content;
+//// // stable.priority = flex.priority != null ? flex.priority : stable.priority;
+//// // stable.urgent = flex.urgent != null ? flex.urgent : stable.urgent;
+//// // stable.tags = flex.tags != null ? flex.tags : stable.tags;
+//// }
+//
+//// @ObjectCopier(strategy = ObjectCopier.CopyStrategy.DEEP)
+//// private void deepConvert(MessageFlex flex, MessageStable stable) {
+//// // 深拷贝版本的转换
+//// }
+//
+// public void processConversion(MessageFlex flex, MessageStable stable) {
+// convert(flex, stable);
+// // 其他业务逻辑
// }
-
-// @ObjectCopier(strategy = ObjectCopier.CopyStrategy.DEEP)
-// private void deepConvert(MessageFlex flex, MessageStable stable) {
-// // 深拷贝版本的转换
-// }
-
- public void processConversion(MessageFlex flex, MessageStable stable) {
- convert(flex, stable);
- // 其他业务逻辑
- }
-
-}
+//
+//}
diff --git a/autil-incubator/pom.xml b/autil-incubator/pom.xml
index a5e35d2..1a8cc27 100644
--- a/autil-incubator/pom.xml
+++ b/autil-incubator/pom.xml
@@ -16,9 +16,8 @@
https://github.com/ACANX/AUtil
- 25
+ 21
UTF-8
-
3.14.1
diff --git a/autil-test/pom.xml b/autil-test/pom.xml
index 762be52..f24e0dc 100644
--- a/autil-test/pom.xml
+++ b/autil-test/pom.xml
@@ -81,7 +81,7 @@
jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED
- com.acanx.util.incubator.annotation.ann.ObjectCopierProcessor
+ com.acanx.util.incubator.annotation.CopierProcessor
${project.build.directory}/generated-sources/annotations
diff --git a/autil-test/src/main/java/com/acanx/util/enums/Copy2.java b/autil-test/src/main/java/com/acanx/util/enums/Copy2.java
index 9b46a0e..e004f13 100644
--- a/autil-test/src/main/java/com/acanx/util/enums/Copy2.java
+++ b/autil-test/src/main/java/com/acanx/util/enums/Copy2.java
@@ -3,7 +3,7 @@
import com.acanx.meta.model.test.annotation.model.MessageFlex;
import com.acanx.meta.model.test.annotation.model.MessageStable;
-import com.acanx.util.incubator.annotation.ObjectCopier;
+import com.acanx.util.incubator.annotation.Copier;
/**
* CopyTest
@@ -15,11 +15,11 @@ public class Copy2 {
- @ObjectCopier
+ @Copier
private void flexToStable(MessageFlex flex, MessageStable stable) {};
- @ObjectCopier
+ @Copier
private void flex2ToStable(MessageFlex flex, MessageStable stable) {};
diff --git a/autil-test/src/test/java/com/acanx/util/annotation/CopyTest.java b/autil-test/src/test/java/com/acanx/util/annotation/CopyTest.java
index 946bdaa..888b2c3 100644
--- a/autil-test/src/test/java/com/acanx/util/annotation/CopyTest.java
+++ b/autil-test/src/test/java/com/acanx/util/annotation/CopyTest.java
@@ -2,7 +2,7 @@
import com.acanx.meta.model.test.annotation.model.MessageFlex;
import com.acanx.meta.model.test.annotation.model.MessageStable;
-import com.acanx.util.incubator.annotation.ObjectCopier;
+import com.acanx.util.incubator.annotation.Copier;
/**
* CopyTest
@@ -14,25 +14,25 @@ public class CopyTest {
- @ObjectCopier
+ @Copier
private void convert(MessageFlex flex, MessageStable stable) {};
-// @ObjectCopier(ignoreNull = true, exclude = {"internalId"})
-// private void convert1(MessageFlex flex, MessageStable stable) {
-// // 编译后这个方法会被增强为:
-// // if (flex == null) throw new IllegalArgumentException("Source object cannot be null");
-// // if (stable == null) throw new IllegalArgumentException("Target object cannot be null");
-// // stable.content = flex.content != null ? flex.content : stable.content;
-// // stable.priority = flex.priority != null ? flex.priority : stable.priority;
-// // stable.urgent = flex.urgent != null ? flex.urgent : stable.urgent;
-// // stable.tags = flex.tags != null ? flex.tags : stable.tags;
-// }
-
-// @ObjectCopier(strategy = ObjectCopier.CopyStrategy.DEEP)
-// private void deepConvert(MessageFlex flex, MessageStable stable) {
-// // 深拷贝版本的转换
-// }
+ @Copier(ignoreNull = true, exclude = {"internalId"})
+ private void convert1(MessageFlex flex, MessageStable stable) {
+ // 编译后这个方法会被增强为:
+ // if (flex == null) throw new IllegalArgumentException("Source object cannot be null");
+ // if (stable == null) throw new IllegalArgumentException("Target object cannot be null");
+ // stable.content = flex.content != null ? flex.content : stable.content;
+ // stable.priority = flex.priority != null ? flex.priority : stable.priority;
+ // stable.urgent = flex.urgent != null ? flex.urgent : stable.urgent;
+ // stable.tags = flex.tags != null ? flex.tags : stable.tags;
+ }
+
+ @Copier(strategy = Copier.CopyStrategy.DEEP)
+ private void deepConvert(MessageFlex flex, MessageStable stable) {
+ // 深拷贝版本的转换
+ }
public void processConversion(MessageFlex flex, MessageStable stable) {
convert(flex, stable);
diff --git a/pom.xml b/pom.xml
index c6a4cc6..56dfcbd 100644
--- a/pom.xml
+++ b/pom.xml
@@ -36,9 +36,9 @@
0.4.3-SNAPSHOT
UTF-8
UTF-8
- 25
- 25
- 25
+ 21
+ 21
+ 21
3.14.1
3.1.2
3.5.0
@@ -72,20 +72,20 @@
-
-
- sonatype-snapshots
- Sonatype Snapshot Repository
- https://central.sonatype.com/repository/maven-snapshots/
-
- false
-
-
- true
- always
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+