`

学习Java Annotation

阅读更多
    从Java5.0版发布以来,5.0平台提供了一个正式的annotation功能:允许开发者定义、使用自己的annoatation类型。此功能由一个定义annotation类型的语法和一个描述annotation声明的语法,读取annotaion的API,一个使用annotation修饰的class文件,一个annotation处理工具(apt)组成。

     Annotation类型声明于一般的接口声明极为类似,区别只在于它在interface关键字前面使用“@”符号。annotation类型的每个方法声明定义了一个annotation类型成员,但方法声明不必有参数或者异常声明;方法返回值的类型被限制在以下的范围:primitives、String、Class、enums、annotation和前面类型的数组;方法可以有默认值。
下面是一个简单的annotation类型声明:
如,

/**   
* Describes the Request-For-Enhancement(RFE) that led    
* to the presence of the annotated API element.   
*/  
public @interface RequestForEnhancement {      
      int    id();       
      String synopsis();      
      String engineer() default "[unassigned]";      
      String date();    default "[unimplemented]";  
}

注:如果在annotations中只有唯一一个成员,则该成员应命名为value.

Annotation工作原理
    在java5.0中Java.lang.reflect提供的反射API被扩充了读取运行时annotation的能力。reflect是如何帮助我们访问class中的annotation呢?
     java.lang.reflect.AnnotatedElement是重要的接口,它代表了提供查询annotation能力的程序成员。这个接口被java.lang.Package、java.lang.Class实现,并间接地被Method类、Constructor类、java.lang.reflect的Field类实现。而annotation中的方法参数可以通过Method类、Constructor类的getParameterAnnotations()方法获得。
    我们仅要知道class的方法是否使用了annotation修饰,可以用 isAnnotationPresent()方法进行检查。而当需要处理具有成员的annotation时,我们可使用getAnnotation()方法来获得annotation的成员信息(成员名称、成员值)。如果annotation存在,那么实现了相应的annotation类型接口的对象将被getAnnotation()方法返回,接着调用定义在annotation类型中的成员方法可以方便地获得任何成员值。

自定义Annotation
Annotation类型使用关键字@interface
Annotation类型的方法必须声明为无参数、无异常抛出的。这些方法定义了annotation的成员:方法名成为了成员名,而方法返回值成为了成员的类型。而方法返回值类型必须为primitive类型、String、Class类型、枚举类型、annotation类型或者由前面类型之一作为元素的一维数组。方法的后面可以使用default和一个默认数值来声明成员的默认值,null不能作为成员默认值,
Annotation类型和它的方法不能使用annotation类型的参数、成员不能是generic。只有返回值类型是Class的方法可以在annotation类型中使用generic,因为此方法能够用类转换将各种类型转换为Class。

Meta-Annotation
   Java5.0定义了4个标准的meta-annotation类型,它们被用来提供对其它annotation类型作说明。这些类型和它们所支持的类在java.lang.annotation包中可以找到。
1.Target
   作为meta-annotation类型的Target,它描述了annotation所修饰的程序成员的类型。当一个annotation类型没有Target时,它将被作为普通的annotation看待。当将它修饰一个特定的程序成员时,它将发挥其应用的作用,例如:Override用于修饰方法时,增加了@Target这个meta-annotation就使编译器对annotation作检查,从而去掉修饰错误类型的Override。Target meta-annotation类型有唯一的value作为成员。这个成员的类型是java.lang.annotation.ElementType[]类型的,ElementType类型是可以被标注的程序成员的枚举类型。
2.Retention
   Retention描述了annotation是否被编译器丢弃或者保留在class文件;如果保留在class文件中,是否在class文件被装载时被虚拟机读取。默认情况下,annotation被保存在class文件中,但在运行时并不能被反射访问。Retention具有三个取值:source、class、runtime,这些取值来自java.lang.annotation.RetentionPolicy的枚举类型值。Retention meta-annotation类型有唯一的value作为成员,它的取值来自java.lang.annotation.RetentionPolicy的枚举类型值。
3.Documented
   Documented是一个meta-annotation类型,用于描述其它类型的annotation应该被作为被标注的程序成员的公共API,因此可以被例如javadoc此类的工具文档化。Documented是一个marker annotation,没有成员。
4.Inherited
   @Inherited meta-annotation也是一个marker annotation,它阐述了某个被标注的类型是被继承的。如果一个使用了@Inherited修饰的annotation类型被用于一个class,则这个annotation将被用于该class的子类。

注意:@Inherited annotation类型是被标注过的class的子类所继承。类并不从它所实现的接口继承annotation,方法并不从它所重载的方法继承annotation。

annotation实例分析
1.BRFW(Beaninfo Runtime FrameWork)定义:
本人编写的一个annotation功能演示框架。顾名思义,BRFW就是在运行时取得bean信息的框架。

2.BRFW的功能:
A.源代码级annotation:在bean的源代码中使用annotation定义bean的信息;
B.运行时获取bean数据:在运行时分析bean class中的annotation,并将当前bean class中field信息取出,功能类似xdoclet;
C.运行时bean数据的xml绑定:将获得的bean数据构造为xml文件格式展现。熟悉j2ee的朋友知道,这个功能类似jaxb。

3.BRFW框架:
BRFW主要包含以下几个类:
A.Persistent类:定义了用于修饰类的固有类型成员变量的annotation。
B.Exportable类:定义了用于修饰Class的类型的annotation。
C.ExportToXml类:核心类,用于完成BRFW的主要功能:将具有Exportable Annotation的bean对象转换为xml格式文本。
D.AddressForTest类:被A和B修饰过的用于测试目的的地址bean类。其中包含了地址定义所必需的信息:国家、省级、城市、街道、门牌等。
E.AddressListForTest类:被A和B修饰过的友人通讯录bean类。其中包含了通讯录所必备的信息:友人姓名、年龄、电话、住址(成员为AddressForTest类型的ArrayList)、备注。需要说明的是电话这个bean成员变量是由字符串类型组成的ArrayList类型。由于朋友的住址可能不唯一,故这里的住址为由AddressForTest类型组成的ArrayList。
从上面的列表中,可以发现A、B用于修饰bean类和其类成员;C主要用于取出bean类的数据并将其作xml绑定,代码中使用了E作为测试类;E中可能包含着多个D。
在了解了这个简单框架后,我们来看一下BRFW的代码吧!

4.BRFW源代码分析:
A.Persistent类:
清单1:

package com.bjinfotech.practice.annotation.runtimeframework;
import java.lang.annotation.*;
/** * 用于修饰类的固有类型成员变量的annotation * @author cleverpig * */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Persistent {      
  String value() default "";
}

B.Exportable类:
清单2:

package com.bjinfotech.practice.annotation.runtimeframework;
import java.lang.annotation.*;
/** * 用于修饰类的类型的annotation * @author cleverpig * */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Exportable {       
//名称       
String name() default "";       
//描述       
String description() default "";       
//省略name和description后,用来保存name值       
String value() default "";      
}


C.AddressForTest类:
清单3:

package com.bjinfotech.practice.annotation.runtimeframework;
/** * 用于测试的地址类 * @author cleverpig * */
@Exportable("address")
public class AddressForTest {       
//国家       
@Persistent      
private String country=null;               
//省级       
@Persistent       
private String province=null;              
//城市      
@Persistent       
private String city=null;               
//街道       
@Persistent       
private String street=null;       
//门牌       
@Persistent      
private String doorplate=null;               
public AddressForTest(
String country,String province,                      
String city,String street,String doorplate)
{               
this.country=country;               
this.province=province;               
this.city=city;               
this.street=street;              
this.doorplate=doorplate;       
}       
}


D.AddressListForTest类:
清单4:

package com.bjinfotech.practice.annotation.runtimeframework;
import java.util.*;
/** * 友人通讯录 * 包含:姓名、年龄、电话、住址(多个)、备注
* @author cleverpig
* */
@Exportable(name="addresslist",description="address list")
public class AddressListForTest {      
//友人姓名      
@Persistent       
private String friendName=null;               
//友人年龄       
@Persistent       
private int age=0;               
//友人电话       
@Persistent       
private ArrayList<String> telephone=null;               
//友人住址:家庭、单位       
@Persistent      
private ArrayList<AddressForTest> AddressForText=null;               
//备注      
@Persistent       
private String note=null;               
public AddressListForTest(String name,int age,                       
ArrayList<String> telephoneList,                        
ArrayList<AddressForTest> addressList,                       
String note){              
this.friendName=name;              
this.age=age;               
this.telephone=new ArrayList<String>(telephoneList);              
this.AddressForText=new ArrayList<AddressForTest>(addressList);               
this.note=note;                      
}}


E.ExportToXml类:
清单5:

package com.bjinfotech.practice.annotation.runtimeframework;
import java.lang.reflect.Field;
import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
import java.util.ArrayList;
/** * 将具有Exportable Annotation的对象转换为xml格式文本 
* @author cleverpig * */
public class ExportToXml {      
/**         * 返回对象的成员变量的值(字符串类型)        
* @param field 对象的成员变量        
* @param fieldTypeClass 对象的类型        
* @param obj 对象        
* @return 对象的成员变量的值(字符串类型)       
*/       
private String getFieldValue(Field field,Class fieldTypeClass,Object obj){               
String value=null;                              
try{                       
     if (fieldTypeClass==String.class){                               
value=(String)field.get(obj);                        }                      
else if (fieldTypeClass==int.class){                                value=Integer.toString(field.getInt(obj));                        }                       
else if (fieldTypeClass==long.class){                                value=Long.toString(field.getLong(obj));                        }                       
else if (fieldTypeClass==short.class){                                value=Short.toString(field.getShort(obj));                        }                       
else if (fieldTypeClass==float.class){                                value=Float.toString(field.getFloat(obj));                        }                       
else if (fieldTypeClass==double.class){                                value=Double.toString(field.getDouble(obj));                        }                       
else if (fieldTypeClass==byte.class){                                value=Byte.toString(field.getByte(obj));                        }                       
else if (fieldTypeClass==char.class){                                value=Character.toString(field.getChar(obj));                        }                       
else if (fieldTypeClass==boolean.class){                              
value=Boolean.toString(field.getBoolean(obj));                      
}              
} catch(Exception ex){                       
ex.printStackTrace();                       
value=null;              
}               
return value;      
}               
/**        
* 输出对象的字段,当对象的字段为Collection或者Map类型时,要调用exportObject方法继续处理        
* @param obj 被处理的对象       
* @throws Exception        
*/      
public void exportFields(Object obj) throws Exception{               
Exportable exportable=obj.getClass().getAnnotation(Exportable.class);                       
if (exportable!=null){                       
if (exportable.value().length()>0){
//  System.out.println("Class annotation Name:"+exportable.value());                      
}                       
else{
//  System.out.println("Class annotation Name:"+exportable.name());                     
  }              
}               
else{
//  System.out.println(obj.getClass()+"类不是使用Exportable标注过的");   }                               
//取出对象的成员变量              
Field[] fields=obj.getClass().getDeclaredFields();                            
   for(Field field:fields){                       
//获得成员变量的标注                      
Persistent fieldAnnotation=field.getAnnotation(Persistent.class);                      
if (fieldAnnotation==null){   continue;                        }                       
//重要:避免java虚拟机检查对私有成员的访问权限                      
field.setAccessible(true);                      
Class typeClass=field.getType();                      
String name=field.getName();                      
String value=getFieldValue(field,typeClass,obj);                                              
//如果获得成员变量的值,则输出                      
if (value!=null){                              
System.out.println(getIndent()+"<"+name+">\n" +getIndent()+"\t"+value+"\n"+getIndent()+"</"+name+">");                       
}                      
//处理成员变量中类型为Collection或Map                      
else if ((field.get(obj) instanceof Collection)                                    
(field.get(obj) instanceof Map)){                             
  exportObject(field.get(obj));                        }                       
else{                              
exportObject(field.get(obj));                       
}                                      
}      
}   
//缩进深度     
  int levelDepth=0;      
//防止循环引用的检查者,循环引用现象如:a包含b,而b又包含a      
Collection<Object> cyclicChecker=new ArrayList<Object>();               
/**      
  * 返回缩进字符串     
   * @return       
*/   
    private String getIndent(){              
String s="";            
   for(int i=0;i<levelDepth;i++){                    
   s+="\t";                }              
return s;      
}       
/**       
* 输出对象,如果对象类型为Collection和Map类型,则需要递归调用exportObject进行处理      
  * @param obj       
* @throws Exception       
*/       
public void exportObject(Object obj) throws Exception{             
  Exportable exportable=null;           
    String elementName=null;                            
   //循环引用现象处理              
if (cyclicChecker.contains(obj)){                    
   return;             
  }                             
  cyclicChecker.add(obj);                              
//首先处理Collection和Map类型             
  if (obj instanceof Collection){                  
     for(Iterator i=((Collection)obj).iterator();i.hasNext();){                             
  exportObject(i.next());                     
  }              
}             
  else if (obj instanceof Map){           
            for(Iterator i=((Map)obj).keySet().iterator();
i.hasNext();){                        
       exportObject(i.next());                      
}             
  }  else{  
                     exportable=obj.getClass().getAnnotation(Exportable.class);                       
//如果obj已经被Exportable Annotation修饰过了(注意annotation是具有继承性的),                      
//则使用其name作为输出xml的元素name                     
  if (exportable!=null){                            
   if (exportable.value().length()>0){                                      
elementName=exportable.value();                              
}  else{                                  
     elementName=exportable.name();                              
}                      
}                      
//未被修饰或者Exportable Annotation的值为空字符串,                      
//则使用类名作为输出xml的元素name                  
     if (exportable==null||elementName.length()==0){                           
    elementName=obj.getClass().getSimpleName();                    
   }                     
  //输出xml元素头               
        System.out.println(getIndent()+"<"+elementName+">");                
       levelDepth++;                       
//如果没有被修饰,则直接输出其toString()作为元素值                  
     if (exportable==null){                           
    System.out.println(getIndent()+obj.toString());                    
   }                       
//否则将对象的成员变量导出为xml              
         else{                       
        exportFields(obj);                
       }                  
     levelDepth--;       
                //输出xml元素结尾            
           System.out.println(getIndent()+"</"+elementName+">");                 
  }             
  cyclicChecker.remove(obj);      
}            
   public static void main(String[] argv){            
   try{                    
   AddressForTest ad=new AddressForTest("China","Beijing",  "Beijing","winnerStreet","10");                   
    ExportToXml test=new ExportToXml();                                           
ArrayList<String> telephoneList=new ArrayList<String>();                    
   telephoneList.add("66608888");     
   telephoneList.add("66608889");                      
ArrayList<AddressForTest> adList=new ArrayList<AddressForTest>();                     
  adList.add(ad);                 
AddressListForTest adl=new AddressListForTest("coolBoy",                             
18,telephoneList,adList,"some words");                 
test.exportObject(adl);      }         
catch(Exception ex){       
                ex.printStackTrace();           
    }   
    }
}

   在ExportToXml类之前的类比较简单,这里必须说明一下ExportToXml类:此类的核心函数是exportObject和exportFields方法,前者输出对象的xml信息,后者输出对象成员变量的信息。由于对象类型和成员类型的多样性,所以采取了以下的逻辑:
   在exportObject方法中,当对象类型为Collection和Map类型时,则需要递归调用exportObject进行处理;而如果对象类型不是Collection和Map类型的话,将判断对象类是否被Exportable annotation修饰过:如果没有被修饰,则直接输出<对象类名>对象.toString()</对象类名>作为xml绑定结果的一部分;如果被修饰过,则需要调用exportFields方法对对象的成员变量进行xml绑定。
   在exportFields方法中,首先取出对象的所有成员,然后获得被Persisitent annotation修饰的成员。在其后的一句:field.setAccessible(true)是很重要的,因为bean类定义中的成员访问修饰都是private,所以为了避免java虚拟机检查对私有成员的访问权限,加上这一句是必需的。接着后面的语句便是输出<成员名>成员值</成员名>这样的xml结构。像在exportObject方法中一般,仍然需要判断成员类型是否为Collection和Map类型,如果为上述两种类型之一,则要在exportFields中再次调用exportObject来处理这个成员。
    在main方法中,本人编写了一段演示代码:建立了一个由单个友人地址类(AddressForTest)组成的ArrayList作为通讯录类(AddressForTest)的成员的通讯录对象,并且输出这个对象的xml绑定,运行结果如下:

清单6:

<addresslist>      
    <friendName>  coolBoy </friendName>     
    <age>18  </age>      
    <String> 66608888 </String>   
    <String> 66608889 </String>    
    <address>              
        <country>  China  </country>         
        <province>  Beijing   </province>               
        <city>  Beijing </city>  
        <street>  winnerStreet  </street>             
        <doorplate> 10  </doorplate> 
   </address>      
    <note>some words </note>
</addresslist>


APT实例分析:
1.何谓APT?
    根据sun官方的解释,APT(annotation processing tool)是一个命令行工具,它对源代码文件进行检测找出其中的annotation后,使用annotation processors来处理annotation。而annotation processors使用了一套反射API并具备对JSR175规范的支持。annotation processors处理annotation的基本过程如下:首先,APT运行annotation processors根据提供的源文件中的annotation生成源代码文件和其它的文件(文件具体内容由annotation processors的编写者决定),接着APT将生成的源代码文件和提供的源文件进行编译生成类文件。简单的和前面所讲的annotation实例BRFW相比,APT就像一个在编译时处理annotation的javac。而且从sun开发者的blog中看到,java1.6 beta版中已将APT的功能写入到了javac中,这样只要执行带有特定参数的javac就能达到APT的功能。
2.为何使用APT?
    使用APT主要目的是简化开发者的工作量,因为APT可以在编译程序源代码的同时,生成一些附属文件(比如源文件、类文件、程序发布描述文字等),这些附属文件的内容也都是与源代码相关的。换句话说,使用APT就是代替了传统的对代码信息和附属文件的维护工作。使用过hibernate或者beehive等软件的朋友可能深有体会。APT可以在编译生成代码类的同时将相关的文件写好,比如在使用beehive时,在代码中使用annotation声明了许多struct要用到的配置信息,而在编译后,这些信息会被APT以struct配置文件的方式存放。
3.如何定义processor?
A.APT工作过程:
从整个过程来讲,首先APT检测在源代码文件中哪些annotation存在。然后APT将查找我们编写的annotation processor factories类,并且要求factories类提供处理源文件中所涉及的annotation的annotation processor。接下来,一个合适的annotation processors将被执行,如果在processors生成源代码文件时,该文件中含有annotation,则APT将重复上面的过程直到没有新文件生成。
B.编写annotation processors:
编写一个annotation processors需要使用java1.5 lib目录中的tools.jar提供的以下4个包:
com.sun.mirror.apt: 和APT交互的接口;
com.sun.mirror.declaration: 用于模式化类成员、类方法、类声明的接口;
com.sun.mirror.type: 用于模式化源代码中类型的接口;
com.sun.mirror.util: 提供了用于处理类型和声明的一些工具。

每个processor实现了在com.sun.mirror.apt包中的AnnotationProcessor接口,这个接口有一个名为“process”的方法,该方法是在APT调用processor时将被用到的。一个processor可以处理一种或者多种annotation类型。
一个processor实例被其相应的工厂返回,此工厂为AnnotationProcessorFactory接口的实现。APT将调用工厂类的getProcessorFor方法来获得processor。在调用过程中,APT将提供给工厂类一个AnnotationProcessorEnvironment 类型的processor环境类对象,在这个环境对象中,processor将找到其执行所需要的每件东西,包括对所操作的程序结构的参考,与APT通讯并合作一同完成新文件的建立和警告/错误信息的传输。

提供工厂类有两个方式:通过APT的“-factory”命令行参数提供,或者让工厂类在APT的发现过程中被自动定位(关于发现过程详细介绍请看http://java.sun.com/j2se/1.5.0/docs/guide/apt/GettingStarted.html)。前者对于一个已知的factory来讲是一种主动而又简单的方式;而后者则是需要在jar文件的META-INF/services目录中提供一个特定的发现路径:
在包含factory类的jar文件中作以下的操作:在META-INF/services目录中建立一个名为com.sun.mirror.apt.AnnotationProcessorFactory 的UTF-8编码文件,在文件中写入所有要使用到的factory类全名,每个类为一个单独行。

另参见:Getting Started with the Annotation Processing Tool (apt)
http://java.sun.com/j2se/1.5.0/docs/guide/apt/GettingStarted.html
来源:开心小屋
http://www.blogjava.net/happyy2k/archive/2007/05/09/116079.html
分享到:
评论

相关推荐

    JAVA Annotation学习

    通过上述示例中的JavaAnnotation压缩包,你可能包含了注解的使用示例、源码以及相关的博客文章。源码可以帮助你理解注解的实际应用,而博客文章可能深入解释了注解的工作原理和最佳实践。学习这些材料,将有助于你...

    java annotation demo

    Java 注解(Annotation)是Java语言提供的一种元编程机制,它允许程序员在源代码的各个元素(如类、方法、变量...在"java annotation demo"中,你可以学习到如何定义、应用和解析注解,进一步掌握这一强大的编程特性。

    Java Annotation学习整理.doc

    Java Annotation学习整理

    Java 5 annotation 学习笔记

    Java 5引入的注解(Annotation)是一种元数据,它提供了在代码中嵌入信息的方式,这些信息可以被编译器、JVM或其他工具在编译时或运行时使用。注解可以用来简化代码,提高可维护性,并帮助工具进行静态分析。 1. ...

    Java注解学习1-Java Annotation认知(包括框架图、详细介绍、示例说明)

    Java 注解(Annotation)是自 JDK 5.0 版本引入的一种元数据机制,用于向编译器、开发工具和运行时系统提供有关代码的附加信息。它们并不直接影响程序的执行,但可以用于代码分析、编译时验证或者运行时处理。本文将...

    Java Annotation手册

    Java Annotation手册(学习)

    Java annotation元注解原理实例解析

    Java Annotation元注解原理实例解析 Java annotation是Java语言中的一种元编程技术,用于在编译期或运行期提供元数据,以便简化代码、提高开发效率和程序的可读性。在这篇文章中,我们将深入探讨Java annotation的...

    第一章重难点之javaAnnotation学习整理.pdf

    Java Annotation,也称为注解,是Java编程语言中的一种元数据机制,用于向编译器、JVM或工具提供信息。这些信息可以用来执行编译时检查、运行时处理、文档生成等多种任务,使得代码更加易读、易维护,同时也为自动化...

    Annotation--学习:JDK内建Annotation

    在Java编程语言中,Annotation(注解)是一种元数据,它提供了在编译时和运行时为代码添加信息的方式。注解是自Java 5版本引入的特性,它增强了代码的可读性和可维护性,同时也简化了某些编程任务。在本篇中,我们将...

    java annotation 实例

    Java注解(Annotation)是Java语言提供的一种元数据机制,用于在代码中插入额外的信息,这些信息可以被编译器或运行时环境读取。它们主要用于简化代码的维护、提高可读性,以及实现编译时和运行时的检查。在本实例中...

    java annotation学习

    Java注解(Annotation)是Java语言的一个重要特性,它为代码提供了一种元数据,用于在编译时或运行时进行程序元素的处理。在Java中,注解是一种声明式编程方式,它允许程序员在代码中添加一些特殊标记,这些标记可以...

    免费下载:自己整理的java学习资料

    javaAnnotation.doc java_xml_api.chm JAVA分页大全.txt JAVA设计模式.chm JAVA配置文件编写说明文档.pdf jboss.pdf log4j使用教程.txt struts_action.txt struts标签.chm swing02.doc Tomcat.chm webAndXml.pdf ...

    java annotation入门

    Java 注解(Annotation)是 Java 语言中的一个重要特性,它为程序员提供了元数据,即关于...对于更高级的用法,如自定义注解生成 XML 映射文件等,可以进一步阅读《Java Annotation 高级应用》等相关资料进行深入学习。

    浙大软件学院培训课件java reflection annotation

    Java反射(Reflection)和注解...学习和掌握这两者,对于提升Java开发者的技能水平和解决复杂问题的能力至关重要。通过“浙大软件学院”的培训课件,开发者可以深入理解这两个特性,并学会在实际项目中有效运用。

    Java基础之注解Annotation初入篇_刘永雷

    学习java自定义注解的小例子,处理运行时注解方法,可以结合博客学习,博客地址: http://blog.csdn.net/liuyonglei1314/article/details/59494503

    JAVA帮助文档,为用户学习JAVA带来方便

    理解如何定义类,创建对象,以及使用继承、封装和多态性是Java学习的基础。 3. **包(Package)**:Java的包管理机制有助于组织和重用代码,如`java.lang`、`java.util`和`java.io`等核心包,它们包含了大量的常用...

    struts2 使用Annotation 配置的小例子

    在Java中,Annotation是一种元数据,它提供了一种安全的方式向编译器、JVM或者第三方工具提供信息。在Struts2中,Annotation允许开发者无需XML配置文件就能定义Action、结果类型、拦截器等。 在"struts2 使用...

    Java API.tar JAVA AP学习JAVA必备手册

    10. **注解(Annotation)**:注解是Java提供的一种元数据,可以用于代码分析、编译时和运行时处理。学习如何创建和使用自定义注解,以及常见的内置注解,如`@Override`、`@Deprecated`等。 在"Java API.tar"压缩...

Global site tag (gtag.js) - Google Analytics