如何实现注释来限制FunctionalInterface等成员函数,函数类型等的数量?

时间:2018-02-01 19:05:11

标签: java annotations

有许多内部Java注释,如SuppressWarningFunctionalInterface等,它们可以使用注释限制类的成员,扩展类甚至指定编译器选项,但是如何才能正常的程序员撰写这样的注释?

我搜索了注释主题,我所能找到的就是为注释添加一些元值,如this,以及如何使用注释,但我找不到任何解释如何实现高级注释的内容。任何指示都会有所帮助。

3 个答案:

答案 0 :(得分:2)

您要找的是编译时注释

基本上,注释处理可以基于其RetentionPolicy来完成。根据{{​​3}},有3种RetentionPolicy -

  

CLASS 编译器将在类文件中记录注释   但在运行时不需要由VM保留。

     

RUNTIME 注释将由编译器记录在类文件中,并在运行时由VM保留,因此可以反射性地读取它们。

     

SOURCE 编译器将丢弃注释。

编译时注释处理与RetentionPolicy.SOURCE相关,因为您希望在编译时处理源文件,类似于@Override等其他注释。

下面是一个简单的编译时注释处理器的一个例子 -

  1. 创建Maven项目 - Annotation_Compile_Time -

    (A)在此项目中创建编译时注释MyAnnotation -

    package xxx;
    
    import java.lang.annotation.Documented;
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Inherited;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    @Documented
    @Target(ElementType.TYPE)
    @Inherited
    @Retention(RetentionPolicy.SOURCE)
    public @interface MyAnnotation {
    
    }
    

    (B)创建注释处理器MyAnnotationProcessor -

    package xxx;
    
    import java.util.Set;
    
    import javax.annotation.processing.AbstractProcessor;
    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.TypeElement;
    import javax.tools.Diagnostic;
    
    @SupportedAnnotationTypes("xxx.MyAnnotation ")
    @SupportedSourceVersion(SourceVersion.RELEASE_8)
    public class MyAnnotationProcessor extends AbstractProcessor {
    
        public MyAnnotationProcessor () {
            super();
        }
    
        @Override
        public boolean process(Set<? extends TypeElement> typeElementSet, RoundEnvironment roundEnv) {
    
            for (Element e : roundEnv.getRootElements()) {
                String className = e.toString();
                String message = "Annotated class - " + className;
                System.out.println(message);
                processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE, message);
            }
    
            return false;
        }
    }
    

    (C)在目录 - javax.annotation.processing.Processor中创建src/main/resources/META-INF/services个文件,内容低于 -

    xxx.MyAnnotationProcessor
    

    (D)使用构建配置更新pom.xml -

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.7.0</version>
                <configuration>
                    <verbose>true</verbose>
                    <fork>true</fork>
                    <compilerArgument>-proc:none</compilerArgument>
                </configuration>
            </plugin>
        </plugins>
    </build>
    

    (E)使用mvn clean install编译并安装此项目。

  2. 创建另一个Maven项目 - Annotation_User - 此项目将使用上述项目中定义的注释。在此项目中使用此批注创建2个源文件

    (A)AnnotationUser1 -

    package xxx.consumer;
    
    import xxx.MyAnnotation;
    
    @MyAnnotation
    public class AnnotationUser1 {
    
        private String message;
    
        public AnnotationUser1(String message) {
            this.message = message;
        }
    
        public String getMessage() {
            return message;
        }
    
        public void setMessage(String message) {
            this.message = message;
        }
    
    }
    

    (B)AnnotationUser2 -

    package xxx.consumer;
    
    import xxx.MyAnnotation;
    
    @MyAnnotation
    public class AnnotationUser2 {
    
        private String message;
    
        public AnnotationUser1(String message) {
            this.message = message;
        }
    
        public String getMessage() {
            return message;
        }
    
        public void setMessage(String message) {
            this.message = message;
        }
    
    }
    

    (C)使用注释项目依赖项更新pom.xml -

    <dependency>
        <groupId>xxx</groupId>
        <artifactId>Annotation_Compile_Time</artifactId>
        <version>1.0</version>
    </dependency>
    
  3. 现在,无论何时,您将使用mvn clean compile编译此项目,您的项目1中定义的注释处理器将被调用。目前,处理器只是打印使用此批注注释的类的名称。

    您还可以参考Java docs页面了解详情。

    下一步是分析源文件并计算否。方法因为,它是编译时处理,所以你不能使用Reflection API得到否。方法一种解决方案是使用this来解析源文件并计算否。方法或你可以编写自己的逻辑。

    Eclipse AST主要基于编译时注释处理。如果你想做一些有用的事情,最好学习Project lombok

答案 1 :(得分:1)

我认为你需要注释处理:

UPD: 在某些情况下你可以编写javaagent而不是更好,至少因为它至少有一些像bytebuddy这样的工具(这是很棒的恕我直言)

答案 2 :(得分:1)

我正在分享如何制作自定义注释以解决我的应用中的问题。

<强>问题:

我正在使用Spring AOP在我的应用中进行日志记录。对于刚接触AOP的人来说,用简单的单词做的不是在每个方法和类中编写logger.log(),而是可以告诉AOP在之后/之前执行(在我的情况下是日志记录) /每种方法之前和之后。现在的问题是因为每个方法都会被记录,如何防止某个方法(如身份验证)或参数(如密码)被记录。

为此,我创建了一个注释SkipLogging

@Target(value = { ElementType.METHOD, ElementType.TYPE, ElementType.PARAMETER })
@Retention(value = RetentionPolicy.RUNTIME)
public @interface SkipLogging {

}

在我的AOP类中,我提出了一个条件,即如果有任何东西都有这个注释,AOP不应该在那里做 logging 。也许跟随(部分)代码会更有意义:

@Around("within(com.test..*) 
    public Object logAround(final ProceedingJoinPoint joinPoint) throws Throwable {

        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();

        // Log only if class/method is not annotated with SkipLogging
        if (!(signature.getDeclaringType().isAnnotationPresent(SkipLogging.class)
                || method.isAnnotationPresent(SkipLogging.class))) {
            try {
                // Log before method entry
                logger.info("");

                Object result = joinPoint.proceed();

                // Log after method exit
                logger.info("");

                return result;
            } catch (Exception e) {
                // Log after exception
                logger.error("");
            throw e;
            }
        } else {
            return joinPoint.proceed();
        }
    }

没有详细说明,请查看条件:

    if (!(signature.getDeclaringType().isAnnotationPresent(SkipLogging.class)
                        || method.isAnnotationPresent(SkipLogging.class

)))

可防止使用SkipLogging注释的类和方法被记录。同样,我编写了一些代码,用于将这个注释放在参数上并跳过它们。

在接下来的步骤中,我创建了@DebugLogging,@ ErrorLogging等注释,并在我的AOP中进行检查,根据存在的注释编写调试日志或错误日志。