Java 注解

注解是什么

注解是JDK1.5引入的新特性,用于对代码进行说明。注解是一种元数据,用于对代码进行说明,可以对包、类、接口、字段、方法、参数、局部变量等进行注解,可以理解为一种特殊的注释。这个注释是用来给程序看的。

注释是写给人看的,注解是写给程序看的。

为什么要使用注解

在没有注解之前,各种框架中几乎所有的配置以XML方式进行配置,因为XML方式可以降低配置和代码的耦合度,但是随着项目越来越庞大,XML的内容越来越复杂,维护成本变高。所有就有人提出了一种标记式的高耦合的配置方式,这是方式就是注解

注解:与源代码紧绑定,耦合度高,但它便捷,易于维护修改。
XML:与源代码无绑定,耦合度低,更容易扩展,但XML内容复杂,不易维护。

各有优劣!!!

注解作用

生成文档:通过代码里的标识的元数据生成javadoc文档。
编译检查:通过代码里的标识的元数据让编译器在编译期间进行检查验证。
编译动态处理:编译时通过代码里的标识的元数据动态处理,比如说用作动态代码生成。
运行动态处理:运行时通过代码里标识的元数据动态处理,比如说使用反射注入实例。

注解分类

Java提供了3种注解。
元注解:用于定义注解的注解,指定某个注解的生命周期以及作用目标等信息。

  1. @Target 标明注解使用的范围(作用的目标),有以下取值范围
public enum ElementType {
    /*类、接口(包括注解类型)或枚举声明*/
    TYPE,
    /*字段,包括枚举常量*/
    FIELD,
    /*方法*/
    METHOD,
    /*形式参数*/
    PARAMETER,
    /*构造函数*/
    CONSTRUCTOR,
    /*局部变量*/
    LOCAL_VARIABLE,
    /*注解*/
    ANNOTATION_TYPE,
    /*包*/
    PACKAGE,
    /**
     * 表示该注解能写在类型变量的声明语句中(如:泛型声明)
     *
     * @since 1.8
     */
    TYPE_PARAMETER,
    /**
     * 表示该注解能写在使用类型的任何语句中
     *
     * @since 1.8
     */
    TYPE_USE
}
  1. @Retention 表明注解被保留的阶段,有以下取值范围
public enum RetentionPolicy {
	//源码(只能再编译期可加,编译后会被丢弃)
    /*注解将会被编译器丢弃*/
    SOURCE,

	//字节码(会被编译器编译进class文件中,类加载时会被丢弃)-----默认值
    /*注解由编译器记录在类文件中,但不需要在运行时由JVM保留*/
    CLASS,
	
	//运行期(永久保存)
    /*注解由编译器记录在类文件中,运行时由JVM保留,可以通过反射来读取到它们*/
    RUNTIME
}
  1. @Documented 被标识的注解,在执行javadoc文档打包时会被保存进doc文档。
  2. @Inherited 标明注解可继承,也就是说我们的注解修饰了一个类,该类的子类将自动继承父类的该注解。

Java自带标准注解:
@Override 标明重写某个方法,编译器在对Java文件进行编译成字节码的过程中,一旦检测到某个方法上被修饰了该注解,就会去匹配对父类中是否具有一个同样方法签名的函数,如果没有,则不能通过编译。
@Deprecated 标明某个类或方法过时
@SuppressWarnings 标明要忽略的警告

**自定义注解:**可以根据自己的需求定义注解

注解实现

实现方式有基于Spring框架不基于Spring框架两种方式。

不管采用哪种方式,都是按照以下步骤实现和使用注解的

1. 声明注解
2. 实现注解处理器
借助反射,获取注解对象(通常是Class对象,Method对象,Field对象,Constructor对象,Annotation对象),读取注解的属性值,然后根据注解及其属性的值做相应处理。
在这一步骤中最重要的是获取指定包路径下的所有的Class对象,只要获取到Class对象,其他对象都可以获取到,就可以任意操作。
3. 使用注解

先声明一个注解

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.TYPE, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotion {

    String value() default "";
}

不基于Spring框架获取Class对象:

1.借助Spring提供的工具包
首先引入4个jar包:
在这里插入图片描述
代码如下:

