Spring手撸源码系列-给Bean对象注入属性

上一章是给Bean对象注入构造参数,这一章就来实现给Bean对象注入属性,一般来说类有构造方法也会有全局变量的属性,那么我们在创造的时候就需要注入进来,不能不管,这样才是一个完整的创建对象。

对于属性的创建除了基本类型也会有引用类型,引用其他的Bean对象等等都要考虑。

1.工程目录

标红的是新添加的

Spring手撸源码系列-给Bean对象注入属性_第1张图片

 Spring手撸源码系列-给Bean对象注入属性_第2张图片

├─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

2.UML类图

Spring手撸源码系列-给Bean对象注入属性_第3张图片

为什么要添加这些类?马上介绍,首先目前加的三个类都是关于属性方面的,涉及的以前类的更改也是根据需要填充属性而更改的。

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,所以需要标识证明是引用方式。

Spring手撸源码系列-给Bean对象注入属性_第4张图片

所以此类就只有一个属性,就是beanName,这回大家应该清楚了吧。以上是新增加的类,对原有的类进行更改也要介绍一下。

BeanDefination:

由于是类属性,BeanDefination原有就是对Bean对象的定义的,我们要对Bean对象填充属性也应由在此来定义属性,那么就在此加一个属性集合PropertyValues类,用于后面使用。

AbstractAutowireCapableBeanFactory:

这个类之前就是真正创建Bean对象的类,所以现在我们需要创建Bean对象以后,为Bean填充属性,这里就会添加applyPropertyValues()方法用来填充属性。

这节的改动和要添加的类就这些,下面代码实现

3.代码实现:

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

}

所有涉及的类都改动好了,可以进行测试了

4.测试

测试之前的准备

添加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

你可能感兴趣的:(#,spring,spring,java,后端)