在Spring Bean实例过程中,如何使用反射和递归处理的Bean属性填充?

  1. 工程结构
    small-spring-step-04
    └── src
    ├── main
    │ └── java
    │ └── cn.bugstack.springframework.beans
    │ ├── factory
    │ │ ├── factory
    │ │ │ ├── BeanDefinition.java
    │ │ │ ├── BeanReference.java
    │ │ │ └── SingletonBeanRegistry.java
    │ │ ├── support
    │ │ │ ├── AbstractAutowireCapableBeanFactory.java
    │ │ │ ├── AbstractBeanFactory.java
    │ │ │ ├── BeanDefinitionRegistry.java
    │ │ │ ├── CglibSubclassingInstantiationStrategy.java
    │ │ │ ├── DefaultListableBeanFactory.java
    │ │ │ ├── DefaultSingletonBeanRegistry.java
    │ │ │ ├── InstantiationStrategy.java
    │ │ │ └── SimpleInstantiationStrategy.java
    │ │ └── BeanFactory.java
    │ ├── BeansException.java
    │ ├── PropertyValue.java
    │ └── PropertyValues.java
    └── test
    └── java
    └── cn.bugstack.springframework.test
    ├── bean
    │ ├── UserDao.java
    │ └── UserService.java
    └── ApiTest.java
    工程源码:公众号「bugstack虫洞栈」,回复:Spring 专栏,获取完整源码

Spring Bean 容器类关系,如图 5-2

图 5-2

本章节中需要新增加3个类,BeanReference(类引用)、PropertyValue(属性值)、PropertyValues(属性集合),分别用于类和其他类型属性填充操作。
另外改动的类主要是 AbstractAutowireCapableBeanFactory,在 createBean 中补全属性填充部分。
2. 定义属性
cn.bugstack.springframework.beans.PropertyValue

public class PropertyValue {

private final String name;

private final Object value;

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

// ...get/set

}
cn.bugstack.springframework.beans.PropertyValues

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. Bean定义补全
cn.bugstack.springframework.beans.factory.config.BeanDefinition

public class BeanDefinition {

private Class beanClass;

private PropertyValues propertyValues;

public BeanDefinition(Class beanClass) {
    this.beanClass = beanClass;
    this.propertyValues = new PropertyValues();
}

public BeanDefinition(Class beanClass, PropertyValues propertyValues) {
    this.beanClass = beanClass;
    this.propertyValues = propertyValues != null ? propertyValues : new PropertyValues();
}

// ...get/set

}
在 Bean 注册的过程中是需要传递 Bean 的信息,在几个前面章节的测试中都有所体现 new BeanDefinition(UserService.class, propertyValues);
所以为了把属性一定交给 Bean 定义,所以这里填充了 PropertyValues 属性,同时把两个构造函数做了一些简单的优化,避免后面 for 循环时还得判断属性填充是否为空。
4. Bean 属性填充
cn.bugstack.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory

public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory {

private InstantiationStrategy instantiationStrategy = new CglibSubclassingInstantiationStrategy();

@Override
protected Object createBean(String beanName, BeanDefinition beanDefinition, Object[] args) throws BeansException {
    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);
    }

    addSingleton(beanName, bean);
    return bean;
}

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);
}

/**
 * Bean 属性填充
 */
protected 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);
    }
}

public InstantiationStrategy getInstantiationStrategy() {
    return instantiationStrategy;
}

public void setInstantiationStrategy(InstantiationStrategy instantiationStrategy) {
    this.instantiationStrategy = instantiationStrategy;
}

}
这个类的内容稍微有点长,主要包括三个方法:createBean、createBeanInstance、applyPropertyValues,这里我们主要关注 createBean 的方法中调用的 applyPropertyValues 方法。
在 applyPropertyValues 中,通过获取 beanDefinition.getPropertyValues() 循环进行属性填充操作,如果遇到的是 BeanReference,那么就需要递归获取 Bean 实例,调用 getBean 方法。
当把依赖的 Bean 对象创建完成后,会递归回现在属性填充中。这里需要注意我们并没有去处理循环依赖的问题,这部分内容较大,后续补充。BeanUtil.setFieldValue(bean, name, value) 是 hutool-all 工具类中的方法,你也可以自己实现
五、测试

  1. 事先准备
    cn.bugstack.springframework.test.bean.UserDao

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);
}

}
cn.bugstack.springframework.test.bean.UserService

public class UserService {

private String uId;

private UserDao userDao;

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

// ...get/set

}
Dao、Service,是我们平常开发经常使用的场景。在 UserService 中注入 UserDao,这样就能体现出Bean属性的依赖了。
2. 测试用例
@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. UserService 获取bean
UserService userService = (UserService) beanFactory.getBean("userService");
userService.queryUserInfo();

}
]USB Microphone https://www.soft-voice.com/
Wooden Speakers https://www.zeshuiplatform.com/
亚马逊测评 www.yisuping.cn
深圳网站建设www.sz886.com

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值