java注解详解

目录

1 Annotation(注解) 概述

2 元注解

3 自定义注解

3.1 使用关键字@interface定义一个类而已,这个类就是注解。

3.2 定义注解中的属性

3.3  注解中的特殊属性

4 应用实例

 


 

1 Annotation(注解) 概述

(1)、注解起到标识做用,比如Junit的@Test注解。

Junit会在运行时检查方法上是否存在此注解,如果存在,就通过 反射 来运行你的方法。注意标红的反射两个字,反射在注解里相当重要,写完你的自定义注解类后没啥用,必须要用反射才能让它动起来!所以需要对反射有了解,感兴趣的小可爱可以看下这篇:用最直接的大白话来聊一聊Java中的反射机制

(2)、从 JDK 5.0 开始,Java 增加了对 Annotation(注解)的支持。

(3)、 注解其实就是代码里的特殊标记,它可以用于替代配置文件,也就是说,传统方式通过配置文件告诉类如何运行,有了注解技术后,开发人员可以通过注解告诉类如何运行。用了注解以后,可以减少项目的配置文件,使代码看起来更优雅。在Java技术里注解的典型应用是:可以通过反射技术去得到类里面的注解,以决定怎么去运行类。

(4)、注解可以代替配置文件会被大量的应用到实际项目中去,又由于注解看起很简洁,因此如果对注解的知识了解的不够的话,到项目应用中,有时候你会一头雾水,不知道咋回事,会比较懵。所以,学好注解非常重要

(5)、掌握注解技术的要点: 如何定义注解、如何反射注解,并根据反射的注解信息,决定如何去运行类

1注解是一种配置文件,它代替了xml配置文件,使代码更加的简洁。

2注解本身不起作用,起作用的是注解解释器,注解需要和反射一起使用才能发挥大的威力。

  • 提供信息给编译器: 编译器可以利用注解来探测错误和警告信息
  • 编译阶段时的处理: 软件工具可以用来利用注解信息来生成代码、Html文档或者做其它相应处理。
  • 运行时的处理: 某些注解可以在程序运行的时候接受代码的提取

常见的作用有以下几种:

  • 生成文档。这是最常见的,也是java 最早提供的注解。常用的有@see @param @return 等;
  • 在编译时进行格式检查。如@Override放在方法前,如果你这个方法并不是覆盖了超类方法,则编译时就能检查出;
  • 跟踪代码依赖性,实现替代配置文件功能。比较常见的是spring 2.5 开始的基于注解配置。作用就是减少配置。现在的框架基本都使用了这种配置来减少配置文件的数量;
  • 在反射的 Class, Method, Field 等函数中,有许多于 Annotation 相关的接口。可以在反射中解析并使用 Annotation。

2 元注解

元注解:

  元注解的作用就是负责注解其他注解。Java5.0定义了4个标准的meta-annotation类型,它们被用来提供对其它 annotation类型作说明。Java5.0定义的元注解:
    1.@Target,
    2.@Retention,
    3.@Documented,
    4.@Inherited
  这些类型和它们所支持的类在java.lang.annotation包中可以找到。下面我们看一下每个元注解的作用和相应分参数的使用说明。

  @Target:

   @Target说明了Annotation所修饰的对象范围:Annotation可被用于 packages、types(类、接口、枚举、Annotation类型)、类型成员(方法、构造方法、成员变量、枚举值)、方法参数和本地变量(如循环变量、catch参数)。在Annotation类型的声明中使用了target可更加明晰其修饰的目标。

作用:用于描述注解的使用范围(即:被描述的注解可以用在什么地方)

  取值(ElementType)有:

    1.CONSTRUCTOR:用于描述构造器
    2.FIELD:用于描述域
    3.LOCAL_VARIABLE:用于描述局部变量
    4.METHOD:用于描述方法
    5.PACKAGE:用于描述包
    6.PARAMETER:用于描述参数
    7.TYPE:用于描述类、接口(包括注解类型) 或enum声明
 

@Target(ElementType.TYPE)
public @interface Table {
    /**
     * 数据表名称注解,默认值为类名称
     * @return
     */
    public String tableName() default "className";
}
 
