手写Spring:第5章-注入属性和依赖对象

文章目录

一、目标:注入属性和依赖对象

💡 已经完成 实现一个容器、定义和注册Bean、实例化Bean、按照是否包含构造函数实现不同的实例化策略,那么在创建对象实例化,我们还缺少什么?

  • 其实还缺少 类中是否有属性的问题,如果类中包含属性那么在实例化的时候就需要把属性信息填充上,这样才是一个完整的对象创建。
  • 对于属性的填充不只是 int、Long、String,还包括没有实例化的对象属性,都需要在 Bean 创建时进行填充操作。

二、设计:注入属性和依赖对象

💡 技术设计:注入属性和依赖对象

  • 属性填充是在 Bean 使用 newInstance 或者 cglib 创建后,开始补全属性信息,那么就可以在类 AbstractAutowireCapableBeanFactorycreateBean 方法中添加补全属性方法。
  • 属性填充要在类实例化创建之后,也就是需要在 AbstractAutowireCapableBeanFactory#createBean 方法中添加 applyPropertyValues 操作。
  • 由于需要在创建 Bean 时填充属性操作,那么就需要在 bean 定义 BeanDefinition 类中,添加 PropertyValues 信息。
  • 另外是填充属性信息还包括 Bean 的对象类型,也就是需要再定义一个 BeanReference
    • 里面其实就是一个简单的 Bean 名称,再具体的实例化操作时进行递归创建和填充。
    • Spring 源码实现一样,Spring 源码中 BeanReference 是一个接口。

三、实现:注入属性和依赖对象

3.0 引入依赖

pom.xml

xml 复制代码
<dependency>
    <groupId>cn.hutool</groupId>
    <artifactId>hutool-all</artifactId>
    <version>5.5.0</version>
</dependency>

3.1 工程结构

java 复制代码
spring-step-04
|-src
  |-main
  | |-java
  |   |-com.lino.springframework
  |     |-factory
  |     | |-config
  |     | | |-BeanDefinition.java
  |     | | |-BeanReference.java
  |     | | |-SingletonBeanRegistry.java
  |     | |-support
  |     | | |-AbstractAutowireCapableBeanFactory.java
  |     | | |-AbstractBeabFactory.java
  |     | | |-BeanDefinitionRegistry.java
  |     | | |-CglibSubclassingInstantiationStrategy.java
  |     | | |-DefaultListableBeanFactory.java
  |     | | |-DefaultSingletonBeanRegistry.java
  |     | | |-InstantiationStrategy.java
  |     | | |-SimpleInstantiationStrategy.java
  |     | |-BeanFactory.java
  |     |-BeansException.java
  |     |-PropertyValue.java
  |     |-PropertyValues.java
  |-test
    |-java
      |-com.lino.springframework.test
      |-bean
      | |-UserDao.java
      | |-UserService.java
      |-ApiTest.java

3.2 注入属性和依赖对象类图

  • 新增加3个类,BeanReference (类引用)、PropertyValue (属性值)、PropertyValues(属性集合),分别用于类和其他类型属性填充操作。
  • 另外改动的类主要是 AbstractAutowireCapableBeanFactory ,在 createBean 中补全属性填充部分。

3.3 定义属性值和属性集合

3.3.1 定义属性值

PropertyValue.java

java 复制代码
package com.lino.springframework;

/**
 * @description: Bean属性信息
 */
public class PropertyValue {
    /**
     * 属性名称
     */
    private final String name;
    /**
     * 属性值
     */
    private final Object value;

    public PropertyValue(String name, Object value) {
        this.name = name;
        this.value = value;
    }

    public String getName() {
        return name;
    }

    public Object getValue() {
        return value;
    }
}

3.3.2 定义属性集合

PropertyValues.java

java 复制代码
package com.lino.springframework;

import java.util.ArrayList;
import java.util.List;

/**
 * @description: 属性值集合
 */
public class PropertyValues {

    private final List<PropertyValue> propertyValueList = new ArrayList<>();

    public void addPropertyValue(PropertyValue pv) {
        this.propertyValueList.add(pv);
    }

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

