深入挖掘Spring系列 -- Spring里面常用的反射实例化技巧

简介: 深入挖掘Spring系列 -- Spring里面常用的反射实例化技巧

ResolvableType,可解决的数据类型。它为java语言中的所有类型提供了相同的数据结构,其内部封装了一个java.lang.reflect.Type类型的对象。


在讲解这个数据结构之前,首先要了解一些预备知识,我们不妨思考如下2个问题:

在java语法中,哪些元素可以代表一种类型?


在java语法中,哪些元素具有类型?


在jdk中,Type接口代表一种类型,所有的具体类型都需要实现这个接口。


网络异常,图片无法展示
|


从图中可以看出,java语法中的类型可以分为五大类:组件类型为参数化类型或类型变量的数组、参数化类型、通配符表达式类型、类型变量以及所有定义的Class(每个类都是一个具体的类型)。除Class类以外的4个接口是jdk1.5以后出现的,因为单纯的Class类无法描述泛型信息。


回到之前提到的两个问题,现在第一个问题已经得到了答案。那么,java中哪些元素具有类型的属性呢?答案是:只有变量(或者说值,因为变量是值的载体)才具有类型。那么什么是变量呢?变量根据其所在位置不同,包括:成员变量、局部变量、方法形参以及方法返回值。


Class是一种类型,但它本身不具有类型的属性。


言归正传,下面讲解ResolvableType。ResolvableType为所有的java类型提供了统一的数据结构以及API,换句话说,一个ResolvableType对象就对应着一种java类型。我们可以通过ResolvableType对象获取类型携带的信息(举例如下):


getSuperType():获取直接父类型
getInterfaces():获取接口类型
getGeneric(int...):获取类型携带的泛型类型
resolve():Type对象到Class对象的转换
         另外,ResolvableType的构造方法全部为私有的,我们不能直接new,只能使用其提供的静态方法进行类型获取:
forField(Field):获取指定字段的类型
forMethodParameter(Method, int):获取指定方法的指定形参的类型
forMethodReturnType(Method):获取指定方法的返回值的类型
forClass(Class):直接封装指定的类型
复制代码


package com.sise.test;
import org.junit.Assert;
import org.junit.Test;
import org.springframework.core.MethodParameter;
import org.springframework.core.ResolvableType;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
/**
 * @author hao_lin
 * @data 2018/10/8 10:46
 */