@Target(ElementType.FIELD)
public @interface NoDBColumn {
 
}

注解Table 可以用于注解类、接口(包括注解类型) 或enum声明,而注解NoDBColumn仅可用于注解类的成员变量。

  @Retention:

  @Retention定义了该Annotation被保留的时间长短:某些Annotation仅出现在源代码中,而被编译器丢弃;而另一些却被编译在class文件中;编译在class文件中的Annotation可能会被虚拟机忽略,而另一些在class被装载时将被读取(请注意并不影响class的执行,因为Annotation与class在使用上是被分离的)。使用这个meta-Annotation可以对 Annotation的“生命周期”限制。

  作用:表示需要在什么级别保存该注释信息,用于描述注解的生命周期(即:被描述的注解在什么范围内有效)

  取值(RetentionPoicy)有:

    1.SOURCE:在源文件中有效(即源文件保留)
    2.CLASS:在class文件中有效(即class保留)
    3.RUNTIME:在运行时有效(即运行时保留)

  Retention meta-annotation类型有唯一的value作为成员,它的取值来自java.lang.annotation.RetentionPolicy的枚举类型值。具体实例如下:

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Column {
    public String name() default "fieldName";
    public String setFuncName() default "setField";
    public String getFuncName() default "getField"; 
    public boolean defaultDBValue() default false;
}

 Column注解的的RetentionPolicy的属性值是RUTIME,这样注解处理器可以通过反射,获取到该注解的属性值,从而去做一些运行时的逻辑处理

  @Documented:

  @Documented用于描述其它类型的annotation应该被作为被标注的程序成员的公共API,因此可以被例如javadoc此类的工具文档化。Documented是一个标记注解,没有成员。
 

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Column {
    public String name() default "fieldName";
    public String setFuncName() default "setField";
    public String getFuncName() default "getField"; 
    public boolean defaultDBValue() default false;
}

@Inherited:

  @Inherited 元注解是一个标记注解,@Inherited阐述了某个被标注的类型是被继承的。如果一个使用了@Inherited修饰的annotation类型被用于一个class,则这个annotation将被用于该class的子类。

  注意:@Inherited annotation类型是被标注过的class的子类所继承。类并不从它所实现的接口继承annotation,方法并不从它所重载的方法继承annotation。  当@Inherited annotation类型标注的annotation的Retention是RetentionPolicy.RUNTIME,则反射API增强了这种继承性。如果我们使用java.lang.reflect去查询一个@Inherited annotation类型的annotation时,反射代码检查将展开工作:检查class和其父类,直到发现指定的annotation类型被发现,或者到达类继承结构的顶层。
 

/**
 * 
 * @author peida
 *
 */
@Inherited
public @interface Greeting {
    public enum FontColor{ BULE,RED,GREEN};
    String name();
    FontColor fontColor() default FontColor.GREEN;
}

3 自定义注解

使用@interface自定义注解时,自动继承了java.lang.annotation.Annotation接口,由编译程序自动完成其他细节。在定义注解时,不能继承其他的注解或接口。@interface用来声明一个注解,其中的每一个方法实际上是声明了一个配置参数。方法的名称就是参数的名称,返回值类型就是参数的类型(返回值类型只能是基本类型、Class、String、enum)。可以通过default来声明参数的默认值。

定义注解格式:

public @interface 注解名 {定义体}

  注解参数的可支持数据类型:

    1.所有基本数据类型(int,float,boolean,byte,double,char,long,short)
    2.String类型
    3.Class类型
    4.enum类型
    5.Annotation类型
    6.以上所有类型的数组

  Annotation类型里面的参数该怎么设定: 
  第一,只能用public或默认(default)这两个访问权修饰.例如,String value();这里把方法设为defaul默认类型;   
  第二,参数成员只能用基本类型byte,short,char,int,long,float,double,boolean八种基本数据类型和 String,Enum,Class,annotations等数据类型,以及这一些类型的数组.例如,String value();这里的参数成员就为String;  
  第三,如果只有一个参数成员,最好把参数名称设为"value",后加小括号.例:下面的例子FruitName注解就只有一个参数成员。

3.1 使用关键字@interface定义一个类而已,这个类就是注解。