    public PropertyValue getPropertyValue(String propertyName) {
        for (PropertyValue pv : this.propertyValueList) {
            if (pv.getName().equals(propertyName)) {
                return pv;
            }
        }
        return null;
    }
}
  • 这两个类的作用就是创建出一个用于传递类中属性信息的类,因为属性可能会有很多,所以还需要定义一个集合包装下。

3.4 Bean定义补全

BeanReference.java

java 复制代码
package com.lino.springframework.factory.config;

/**
 * @description: Bean 引用
 */
public class BeanReference {

    private final String beanName;

    public BeanReference(String beanName) {
        this.beanName = beanName;
    }

    public String getBeanName() {
        return beanName;
    }
}
  • Bean 注册的过程中是需要传递 Bean 的信息。
  • 所以为了把属性一定交给 Bean 定义,所以这里填充了 PropertyValues 属性,同时把两个构造函数做了一些简单的优化,避免后面 for 循环时还得判断属性填充是否为空。

3.5 Bean属性填充

AbstractAutowireCapableBeanFactory.java

java 复制代码
package com.lino.springframework.factory.support;

import cn.hutool.core.bean.BeanUtil;
import com.lino.springframework.BeansException;
import com.lino.springframework.PropertyValue;
import com.lino.springframework.PropertyValues;
import com.lino.springframework.factory.config.BeanDefinition;
import com.lino.springframework.factory.config.BeanReference;
import java.lang.reflect.Constructor;

/**
 * @description: 实现默认bean创建的抽象bean工厂超类
 */
public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory {

    private InstantiationStrategy instantiationStrategy = new CglibSubclassingInstantiationStrategy();

    @Override
    protected Object createBean(String beanName, BeanDefinition beanDefinition, Object[] args) {
        Object bean = null;
        try {
            bean = createBeanInstance(beanDefinition, beanName, args);
            // 给bean填充属性
            applyPropertyValues(beanName, bean, beanDefinition);
        } catch (Exception e) {
            throw new BeansException("Instantiation of bean failed", e);
        }
        registerSingletonBean(beanName, bean);
        return bean;
    }

    private void applyPropertyValues(String beanName, Object bean, BeanDefinition beanDefinition) {
        try {
            PropertyValues propertyValues = beanDefinition.getPropertyValues();
            for (PropertyValue propertyValue : propertyValues.getPropertyValues()) {
                String name = propertyValue.getName();
                Object value = propertyValue.getValue();
                if (value instanceof BeanReference) {
                    // A 依赖 B,获取 B 的实例化
                    BeanReference beanReference = (BeanReference) value;
                    value = getBean(beanReference.getBeanName());
                }
                // 属性填充
                BeanUtil.setFieldValue(bean, name, value);
            }
        } catch (Exception e) {
            throw new BeansException("Error setting property values: " + beanName);
        }
    }

    protected Object createBeanInstance(BeanDefinition beanDefinition, String beanName, Object[] args) {
        Constructor constructorToUse = null;
        Class<?> beanClass = beanDefinition.getBeanClass();
        Constructor<?>[] declaredConstructors = beanClass.getDeclaredConstructors();
        for (Constructor ctor : declaredConstructors) {
            if (null != args && ctor.getParameterTypes().length == args.length) {
                constructorToUse = ctor;
                break;
            }
        }
        return getInstantiationStrategy().instantiate(beanDefinition, beanName, constructorToUse, args);
    }

    public InstantiationStrategy getInstantiationStrategy() {
        return instantiationStrategy;
    }

    public void setInstantiationStrategy(InstantiationStrategy instantiationStrategy) {
        this.instantiationStrategy = instantiationStrategy;
    }
}
  • 这个类主要包括三个方法:createBeancreateBeanInstanceapplyPropertyValues,这里我们主要关注 createBean 的方法中调用的 applyPropertyValues 方法。
  • applyPropertyValues 中,通过获取 beanDefinition.getPropertyValues() 循环进行属性填充操作。
    • 如果遇到的是 BeanReference,那么就需要递归获取 Bean 实例,调用 getBean 方法。
  • 当把依赖的 Bean 对象创建完成后,会递归回现在属性填充中。

四、测试:注入属性和依赖对象