public class ResolvableTypeTest {
    static class ExtendsList extends ArrayList<CharSequence> {
        public String field1;
        public List<String> stringList(int id) {
            List<String> list = new ArrayList<>();
            list.add(id + "iii");
            return list;
        }
        public void handlePassword(HeadVO<User, Accout> param) {
        }
    }
    /**
     * 如何防止某个参数为空,进行反射实例化的做法
     *
     * @throws NoSuchMethodException
     * @throws IllegalAccessException
     * @throws InstantiationException
     */
    @Test
    public void handleParameter() throws NoSuchMethodException, IllegalAccessException, InstantiationException {
        Method method = ExtendsList.class.getMethod("handlePassword", HeadVO.class);
        //获取到相应方法的第一个参数
        ResolvableType resolvableType = ResolvableType.forMethodParameter(method, 0);
        //获取到相应方法的第一个参数的泛型类型的第二个参数类型
        Class<?> bodyClass = resolvableType.getGeneric(1).resolve();
        Accout accout = (Accout) bodyClass.newInstance();
        System.out.println(accout.toString());
    }
    /**
     * 获取类名
     */
    @Test
    public void forClass() {
        ResolvableType type = ResolvableType.forClass(ExtendsList.class);
        Assert.assertTrue(type.getType().equals((Type) ExtendsList.class));
    }
    /**
     * 获取字段的名称
     */
    @Test
    public void forField() throws NoSuchFieldException {
        //公开方法才有权限获取
        Field[] fields = ExtendsList.class.getFields();
        for (Field field : fields) {
            System.out.println(field.getName());
        }
        //公开方法才有权限获取
        Field field = ExtendsList.class.getField("field1");
        //获取到相应字段是属于String类型
        ResolvableType type = ResolvableType.forField(field);
        Assert.assertTrue(type.getType().equals(field.getGenericType()));
    }
    /**
     * 获取一个方法的参数类型
     *
     * @throws NoSuchMethodException
     */
    @Test
    public void forMethodParameter() throws NoSuchMethodException {
        Method method = ExtendsList.class.getMethod("stringList", int.class);
        MethodParameter methodParameter = MethodParameter.forMethodOrConstructor(method, 0);
        ResolvableType type = ResolvableType.forMethodParameter(methodParameter);
        Assert.assertTrue(type.getType().equals(method.getGenericParameterTypes()[0]));
    }
    /**
     * 获取方法的返回类型
     *
     * @throws NoSuchMethodException
     */
    @Test
    public void forMethodReturnType() throws NoSuchMethodException {
        Method method = ExtendsList.class.getMethod("stringList", int.class);
        ResolvableType type = ResolvableType.forMethodReturnType(method);
        System.out.println(type.getType());
        MethodParameter methodParameter = MethodParameter.forMethodOrConstructor(method, 0);
        ResolvableType type1 = ResolvableType.forMethodParameter(methodParameter);
        System.out.println(type1.getType());
    }
    /**
     * 获取自身的一个类型
     */
    @Test
    public void forClassTest() {
        ResolvableType type = ResolvableType.forClass(ExtendsList.class);
        System.out.println(type.getType());
        System.out.println(type.getRawClass());
        Assert.assertTrue(type.getType().equals(ExtendsList.class));
        Assert.assertTrue(type.getRawClass().equals(ExtendsList.class));
    }
    /**
     * 获取该类的父类类型
     */
    @Test
    public void getSuperTypeTest() {
        ResolvableType type = ResolvableType.forType(ExtendsList.class);
        ResolvableType superType = type.getSuperType();
        System.out.println(superType.getType());
        System.out.println(superType.getRawClass());
        System.out.println(superType.getGeneric().getType());
    }
    /**
     * 可以将ExtendsList以as的方式转换一下,向上取接口或父类
     */
    @Test
    public void asTest() {
        ResolvableType type = ResolvableType.forType(ExtendsList.class);
        ResolvableType listType = type.as(ArrayList.class);
        System.out.println(listType.getType());
        System.out.println(listType.getRawClass());
        //获取到继承的父类的泛型类型
        System.out.println(listType.getGeneric().getType());
    }
}
复制代码


最后,总结一下ResolvableType的使用场景。它的使用场景是非常广泛的,在spring框架中需要反射的时候,为了不丢失泛型信息,通常都会使用ResolvableType封装所有的类型。

目录
相关文章
|
5月前
|
Java Spring 容器
解读spring5源码中实例化单例bean的调用链
解读spring5源码中实例化单例bean的调用链
|
5月前
|
XML Java 数据格式
|
6月前
|
Java Spring 容器
Spring核心概念、IoC和DI的认识、Spring中bean的配置及实例化、bean的生命周期
Spring核心概念、IoC和DI的认识、Spring中bean的配置及实例化、bean的生命周期
54 0
|
6月前
|
XML 缓存 Java
Spring中的bean 实例化原理
Spring中的bean 实例化原理
50 1
|
6月前
|
XML 缓存 Java
天天用 Spring,bean 实例化原理你懂吗
天天用 Spring,bean 实例化原理你懂吗
47 0
|
6月前
|
Java Maven 数据安全/隐私保护
代码优雅升级,提升开发效率:挖掘Spring AOP配置的学习宝藏!
代码优雅升级,提升开发效率:挖掘Spring AOP配置的学习宝藏!
|
6月前
|
缓存 Java 测试技术
Spring5源码(23)-Spring通过工厂方法实例化bean
Spring5源码(23)-Spring通过工厂方法实例化bean
46 0
|
6月前
|
缓存 Java 测试技术
Spring5源码(22)-Spring通过有参构造方法实例化单例bean
Spring5源码(22)-Spring通过有参构造方法实例化单例bean
91 0
|
6月前
|
缓存 Java Spring
Spring5源码(21)-Spring通过无参构造方法实例化单例bean
Spring5源码(21)-Spring通过无参构造方法实例化单例bean
61 0
|
6月前
|
缓存 Java Spring
Spring5源码(20)-Spring实例化单例bean的准备工作
Spring5源码(20)-Spring实例化单例bean的准备工作
35 0