Spring类ResolvableType、BeanWrapper使用

  在阅读Spring、SpringMVC 源码的时候经常看到这几个类,简单总结下这几个工具类的核心使用方法。

1. ResolvableType

  直译过来就是可解析类型。 实际是对java自带的Type 的一层包装,用于解析各种参数以及泛型、泛型实际类型等。Type 主要继承关系如下:

 主要类如下:

        /**
         * Type 表示类型,java 所有的原生类型、参数化类型、变量类型、原子类型等都是该类实现。其主要实现有:
         * java.lang.Class 类
         * java.lang.reflect.ParameterizedType  参数化类型
         * java.lang.reflect.WildcardType WildcardType represents a wildcard type expression, such as {@code ?}, {@code ? extends Number}, or {@code ? super Integer}
         * java.lang.reflect.TypeVariable  List<T> 这样的类型
         */

   ResolvableType 提供了一些主要api, forClass 针对class, forField 针对属性,forMethodXXX针对方法参数和方法返回值。

1. 原生jdk 方法获取方式

复制代码
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.List;
import java.util.Map;

public class PlainTest2 {

    public static void main(String[] args) throws Exception {
        /**
         * Type 表示类型,java 所有的原生类型、参数化类型、变量类型、原子类型等都是该类实现。其主要实现有:
         * java.lang.Class 类
         * java.lang.reflect.ParameterizedType  参数化类型
         * java.lang.reflect.WildcardType WildcardType represents a wildcard type expression, such as {@code ?}, {@code ? extends Number}, or {@code ? super Integer}
         * java.lang.reflect.TypeVariable  List<T> 这样的类型
         */

        //
        Type[] genericInterfaces = Wrapper.class.getGenericInterfaces();

        // 属性
        Field field = Wrapper.class.getDeclaredField("ids");
        Type genericType = field.getGenericType();
        Field field2 = Wrapper.class.getDeclaredField("names");
        Type genericType2 = field2.getGenericType();
        Field field3 = Wrapper.class.getDeclaredField("names2");
        Type genericType3 = field3.getGenericType();

        // 方法
        Method method = PlainTest2.class.getMethod("test", List.class, Map.class, String.class, Wrapper.class, String[].class, int[].class);
        // 获取方法参数
        Type[] genericParameterTypes = method.getGenericParameterTypes();
        // 获取返回值
        Type genericReturnType = method.getGenericReturnType();

        System.out.println(genericReturnType);
        // 如果想获取实际类型,ParameterizedType 表示是一种参数化类型,也就是泛型. 返回实际类型的数组, 比如Map<String, Object> 就是两个Type
        Type[] actualTypeArguments = ((ParameterizedType) genericReturnType).getActualTypeArguments();
        System.out.println(actualTypeArguments[0]); // actualTypeArguments[0] 实际是个class 对象
        // 获取其实际类型
        Type rawType = ((ParameterizedType) genericReturnType).getRawType(); // List.class, rawType实际是个class 对象
        System.out.println(rawType);
    }

    public static List<String> test(List<Integer> datas, Map<String, Object> map, String s, Wrapper wrapper, String[] strs, int[] ints) throws NoSuchMethodException {
        return null;
    }

    private static class Wrapper<T> {

        private String name;

        private List<Integer> ids;

        private List<? extends Number> names;

        private List<T> names2;
    }

}
复制代码

结果如下:

控制台:

java.util.List<java.lang.String>
class java.lang.String
interface java.util.List

debug 各对象:

 2. Spring 的 ResolvableType 使用方式

