1. 注解的概念
注解(Annotation)就是对声明的元素进行说明,注释的。
元素可以是:类、字段、方法、方法参数…
给元素添加注解相当于贴了一个标签,至于此注解什么时候用、怎么用不归它管。
单但给某个元素添加了个注解,却不写该注解相关的逻辑判断,屁用没有。
比如快递的分拣:
集散中心有很多快递,快递上有商家贴的标签,标签上有收件人的地址等信息。
分拣机器通过标签将同一个城市、同一个区的快递放到一个口袋中。
商家给货物贴标签的动作就相当于给某个元素添加注解。
分拣机通过标签分拣相当于程序通过注解区分元素(具体怎么用由你决定,区分只是其中一种逻辑而已)
2. 注解可以声明的位置
包上、类上、方法上、字段上、形参上、局部变量上等等。
package java.lang.annotation;
public enum ElementType {
/** Class, interface (including annotation type), or enum declaration */
//类、接口、注解类、枚举类型
TYPE,
/** Field declaration (includes enum constants) */
//字段
FIELD,
/** Method declaration */
//方法
METHOD,
/** Formal parameter declaration */
//形参
PARAMETER,
/** Constructor declaration */
//构造器上
CONSTRUCTOR,
/** Local variable declaration */
//局部变量上
LOCAL_VARIABLE,
/** Annotation type declaration */
//注解类上
ANNOTATION_TYPE,
/** Package declaration */
//包上
PACKAGE,
/**
* Type parameter declaration
*
* @since 1.8
*/
//自定义类型参数上
TYPE_PARAMETER,
/**
* Use of a type
*
* @since 1.8
*/
//使用类型的任意语句中
TYPE_USE
}
3. 元注解
元注解就是自定义注解上的注解,作用是设置自定义注解的作用域、声明位置等信息。
Java元注解:
@Retention :描述自定义注解被保留的阶段
@Target :描述自定义注解的作用位置
@Documented :描述自定义注解是否被抽取到API文档中
@Inherited :描述自定义注解是否被子类继承
@Native :表示这个字段可以被本地代码引用,常常被代码生成工具使用.
@Repeatable :重复注解,允许一个元素上多次使用同一个注解
4. 自定义注解
package annotation_test;
import java.lang.annotation.*;
/**
* @Date: 2022/4/28 11:04
* @author: ZHX
* @Description: 自定义注解
*/
@Retention(RetentionPolicy.SOURCE)
@Target(ElementType.ANNOTATION_TYPE)
public @interface CustomAnnotation { //注解隐式继承Annotation接口,注解也是个接口
//可定义属性的类型:基本类型、String、枚举、注解、以上类型的数组
int i();
String value() default ""; //如果属性只有value则可以不写。
Override c();//注解类型
ElementType e1(); //枚举类型
ElementType[] e2(); //枚举数组,使用时数组只有一个值可以省略大括号.
}
5. JDK内置的注解
JDK预定义好的注解,直接用就好了。
@Override 检查覆盖重写
@Deprecated 标记已过时
@SupressWarinings("all") 压制警告
@FunctionalInterface 标识该接口符合函数式接口
@SafeVarargs 断言参数安全,压制"unchecked"警告
6. 练习:注解+反射批量执行方法
要求:
有个annotation_test包,包中有多个类,每个类中都有多个方法。
将包中所有类中的所有带有@Test注解的方法都执行一遍。
类结构、自定义注解结构
package annotation_test;
/**
* @Date: 2022/4/28 14:28
* @author: ZHX
* @Description:
*/
public class A {
@Test
public void method1(){
System.out.println("A:method1");
}
@Test
public void method2(){
System.out.println("A:method2");
System.out.println("");
}
@Test
public void method3(){
System.out.println("A:method3");
}
}
package annotation_test;
/**
* @Date: 2022/4/28 14:28
* @author: ZHX
* @Description:
*/
public class B {
@Test
public void method1(){
System.out.println("B:method1");
}
@Test
public void method2(){
System.out.println("B:method2");
System.out.println("");
}
@Test
public void method3(){
System.out.println("B:method3");
}
}
package annotation_test;
import java.lang.annotation.*;
/**
* @Date: 2022/4/28 11:04
* @author: ZHX
* @Description: 自定义注解@Test, 用来批量测试方法
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Test { //注解隐式继承Annotation接口,注解也是个接口
}
实现
import java.io.File;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
/**
* @Date: 2022/4/28 14:30
* @author: ZHX
* @Description: 简单实现下包扫描,批量执行指定方法。Junit测试一样的原理,递归+反射+注解实现的。
*/
public class Test {
public static void main(String[] args) throws ClassNotFoundException, IOException, InvocationTargetException, IllegalAccessException, InstantiationException {
//包扫描,将annotation_test包中的所有类的所有带@Test方法
String packageName = "annotation_test";
ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
//获取资源的url路径
URL url = contextClassLoader.getResource(packageName);
String path = url.getPath();// /D:/Project/java-advance/out/production/day15-annotation/annotation_test
String protocol = url.getProtocol();
//是文件,
if (protocol.equals("file")) {
File directory = new File(path);
//得到包下的所有文件和文件夹。
File[] files = directory.listFiles();
for (File file : files) {
String f = file.getPath(); // D:\Project\java-advence\out\production\day15-annotation\annotation_test\B.class
//是.class文件,符合类加载
if (f.endsWith(".class")) {
//处理得到 类的全限定名 包名.类名
String prefix = Test.class.getClassLoader().getResource("").getPath().substring(1);
f = f.substring(prefix.length(), f.length() - 6).replace("\\", ".");
//利用反射 判断方法上是否有@Test注解并执行
Class<?> clazz = Class.forName(f);
//跳过接口
if (clazz.isInterface()) continue;
Method[] methods = clazz.getMethods();
Object o = clazz.newInstance();
for (Method method : methods) {
if (method.isAnnotationPresent(annotation_test.Test.class)) {
method.invoke(o);
}
}
}
}
}
}
}
注: junit测试底层就是这个原理,什么spring注解等等都是这样实现的。