zoukankan      html  css  js  c++  java
  • Java内省

    1.  什么是内省? 

      内省(Introspector)是Java语言对JavaBean类属性、事件的处理方法。
      例如类User中有属性name,那么必定有getName,setName方法,我们可以通过他们来获取或者设置值,这是常规操作。
      Java提供了一套API来访问某个属性的getter/setter方法,这些API存放在java.beans中。

      在计算机科学中,内省是指计算机程序在运行时(Run time)检查对象(Object)类型的一种能力,通常也可以称作运行时类型检查。
      不应该将内省和反射混淆。相对于内省,反射更进一步,是指计算机程序在运行时(Run time)可以访问、检测和修改它本身状态或行为的一种能力。

    内省和反射的区别:

      反射是在运行状态把Java类中的各种成分映射成相应的Java类,可以动态的获取所有的属性以及动态调用任意一个方法,强调的是运行状态。 
      内省机制是通过反射来实现的,BeanInfo用来暴露一个bean的属性、方法和事件,以后我们就可以操纵该JavaBean的属性。

    我自己的理解是:

      内省更多的用在获取属性以及属性的的getter、setter方法,进行一些其他的操作。

      反射更多的用在反射创建对象以及调用方法等操作。

    2.  内省类库简介

    比如User类:

    package cn.qlq;
    
    public class User {
    
        private int age;
    
        private String name;
    
        private Address address;
    
        private boolean deleted;
    
        public int getAge() {
            return age;
        }
    
        public void setAge(int age) {
            this.age = age;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public Address getAddress() {
            return address;
        }
    
        public void setAddress(Address address) {
            this.address = address;
        }
    
        public boolean isDeleted() {
            return deleted;
        }
    
        public void setDeleted(boolean deleted) {
            this.deleted = deleted;
        }
    
    }
    
    class Address {
    
        private String province;
    
        private String city;
    
        public String getProvince() {
            return province;
        }
    
        public void setProvince(String province) {
            this.province = province;
        }
    
        public String getCity() {
            return city;
        }
    
        public void setCity(String city) {
            this.city = city;
        }
    
    }

    主要涉及的类库如下:

    (1)Introspector类,可以获取BeanInfo类,常见的使用方法如下: (重要)

            BeanInfo beanInfo = Introspector.getBeanInfo(user.getClass());
    
            // 第二个参数代表停止的类,也就是如果有继承关系不获取第二个参数及其父类的信息
            BeanInfo beanInfo2 = Introspector.getBeanInfo(user.getClass(), Object.class);

    (2)BeanInfo类:通过上面的内省类获得 (重要)

            BeanInfo beanInfo = Introspector.getBeanInfo(user.getClass(), Object.class);

    作用1:获取BeanDescriptor一个全局的类描述信息

            // 获取BeanDescriptor-一个全局的类描述信息
            BeanDescriptor beanDescriptor = beanInfo.getBeanDescriptor();
            System.out.println(beanDescriptor);

    作用2:获取所有的PropertyDescriptor属性描述器(如果获取BeanInfo的时候第二个参数没传会获取从Object继承的信息)

        private static void testBeanInfo2(User user) throws Exception {
            BeanInfo beanInfo = Introspector.getBeanInfo(user.getClass(), Object.class);
    
            // 获取PropertyDescriptor描述器
            PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
            for (PropertyDescriptor propertyDescriptor : propertyDescriptors) {
                // 获取属性类型
                Class<?> propertyType = propertyDescriptor.getPropertyType();
                // 获取属性名称
                String name = propertyDescriptor.getName();
                // 获取属性的读方法,getXXX
                Method readMethod = propertyDescriptor.getReadMethod();
                // 获取属性的写方法,setXXX
                Method writeMethod = propertyDescriptor.getWriteMethod();
    
                System.out.println(name);
                System.out.println(propertyType);
                System.out.println(readMethod.getName());
                System.out.println(writeMethod.getName());
                System.out.println("==============");
            }
        }

    结果:

    address
    class cn.qlq.Address
    getAddress
    setAddress
    ==============
    age
    int
    getAge
    setAge
    ==============
    deleted
    boolean
    isDeleted
    setDeleted
    ==============
    name
    class java.lang.String
    getName
    setName
    ==============

    作用3: 获取MethodDescriptor描述器(如果获取BeanInfo的时候第二个参数没传会获取从Object继承的方法)

            BeanInfo beanInfo = Introspector.getBeanInfo(user.getClass(), Object.class);
    
            // 获取MethodDescriptor描述器
            MethodDescriptor[] methodDescriptors = beanInfo.getMethodDescriptors();
            for (MethodDescriptor methodDescriptor : methodDescriptors) {
                Method method = methodDescriptor.getMethod();
                System.out.println(method);
            }

    结果:

    public java.lang.String cn.qlq.User.getName()
    public void cn.qlq.User.setAge(int)
    public void cn.qlq.User.setName(java.lang.String)
    public int cn.qlq.User.getAge()
    public boolean cn.qlq.User.isDeleted()
    public cn.qlq.Address cn.qlq.User.getAddress()
    public void cn.qlq.User.setAddress(cn.qlq.Address)
    public void cn.qlq.User.setDeleted(boolean)

    作用4:获取EventSetDescriptor(这个不常用)

            BeanInfo beanInfo = Introspector.getBeanInfo(user.getClass(), Object.class);
    
            // 获取EventSetDescriptor描述器
            EventSetDescriptor[] eventSetDescriptors = beanInfo.getEventSetDescriptors();
            for (EventSetDescriptor eventSetDescriptor : eventSetDescriptors) {
                System.out.println(eventSetDescriptor);
            }

    (3)BeanDescriptor  一个全局的Bean描述信息,一般没啥用

            BeanInfo beanInfo = Introspector.getBeanInfo(user.getClass(), Object.class);
            
            // 获取BeanDescriptor-一个全局的类描述信息
            BeanDescriptor beanDescriptor = beanInfo.getBeanDescriptor();
            System.out.println(beanDescriptor);

    (4)PropertyDescriptor  属性描述器,可以获取到属性名称、属性的get方法以及set方法,进而做到修改方法。

    其获取方式可以通过BeanInfo获取,也可以通过直接new的方式获取。

    方式一:

            BeanInfo beanInfo = Introspector.getBeanInfo(user.getClass(), Object.class);
    
            // 获取PropertyDescriptor描述器
            PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
            for (PropertyDescriptor propertyDescriptor : propertyDescriptors) {
                // 获取属性类型
                Class<?> propertyType = propertyDescriptor.getPropertyType();
                // 获取属性名称
                String name = propertyDescriptor.getName();
                // 获取属性的读方法,getXXX
                Method readMethod = propertyDescriptor.getReadMethod();
                // 获取属性的写方法,setXXX
                Method writeMethod = propertyDescriptor.getWriteMethod();
    
                System.out.println(name);
                System.out.println(propertyType);
                System.out.println(readMethod.getName());
                System.out.println(writeMethod.getName());
                System.out.println("==============");
            }

    方式二:通过直接new的方式

            User user = new User();
            user.setName("zhangsan");
    
            PropertyDescriptor propertyDescriptor = new PropertyDescriptor("name", User.class);
    
            // 获取getter方法读取属性
            Method readMethod = propertyDescriptor.getReadMethod();
            Object invoke = readMethod.invoke(user);
            System.out.println(invoke);
    
            // 获取setter方法修改属性
            Method writeMethod = propertyDescriptor.getWriteMethod();
            writeMethod.invoke(user, "lisi");
    
            System.out.println(user.getName());

    结果:

    zhangsan
    lisi

    如果属性不存在会报错如下:

    Exception in thread "main" java.beans.IntrospectionException: Method not found: isNamess
    at java.beans.PropertyDescriptor.<init>(PropertyDescriptor.java:107)
    at java.beans.PropertyDescriptor.<init>(PropertyDescriptor.java:71)
    at cn.qlq.Client.main(Client.java:14)

    (5)  MethodDescriptor 方法描述器,可以获取方法的名称、Method等信息

    其获取方式可以通过BeanInfo获取

            BeanInfo beanInfo = Introspector.getBeanInfo(user.getClass(), Object.class);
    
            // 获取MethodDescriptor描述器
            MethodDescriptor[] methodDescriptors = beanInfo.getMethodDescriptors();
            for (MethodDescriptor methodDescriptor : methodDescriptors) {
                System.out.println(methodDescriptor);
                
                Method method = methodDescriptor.getMethod();
            }

    3.  内省的应用

      采用内省实现的JavaBean转Map和List<Bean>转List<Map>、Map转Bean、Maps转Beans、实现将1个bean的属性赋值给另1个bean(属性拷贝)

    package cn.qs.utils;
    
    import java.beans.BeanInfo;
    import java.beans.Introspector;
    import java.beans.PropertyDescriptor;
    import java.lang.reflect.Method;
    import java.util.ArrayList;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
    
    import org.apache.commons.lang3.ArrayUtils;
    
    public class BeanUtils {
    
        /**
         * 内省进行数据转换-javaBean转map
         * 
         * @param obj
         *            需要转换的bean
         * @return 转换完成的map
         * @throws Exception
         */
        public static <T> Map<String, Object> beanToMap(T obj, boolean putIfNull) throws Exception {
            Map<String, Object> map = new HashMap<>();
            // 获取javaBean的BeanInfo对象
            BeanInfo beanInfo = Introspector.getBeanInfo(obj.getClass(), Object.class);
            // 获取属性描述器
            PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
            for (PropertyDescriptor propertyDescriptor : propertyDescriptors) {
                // 获取属性名
                String key = propertyDescriptor.getName();
                // 获取该属性的值
                Method readMethod = propertyDescriptor.getReadMethod();
                // 通过反射来调用javaBean定义的getName()方法
                Object value = readMethod.invoke(obj);
    
                if (value == null && !putIfNull) {
                    continue;
                }
    
                map.put(key, value);
            }
    
            return map;
        }
    
        public static <T> List<Map<String, Object>> beansToMaps(List<T> objs, boolean putIfNull) throws Exception {
            return beansToMaps(objs, putIfNull, false);
        }
    
        public static <T> List<Map<String, Object>> beansToMaps(List<T> objs, boolean putIfNull, boolean addIndex)
                throws Exception {
    
            List<Map<String, Object>> result = new ArrayList<>();
    
            Map<String, Object> beanToMap = null;
            int index = 0;
            for (Object obj : objs) {
                beanToMap = beanToMap(obj, putIfNull);
                if (addIndex) {
                    beanToMap.put("index", ++index);
                }
    
                result.add(beanToMap);
            }
    
            return result;
        }
    
        /**
         * Map转bean
         * 
         * @param map
         *            map
         * @param clz
         *            被转换的类字节码对象
         * @return
         * @throws Exception
         */
        public static <T> T map2Bean(Map<String, Object> map, Class<T> clz) throws Exception {
            // new 出一个对象
            T obj = clz.newInstance();
            // 获取person类的BeanInfo对象
            BeanInfo beanInfo = Introspector.getBeanInfo(clz, Object.class);
            // 获取属性描述器
            PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
            for (PropertyDescriptor propertyDescriptor : propertyDescriptors) {
                // 获取属性名
                String key = propertyDescriptor.getName();
                Object value = map.get(key);
    
                // 通过反射来调用Person的定义的setName()方法
                Method writeMethod = propertyDescriptor.getWriteMethod();
                writeMethod.invoke(obj, value);
            }
            return obj;
        }
    
        public static <T> List<T> maps2Beans(List<Map<String, Object>> maps, Class<T> clz) throws Exception {
            List<T> result = new ArrayList<>();
            for (Map<String, Object> map : maps) {
                result.add(map2Bean(map, clz));
            }
    
            return result;
        }
    
        /**
         * 复制origin的值到dest上
         * 
         * @param dest
         *            目标对象
         * @param origin
         *            元对象
         * @param setNull
         *            如果源对象属性为null是否拷贝
         * @param excludeFieldNames
         *            排除的属性
         */
        public static <T> void copyProperties(T dest, T origin, boolean setNull, String[] excludeFieldNames) {
            try {
                // 获取person类的BeanInfo对象
                BeanInfo destBeanInfo = Introspector.getBeanInfo(dest.getClass(), Object.class);
    
                // 获取目标属性描述器
                PropertyDescriptor[] destBeanInfoPropertyDescriptors = destBeanInfo.getPropertyDescriptors();
    
                for (PropertyDescriptor propertyDescriptor : destBeanInfoPropertyDescriptors) {
                    // 获取属性名
                    String key = propertyDescriptor.getName();
                    if (ArrayUtils.contains(excludeFieldNames, key)) {
                        continue;
                    }
    
                    // 获取该属性的值
                    Method readMethod = propertyDescriptor.getReadMethod();
    
                    // 如果源对象没有对应属性就跳过
                    Object srcValue = null;
                    try {
                        srcValue = readMethod.invoke(origin);
                    } catch (Exception ignored) {
                        // ignored
                        continue;
                    }
    
                    // 如果源对象的值null且null不设置的时候跳过
                    if (srcValue == null && !setNull) {
                        continue;
                    }
    
                    // 获取setter方法修改属性
                    Method writeMethod = propertyDescriptor.getWriteMethod();
                    writeMethod.invoke(dest, srcValue);
                }
            } catch (Exception ignored) {
                // ignored
            }
        }
    
        public static <T> void copyProperties(T dest, T origin) {
            copyProperties(dest, origin, false, null);
        }
    }

    总结:

      如果通过BeanInfo操作属性的话,一般需要过滤掉Object继承的东西,也就是获取BeanInfo的时候如下:

            BeanInfo beanInfo = Introspector.getBeanInfo(user.getClass(), Object.class);

    关于反射的使用参考: https://www.cnblogs.com/qlqwjy/p/7551550.html

  • 相关阅读:
    每天一个JavaScript实例-从一个div元素删除一个段落
    Ewebeditor最新漏洞及漏洞大全
    WebKit历史项管理的实现
    C# 字符串处理
    Handler具体解释系列(四)——利用Handler在主线程与子线程之间互发消息
    (linux shell)第一章--小试牛刀(上)
    关于如何使用three.js的小教程&lt;一&gt;
    Readprocessmemory使用方法
    CentOS 6.4 U盘启动盘制作、安装及遇到的问题解决
    poj 2774 Long Long Message,后缀数组,求最长公共子串 hdu1403
  • 原文地址:https://www.cnblogs.com/qlqwjy/p/11129557.html
Copyright © 2011-2022 走看看