文章目录
前言
在之前的系列中我们完成了无参或有参的bean初始化,但是随着业务的不断改变,某些业务需要不断新增成员属性,这时如果我们不断增加构造方法,不仅会使代码变得笨重,且使用spring容器进行bean配置也会带来很大的不便,对此我们就需要对框架进行进一步优化。
分析
需求
简单来说,我们的bean很多情况下,会不断新增成员变量,但是我们又不想为了去适配这些成员变量而去不断堆砌构造函数,所以,本次的需求即:如何实现仅仅在配置中配置一次当前类所对应的成员变量bean,就能实现在框架任何一个角落都能够在无需构造函数帮助的情况下,实现初始化。
实现思路
很明显,对于这种需求,我们就是需要在完成某个bean实例化之后,检查他成员变量,通过在容器中搜寻合适的bean进行赋值。
对此,我们就需要对成员变量设计一个专门管理某些bean的成员变量,故我们需要添加一个成员变量类PropertyValue,这个类就包含两个变量,一个name,用于描述成员变量名。一个value,用于表示这个成员变量值。
考虑到我们成员变量可能不止一个,所以我们也得专门设计一个成员变量容器类,来专门操作这么一组字段对象,这个对象就是PropertyValues。
设计完字段对象之后,我们就需要考虑字段对象初始化的方式可能会存在某些差异,对于原始类型,我们只需从容器中找到对应的值赋到bean的成员变量上即可。
但是对于引用类型,情况就复杂多了,这个引用可能包含其他引用,对应引用成员属性我们必须不断递归的去容器中检查当前引用类型的成员变量是否包含引用类型的成员属性。
所以鉴于引用类型特殊情况,我们必须专门设计一个类BeanReference来表示引用类型,当一个PropertyValue的值是BeanReference类型时,我们就需要按照引用类型bean初始化的方式进行处理。
最终不管是原始类型还是引用类型,我们只需将其注入到对应的成员变量中即可。
类图
代码结构
代码
PropertyValue
package cn.shark.springframework.beans;
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;
}
}
PropertyValues
package cn.shark.springframework.beans;
import java.util.ArrayList;
import java.util.List;
public class PropertyValues {
private 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 name) {
for (PropertyValue pv : propertyValueList) {
if (pv.getName().equals(name)) {
return pv;
}
}
return null;
}
}
BeanReference
package cn.shark.springframework.beans.factory.config;
public class BeanReference {
private String beanName;
public BeanReference(String beanName) {
this.beanName = beanName;
}
public String getBeanName() {
return beanName;
}
}
BeanDefinition
package cn.shark.springframework.beans.factory.config;
import cn.shark.springframework.beans.PropertyValues;
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();
}
public Class getBeanClass() {
return beanClass;
}
public void setBeanClass(Class beanClass) {
this.beanClass = beanClass;
}
public PropertyValues getPropertyValues() {
return propertyValues;
}
public void setPropertyValues(PropertyValues propertyValues) {
this.propertyValues = propertyValues;
}
}
AbstractAutowireCapableBeanFactory
package cn.shark.springframework.beans.factory.support;
import cn.hutool.core.bean.BeanUtil;
import cn.shark.springframework.beans.BeansException;
import cn.shark.springframework.beans.PropertyValue;
import cn.shark.springframework.beans.PropertyValues;
import cn.shark.springframework.beans.factory.config.BeanDefinition;
import cn.shark.springframework.beans.factory.config.BeanReference;
import java.lang.reflect.Constructor;
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(beanName, beanDefinition, args);
applyPropertyValues(beanName,bean,beanDefinition);
} catch (Exception e) {
throw new BeansException("Instantiation of bean failed", e);
}
addSingleton(beanName, bean);
return bean;
}
protected Object createBeanInstance(String beanName, BeanDefinition beanDefinition, 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(beanName, beanDefinition, constructorToUse, args);
}
protected void applyPropertyValues(String beanName, Object bean, BeanDefinition beanDefinition) {
try {
for (PropertyValue pv : beanDefinition.getPropertyValues().getPropertyValues()) {
String name = pv.getName();
Object value = pv.getValue();
if (value instanceof BeanReference) {
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;
}
}
测试
package cn.shark.springframework.bean;
import java.util.HashMap;
import java.util.Map;
public class UserDao {
private static Map<String, String> hashMap = new HashMap<>();
static {
hashMap.put("1", "aa");
hashMap.put("2", "bb");
hashMap.put("3", "cc");
}
public String queryUserName(String uId) {
return hashMap.get(uId);
}
}
package cn.shark.springframework.bean;
/**
* 测试bean
*
* @author Zsy
* @date 2021-08-30 21:41
*/
public class UserService {
private String name;
private String uId;
private UserDao userDao;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public UserService(String name) {
this.name = name;
}
public UserService() {
}
public void queryUserInfo() {
System.out.println("查询用户信息:" + userDao.queryUserName(this.uId));
}
}
package cn.shark.springframework;
import cn.shark.springframework.bean.UserDao;
import cn.shark.springframework.bean.UserService;
import cn.shark.springframework.beans.PropertyValue;
import cn.shark.springframework.beans.PropertyValues;
import cn.shark.springframework.beans.factory.config.BeanDefinition;
import cn.shark.springframework.beans.factory.config.BeanReference;
import cn.shark.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.junit.Test;
public class ApiTest {
@Test
public void test() {
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
beanFactory.registerBeanDefinition("userDao", new BeanDefinition(UserDao.class));
PropertyValues propertyValues=new PropertyValues();
propertyValues.addPropertyValue(new PropertyValue("uId","1"));
propertyValues.addPropertyValue(new PropertyValue("userDao",new BeanReference("userDao")));
BeanDefinition beanDefinition=new BeanDefinition(UserService.class,propertyValues);
beanFactory.registerBeanDefinition("userService",beanDefinition);
UserService userService = (UserService) beanFactory.getBean("userService");
userService.queryUserInfo();
}
}
总结
对于本次扩展,我们的填充成员操作都是在AbstractAutowireCapableBeanFactory进行扩充,因为它的上一级使用模板方法约束了它,而继承他的容器填充成员变量的方式都是一致的,所以为了通用性,将填充成员变量的代码放在该类中最为合适。