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 查看对象结果:
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· Obsidian + DeepSeek:免费 AI 助力你的知识管理,让你的笔记飞起来!
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
2018-05-21 【Tomcat】Tomcat容器 web.xml详解