复制代码
import org.springframework.core.MethodParameter;
import org.springframework.core.ResolvableType;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class PlainTest {

    public static void main(String[] args) throws NoSuchFieldException, NoSuchMethodException {
        /** ResolvableType 实际是对 java.lang.reflect.Type 类的包装,并且提供了访问下面方法。官方介绍如下:
         *   Encapsulates a Java {@link java.lang.reflect.Type}, providing access to
         *   {@link #getSuperType() supertypes}, {@link #getInterfaces() interfaces}, and
         *   {@link #getGeneric(int...) generic parameters} along with the ability to ultimately
         *   {@link #resolve() resolve} to a {@link java.lang.Class}.
         */

        // 1. 类测试
        ResolvableType resolvableType = ResolvableType.forClass(IInterface1.class);
        // 获取接口
        ResolvableType[] interfaces = resolvableType.getInterfaces();
        // 获取泛型
        ResolvableType[] generics = resolvableType.getGenerics();
        ResolvableType generic = resolvableType.getGeneric(1);
        // 获取父类
        ResolvableType superType = resolvableType.getSuperType();

        // 2. 属性测试
        // 获取字段属性
        ResolvableType field = ResolvableType.forField(IInterface1.class.getDeclaredField("CACHE"));
        // 获取其父类 AbstractMap<List<String>, List<Integer>>
        ResolvableType superType1 = field.getSuperType();
        /* 将其类型转为map,
         * 实际这样解析结果等价于 private Map<List<String>, List<Integer>> CACHE = new HashMap<>(); 的解析结果
         * asMap 内部调用的 as(Map.class), 所以下面两个等价。 as 内部实际是获取其接口或者父类对象。 同理还有 asCollection() 方法, asCollection() 调用as(Collection.class)
         */
        ResolvableType fieldMap1 = field.asMap();
        ResolvableType fieldMap2 = field.as(Map.class);
        // 获取泛型相关
        Class<?> resolve = field.getGenerics()[0].resolve(); // List<String>
        Class<?> resolve1 = field.getGenerics()[0].getGeneric(0).resolve(); // String
        // 递归获取泛型, 可以理解为获取指定层级的泛型类型
        Class<?> aClass = field.resolveGeneric(1, 0); // Integer (获取List<Integer>的Integer)

        // 3. 方法测试
        Method method = IInterface1.class.getMethod("generateList", List.class);
        // forMethodParameter 方法获取方法相关参数。
        // MethodParameter(method, paramIndex) 是将方法以及参数下标进行包装,下标意义: -1 获取方法返回值,0是第一个参数,1是第二个参数
        MethodParameter methodParameter = new MethodParameter(method, 0);
        ResolvableType resolvableType1 = ResolvableType.forMethodParameter(methodParameter);
        // 获取方法返回值相关描述
        ResolvableType returnType = ResolvableType.forMethodReturnType(method);
        ResolvableType returnType1 = ResolvableType.forMethodParameter(new MethodParameter(method, -1));
    }

    private interface Interface1<T, E> {
        List<T> generateList(List<E> e);
    }

    private interface Interface2<T, E> {
        List<T> generateList2(List<E> e);
    }

    private static class IInterface1<T> implements Interface1<Integer, String>, Interface2<Integer, String> {

        /**
         * 模拟缓存 (这种写法实际是测试转换,实际一般会直接前面用Map 接参数)
         */
        private HashMap<List<String>, List<Integer>> CACHE = new HashMap<>();

        @Override
        public List<Integer> generateList(List<String> e) {
            List<Integer> integers = CACHE.get(e);
            if (integers == null) {

            }
            return null;
        }

        @Override
        public List<Integer> generateList2(List<String> e) {
            return null;
        }

        public T getParam() {
            return null;
        }
    }

}
复制代码

各对象如下:

 2. BeanWrapperImpl 使用

  在spring 整个生命周期还有一个重要的对象就是: BeanWrapperImpl, 从字面意思就可以看出来是一个Bean 包装类实现,继承关系如下:

其核心功能有下面:

1.  BeanWrapper 接口功能: 获取其包装类型以及属性内省相关

2. PropertyAccessor 接口功能: 获取属性值以及修改属性值,支持嵌套对象属性获取以及设置值。

3. TypeConverter接口功能: 进行类型的转换功能, 主要是兼容不同类型的对象之间的转换。(个人理解转换器功能比属性编辑器更强大)

4. PropertyEditorRegistry 接口的功能,属性编辑器。PropertyEditor 是JDK的属性编辑器,主要提供将字符串转换为其他类型对象。

下面研究其简单使用:

1. 定义一些简单的对象:

复制代码
import lombok.Data;

import java.util.List;
import java.util.Map;

@Data
public class User {

    private List<String> likes;

    private Address address;

    private String username;

    private int weight;

    private Map<String, Object> feature;

}


import lombok.Data;

@Data
public class Address {

    private String province;

    private String city;
}
复制代码

2. 测试类:

复制代码
import com.google.common.collect.Lists;
import lombok.Data;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeanWrapperImpl;
import org.springframework.beans.MutablePropertyValues;
import org.springframework.beans.PropertyValue;

import java.beans.PropertyDescriptor;
import java.beans.PropertyEditorSupport;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;

@Data
public class TestClient {

    public static void main(String[] args) {
        User user = getUser();
        BeanWrapper beanWrapper = new BeanWrapperImpl(user);

        // 获取其包装类型以及属性内省相关 (org.springframework.beans.BeanWrapper 相关方法)
        Class<?> wrappedClass = beanWrapper.getWrappedClass();
        Object wrappedInstance = beanWrapper.getWrappedInstance();
        // 获取属性描述器,内省属性
        PropertyDescriptor[] propertyDescriptors = beanWrapper.getPropertyDescriptors();
        PropertyDescriptor addressPropertyDescriptor = beanWrapper.getPropertyDescriptor("address");

        // 获取属性以及修改属性。 org.springframework.beans.PropertyAccessor 接口的方法
        // 1. 获取属性
        // 嵌套获取
        Object address1 = beanWrapper.getPropertyValue("address");
        Object city = beanWrapper.getPropertyValue("address.city");
        // 获取集合属性
        Object likes0 = beanWrapper.getPropertyValue("likes[0]");
        // 获取map 属性
        Object featureAge = beanWrapper.getPropertyValue("feature[age]");
        Object featureAge2 = beanWrapper.getPropertyValue("feature['age']");
        Object featureAge3 = beanWrapper.getPropertyValue("feature[\"age\"]");
        // 2. 修改属性
        // 修改普通属性, k-v 修改
        beanWrapper.setPropertyValue("username", "修改后的");
        beanWrapper.setPropertyValue("address.city", "修改后的city");
        // 使用PropertyValue 进行修改
        PropertyValue pro1 = new PropertyValue("address.city", "修改后的city2");
        beanWrapper.setPropertyValue(pro1);
        // 可以使用Map 进行修改
        Map<String, Object> updateProperties = new LinkedHashMap<>();
        updateProperties.put("feature[age]", 1001);
        updateProperties.put("username2", "修改的username2 ");
        updateProperties.put("weight", 2);
//        beanWrapper.setPropertyValues(updateProperties);
        // 使用map 内部调用的是: setPropertyValues(new MutablePropertyValues(map)); 等价于下面方法,默认后面两个参数都是false
        /**
         * 可以直接使用下面方法。
         * 第二个参数标识 ignoreUnknown 忽略在Bean中找不到的属性, 默认为false, 因为username2 属性不存在,会抛异常 org.springframework.beans.NotWritablePropertyException: Invalid property 'username2' of bean xxx。 设为true 之后会跳过
         * 第三个参数标识 ignoreInvalid 忽略找到,但是没有访问权限的值
         */
        beanWrapper.setPropertyValues(new MutablePropertyValues(updateProperties), true, false);

        // 转换器TypeConverter 用法
        Integer integer = beanWrapper.convertIfNecessary("1122", Integer.class);

        // 属性编辑器PropertyEditor 用法
        beanWrapper.registerCustomEditor(int.class, new PropertyEditorSupport() {
            @Override
            public void setAsText(String text) throws IllegalArgumentException {
                // 故意将年龄加1
                this.setValue(Integer.valueOf(text) + 1);
            }
        });
        beanWrapper.setPropertyValue("weight", "10"); // 11, 会采用自己的PropertyEditor 转换
        System.out.println(user.getWeight());
    }

    private static User getUser() {
        User user = new User();
        Address address = new Address();
        user.setAddress(address);
        Map<String, Object> feature = new HashMap<>();
        user.setFeature(feature);

        address.setCity("beijing");
        address.setProvince("china");
        user.setLikes(Lists.newArrayList("篮球", "足球"));
        user.setUsername("username");
        feature.put("birthday", new Date());
        feature.put("age", 46);

        return user;
    }
}
复制代码

3. debug 查看对象结果:

 

posted @   QiaoZhi  阅读(603)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· Obsidian + DeepSeek:免费 AI 助力你的知识管理,让你的笔记飞起来!
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
历史上的今天:
2018-05-21 【Tomcat】Tomcat容器 web.xml详解
点击右上角即可分享
微信分享提示