和之前定义一个类的不同是:之前定义类使用的是class关键字,现在定义注解使用的是@interface

比如:

public @interface MyAnnotation{ 
 
}

3.2 定义注解中的属性

基本形式:类型 属性名称();    

                 比如:String name(); 

特别注意:(1)属性值后边有一个括号!!!这个是和之前定义属性不一样的地方

                  (2)注解的属性的类型只能是:基本类型、String、Class、枚举、注解类型及以上类型的一维数组。

定义注解属性的默认值:类型 属性名称() default 默认值;

                      比如:String name() default "zhangsan";

举例:定义注解

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface MyAnnotation {
 
    String value() default "abc";
 
    String name() default "zhangsan";
 
}

注解属性定义完以后,使用注解:@MyAnnotation(value = "123",name = "lisi")

举例:使用注解

public class Test {
    
    @MyAnnotation(value = "123",name = "lisi")
    public static void test(){
        System.out.println("这是test");
    }
 
}

举例:利用反射获取方法上的注解,并获取对应的值

public class Test {
 
    public static void main(String[] args) throws Exception {
 
        Class clazz = Class.forName("com.fours.intercepter.Test");
        Method[] ms = clazz.getMethods();
        for (Method m : ms) {
            if(m.isAnnotationPresent(MyAnnotation.class)){
                String value = m.getAnnotation(MyAnnotation.class).value();
                String name = m.getAnnotation(MyAnnotation.class).name();
                System.out.println("value:"+value);
                System.out.println("name:"+name);
            }
        }
    }
 
    @MyAnnotation(value = "123",name = "lisi")
    public static void test(){
        System.out.println("这是test");
    }
 
}

最后看下输出打印的结果

可以看到,我们自定义个一个注解,然后在方法上使用了自己的注解,最后通过反射拿到这个方法上的注解和属性值

由于这里只是个demo,所以只是拿到值后进行了打印,并没有一些复杂的操作

但是,在实际应用中,拿到了方法上自定义的注解和属性值后,那就可以根据你具体的业务逻辑进行一些对应的操作

3.3  注解中的特殊属性

(1)、特殊属性value:如果注解中只有一个属性,而且这个属性的名称是value的话,那么使用注解时可以省略value=部分,可以直接写成这样@MyAnnotation(“xxx");

(2)、特殊类型[] value():如果注解中只有一个属性,而且这个属性名称是value且数据类型是数组,那么
使用方式:四种都ok,根据情况而定
@MyAnnotationDemo1(value={"a","b"})
@MyAnnotationDemo1({"a","b"})
@MyAnnotationDemo1({"a"})
@MyAnnotationDemo1("a")

以上就是注解的基本语法了,下面通过代码写一个真实的小案例,来感受一下注解

4 应用实例

package com.cj.study.annotation.app1;
 
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
 
@Retention(RetentionPolicy.RUNTIME)//注解存在于运行时
@Target(ElementType.METHOD)//说明MyTest注解只能用在方法上
public @interface MyTest {
	
}
package com.cj.study.annotation.app1;
 
public class DemoTest1 {
	
	@MyTest
	public void test1(){
		System.out.println("test1执行了");
	}
	
	public void test2(){
		System.out.println("test2执行了");
	}
}
package com.cj.study.annotation.app1;
 
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
 
//反射注解:
	//取得类的字节码
	//反射其中的成员,此处就是方法成员
	//看谁的上面有MyTest注解
	//谁有,就执行谁
public class MyJunitRunner {
 
	public static void main(String[] args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, InstantiationException {
		//取得类的字节码
		Class clazz = DemoTest1.class;
		//反射其中的成员,此处就是方法成员
		Method methods[] = clazz.getMethods();//得到DemoTest1中的所有公有的方法
		//看谁的上面有MyTest注解
		for(Method m:methods){
			//谁有,就执行谁
			boolean b = m.isAnnotationPresent(MyTest.class);
			System.out.println(b+"==="+m.getName());
			if(b){
				m.invoke(clazz.newInstance(), null);
			}
		}
	}
 
}

发现test1方法执行了, test2方法没执行,和预期的一样。

 

 

 

 

 

 

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值