文章目录
- 一、目标:注入属性和依赖对象
- 二、设计:注入属性和依赖对象
- 三、实现:注入属性和依赖对象
-
- [3.0 引入依赖](#3.0 引入依赖)
- [3.1 工程结构](#3.1 工程结构)
- [3.2 注入属性和依赖对象类图](#3.2 注入属性和依赖对象类图)
- [3.3 定义属性值和属性集合](#3.3 定义属性值和属性集合)
-
- [3.3.1 定义属性值](#3.3.1 定义属性值)
- [3.3.2 定义属性集合](#3.3.2 定义属性集合)
- [3.4 Bean定义补全](#3.4 Bean定义补全)
- [3.5 Bean属性填充](#3.5 Bean属性填充)
- 四、测试:注入属性和依赖对象
-
- [4.1 用户Bean对象](#4.1 用户Bean对象)
-
- [4.1.1 用户Dao对象](#4.1.1 用户Dao对象)
- [4.1.2 用户Service对象](#4.1.2 用户Service对象)
- [4.2 单元测试](#4.2 单元测试)
- 五、总结:注入属性和依赖对象
一、目标:注入属性和依赖对象
💡 已经完成 实现一个容器、定义和注册Bean、实例化Bean、按照是否包含构造函数实现不同的实例化策略,那么在创建对象实例化,我们还缺少什么?
- 其实还缺少
类中是否有属性的问题
,如果类中包含属性那么在实例化的时候就需要把属性信息填充上,这样才是一个完整的对象创建。 - 对于属性的填充不只是
int、Long、String
,还包括没有实例化的对象属性,都需要在 Bean 创建时进行填充操作。
二、设计:注入属性和依赖对象
💡 技术设计:注入属性和依赖对象
- 属性填充是在 Bean 使用
newInstance
或者cglib
创建后,开始补全属性信息,那么就可以在类AbstractAutowireCapableBeanFactory
的createBean
方法中添加补全属性方法。
- 属性填充要在类实例化创建之后,也就是需要在
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;
}
}
- 这个类主要包括三个方法:
createBean
、createBeanInstance
、applyPropertyValues
,这里我们主要关注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 对象时,需要递归处理。
- 最后在属性填充时需要用到反射操作,也可以使用一些工具类处理。