当前位置: 首页 > 知识库问答 >
问题:

为Lombok创建自定义注释

浦毅
2023-03-14

我在代码中使用Lombok自动生成getter和setter代码。我想添加其他个人注释并使用它。

例如,我想添加一个@Exist方法来验证列表中是否存在一个键:

@Getter    @Setter
public class User {

    private String name;
    private List<Integer> keys;

    public boolean existKeys(Integer key) {
        boolean exist = keys.contains(key);
        return exist;
    }
}

创建注释后,我将执行以下操作:

@Getter    @Setter
public class User {

    private String name;
    @Exist
    private List<Integer> keys;
} 

共有1个答案

宰父子安
2023-03-14

如果您已经在使用Lombok,那么可以添加自定义Lombok转换注释和处理程序。

>

创建处理程序

@ProviderFor(JavacAnnotationHandler.class)
public class HandleExists extends JavacAnnotationHandler<Exists>{ ...` 

以处理注释。处理程序类包必须以lombok开头 前缀。如果除了javac之外还需要支持Eclipse等,则需要编写更多扩展适当框架类的处理程序。

在处理程序中重写/实现处理()方法,通过AST操作生成所需的代码。

您可以将@Getter实现作为示例:

译注:Getter.java

搬运者:搬运者。JAVA

您还可以查看其他注释和处理程序的源代码,以了解如何生成特定代码。

您需要添加对lombok、JDK的依赖tools.jar.

一些资源:

>

  • lombok-pg项目具有一组自定义lombok注释的源,特别是FluentSetter.java、HandleFluentSetter.java/FluentSetterHandler.java

    自定义转换概述

    带有解释的简单注释示例。

    注意,这里有一些要点要考虑

    • 这是一堆需要编写和维护的非平凡代码。如果您计划使用注释5-6次,那就不值得了
    • 您可能需要使用lombok升级更改注释处理器实现
    • lombok所依赖的编译器中的漏洞也可能被关闭(这样整个lombok项目将发生巨大的变化或不再存在;在这种情况下,如果您广泛使用lombok,即使只是为了@Getter,您也会遇到更严重的问题)

    没有Lombok的一个更复杂的替代方案是使用标准注释处理来生成代码,但是,AFAIK,您不能更改原始类,必须生成/使用扩展它们的类(除非您将利用与Lombok相同的后门或诉诸于像CGLib或ASM这样的代码操作)。

    下面是一些创建自定义Lombok注释的工作代码,我称之为@Contains。

    它只是javac实现,没有Eclipse等。我想为Eclipse或其他IDE创建一个类似的处理程序并不难。

    它将生成委派给fieldName的fieldNameContains()成员方法。包含()。

    注意,该代码只是一个快速而肮脏(但仍在工作)的示例。对于产品级注释,您将需要处理许多边界条件、检查正确的类型、处理Lombok配置等,正如在Lombok或Lombok pg库源中可以看到的那样。

    某个人。JAVA

    @Getter
    @Setter
    public class SomeEntity {
    
        @NonNull
        @Contains
        private Collection<String> fieldOne = new ArrayList<>();
    
        @NonNull
        @Contains
        private Collection<String> fieldTwo = new ArrayList<>();
    
    }
    

    一些实体测试。JAVA

    public class SomeEntityTest {
    
        @Test
        public void test() {
            SomeEntity entity = new SomeEntity();
    
            Collection<String> test1 = Arrays.asList(new String[] { "1", "2" });
            entity.setFieldOne(test1);
            assertSame(test1, entity.getFieldOne());
    
            Collection<String> test2 = new HashSet<String>(Arrays.asList(new String[] { "3", "4" }));
            entity.setFieldTwo(test2);
            assertSame(test2, entity.getFieldTwo());
    
            assertTrue(entity.fieldOneContains("1"));
            assertTrue(entity.fieldOneContains("2"));
            assertFalse(entity.fieldOneContains("3"));
            assertFalse(entity.fieldOneContains("4"));
    
            assertFalse(entity.fieldTwoContains("1"));
            assertFalse(entity.fieldTwoContains("2"));
            assertTrue(entity.fieldTwoContains("3"));
            assertTrue(entity.fieldTwoContains("4"));
    
            try {
                entity.setFieldOne(null);
                fail("exception expected");
            } catch (Exception ex) {
            }
    
            try {
                entity.setFieldTwo(null);
                fail("exception expected");
            } catch (Exception ex) {
            }
    
        }
    }
    

    包含。JAVA

    @Target({ElementType.FIELD})
    @Retention(RetentionPolicy.SOURCE)
    public @interface Contains {
        Class<?>[] types() default {};
        Class<?>[] excludes() default {};
    }
    

    小菜一碟。JAVA

    @ProviderFor(JavacAnnotationHandler.class) 
    @HandlerPriority(65536) 
    @ResolutionResetNeeded 
    public class HandleContains extends JavacAnnotationHandler<Contains> {
        
        @Override 
        public void handle(AnnotationValues<Contains> annotation, JCAnnotation ast, JavacNode annotationNode) {
            
            try {
                JavacNode node = annotationNode.up();
                if (node.getKind() != Kind.FIELD) {
                    annotationNode.addError("@Contains is allowed only on fields");
                    return;
                }
                Name delegateName = annotationNode.toName(node.getName());
                JavacResolution reso = new JavacResolution(annotationNode.getContext());
                JCTree member = node.get();
                if (member.type == null) {
                    reso.resolveClassMember(node);
                }
                Type delegateType = member.type;
                if (delegateType instanceof ClassType) {
                    ClassType ct = (ClassType) delegateType;
                    //TODO validate that this field is a collection type
                    // if(!Collection)
                    //   annotationNode.addError("@Contains can only be used on collections");
                    final String methodName = "contains";
                    MethodSig methodSig = getMethodBinding(methodName, ct, annotationNode.getTypesUtil());
                    if (methodSig == null) throw new Exception("no method " + methodName + " in " + ct.tsym.name);
                    JCMethodDecl methodDecl = createDelegateMethod(methodSig, annotationNode, delegateName);
                    injectMethod(node.up(), methodDecl);
                } else {
                    annotationNode.addError("@Contains can only use concrete class types");
                    return;
                }
            } catch (Exception ex) {
                //ex.printStackTrace();
                annotationNode.addError("@Contains unexpected error: " + ex.getMessage());
            }
            
        }
        
        public JCMethodDecl createDelegateMethod(MethodSig sig, JavacNode annotation, Name delegateName) throws TypeNotConvertibleException {
            
            JavacTreeMaker maker = annotation.getTreeMaker();
            
            com.sun.tools.javac.util.List<JCAnnotation> annotations;
            if (sig.isDeprecated) {
                annotations = com.sun.tools.javac.util.List.of(maker.Annotation(genJavaLangTypeRef(annotation, "Deprecated"), com.sun.tools.javac.util.List.<JCExpression>nil()));
            } else {
                annotations = com.sun.tools.javac.util.List.nil();
            }
            
            JCModifiers mods = maker.Modifiers(PUBLIC, annotations);
            JCExpression returnType = JavacResolution.typeToJCTree((Type) sig.type.getReturnType(), annotation.getAst(), true);
            boolean useReturn = sig.type.getReturnType().getKind() != TypeKind.VOID;
            ListBuffer<JCVariableDecl> params = sig.type.getParameterTypes().isEmpty() ? null : new ListBuffer<JCVariableDecl>();
            ListBuffer<JCExpression> args = sig.type.getParameterTypes().isEmpty() ? null : new ListBuffer<JCExpression>();
            ListBuffer<JCExpression> thrown = sig.type.getThrownTypes().isEmpty() ? null : new ListBuffer<JCExpression>();
            ListBuffer<JCTypeParameter> typeParams = sig.type.getTypeVariables().isEmpty() ? null : new ListBuffer<JCTypeParameter>();
            ListBuffer<JCExpression> typeArgs = sig.type.getTypeVariables().isEmpty() ? null : new ListBuffer<JCExpression>();
            Types types = Types.instance(annotation.getContext());
            
            for (TypeMirror param : sig.type.getTypeVariables()) {
                Name name = ((TypeVar) param).tsym.name;
                
                ListBuffer<JCExpression> bounds = new ListBuffer<JCExpression>();
                for (Type type : types.getBounds((TypeVar) param)) {
                    bounds.append(JavacResolution.typeToJCTree(type, annotation.getAst(), true));
                }
                
                typeParams.append(maker.TypeParameter(name, bounds.toList()));
                typeArgs.append(maker.Ident(name));
            }
            
            for (TypeMirror ex : sig.type.getThrownTypes()) {
                thrown.append(JavacResolution.typeToJCTree((Type) ex, annotation.getAst(), true));
            }
            
            int idx = 0;
            String[] paramNames = sig.getParameterNames();
            boolean varargs = sig.elem.isVarArgs();
            for (TypeMirror param : sig.type.getParameterTypes()) {
                long flags = JavacHandlerUtil.addFinalIfNeeded(Flags.PARAMETER, annotation.getContext());
                JCModifiers paramMods = maker.Modifiers(flags);
                Name name = annotation.toName(paramNames[idx++]);
                if (varargs && idx == paramNames.length) {
                    paramMods.flags |= VARARGS;
                }
                params.append(maker.VarDef(paramMods, name, JavacResolution.typeToJCTree((Type) param, annotation.getAst(), true), null));
                args.append(maker.Ident(name));
            }
            
            JCExpression accessor = maker.Select(maker.Ident(annotation.toName("this")), delegateName);
            
            JCExpression delegateCall = maker.Apply(toList(typeArgs), maker.Select(accessor, sig.name), toList(args));
            JCStatement body = useReturn ? maker.Return(delegateCall) : maker.Exec(delegateCall);
            JCBlock bodyBlock = maker.Block(0, com.sun.tools.javac.util.List.of(body));
            StringBuilder generatedMethodName = new StringBuilder(delegateName);
            generatedMethodName.append(sig.name.toString());
            generatedMethodName.setCharAt(delegateName.length(), Character.toUpperCase(generatedMethodName.charAt(delegateName.length())));
            return recursiveSetGeneratedBy(maker.MethodDef(mods, annotation.toName(generatedMethodName.toString()), returnType, toList(typeParams), toList(params), toList(thrown), bodyBlock, null), annotation.get(), annotation.getContext());
        }
        
        public static <T> com.sun.tools.javac.util.List<T> toList(ListBuffer<T> collection) {
            return collection == null ? com.sun.tools.javac.util.List.<T>nil() : collection.toList();
        }
        
        public static class MethodSig {
            final Name name;
            final ExecutableType type;
            final boolean isDeprecated;
            final ExecutableElement elem;
            
            MethodSig(Name name, ExecutableType type, boolean isDeprecated, ExecutableElement elem) {
                this.name = name;
                this.type = type;
                this.isDeprecated = isDeprecated;
                this.elem = elem;
            }
            
            String[] getParameterNames() {
                List<? extends VariableElement> paramList = elem.getParameters();
                String[] paramNames = new String[paramList.size()];
                for (int i = 0; i < paramNames.length; i++) {
                    paramNames[i] = paramList.get(i).getSimpleName().toString();
                }
                return paramNames;
            }
            
            @Override public String toString() {
                return (isDeprecated ? "@Deprecated " : "") + name + " " + type;
            }
        }
        
        public MethodSig getMethodBinding(String name, ClassType ct, JavacTypes types) {
            MethodSig result = null;
            TypeSymbol tsym = ct.asElement();
            if (tsym == null) throw new IllegalArgumentException("no class");
            
            for (Symbol member : tsym.getEnclosedElements()) {
                if (member.getKind() != ElementKind.METHOD || !name.equals(member.name.toString())) {
                    continue;
                }
                if (member.isStatic()) continue;
                if (member.isConstructor()) continue;
                ExecutableElement exElem = (ExecutableElement) member;
                if (!exElem.getModifiers().contains(Modifier.PUBLIC)) continue;
                ExecutableType methodType = (ExecutableType) types.asMemberOf(ct, member);
                boolean isDeprecated = (member.flags() & DEPRECATED) != 0;
                result = new MethodSig(member.name, methodType, isDeprecated, exElem);
            }
            if (result == null) {
                if (ct.supertype_field instanceof ClassType) {
                    result = getMethodBinding(name, (ClassType) ct.supertype_field, types);
                }
                if (result == null) {
                    if (ct.interfaces_field != null) {
                        for (Type iface : ct.interfaces_field) {
                            if (iface instanceof ClassType) {
                                result = getMethodBinding(name, (ClassType) iface, types);
                                if (result != null) {
                                    break;
                                }
                            }
                        }
                    }
                }
            }
            return result;
        }
    }
    

  •  类似资料:
    • 问题内容: 注释如何与Java一起使用?以及如何创建这样的自定义注释: 基本上,我需要保留的POJO在持久化时像这样进行序列化: 这样,实际的生成/持久对象是这样的: 任何想法如何实现这一点? 问题答案: 如果创建自定义注释,则必须使用此处的 API 示例进行处理。您可以参考如何声明注释。 这是Java中的示例注释声明的样子。 并被称为。 表示您想在运行时保留注释,并且可以在运行时访问它。 表示您

    • 问题内容: 我用我的代码自动生成和代码。我想添加其他个人并使用它。 例如,我想添加一个方法来验证列表中键的存在: 创建注释后,我将只需要执行以下操作: 问题答案: 一般注意事项 如果您已经在使用Lombok,则可以添加自定义Lombok转换批注和处理程序。 使用和定义存在注释 创建一个处理程序 public class HandleExists extends JavacAnnotationHan

    • 下面是Lombok的注释的java文档: 如果加上一个参数,lombok将在方法/构造函数主体的开头插入一个空检查,抛出一个{@code NullPointerException},参数名作为消息。如果放在字段上,任何为该字段赋值的生成方法也将生成这些空检查。请注意,任何名为{@code NonNull}且带有任何大小写和任何包的注释都将导致为生成的方法生成nullchecks(并且注释将被复制到

    • 问题内容: 一个项目需要大量使用以下Jackson注释组合。因此,有没有一种方法可以创建另一个注释来避免丑陋的复制/粘贴: 更新: 我已经尝试过了,但是没有成功:-( 问题答案: 使用解决问题:

    • 我想编写自定义Lombok注释处理程序。我知道http://notatube.blogspot.de/2010/12/project-lombok-creating-custom.html.但是当前的lombok jar文件并不包含很多内容。类文件,但文件名为。症状自评量表。取而代之的是龙目山。 我发现,的。SCL. lombok文件是. class文件,Lombok的构建脚本在生成jar文件时重