自定义注解+反射读取
一、注解
1.简介
- JDK1.5引入的新技术,不是程序本身,可以对程序作出解释(跟注释也没什么区别)
- 注解可以被其它程序(如:编译器)读取(这是与注释最大的区别,它可以作为信息处理的处理流程)
2.内置注解
@Override
- 定义在
java .lang.Override
中,此注释只适用于修辞方法,表示一个方法声明打算重写超类中的另一个方法声明
@Deprecated
- 定义在
java.lang.Deprecated
中,此注册可用于修辞方法、属性、类,表示不鼓励成员有使用这样的元素,通常是因为它很危险或者存在更好的选择,但是它也依旧能使用,只是不建议了
@SuppressWarnings
- 定义在
java.lang.SuppressWarnings
中,用来抑制编译时的警告信息
参数 | 说明 |
---|---|
deprecation | 使用了过时的类或方法的警告 |
unchecked | 执行了未检查的转换时的警告,如使用集合时未指定泛型 |
fallthrough | 当在switch语句时发生case穿透 |
path | 在类路径、源文件路径等中有不存在路径的警告 |
serial | 当在可序列化的类上缺少serialVersionUID定义时的警告 |
finally | 任何finally子句不能完成时的警告 |
all | 关于以上所有情况的警告 |
其实
@SuppressWarnings
注解用的也比较少,大多数人都是喜欢用all
参数而已
二、自定义注解
1.使用@interface自定义注解
使用其注解会自动继承java.lang.annotation.Annotation
接口
2.要点
2.1.@interface用来声明一个注解
- 格式为:
public @interface 注解名(定义体)
2.2.其接口内每个方法实际是声明一个配置参数
- 方法的名称就是参数的名称
- 返回值类型就是参数的类型(返回值类型只能是基本类型、Class、String、enum)
- 可以通
default
来声明参数的默认值 - 如果只有一个参数成员,一般参数名为
value
2.3.注意
- 注解元素必须要有值!我们定义注解元素时,经常使用空字符串、0作为默认值
- 也经常使用负数(比如:
-1
)表示不存在的含义
3.元注解
3.1介绍
- 元注解是作用就是负责注解其他注解
- Java定义了4个标准的
meta-annotation
类型,他们被用来提供对其它annotation
类型作说明
3.2类型
这些类型和它们所支持的类在java.lang.annotation
包中可以找到:
@Target
作用:用于描述注解的适用范围(即:被描述的注解可以用在什么地方)
所修饰范围 | 取值 ElementType |
---|---|
package 包 | PACKAGE |
类、接口、枚举、Annotation类型 | TYPE |
类型成员(方法、构造方法、成员变量、枚举值) | CONSTRUCTOR:用户描述构造器 FIELD:用于描述域 METHOD:用于描述方法 |
方法参数和本地变量 | LOCAL、VARIABLE::用于描述局部变量 METHOD:用于描述参数 |
package java.lang.annotation;
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Target {
/**
* Returns an array of the kinds of elements an annotation type
* can be applied to.
* @return an array of the kinds of elements an annotation type
* can be applied to
*/
ElementType[] value();
}
@Retention
取值RetentionPolicy | 作用 |
---|---|
SOURCE | 在源文件中有效(即源文件保留) 当Java文件编译成class文件注解被遗弃 |
CLASS | 在class文件中有效(即class保留) 但jvm加载class文件时候被遗弃 |
RUNTIME | 在运行时有效(即运行时保留) 注解不仅被保存到class文件中,jvm加载class文件之后,仍然存在 Runtime可以被反射机制读取 |
这3个生命周期分别对应于:Java源文件(.java文件) —> .class文件 —> 内存中的字节码
首先要明确生命周期长度 SOURCE < CLASS < RUNTIME ,所以前者能作用的地方后者一定也能作用。一般如果需要在运行时去动态获取注解信息,那只能用 RUNTIME 注解;如果要在编译时进行一些预处理操作,比如生成一些辅助代码(如 ButterKnife),就用 CLASS注解;如果只是做一些检查性的操作,比如 @Override 和 @SuppressWarnings,则可选用 SOURCE 注解。
package java.lang.annotation;
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Retention {
/**
* Returns the retention policy.
* @return the retention policy
*/
RetentionPolicy value();
}
@Document
@Inherited
3.3实现
注解类:
package cn.edu.chan.test;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @Author 蝉
* @Date 2021/8/7 12:38
*/
@Target(value={ElementType.METHOD,ElementType.TYPE}) //能注解到方法和类上
@Retention(RetentionPolicy.RUNTIME)
public @interface AnnotationTest01 {
String str() default ""; //若没有定义初始值,定义的注解上需要传入相应值,否则报错
int integer1() default 0;
int integer2() default -1; //-1表示这个值不存在的含义
}
实现测试类:
package cn.edu.chan.test;
/**
* @Author 蝉
* @Date 2021/8/7 13:10
*/
//@AnnotationTest01
public class Test01 {
@AnnotationTest01(str = "11",integer1 = 0,integer2 = 1)
public void test01(){
}
}
三、反射读取注解
1.什么是ORM(Object Relationship Mapping)
缩写如其名:对象关系映射
如:
- 类与表结构对应
- 属性和字段对应
- 对象和记录对应
2.使用注解完成类与表结构的映射关系
类的注解类:
package cn.edu.chan.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @author 蝉
* @version 1.0
* @date 2021/8/8 10:08
*/
@Target(value={ElementType.TYPE}) //能注解到类上
@Retention(RetentionPolicy.RUNTIME)
public @interface AnnotationTest02 {
String value();
}
属性的注解类:
package cn.edu.chan.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @author 蝉
* @version 1.0
* @date 2021/8/8 10:13
*/
@Target(value={ElementType.FIELD}) //能注解到属性上
@Retention(RetentionPolicy.RUNTIME)
public @interface AnnotationTest03 {
String columName();
String type();
int length();
}
实体:
package cn.edu.chan.annotation;
/**
* @author 蝉
* @version 1.0
* @date 2021/8/8 10:08
*/
@AnnotationTest02("user")
public class User {
@AnnotationTest03(columName = "name", type = "varchar", length = 10)
private String username;
@AnnotationTest03(columName = "psw", type = "varchar", length = 15)
private String password;
@AnnotationTest03(columName = "age", type = "int", length = 3)
private int age;
public User() {
}
public User(String username, String password, int age) {
this.username = username;
this.password = password;
this.age = age;
}
@Override
public String toString() {
return "User{" +
"username='" + username + '\'' +
", password='" + password + '\'' +
", age='" + age + '\'' +
'}';
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
反射测试类:
package cn.edu.chan.annotation;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
/**
*使用反射读取注解的信息,模拟处理注解信息的流程
* @author 蝉
* @version 1.0
* @date 2021/8/8 10:08
*/
public class Test02 {
public static void main(String[] args) throws ClassNotFoundException {
Class clazz = Class.forName("cn.edu.chan.annotation.User");
//获取类的所有的注解
Annotation[] annotations = clazz.getAnnotations();
for (Annotation annotation : annotations) {
System.out.println(annotation);
}
//获得类的指定的注解
AnnotationTest02 annotation = (AnnotationTest02)clazz.getAnnotation(AnnotationTest02.class);
System.out.println(annotation.value());
//获得类的属性的注解
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
AnnotationTest03 annotationTest03 = field.getAnnotation(AnnotationTest03.class);
System.out.println(annotationTest03.columName()+"___"+
annotationTest03.type()+"___"+annotationTest03.length());
}
//注解反射获取表名等信息,拼出SQL语句,然后执行相关SQL,在数据库中可生成相应实体类表
}
}
四、反射机制
1.概述
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
- 要解剖一个类,必须先要获取到该类的字节码文件对象,而解剖使用的就是Class类中的方法.所以先要获取到每一个字节码文件对应的Class类型的对象。
- 对象是表示或封装一些数据。
- 一个类被加载后,JVM会创建一个对应该类的Class对象,类的整个结构信息会放到对应的Class对象中。
- 这个Class对象就像一面镜子一样,通过这面镜子我可以看到对应类的全部信息。
2.Class对象的获取
Class对象的获取并没有那么高大上,主要的一些获取方法还是那些常用的,记住比如
Class.forName(全限定类名)
等方法就够用了
package cn.edu.chan.myclass;
/**
* @author 蝉
* @version 1.0
* @date 2021/8/8 11:13
*/
public class Test01 {
public static void main(String[] args) {
String path = "cn.edu.chan.annotation.User";
try {
Class clazz = Class.forName(path);
//对象是表示或封装一些数据.一个类被加载后,JVM会创建一个对应该类的Class对象
// 类的整个结构信息会放到对应的Class对象中。
//这个Class对象就像一面镜子一样,通过这面镜子我可以看到对应类的全部信息。
System.out.println(clazz.hashCode());
Class clazz2 = Class.forName(path); //一个类只对应一个Class对象
System.out.println(clazz2.hashCode());
Class strClazz = String.class;
Class strClazz2 = path.getClass();
System.out.println(strClazz==strClazz2);
Class intClazz =int.class;
int[] arr01 = new int[10];
int[][] arr02 = new int[30][3];
int[] arr03 = new int[30];
double[] arr04 = new double[10];
System.out.println(arr01.getClass().hashCode());
System.out.println(arr02.getClass().hashCode());
System.out.println(arr03.getClass().hashCode());
System.out.println(arr04.getClass().hashCode());
} catch (Exception e) {
e.printStackTrace();
}
}
}
结果:
1163157884
1163157884
true
1956725890
356573597
1956725890
1735600054
Process finished with exit code 0
这里也很清楚的得出:一个类对应只同一个Clss对象,但是不同的维度
对应的Class对象又不同。
3.应用反射的API,获取类的信息(类的名字、属性、方法、构造器等)
package cn.edu.chan.myConstructor;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
/**
* 应用反射的API,获取类的信息(类的名字、属性、方法、构造器等)
*
* @author 蝉
* @version 1.0
* @date 2021/8/8 23:47
*/
public class Test01 {
public static void main(String[] args) {
String path = "cn.edu.chan.annotation.User";
try {
Class clazz = Class.forName(path);
//获取类的名字
System.out.println(clazz.getName()); //获得包名+类名:cn.edu.chan.annotation.User
System.out.println(clazz.getSimpleName()); //获的类名:User
//获取属性信息
// Field[] fields = clazz.getFields(); //只能获得public的field
Field[] fields = clazz.getDeclaredFields(); //获得所有的field
Field f = clazz.getDeclaredField("username");
System.out.println(fields.length);
for (Field temp : fields) {
System.out.println("属性:" + temp);
}
//获取方法信息
Method[] methods = clazz.getDeclaredMethods();
Method m01 = clazz.getDeclaredMethod("getUsername", null);
//如果方法有参,则必须传递参数类型对应的class对象
Method m02 = clazz.getDeclaredMethod("setUsername", String.class);
for (Method m : methods) {
System.out.println("方法:" + m);
}
//获得构造器信息
Constructor[] constructors = clazz.getDeclaredConstructors();
Constructor c = clazz.getDeclaredConstructor(String.class, String.class, int.class);
System.out.println("获得构造器:" + c);
for (Constructor temp : constructors) {
System.out.println("构造器:" + temp);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
结果:
**cn.edu.chan.annotation.User
User
3
属性:private java.lang.String cn.edu.chan.annotation.User.username
属性:private java.lang.String cn.edu.chan.annotation.User.password
属性:private int cn.edu.chan.annotation.User.age
方法:public java.lang.String cn.edu.chan.annotation.User.toString()
方法:public void cn.edu.chan.annotation.User.setUsername(java.lang.String)
方法:public java.lang.String cn.edu.chan.annotation.User.getUsername()
方法:public int cn.edu.chan.annotation.User.getAge()
方法:public void cn.edu.chan.annotation.User.setPassword(java.lang.String)
方法:public void cn.edu.chan.annotation.User.setAge(int)
方法:public java.lang.String cn.edu.chan.annotation.User.getPassword()
获得构造器:public cn.edu.chan.annotation.User(java.lang.String,java.lang.String,int)
构造器:public cn.edu.chan.annotation.User()
构造器:public cn.edu.chan.annotation.User(java.lang.String,java.lang.String,int)**
4. 通过反射API动态的操作:构造器、方法、属性
package cn.edu.chan.myConstructor;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
/**
* 应用反射的API,获取类的信息(类的名字、属性、方法、构造器等)
*
* @author 蝉
* @version 1.0
* @date 2021/8/8 23:47
*/
public class Test01 {
public static void main(String[] args) {
String path = "cn.edu.chan.annotation.User";
try {
Class clazz = Class.forName(path);
//获取类的名字
System.out.println(clazz.getName()); //获得包名+类名:cn.edu.chan.annotation.User
System.out.println(clazz.getSimpleName()); //获的类名:User
//获取属性信息
// Field[] fields = clazz.getFields(); //只能获得public的field
Field[] fields = clazz.getDeclaredFields(); //获得所有的field
Field f = clazz.getDeclaredField("username");
System.out.println(fields.length);
for (Field temp : fields) {
System.out.println("属性:" + temp);
}
//获取方法信息
Method[] methods = clazz.getDeclaredMethods();
Method m01 = clazz.getDeclaredMethod("getUsername", null);
//如果方法有参,则必须传递参数类型对应的class对象
Method m02 = clazz.getDeclaredMethod("setUsername", String.class);
for (Method m : methods) {
System.out.println("方法:" + m);
}
//获得构造器信息
Constructor[] constructors = clazz.getDeclaredConstructors();
Constructor c = clazz.getDeclaredConstructor(String.class, String.class, int.class);
System.out.println("获得构造器:" + c);
for (Constructor temp : constructors) {
System.out.println("构造器:" + temp);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
结果:
User{username='null', password='null', age='0'}
小明
小王
小红
小红
5.通过反射获取泛型信息
package cn.edu.chan.myConstructor;
import cn.edu.chan.annotation.User;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.List;
import java.util.Map;
/**
* 通过反射获取泛型信息
* @author 蝉
* @version 1.0
* @date 2021/8/9 10:03
*/
public class Test03 {
public void test01(Map<String, User> map, List<User> list){
System.out.println("Test03.test01()");
}
public Map<Integer,User> test02(){
System.out.println("Test03.test02()");
return null;
}
public static void main(String[] args) {
try {
//获得指定方法参数泛型信息
Method m = Test03.class.getMethod("test01", Map.class,List.class);
//getGenericParameterTypes:返回-方法参数为泛型(参数化类型)的Type类型的数组 Type[]
Type[] t = m.getGenericParameterTypes();
for (Type paramType : t) {
System.out.println("#"+paramType);
//ParameterizedType是一个接口,这个类可以用来检验泛型是否被参数化
if(paramType instanceof ParameterizedType){
//获取运行时期泛型的具体类型
Type[] genericTypes = ((ParameterizedType) paramType).getActualTypeArguments();
for (Type genericType : genericTypes) {
System.out.println("泛型类型:"+genericType);
}
}
}
//获得指定方法返回值泛型信息
Method m2 = Test03.class.getMethod("test02", null);
//getGenericReturnType:返回-返回值为泛型(参数化类型)的Type类型的数组 Type[]
Type returnType = m2.getGenericReturnType();
//ParameterizedType是一个接口,这个类可以用来检验泛型是否被参数化
System.out.println("###"+returnType);
if(returnType instanceof ParameterizedType){
//获取运行时期泛型的具体类型
Type[] genericTypes = ((ParameterizedType) returnType).getActualTypeArguments();
for (Type genericType : genericTypes) {
System.out.println("返回值,泛型类型:"+genericType);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
结果:
#java.util.Map<java.lang.String, cn.edu.chan.annotation.User>
泛型类型:class java.lang.String
泛型类型:class cn.edu.chan.annotation.User
#java.util.List<cn.edu.chan.annotation.User>
泛型类型:class cn.edu.chan.annotation.User
###java.util.Map<java.lang.Integer, cn.edu.chan.annotation.User>
返回值,泛型类型:class java.lang.Integer
返回值,泛型类型:class cn.edu.chan.annotation.User
Type 是所有类型的高级公共接口,当然也是Class的父类。
它们包括原始类型、参数化类型、数组类型、类型变量和基本类型。