手写简单的Spring框架——my-mini-spring -- IOC篇 -- 简单的IOC容器 (二)

项目的Github地址:github仓库
Gitee地址:gitee仓库
个人博客地址sillybaka的博客

此篇所在的分支为:bean-property-injection
该篇 实现了根据beanDefinition对bean进行实例化的策略方法、以及属性注入

简单的IOC容器

1、 实例化Bean的策略模式

Bean的实例化模式可以分为两个:

  1. 使用jdk的反射获取类的构造函数来实例化
  2. 使用Cglib动态代理来生成实例对象

image-20221105110034393

InstantiationStrategy(策略接口)

public interface InstantiationStrategy {

    /**
     * 使用某种策略实例化Bean对象
     * @param beanDefinition
     * @param <T>
     * @return
     */
    <T> T instantiation(BeanDefinition<T> beanDefinition);
}

CglibSubclassingInstantiationStrategy(Cglib实例化bean,未实现)

public class CglibSubclassingInstantiationStrategy implements InstantiationStrategy {
    @Override
    public <T> T instantiation(BeanDefinition<T> beanDefinition) {
        return null;
    }
}

SimpleInstantiationStrategy(Jdk实例化bean,只实现这个)

public class SimpleInstantiationStrategy implements InstantiationStrategy {
    @Override
    public <T> T instantiation(BeanDefinition<T> beanDefinition) {
        Class<T> clazz = beanDefinition.getType();

        T instance = null;
        try {
            Constructor<T> constructor = clazz.getDeclaredConstructor();

            instance = constructor.newInstance();
        } catch (NoSuchMethodException | InvocationTargetException | InstantiationException | IllegalAccessException e) {
            e.printStackTrace();
        }

        return instance;
    }
}

2、 为Bean对象注入普通属性(底层使用setter注入)

使用PropertyValue类存每一对属性(Key-Value类型) 比用Map存更灵活

使用PropertyValues存每一个对象的所有PropertyValue(相当于一个工具类)

使用 属性名 通过反射获取属性的类型然后再获取该属性的Setter方法,再反射调用Setter方法进行注入

但实际上Spring中使用的是PropertyDescritor(底层原理,将Bean类的所有属性类型、名字读取到一个hashmap里,然后就可以通过属性名字来获取某属性的setter、getter等方法)

PropertyValue(表示Bean中的一个属性)

public class PropertyValue {
    /**
     * 属性名字
     */
    private String propertyName;
    /**
     * 属性值
     */
    private Object propertyValue;
}

PropertyValues(表示bean的所有属性)

public class PropertyValues {
    private final List<PropertyValue> propertyValueList = new ArrayList<>();

    public void addPropertyValue(PropertyValue pv){
        if(pv == null){
            throw new IllegalArgumentException("初始化bean添加的属性不能为空");
        }
        for (int i = 0; i < propertyValueList.size(); i++) {
            if(propertyValueList.get(i).getPropertyName().equals(pv.getPropertyName())){
                // 覆盖已有的属性值
                propertyValueList.set(i,pv);
                return;
            }
        }
        // 否则就添加
        propertyValueList.add(pv);
    }

    public PropertyValue[] getPropertyValues(){
        return propertyValueList.toArray(new PropertyValue[0]);
    }
}

PropertyUtil(用于获取属性getter、setter的工具类)

public class PropertyUtils {
    /**
     * 使用Bean类获取其所有属性的PropertyDescriptor Map
     */
    private static final Map<Class<?>, Map<String,PropertyDescriptor>> BEANS_PROPERTY_MAP;

    /**
     * 冗余项:用于获取Bean类的每种属性类型
     */
    private static final Map<Class<?>, Map<String,Class<?>>> BEANS_PROPERTY_TYPE_MAP;

    static {
        BEANS_PROPERTY_MAP = new HashMap<>();
        BEANS_PROPERTY_TYPE_MAP = new HashMap<>();
    }

    public static void addBeanPropertyMap(Class<?> clazz, Map<String,PropertyDescriptor> map){
        BEANS_PROPERTY_MAP.putIfAbsent(clazz,map);
    }

    /**
     * 根据指定类的clazz获取其propertyDescriptorMap
     * @param clazz
     * @return
     */
    public static Map<String,PropertyDescriptor> getBeanPropertyMap(Class<?> clazz){
        Map<String, PropertyDescriptor> descriptorMap = BEANS_PROPERTY_MAP.get(clazz);
        if(descriptorMap == null){
            // 懒汉式单例加载
            synchronized (PropertyUtils.class){
                descriptorMap = BEANS_PROPERTY_MAP.get(clazz);
                if(descriptorMap == null){
                    addPropertyDescriptor(clazz);
                }
                descriptorMap = BEANS_PROPERTY_MAP.get(clazz);
            }
        }
        return descriptorMap;
    }


