当前位置: 首页 > 科技观察

注解在Java中是如何工作的

时间:2023-03-17 22:04:48 科技观察

自Java5.0引入注解以来,它已经成为Java平台中非常重要的一部分。在开发过程中,我们经常会在应用代码中看到@Override、@Deprecated等注解。在这篇文章中,我将告诉你注解是什么,为什么要引入注解,注解是如何工作的,如何编写自定义注解(通过实例),什么时候可以使用注解,以及***注解和ADF(应用程序开发框架).这需要一段时间,所以给自己喝杯咖啡,让我们深入注释的世界。什么是注解?注释可以用一个词来描述,那就是元数据,也就是描述数据的数据。因此,可以说注解是源代码的元数据。比如下面的代码:@OverridepublicStringtoString(){return"ThisisStringRepresentationofcurrentobject.";}在上面的代码中,我重写了toString()方法,使用了@Override注解。但是,即使我不在代码上打上@Override注解,程序也能正常执行。那么,注解是什么意思呢?这样写有什么好处吗?其实@Override是告诉编译器这个方法是重写的方法(描述方法的元数据),如果父类中不存在这个方法,编译器就会报错,说明这个方法没有重写父类中的方法。如果我不小心打错了,比如把toStrring()写成了toStrring(){doubler},而且我没有使用@Override注解,程序仍然可以编译运行。但是结果会和我预想的大相径庭。现在我们了解了注释是什么,并且使用它们有助于阅读程序。注解是应用于类、方法、参数、变量、构造函数和包声明的特殊修饰符。它是JSR-175标准选择的用于描述元数据的工具。为什么要引入注解?在使用Annotation之前(甚至之后),XML被广泛用于描述元数据。不知道从什么时候开始,一些应用程序开发人员和架构师发现XML的维护越来越差。他们希望使用与代码紧密耦合的东西,而不是像XML这样与代码松散耦合(在某些情况下完全分离)的代码描述。如果你谷歌“XMLvs.annotations”,你会看到很多关于这个问题的争论。最有意思的是,居然引入了XML配置,将代码和配置分开。以上两种观点可能会让你感到困惑。两种观点看似循环,但各有优缺点。下面我们通过一个例子来了解一下两者的区别。如果要为应用程序设置许多常量或参数,在这种情况下XML是一个不错的选择,因为它不会链接到特定代码。如果要将方法声明为服务,最好使用Annotation,因为在这种情况下,注解和方法需要紧耦合,开发人员也必须意识到这一点。另一个非常重要的因素是Annotation定义了描述元数据的标准方式。在此之前,开发人员通常使用自己的方式来定义元数据。例如,使用标记接口、注释、transient关键字等。每个程序员都以自己的方式定义元数据,这与Annotation的标准方式不同。目前很多框架将XML和Annotation结合起来,以平衡两者的优劣。注释如何工作?如何编写自定义注解?在讲这部分之前,建议大家先下载Annotation示例代码AnnotationsSample.zip。下载后,将其放入您习惯的IDE中。这些代码将帮助您更好地理解Annotation机制。写Annotation很简单,Annotation的定义可以类比接口的定义。我们来看两个例子:一个是标准注解@Override,另一个是用户自定义注解@Todo。@Target(ElementType.METHOD)@Retention(RetentionPolicy.SOURCE)public@interfaceOverride{}你可能对@Override注解有些疑惑。它什么都不做,那么它如何检查父类中是否存在同名函数。当然,不要惊讶,我只是在开玩笑。@Override注解的定义可不止这么一点代码。这部分非常重要,我不得不再重复一遍:注解只是元数据,与业务逻辑无关。有点难以理解,但仅此而已。如果注释不包含业务逻辑,则必须有人实现该逻辑。元数据用户这样做。注解仅提供有关其定义的属性(类/方法/包/字段)的信息。注释(也是一些代码)的用户阅读此信息并实现必要的逻辑。当我们使用Java的注解(比如@Override)时,JVM就是一个用户,它是在字节码层面工作的。到目前为止,应用程序开发人员无法控制也无法使用自定义注释。因此,让我们解释一下如何编写自定义注释。下面就一一说说编写自定义Annotations的要点。在上面的示例中,您看到了一些应用于注释的注释。J2SE5.0版本在java.lang.annotation中提供了四个元注解,专门注解其他注解:@Documented——JavaDoc中是否包含该注解@Retention——何时使用注解@Target?–使用注解的地方@Inherited–是否允许子类继承注解@Documented–一个简单的Annotations标签注解,表示是否给java文档添加注解信息。@Retention–定义注解的生命周期。RetentionPolicy.SOURCE——在编译时丢弃。这些注解在编译后没有任何意义,所以没有写入字节码。@Override、@SuppressWarnings属于此类注解。RetentionPolicy.CLASS——在类加载时丢弃。在字节码文件的处理中很有用。注释默认使用此方法。RetentionPolicy.RUNTIME——永远不会被丢弃,运行时会保留注解,所以可以使用反射机制读取注解的信息。我们自定义的注解通常使用这种方法。@Target–指示使用注解的位置。除非明确说明,否则此注释可以放在任何地方。以下是一些可用的参数。需要注意的是,属性的??注解是兼容的。如果要为所有7个属性添加注释并仅排除一个属性,则需要在定义目标中包含所有属性。ElementType.TYPE:用于描述类、接口或枚举声明ElementType.FIELD:用于描述实例变量ElementType.METHODElementType.PARAMETERElementType.CONSTRUCTORElementType.LOCAL_VARIABLElementType.ANNOTATION_TYPE另一个注解ElementType.PACKAGE用于记录java文件的包信息@Inherited–定义注解与子类的关系那么,注解的内部是如何定义的呢?注解只支持基本类型、String和枚举类型。注解中的所有属性都定义为方法,并允许提供默认值。@Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)@interfaceTodo{publicenumPriority{LOW,MEDIUM,HIGH}publicenumStatus{STARTED,NOT_STARTED}Stringauthor()默认“Yash”;Prioritypriority()defaultPriority.LOW;Statusstatus()defaultStatus.NOT_STARTED;}下面的例子演示了如何使用上面的注解。@Todo(priority=Todo.Priority.MEDIUM,author="Yashwant",status=Todo.Status.STARTED)publicvoidincompleteMethod1(){//Somebusinesslogiciswritten//Butit'snotcompleteyet}如果注解中只有一个属性,可以直接命名为“value”,使用时无需指明属性名。@interfaceAuthor{Stringvalue();}@Author("Yashwant")publicvoidsomeMethod(){}但到目前为止一切看起来都不错。我们定义自己的注释并将它们应用于业务逻辑方法。现在我们需要编写一个用户程序来调用我们的注解。这里需要用到反射机制。如果熟悉反射代码,就会知道反射可以提供类名、方法和实例变量对象。所有这些对象都有一个返回注释信息的getAnnotation()方法。我们需要将这个对象转换为我们自定义的注解(通过instanceOf()检查后),我们也可以调用自定义注解中的方法。看一下下面的示例代码,使用上面的注释:ClassbusinessLogicClass=BusinessLogic.class;for(Methodmethod:businessLogicClass.getMethods()){TodotodoAnnotation=(Todo)method.getAnnotation(Todo.class);if(todoAnnotation!=null){System.out.println("MethodName:"+method.getName());System.out.println("作者:"+todoAnnotation.author());System.out.println("优先级:"+todoAnnotation.priority());System.out.println("状态:"+todoAnnotation.status());}}注解用例注解非常强大。Spring和Hebernate等框架在日志记录和有效性方面广泛使用注释。可以在使用标记接口的地方应用注释。区别在于标记接口用于定义完整的类,但您可以为单个方法定义注释,例如是否将方法公开为服务。在最新的servlet3.0中引入了许多新的注解,尤其是与servlet安全相关的注解。HandlesTypes–此注释用于表示传递给ServletContainerInitializer的一组应用程序类。HttpConstraint——该注解表示应用程序请求所有HTTP方法的安全约束,它不同于ServletSecurity注解中定义的HttpMethodConstraint安全约束。HttpMethodConstraint–表示不同类型请求的安全约束,与ServletSecurity注解中描述HTTP协议方法类型的注解不同。MultipartConfig——这个注解被标记在Servlet上,表示Servlet要处理的请求的MIME类型是multipart/form-data。ServletSecurity该注解被标记在Servlet继承类上,强制HTTP协议请求遵循安全约束。WebFilter——该注解用于声明一个服务器过滤器;WebInitParam–该注解用于声明Servlet或过滤器中的初始化参数,通常与@WebServlet或@WebFilter一起使用。WebListener——该注解为Web应用程序上下文中不同类型的事件声明侦听器。WebServlet–此注释用于声明Servlet的配置。ADF(应用程序框架)和注解现在我们开始本文的最后一部分。应用程序框架(称为ADF)由Oracle开发,用于创建Oracle融合应用程序。我们了解了注解的优点和缺点,也知道如何编写自定义注解,但是我们应该在ADF的什么地方应用注解呢?ADF是否提供一些简单的注释?问得好,ADF中确实有很多使用注解的限制。前面提到的Spring和Hibernate等应用程序框架使用AOP(Aspect-OrientedProgramming)。在AOP中,框架提供了一种在事件的预处理和后处理中注入代码的机制。例如:你有一个钩子在方法执行前后添加代码,所以你可以在这些地方写你的用户代码。ADF不使用AOP。如果我们有任何可用的注释用例,我们可能需要通过继承来实现它们。