4.1 用户Bean对象

4.1.1 用户Dao对象

UserDao.java

java 复制代码
package com.lino.springframework.test.bean;

import java.util.HashMap;
import java.util.Map;

/**
 * @description: 模拟用户DAO类
 */
public class UserDao {

    private static Map<String, String> hashMap = new HashMap<>();

    static {
        hashMap.put("10001", "张三");
        hashMap.put("10002", "李四");
        hashMap.put("10003", "王五");
    }

    public String queryUserName(String uId) {
        return hashMap.get(uId);
    }
}

4.1.2 用户Service对象

UserServce.java

java 复制代码
package com.lino.springframework.test.bean;

/**
 * @description: 模拟用户 Bean 对象
 */
public class UserService {

    private String uId;

    private UserDao userDao;

    /**
     * 查询用户信息
     */
    public void queryUserInfo() {
        System.out.println("查询用户信息: " + userDao.queryUserName(uId));
    }

    public String getuId() {
        return uId;
    }

    public void setuId(String uId) {
        this.uId = uId;
    }

    public UserDao getUserDao() {
        return userDao;
    }

    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }
}
  • UserService 中注入 UserDao,这样就能体现出 Bean 属性的依赖。

4.2 单元测试

ApiTest.java

java 复制代码
@Test
public void test_BeanFactory() {
    // 1.初始化 BeanFactory
    DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();

    // 2.UserDao注册
    beanFactory.registerBeanDefinition("userDao", new BeanDefinition(UserDao.class));

    // 3.UserService 设置属性[uId、userDao]
    PropertyValues propertyValues = new PropertyValues();
    propertyValues.addPropertyValue(new PropertyValue("uId", "10001"));
    propertyValues.addPropertyValue(new PropertyValue("userDao", new BeanReference("userDao")));

    // 4.UserService 注入bean
    BeanDefinition beanDefinition = new BeanDefinition(UserService.class, propertyValues);
    beanFactory.registerBeanDefinition("userService", beanDefinition);

    // 5.获取bean
    UserService userService = (UserService) beanFactory.getBean("userService");
    userService.queryUserInfo();
}
  • 与直接获取 Bean 对象不同,这次我们还需要先把 userDao 注入到 Bean 容器中。
    • beanFactory.registerBeanDefinition("userDao", new BeanDefinition(UserDao.class))
  • 接下来就是属性填充的操作。
    • 一种是普通属性:new PropertyValue("uId", "10001")
    • 另外一种是对象属性:new PropertyValue("userDao", new BeanReference("userDao"))
  • 最后是正常获取 userService 对象,调用方法即可。

测试结果

java 复制代码
查询用户信息: 张三
  • 从测试结果来看,属性填充已经起作用了,因为只有属性填充后,才能调用到 Dao 方法,如:userDao.queryUserName(uId)

五、总结:注入属性和依赖对象

  • 本章对 AbstructAutowireCapableBeanFactory 类中的创建对象功能又做了扩充,依赖于是否有构造函数的实例化策略完成后。
    • 开始补充 Bean 属性信息。当遇到 Bean 属性为 Bean 对象时,需要递归处理。
    • 最后在属性填充时需要用到反射操作,也可以使用一些工具类处理。
相关推荐
考虑考虑20 分钟前
Jpa使用union all
java·spring boot·后端
用户37215742613542 分钟前
Java 实现 Excel 与 TXT 文本高效互转
java
浮游本尊2 小时前
Java学习第22天 - 云原生与容器化
java
渣哥4 小时前
原来 Java 里线程安全集合有这么多种
java
间彧4 小时前
Spring Boot集成Spring Security完整指南
java
间彧4 小时前
Spring Secutiy基本原理及工作流程
java
Java水解5 小时前
JAVA经典面试题附答案(持续更新版)
java·后端·面试
洛小豆7 小时前
在Java中,Integer.parseInt和Integer.valueOf有什么区别
java·后端·面试
前端小张同学8 小时前
服务器上如何搭建jenkins 服务CI/CD😎😎
java·后端
ytadpole8 小时前
Spring Cloud Gateway:一次不规范 URL 引发的路由转发404问题排查
java·后端