反射与注解

⼀、反射

运⾏时的类信息(将类的各个组成部分封装成为类信息对象)
类是描述⼀组对象的抽象描述,⽽反射则是描述⼀组类

当ClassLoader加载类时,会⾃动获悉类完整构造⾃动封装成⼀个类信息(包括类、属性、⽅法等)
Java的反射机制在⽤在程序运⾏时,可以动态的获取类的信息,调⽤对象的⽅法等

Class的重要组成

  阐述常⽤的组成部分使⽤及常⽤API

  • Class 类

获取类的三种⽅式:

1. Class.forName("全类名") //常⽤于配置使⽤
2. 类型.class
3. 对象.getClass()  

//延伸
//获取全类名
1. class对象.getName();
//获取简单类名
2. class对象.getSimpleName();
//获取⽗类
3. class对象.getSuperclass();

  • Modifier 修饰
class对象.getModifiers();
//⽤于获取类的修饰,返回int类型 
//⽐如如果是默认修饰,则返回0 ,public返回1 对应的返回值对应修饰请参考jdk⽂档
  • Field 属性

下⾯操作返回的是⼀个Field属性对象

//获取对象的public修饰的属性
1. class对象.getField("属性名")
//获取public修饰的所有的属性
2. class对象.getFields()
//根据属性名获取对象属性(包含私有)
3. class对象.getField("属性名")
//获取对象所属性
4class对象.getDeclaredFields();

  操作属性对象

//获取对象的属性值,此处的obj你需要取值的类属性值的对象
1. filed对象.get(obj)
//对象属性赋值,此处的obj你需要取值的类属性值的对象,value是你需要设置的值
2. filed对象.set(obj,value)
  • Method ⽅法
  • 下⾯操作返回的是是⼀个Method对象
//获取对象的public修饰的⽅法
1. class对象.getMethod("⽅法名",Object类型的可变参...)
//获取public修饰的所有的⽅法
2. class对象.getMethods()
//根据属性名获取对象⽅法(包含私有)
3. class对象.getMethod("⽅法名",Object类型的可变参...)
//获取对象所⽅法
4. class对象.getDeclaredMethods();

--操作⽅法对象

//执⾏⽅法,此处的obj你需要执⾏的⽅法的对象,可变参是你调⽤这个⽅法需要传的参数
1.method对象.invoke(obj,可变参...)
  • Construtor 构造
//获取构造
1. class对象.getConstructor(Class类型的可变参...)
//获取所有构造
2. class对象.getConstructors()
//同上两个⼀样,有Declared相应的⽅法

       由上可知,我们⼀般在反射操作上,⼀般都会使⽤带有Declared的⽅法,因为可获取全部的包括私有的⼀些属性、⽅法,使⽤不带Declared的⽅法则可获取包含⽗类的⼀些公有属性、⽅法,Declared只能获取⾃⼰的,若此属性、⽅法是有私有权限控制的,此时我们直接操作属性、⽅法会抛出⼀个java.lang.IllegalAccessException异常,告诉你没有访问权限。获取Declared的⽅法、属性后,若此⽅法、属性是私有的需要设置访问权限setAccessible(true),⼜称暴⼒破解,这样我们就能顺利操作⽅法、属性等数据了。

反射延伸——内省

内省(Introspector) 是Java 语⾔对 JavaBean 类属性、事件的⼀种缺省处理⽅法
⾥边运⽤了反射的⼀些基本原理将JavaBean封装成⼀个BeanInfo对象,⽅便我们进⾏操作.

下⾯的⼀个例⼦是对象转map

/**
  * 对象转Map
  *
  * @param s 对象
  * @param <S> 类型
  */