/**
  * 借助Sping的工具类,扫描指定包,根据的bean定义获取包下的所有的Class对象
  * @param packName  包路径
  * @return
  * @throws ClassNotFoundException
  */
 public static Class[] getAllClass1(String packName) throws ClassNotFoundException {
     /*
     * ClassPathScanningCandidateComponentProvider是Spring提供的工具,可以按照自定义的类型,查找ClassPath下符合要求的class文件
     * useDefaultFilters:
     * true表示使用默认的typeFileter,会查出来很多自己不想要的class
     * false表示不使用默认的typeFileter
     */
     ClassPathScanningCandidateComponentProvider classPathScanningCandidateComponentProvider =
             new ClassPathScanningCandidateComponentProvider(false);
     //设置自定义的typeFilter
     classPathScanningCandidateComponentProvider.addIncludeFilter(new AnnotationTypeFilter(MyAnnotion.class));
     //根据自定义的typeFilter,在指定的包下获取满足条件的bean的定义
     Set<BeanDefinition> beanDefinitions =  classPathScanningCandidateComponentProvider.findCandidateComponents(packName);
     if (beanDefinitions.isEmpty()) {
         return null;
     }
     Class[] clazzes = new Class[beanDefinitions.size()];
     //根据bean的定义获取bean的名称,然后通过反射获取bean的class对象,将class对象放入数组中
     int i = 0;
     for (BeanDefinition beanDefinition : beanDefinitions) {
         Class clazz = Class.forName(beanDefinition.getBeanClassName());
         if (Objects.isNull(clazz)) {
             continue;
         }
         clazzes[i++] = clazz;
     }
     return clazzes;
 }

2.借助reflections反射工具包
首先引入3个jar包:
在这里插入图片描述
代码如下:

/**
  * 借助reflections反射工具包,扫描指定包,获取包下所有的Class对象
  * @param packName  包路径
  * @return
  */
 public static Class[] getAllClass2(String packName) {
     Reflections reflections = new Reflections(PATH);
     Set<Class<?>> typesAnnotatedWith = reflections.getTypesAnnotatedWith(MyAnnotion.class);
     Class[] classes = new Class[typesAnnotatedWith.size()];
     int i = 0;
     for (Class c : typesAnnotatedWith) {
         classes[i++] = c;
     }
     return classes;
 }

3.自己实现(采用JDK的API)
只需依赖JDK 1.8即可
代码如下:

/**
  * 自己实现:获取指定包路径下的所有类的Class对象
  * @param packName   包路径
  * @return
  * @throws IOException
  * @throws ClassNotFoundException
  */
 public static Class[] getAllClass3(String packName) throws IOException, ClassNotFoundException {
     URL resource = Thread.currentThread().getContextClassLoader().getResource(packName.replace('.', '/'));
     String[] files = new File(resource.getFile()).list();
     Class[] clazzs = new Class[files.length];
     for (int i = 0; i < files.length; i++) {
         clazzs[i] = Class.forName(packName + "." + files[i].replace(".class", ""));
     }
     return clazzs;
 }

不基于Spring框架获取Class对象:

待补充

注解本质(实现原理)

首先定义一个注解的关键字为"@interface",格式如下:

public @interface 注解名称{
	属性列表;
}

首先我们先按照注解的定义创建一个注解MyAnnotation.java,编译之后得到MyAnnotation.class文件
在这里插入图片描述
然后再用javap -c MyAnnotation.class命令进行反编译,结果如下

在这里插入图片描述
可以看出反编译之后的注解其实就是一个接口,它继承了Annotation接口。

没错,注解的本质就是一个继承了Annotation接口的接口。

解析一个类或者方法的注解有两种方式,一种是编译器的直接扫描,一种是运行期反射

编译器的扫描指的是编译器在对Java代码编译成字节码的过程中检测到某个类或方法被一下注解修饰,这时会对这些注解进行处理。

运行期反射将会在下面进行详细说明

对于一个类或者接口来说,Class类对象中提供了以下方法用于反射注解。

  • getAnnotation:返回指定的注解
  • isAnnotationPresent:判断当前元素是否被指定注解修饰
  • getAnnotations:返回所有的注解
  • getDeclaredAnnotation:返回本元素的指定注解
  • getDeclaredAnnotations:返回本元素的所有注解,不包含父类继承而来的。

首先

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值