zoukankan      html  css  js  c++  java
  • 反射与注解

    ⼀、反射

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

    当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的加载顺序控制的~~

  • 相关阅读:
    ORA00600 [4400][48]错误一例
    西宁旅记:管中窥豹
    ORA00600: INTERNAL ERROR CODE, ARGUMENTS: [729], [10992], [SPACE LEAK] Example
    Exadata Server Hardware Details
    Script:收集RAC性能诊断信息
    Find Past Image in RAC Global Cache
    诊断RAC数据库的启动
    Script To Monitor RDBMS Session UGA and PGA Current And Maximum Usage Over Time
    Performance: PostgreSQL VS SQLSERVER
    Internal_Function with Encryption in SQL PLAN
  • 原文地址:https://www.cnblogs.com/chendezhen/p/15330550.html
Copyright © 2011-2022 走看看