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 annotations, RoundEnvironment roundEnv) { - try { - if (roundEnv.processingOver()) { - printUsageInstructions(); - return false; - } - - Set 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 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 annotations, RoundEnvironment roundEnv) { +// try { +// if (roundEnv.processingOver()) { +// printUsageInstructions(); +// return false; +// } +// +// Set 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 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 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 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 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 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 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 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 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 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 - - - + + + + + + + + + + + + + +