public static <S> Map<String, Object> objectToMap(S s) throws IntrospectionException, InvocationTargetException, IllegalAccessException {
    Map<String, Object> map = new HashMap<>();
    //使⽤内省获取对象的基本信息
    BeanInfo beanInfo =
    Introspector.getBeanInfo(BeanUtils.getRealClass(s.getClass()));
    //获取对象的属性描述进⾏遍历处理
    PropertyDescriptor[] pds = beanInfo.getPropertyDescriptors();
    for (PropertyDescriptor pd : pds) {
        //获取属性名
       if ("class".equals(pd.getName())) {
           continue;
       }
       //获取set⽅法并执⾏
       Object invoke = pd.getReadMethod().invoke(s);
       map.put(pd.getDisplayName(), invoke == null ? "" :((Date.class.equals(invoke.getClass()) ||java.sql.Date.class.equals(invoke.getClass())) ? DateUtils.toString((Date)invoke) : invoke));
    }
    return map;
}

⼆、注解

注解(也被称为是元数据),为我们在代码中添加信息提供的⼀个形式化的⽅法,让我们可以在稍后的某些时候⽅便的使⽤这些数据。也就是打个标签啦~

Java 内置了许多的注解,在Java Annotation可以找到内置的所有注解,我们最常⻅的注解应该 是@Override,表示这个⽅法是⼀个重写的⽅法。

1、如何创建注解

所有的注解本身都继承于java.lang.annotation.Annotation, 每⼀个注解本身就是⼀个interface,但是注解这种interface有其特殊性,所以,所有的注解都是这样定义的:
publice @interface xxxx {}

@Override的定义如下:

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

可以看到上⾯⽤到@Targe、@Retention的注解,此类注解为元注解。

元注解(使⽤在注解上的注解)
  • @Documented
标记这个注解@Deprecated将会出现在Java Doc之中
  • @Retention
标明这个注解的⽣命周期,RententionPolicy⾥⾯定义了三种⽣命周期,分别是SOURCE,CLASS,RUNTIME三种,SOURCE表示在编译阶段抛弃,CLASS表示会被记录到class⽂件⾥⾯,但不会出现在vm⾥⾯运⾏,RUNTIME表示在运⾏期⾥⾯存活
  • @Target
表示这个注解可以⽤在何处,表明它可以⽤在构造函数,字段,本地变量,⽅法,包,参数,类
  • @Inherited
表示使⽤了这个注解的注解,再⽤到类上时,可被⼦类继承
  • @Repeatable
表示注解可以重复使⽤,是Java 8引进的特性。之前注解只能在同⼀个地⽅⽤⼀次,⽤了@Repeatable,注解就可以在同⼀个地⽅使⽤多次了

2、注解的属性

       注解的属性也叫成员变量。注解只有属性没有⽅法,注解的成员变量在注解的定义中以“⽆形参的⽅法”形式来声明,其⽅法名定义了该成员变量的名字,其返回值定义了该成员变量的类型,且可⽤default进⾏默认赋值,如果没有指定默认值,则此注解属性是必须赋值的!!
需要注意的是,在注解中定义属性时它的类型必须是 8 种基本数据类型外加 类、接⼝、注解及它们的数组。

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.PARAMETER})
public @interface NotNull {
    /**
     * 提示信息
     */
    String message() default "";
    /**
     * 是否迭代校验
     */
    boolean isIteration() default false;
}

上⾯的代码定义了NotNull这个注解,它拥有message和isIteration两个属性,在使⽤的时候我们可以 给他们赋值:

@NotNull(isIteration = true)
private AuditInfo audit_info;

另外,还有⼀种情况。如果⼀个注解内仅仅只有⼀个名字为 value 的属性时,应⽤这个注解时可以直接接属性值填写到括号内,如下:

//定义
@Retention(RetentionPolicy.RUNTIME)
public @interface Order {
    int value() default 0;
}
//使⽤
@Order(Integer.MIN_VALUE)
public class SituationSynProcessor extends
BaseExtendSynProcessor<List<Situation>>

3、注解的提取、注解与反射

⼀开始我们已经说了,注解只不过是⼀个标签⽽已,如果没有⼀个提取它解释它使⽤它的解释器,它将不会有什么作⽤。

