java注解实现原理

       注解并不直接影响代码语义,但是它工作的方式被看做类似程序的工具或者类库,它会反过来对正在运行的程序语义有所影响。注解可以从源文件、class文件或者在jvm运行时以反射等多种方式被读取。

   

一、java的内置注解

1)Override:该注解只能修饰方法,表示子类要重写父类对应的方法。

2)Deprecated:该注解可由于修饰类、方法、属性。表示不建议被使用。

public class DeprecatedTest
{
	@Deprecated
	public void doSomething()
	{
		System.out.println("deprecated");
	}
	public static void main(String[] args)
	{
		new DeprecatedTest().doSomething();
	}
}
3)SuppressWarnings("unchecked"):用来抑制编译时的警告信息,与前两个注释不同的地方是,这个注解需要添加一个参数才能正确使用,可以被使用的参数值都已经被定义好的。


二、自定义注解

       自定义注解只能通过@Interface关键字来定义,当我们用@Interface关键字定义一个注解时,该注解默认自动继承了java.lang.annotation接口。

       另外需要注意的是,java.lang.annotation是一个接口而不是注解。

1)定义一个注解

/**
 * @author Administrator
 * 定义一个注解
 */
public @interface Annotation
{
	String value();
}

如上,我们使用关键字@Interface定义了一个注解,并且注解里还定义个一个属性value(当做一个属性看待)

2)使用注解:

@Annotation(value="hello")
public class AnnotationUsage
{
	@Annotation("world")
	public void method()
	{
		System.out.println("usage of annotation");
	}
	public static void main(String[] args)
	{
		AnnotationUsage au = new AnnotationUsage();
		au.method();
	}
}
注解里面定义属性,可以任意取名,但其中value是一个特殊的属性名,如上可以看到,一般我们必须以name=value的方式赋值,即明确要给谁赋值,但是如果注解中的属性名为value时,可以直接赋值,就如在method方式上添加的注解一样。


三、java的元注解

       java元注解的作用就是负责注解其他的注解,并使用注解。java一共为我们提供了四个元注解:@Retention、@Target、@Document、@Inherited

1)  @Retention:

         定义了被注解的注解的生命周期。有些注解出现在源代码中,而被编译器丢弃;有些则编译在class文件中;编译在class文件中的注解可能被虚拟机忽略;有些则在class类被装载时被读取。

     声明周期的枚举值,通过RetentionPolicy获取

     SOURCE:在源文件中有效(即源文件保留)

     CLASS:在class文件中有效(即class文件中保留)

     RUNTIME:在运行时有效(即运行时保留)

      

     下面我们来看看java内置注解的声明周期:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}
仅保留在源文件中,且作用范围只能在方法上。

@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface Deprecated {
}
保留到运行期间

@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
@Retention(RetentionPolicy.SOURCE)
public @interface SuppressWarnings
仅在编译期间告知编译程序来抑制警告,所以不必将这个注解信息保存在class文件中


注解中大量用到了java反射技术,java提供了以下API,用于注解和反射

Class、Construtor、Field、Method、Package等类,都实现了AnnotatedElement接口,而该接口提供了获取注解、判断注解是否存在等公共方法。

a)定义一个注解,该注解被元注解限定的声明周期为.RUNTIME

@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation
{
	String hello() default "shensiyuan";
	String world();
}
b)用定义的注解去注解方法和类:

@MyAnnotation(hello="beijing",world="shanghai")
public class MyTest
{
	@MyAnnotation(hello="hangzhou",world="xihu")
	public void output()
	{
		System.out.println("doSomething");
	}
}
c)方法测试

	public static void main(String[] args) throws Exception, NoSuchMethodException
	{
		MyTest mt = new MyTest();
		Class<MyTest> clssType = (Class<MyTest>) mt.getClass();
		Method method = clssType.getMethod("output",new Class[]{});
		System.out.println(method.getName());
		
		//判断注解MyAnnotation是否存在于方法上,存在返回true,不存在返回false
		if(method.isAnnotationPresent(MyAnnotation.class))
		{
			method.invoke(mt,new Object[]{});
		}
	}
d)运行结果

output
doSomething
从结果可以看出,在运行期间,注解还是存在于方法之上的,即注解MyAnnotation的声明周期为运行期间。

现在我们做如下测试,把自定义的注解做如下改动:

@Retention(RetentionPolicy.CLASS)
public @interface MyAnnotation
{
	String hello() default "shensiyuan";
	String world();
}
或者

@Retention(RetentionPolicy.SOURCE)
public @interface MyAnnotation
{
	String hello() default "shensiyuan";
	String world();
}
运行得到的结果为:

output

2)@Target

      用于描述注解所能修饰的对象范围,从ElementType中取值

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

 如java的内置注解@Override只能注解方法:   

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}

3) @Documented:

     可以让被该注解标示的注解被如javadoc此类的工具文档化

    生成javadoc的步骤:选中类所在的包—>project ->Generate Javadoc

    在生成的index.html中,注解信息会被文档化    

@MyDocumentAnnotation
public void output()

三、基于注解的框架实现

JUnit单元测试有两个基本版本:JUnit3和JUnit4,前者是基于java的反射实现的,而后者是基于java的注解和反射实现的。

先看看JUnit3的实现:

package annotation;

import junit.framework.TestCase;

public class TestJUint3 extends TestCase
{
	public void testSayHello()
	{
		System.out.println("hello world");
	}
	
	public void sayHello()
	{
		System.out.println("say hello");
	}
	
	public void testSayWorld()
	{
		System.out.println("say world");
	}
}
运行结果:

hello world
say world
从结果可以看到,这个是通过测试类的Class对象,获取所有的方法,然后只执行以“test”开头的方法。

在来看看JUnit4:

package annotation;

import org.junit.Test;

public class TestJUnit4
{
	@Test
	public void testSayHello()
	{
		System.out.println("hello world");
	}
	
	@Test
	public void sayHello()
	{
		System.out.println("say hello");
	}
	
	public void testSayWorld()
	{
		System.out.println("say world");
	}
}
执行结果:

hello world
say hello
可以看出来,这次测试类里面只执行了被@Test注解的方法,而以test开头的方法不再被执行。

虽然我们没有看源代码,但是我们可以猜想出来JUnit4的实现原理:

1)通过测试类的类名获取其Class对象

2)获取该类下面所有的public方法,即一个method数组,然后遍历该数组,获取每一个method对象

3)通过method.isAnnotationPresent(Test.class)方法,

4)如果返回true,则表示该方法被@Test所注解,执行该方法,否则不执行。

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值