Java中的注解以及自定义注解

1、Annotation(注解) 概述

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

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

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

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

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

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

2、自定义 Annotation

2.1、定义注解本身

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

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

比如:

public @interface MyAnnotation{ 
 
}

2.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,所以只是拿到值后进行了打印,并没有一些复杂的操作

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

等下咱们会用自定义的注解来模拟实现一个 @Test单元测试的功能

2.3、注解中的特殊属性

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

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

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

3、写一个自定义注解的真实案例

了解了上述的内容后,咱们按照上边介绍的语法,开始自定义一个注解来模拟实现 @Test单元测试的功能

package com.cj.study.annotation.app1;
 
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方法没执行的话,就对了。

运行看下运行结果:

但是从结果可以看出和咱们预期的并不一样,那到底怎么回事呢?

原因是:定义注解的时候除了上边说的语法,还需要一个东西,那就是“元注解”

那什么是元注解呢?

简单的来说服务于注解的注解就是元注解,它主要用来标识你写的注解保留范围(作用范围)以及出现的位置

JDK中定义了四种元注解:

@Retention:注解的保留范围,是个枚举,有如下可选值
        RetentionPolicy.SOURCE:注解存在于源文件中
        RetentionPolicy.CLASS:注解存在于源字节码文件中
        RetentionPolicy.RUNTIME:注解存在于运行时

@Target:注解出现的位置(比如字段上、方法上等),也是个枚举,有如下可选值

  • ElementType.TYPE:允许被修饰的注解作用在类、接口和枚举上;
  • ElementType.FIELD:允许作用在属性字段上;
  • ElementType.METHOD:允许作用在方法上;
  • ElementType.PARAMETER:允许作用在方法参数上;
  • ElementType.CONSTRUCTOR:允许作用在构造器上;
  • ElementType.LOCAL_VARIABLE:允许作用在本地局部变量上;
  • ElementType.ANNOTATION_TYPE:允许作用在注解上;
  • ElementType.PACKAGE:允许作用在包上。
  • ElementType.TYPE_PARAMETER
  • ElementType.TYPE_USE

@Documented: 用于指定被该元 Annotation 修饰的 Annotation 类将被 javadoc 工具提取成文档.

@Inherited: 被它修饰的 Annotation 将具有继承性.如果某个类使用了被 @Inherited 修饰的 Annotation, 则其子类将自动具有该注解

常用的是前两种,了解了元注解后,下面把咱们的代码加上元注解再试一下

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
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值