通过反射获取注解:

//Class 对象的 isAnnotationPresent() ⽅法判断它是否应⽤了某个注解
public boolean isAnnotationPresent(Class<? extends Annotation>annotationClass) {}
//getAnnotation() ⽅法来获取 Annotation 对象
public <A extends Annotation> A getAnnotation(Class<A> annotationClass) {}
//获取所有注解
public Annotation[] getAnnotations() {}

下⾯示例将会暂时简单的注解@Order的提取与使⽤

//1.定义
@Retention(RetentionPolicy.RUNTIME)
public @interface Order {

    int value() default 0;
}

//2.使⽤
@Order(Integer.MIN_VALUE)
public class SituationSynProcessor extends BaseExtendSynProcessor<List<Situation>>
package com.gzzm.govps.itemsyn;

import com.gzzm.platform.commons.Tools;
import net.cyan.commons.util.BeanUtils;
import net.cyan.commons.util.ClassResolver;
import net.cyan.commons.util.CollectionUtils;

import java.util.*;
/**
 * 事项同步扩展处理器扫描器
 *
 * @author yiuman
 * @date 2019-12-04
 */
public final class ExtendProcessorResolver implements ClassResolver {
    //未排序Class Set
    private final static Set<Class<? extends BaseExtendSynProcessor<?>>> notOrderExtendSynProcessors
     = new HashSet<>();
    //记录BaseExtendSynProcessor处理器的类型及其Order顺序
    private final static Map<Class<? extends BaseExtendSynProcessor<?>>,Integer> orderProcessorRecord
     = new HashMap<>();
    //已排序的Class Set
    private final static Set<Class<? extends BaseExtendSynProcessor<?>>> extendSynProcessors = new LinkedHashSet<>();
    public ExtendProcessorResolver() {
    }
    /**
     * @return 返回⼀个已经排序过的处理器Set
     */
    static Set<Class<? extends BaseExtendSynProcessor<?>>> getExtendProcessors() {
        if (CollectionUtils.isEmpty(extendSynProcessors)) {
            notOrderExtendSynProcessors.stream().sorted(Comparator.comparingInt(orderProcessorRecord::get))
            .forEach(extendSynProcessors::add);
        }
    return extendSynProcessors;
}
    @SuppressWarnings("unchecked")
    @Override
    public void resolve(Class<?> aClass) {
        try {
            if (BeanUtils.getRealClass(aClass).getSuperclass() != null && 
            BaseExtendSynProcessor.class.equals(BeanUtils.getRealClass(aClass).getSuperclass())) {
                //此处通过反射获取注解
                Order order = aClass.getAnnotation(Order.class);
                //将获取到的注解值与使⽤了该注解的类型添加进 Order记录Map⾥
                orderProcessorRecord.put((Class<? extends BaseExtendSynProcessor<?>>) aClass,
                 order == null ? 0 : order.value());
                //将类型添加进未进⾏排序的Set中
                notOrderExtendSynProcessors.add((Class<? extends BaseExtendSynProcessor<?>>) aClass);
            }
        } catch (Throwable a) {
            Tools.log(a);
        }
    }
}

说明:上⾯的真正⽤到的是,那些Set集合中处理器,因为可能要指定某些处理器的执⾏顺序,⽐如A处 理器⼀定要B处理器之前运⾏,我们平时也可以写死让他们指定顺序执⾏,这在处理器较少的时候是可 以这么做,但是如果处理器有10呢?下次⼜加多10个呢?⽽且还需要执⾏他们某些的执⾏顺序呢?所以 这⾥是⽤了Order注解对他们的⼀个执⾏顺序进⾏指定,以后⽆论怎么加只要注意下Order中的value值 定义就好。~ Springboot中的Bean加载顺序也是⽤了这个同名注解进⾏Bean的加载顺序控制的~~

原文地址:https://www.cnblogs.com/chendezhen/p/15330550.html