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

  • 相关阅读:
    Maven关于web.xml中Servlet和Servlet映射的问题
    intellij idea的Maven项目运行报程序包找不到的错误
    修改Maven项目默认JDK版本
    刷题15. 3Sum
    刷题11. Container With Most Water
    刷题10. Regular Expression Matching
    刷题5. Longest Palindromic Substring
    刷题4. Median of Two Sorted Arrays
    刷题3. Longest Substring Without Repeating Characters
    刷题2. Add Two Numbers
  • 原文地址:https://www.cnblogs.com/qlqwjy/p/11129557.html
Copyright © 2011-2022 走看看