    /**
     * 根据指定类的class将其所有属性的PropertyDescriptor添加到map中 (没有内嵌Bean的才能调用)
     * 生命周期开始为在XMl扫描时按需调用,只能注入基本属性
     * @param clazz 指定类的class
     */
    public static void addPropertyDescriptor(Class<?> clazz){
        Map<String, PropertyDescriptor> descriptorMap = BEANS_PROPERTY_MAP.get(clazz);
        if(descriptorMap == null){
            BEANS_PROPERTY_MAP.putIfAbsent(clazz,new HashMap<>());
            descriptorMap = BEANS_PROPERTY_MAP.get(clazz);
        }
        // 获取该类的所有属性的Descriptor
        Field[] fields = clazz.getDeclaredFields();
        for(Field field : fields){
            String propertyName = field.getName();
            try {
                Class<?> propertyType = field.getType();

                PropertyDescriptor pv = new PropertyDescriptor(propertyName, clazz);

                descriptorMap.put(propertyName,pv);
                addPropertyType(clazz, propertyName, propertyType);

            } catch (IntrospectionException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 根据指定类的的class和propertyName添加PropertyDescriptor到map中,可以指定property类型
     * @param clazz 指定类的class
     * @param propertyName 属性名
     * @param anotherType  若不为空 则设置为该property的别类型
     */
    public static void addPropertyDescriptor(Class<?> clazz, String propertyName, @Nullable Class<?> anotherType){
        PropertyDescriptor pd;

        try {
            Field field = clazz.getDeclaredField(propertyName);
            Class<?> propertyType = field.getType();

            pd = new PropertyDescriptor(propertyName, clazz);

            // 添加进map中
            Map<String, PropertyDescriptor> descriptorMap = BEANS_PROPERTY_MAP.get(clazz);
            if(descriptorMap == null){
                BEANS_PROPERTY_MAP.putIfAbsent(clazz,new HashMap<>());
                descriptorMap = BEANS_PROPERTY_MAP.get(clazz);
            }
            descriptorMap.put(propertyName,pd);

            // 检查是否有设置另外的类型
            if(anotherType != null){
                addPropertyType(clazz, propertyName, anotherType);
            }else {
                addPropertyType(clazz,propertyName,propertyType);
            }

        } catch (NoSuchFieldException | IntrospectionException e) {
            e.printStackTrace();
        }
    }

    public static void addPropertyType(Class<?> clazz, String propertyName, Class<?> propertyType){
        Map<String, Class<?>> propertyTypeMap = BEANS_PROPERTY_TYPE_MAP.get(clazz);
        if(propertyTypeMap == null){
            synchronized (PropertyUtils.class){
                BEANS_PROPERTY_TYPE_MAP.putIfAbsent(clazz,new HashMap<>());
                propertyTypeMap = BEANS_PROPERTY_TYPE_MAP.get(clazz);
            }
        }
        propertyTypeMap.put(propertyName,propertyType);
    }

    public static Class<?> getPropertyType(Class<?> clazz, String propertyName){
        Map<String, Class<?>> propertyTypeMap = BEANS_PROPERTY_TYPE_MAP.get(clazz);
        if(propertyTypeMap == null){
            throw new IllegalArgumentException("该Bean类型不存在");
        }
        Class<?> propertyType = propertyTypeMap.get(propertyName);
        if(propertyType == null){
            throw new IllegalArgumentException("该Bean不存在该属性名: " + propertyName);
        }
        return propertyType;
    }

    public static Map<String,Class<?>> getPropertyTypeMap(Class<?> clazz){
        Map<String, Class<?>> propertyTypeMap = BEANS_PROPERTY_TYPE_MAP.get(clazz);
        if(propertyTypeMap == null){
            throw new IllegalArgumentException("该Bean类型不存在");
        }
        return propertyTypeMap;
    }
}

3、 为Bean对象注入Bean属性(有循环依赖风险,后面高级篇解决)

在Spring的xml Bean配置中,是使用beanNamebeanType来配置嵌套bean属性的

<bean id="xxx" class="xxx" >
	<property name="xxx" value="xxx"></property>
	// 第一种 直接注入(级联)
	<bean id="xxxx" class="xxxx">
	</bean>
// 第二种 使用引用注入
<property name="xxx" ref="refXXX"></property>
</bean>
<bean id="refXXX" class="xxx">
</bean>
  1. 若是直接注入(级联、内嵌),则相当于再定义了一个Bean,用BeanDefinition存信息,先创建这个内嵌Bean,再赋值给外层Bean**(内部Bean一般是多例的,所以要以多例模式的方式创建)**
  2. 若是引用注入,则使用一个BeanReference类,记录引用的信息,再获取引用的那个Bean,再复制**(要考虑循环依赖的情况)**(懒汉式,所以不用考虑引用的Bean是否已创建)

BeanReference(用于处理XML配置文件中的bean引用标签)

public class BeanReference {
    /**
     * bean的名字
     */
    private String beanName;
    /**
     * bean的类型
     */
    private String beanType;
}

4、注入Bean属性的实现

AbstractAutowireCapableBeanFactory(自动装配属性的bean工厂)

public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory{

    private static final InstantiationStrategy INSTANTIATION_STRATEGY = new SimpleInstantiationStrategy();

    @Override
    protected <T> T createBean( BeanDefinition<T> beanDefinition) {
        return doCreateBean(beanDefinition);
    }

    /**
     * 创建Bean实例的实际逻辑
     */
    public <T> T doCreateBean(BeanDefinition<T> beanDefinition){
//        if(!name.equals(beanName)){
//            log.error("BeanName和BeanDefinition中的名字不对应,创建bean实例失败");
//            throw new IllegalArgumentException("BeanName和BeanDefinition中的名字不对应,创建bean实例失败");
//        }

        T beanInstance = INSTANTIATION_STRATEGY.instantiation(beanDefinition);
        autoWirePropertyValues(beanInstance,beanDefinition);
        return beanInstance;
    }

    /**
     * 为bean实例对象自动装配属性 (底层使用setter注入)
     * @param bean 实例对象
     * @param beanDefinition bean定义
     */
    public <T> void autoWirePropertyValues(T bean, BeanDefinition<T> beanDefinition){
        PropertyValues propertyValues = beanDefinition.getPropertyValues();

        Class<?> clazz = bean.getClass();

        //todo 获取该Bean类的所有PropertyDescriptor --> 生命周期开始为在XMl扫描时按需调用
        Map<String, PropertyDescriptor> beanPropertyMap = PropertyUtils.getBeanPropertyMap(clazz);

        Map<String, Class<?>> propertyTypeMap = PropertyUtils.getPropertyTypeMap(clazz);

        for(PropertyValue pv : propertyValues.getPropertyValues()){

            String propertyName = pv.getPropertyName();
            Object propertyValue = pv.getPropertyValue();

            try {
                //todo xml配置中 普通属性不能配置类型 应该以其他方式获取
//                Class<?> propertyType = clazz.getDeclaredField(propertyName).getType();
                PropertyDescriptor pd = beanPropertyMap.get(propertyName);

                // 从属性类型map中获取属性类型
                Class<?> propertyType = propertyTypeMap.get(propertyName);

                Method setterMethod = pd.getWriteMethod();
                // 引用类型
                if(propertyType == BeanReference.class){

                    BeanReference beanReference = (BeanReference) propertyValue;
                    String beanName = beanReference.getBeanName();
                    //todo 获取Bean实例 --> 1、有可能该Bean实例尚未创建 应该重写为阻塞等待 (不会有这种情况,因为是懒汉式的设计)
                    //                    2、有可能会发生循环依赖,在后面再解决
                    Object innerBean = getBean(beanName);

                    setterMethod.invoke(bean,innerBean);

                }else if(propertyType == BeanDefinition.class){
                // Bean类型(级联定义了一个Bean)
                    //todo 先根据Bean定义创建Bean实例  --> 有可能破坏单例 需要保存在多例注册表中
                    BeanDefinition<?> innerBeanDefinition = (BeanDefinition<?>) propertyValue;
                    Object innerBean = createBean(innerBeanDefinition);
                    setterMethod.invoke(bean,innerBean);
                }else {
                // 普通属性 直接使用Setter方法注入
                    setterMethod.invoke(bean, propertyValue);
                }

            } catch (InvocationTargetException | IllegalAccessException e) {
                e.printStackTrace();
            }
        }
    }
}
eBean(innerBeanDefinition);
                    setterMethod.invoke(bean,innerBean);
                }else {
                // 普通属性 直接使用Setter方法注入
                    setterMethod.invoke(bean, propertyValue);
                }

            } catch (InvocationTargetException | IllegalAccessException e) {
                e.printStackTrace();
            }
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值