上一章是给Bean对象注入构造参数,这一章就来实现给Bean对象注入属性,一般来说类有构造方法也会有全局变量的属性,那么我们在创造的时候就需要注入进来,不能不管,这样才是一个完整的创建对象。
对于属性的创建除了基本类型也会有引用类型,引用其他的Bean对象等等都要考虑。
标红的是新添加的
├─src
│ ├─main
│ │ ├─java
│ │ │ └─com
│ │ │ └─spring
│ │ │ └─sourcecode
│ │ │ │ SourcecodeApplication.java
│ │ │ │
│ │ │ └─springframework
│ │ │ │ BeanDefinition.java
│ │ │ │ BeanFactory.java
│ │ │ │
│ │ │ └─beans
│ │ │ │ BeansException.java
│ │ │ │ PropertyValue.java
│ │ │ │ PropertyValues.java
│ │ │ │
│ │ │ └─factory
│ │ │ │ BeanFactory.java
│ │ │ │
│ │ │ ├─config
│ │ │ │ BeanDefinition.java
│ │ │ │ BeanReference.java
│ │ │ │ SingletonBeanRegistry.java
│ │ │ │
│ │ │ └─support
│ │ │ AbstractAutowireCapableBeanFactory.java
│ │ │ AbstractBeanFactory.java
│ │ │ BeanDefinitionRegistry.java
│ │ │ CglibSubclassingInstantiationStrategy.java
│ │ │ DefaultListableBeanFactory.java
│ │ │ DefaultSingletonBeanRegistry.java
│ │ │ InstantiationStrategy.java
│ │ │ SimpleInstantiationStrategy.java
│ │ │
│ │ └─resources
│ │ application.properties
│ │
│ └─test
│ └─java
│ └─com
│ └─spring
│ └─sourcecode
│ │ SourcecodeApplicationTests.java
│ │
│ └─springframework
│ │ ApiTest.java
│ │ ApiTest03.java
│ │ ApiTest04.java
│ │ ApiTest05.java
│ │
│ └─test
│ UserDao.java
│ UserService.java
为什么要添加这些类?马上介绍,首先目前加的三个类都是关于属性方面的,涉及的以前类的更改也是根据需要填充属性而更改的。
PropertyValue:
定义属性类,这个类其实就是对属性的包装,name是属性的key,value是属性的值,通过此我们就可将对应的key与value填充到对应的Bean对象里。
PropertyValues:
属性集合类,一个类里不一定只有一个属性,可能会是很多个,所以将定义属性的集合类,依赖PropertyValue类,将每个PropertyValue添加到List里,所以就有addPropertyValue()方法,添加到集合以后后续在填充时需要从此类里取出集合,所以就要有getPropertyValues()方法。
BeanReference:
这个是Bean的引用类,可能这个正常是不太理解的,这个其实就是对引用类Bean的引用,比如我有一个UserService的Bean对象注册了,我还有UserDao的bean对象也注册了,我需要在userService引用UserDao里的某个方法,那么我就需要从容器里取出userDao对象对吧,那么对于UserService类来说这个userDao就是属性,只不过这个属性比较特殊,是个引用类对象,在属性填充时需要通过bean的名称指定从容器里取出Bean,所以需要标识证明是引用方式。
所以此类就只有一个属性,就是beanName,这回大家应该清楚了吧。以上是新增加的类,对原有的类进行更改也要介绍一下。
BeanDefination:
由于是类属性,BeanDefination原有就是对Bean对象的定义的,我们要对Bean对象填充属性也应由在此来定义属性,那么就在此加一个属性集合PropertyValues类,用于后面使用。
AbstractAutowireCapableBeanFactory:
这个类之前就是真正创建Bean对象的类,所以现在我们需要创建Bean对象以后,为Bean填充属性,这里就会添加applyPropertyValues()方法用来填充属性。
这节的改动和要添加的类就这些,下面代码实现
PropertyValue:属性类
// 第5节-定义属性
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:属性集合类
// 第5节-属性集合,可能一个类会有多个属性
public class PropertyValues {
private final List 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;
}
}
BeanReference:bean的引用类
// 第05节添加,做Bean的引用时使用
public class BeanReference {
private final String beanName;
public BeanReference(String beanName) {
this.beanName = beanName;
}
public String getBeanName() {
return beanName;
}
}
BeanDefinition:bean定义
// Bean定义
public class BeanDefinition {
// 按上一章的话这里已经把Object改成了Class,这样就可以把bean的实例化操作放到容器中处理了
private Class beanClass;
// 第05节加,属性,所以为了把属性交给bean定义,所以这里填充了propertyValues属性
private PropertyValues propertyValues;
public BeanDefinition(Class beanClass) {
this.beanClass = beanClass;
// 此处加入new PropertyValues()目的是在Bean对象没有属性时后续获取会报空错,在此处理
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:这里除了上一章节的创建构造函数参数实例化,这一章加了Bean属性填充,这个方法设计的很妙,我们从beanDefinition里取出属性集合,普通的属性可直接填充到当前bean里,如遇到beanReference以后我们从中获取到beanName以后使用递归方式获取getBean()开始递归当前方法然后进行属性填充就可以了。
public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory {
private InstantiationStrategy instantiationStrategy = new SimpleInstantiationStrategy();
@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);
}
// 获取bean实例以后添加到单例对象中
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);
}
public InstantiationStrategy getInstantiationStrategy() {
return instantiationStrategy;
}
// 第五节添加属性填充**********************************
/**
* 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) {
// 如遇到bean的引用,继续递归做bean的创建或者获取bean操作
BeanReference beanReference = (BeanReference) value;
value = getBean(beanReference.getBeanName());
}
// 给bean对象属性填充
BeanUtil.setFieldValue(bean, name, value);
}
} catch (Exception e) {
throw new BeansException("Error setting property values:" + beanName);
}
}
}
所有涉及的类都改动好了,可以进行测试了
测试之前的准备
添加UserDao类,目的作为UserService的引用。
public class UserDao {
private static Map hashMap = new HashMap<>();
static {
hashMap.put("10001", "hahaha");
hashMap.put("10002", "ss");
hashMap.put("10003", "sio");
}
public String queryUserName(String uId) {
return hashMap.get(uId);
}
}
UserService:此类需更改从userDao里调用方法
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;
}
}
单元测试类
public class ApiTest05 {
@Test
public void test_BeanFactory() {
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
// 2.userDao,bean定义注册
beanFactory.registerBeanDefinition("userDao", new BeanDefinition(UserDao.class));
// 3.设置属性[uId、userDao]
PropertyValues propertyValues = new PropertyValues();
propertyValues.addPropertyValue(new PropertyValue("uId", "10002"));
propertyValues.addPropertyValue(new PropertyValue("userDao", new BeanReference("userDao")));
// 4.将UserService和属性注册到BeanDefination
BeanDefinition beanDefination = new BeanDefinition(UserService.class, propertyValues);
beanFactory.registerBeanDefinition("userService", beanDefination);
// 5.userService获取bean(创建bean以后进行UserService的属性填充)
UserService userService = (UserService) beanFactory.getBean("userService");
// 最后userService类属性就全部填充上数据了
userService.queryUserInfo();
}
}
测试结果
其实这个是Spring实现的简单版本,其类名称与Spring源码是一致的,也可对照Spring源码学习,会看到有一些熟悉的属性和方法,我就会很开心。
此学习是通过bugstack虫洞栈手撸Spring源码专栏学习,通过学习融会贯通,根据自己反复推敲、揣摩理解出此文章,所以在此感谢大神给我们带来优秀的博文。
源代码已上传到github上:
https://github.com/dufGIT/spring-sourcecode.git