深入研究Spring-IoC :容器创建的几种方式

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/mengdonghui123456/article/details/77521573

1.前言

本文为对tiny-spring的学习解读,代码参考自tiny-spring。一个手写的Spring简易版框架。

前面已经提到了Spring IOC容器的创建大致分为3个步骤。但是这个三个步骤是有一个演进的过程的,Spring容器创建方式前后有6种,从最基本的实例化创建创建到后来的自动化,这些过程的学习对我们学习理解IOC有很大的帮助。

2.容器创建需要的代码

HelloWorldService:

public interface HelloWorldService {

    void helloWorld();
}

HelloWorldServiceImpl:

public class HelloWorldServiceImpl implements HelloWorldService {

    private String text;

    private OutputService outputService;

    @Override
    public void helloWorld(){
        outputService.output(text);
    }

    public void setText(String text) {
        this.text = text;
    }

    public void setOutputService(OutputService outputService) {
        this.outputService = outputService;
    }

}

OutputService:

public interface OutputService {
    void output(String text);
}

OutputServiceImpl:

public class OutputServiceImpl implements OutputService {

    @Override
    public void output(String text){
        System.out.println(text);
    }

}

tinyioc.xml:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="
    http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
    http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
    http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd
    http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd">

    <bean id="outputService" class="us.codecraft.tinyioc.OutputServiceImpl">
        <property name="helloWorldService" ref="helloWorldService"></property>
    </bean>

    <bean id="helloWorldService" class="us.codecraft.tinyioc.HelloWorldServiceImpl">
        <property name="text" value="Hello World!"></property>
        <property name="outputService" ref="outputService"></property>
    </bean>

    <bean id="beanInitializeLogger" class="us.codecraft.tinyioc.BeanInitializeLogger">
    </bean>

</beans>

3.创建方式演变

1.最基本的容器

IoC最基本的角色有两个:容器(BeanFactory)和Bean本身。这里使用BeanDefinition来封装了bean对象,这样可以保存一些额外的元信息。

// 1.初始化beanfactory
BeanFactory beanFactory = new BeanFactory();

// 2.注入bean
BeanDefinition beanDefinition = new BeanDefinition(new HelloWorldService());
beanFactory.registerBeanDefinition("helloWorldService", beanDefinition);

// 3.获取bean
HelloWorldService helloWorldService = (HelloWorldService) beanFactory.getBean("helloWorldService");
helloWorldService.helloWorld();

输出结果为:“Hello World!”

分析:
Spring 通过xml中对Bean类(HelloWorldService)属性的赋值给予了HelloWorldService中的 String text =“Hello World!”,这样OutputServiceImpl调用output()方法时可以输出text的值。
另外这个容器创建的过程:

1.手动实例化最基本的BeanFatory容器接口。

BeanFactory beanFactory = new BeanFactory();

从上一篇博客可以知道BeanFatory有很多实现类,不同的实现类代表的不通过规格的容器,可以实现不同的功能。BanFatory系列可以实现双亲IOC的配置以及Bean的自动配置功能,ApplicaitonContext系列更是增加了面向框架的一些特性。

2.指定BeanDefinition(容器中的数据结构)

BeanDefinition beanDefinition = new BeanDefinition(new HelloWorldService());

BeanDefinition是容器中的抽象Bean结构,是容器为调用方依赖注入时映射的模板,也就是我们在注入原型时就是参考这个抽象的Bean结构。

3.注册BeanDefinition到容器中

beanFactory.registerBeanDefinition("helloWorldService", beanDefinition);

这里调用了beanFatory的registerBeanDefinition()方法,这个注册方法的底层是HashMap实现的,结构上是将我们解析的BeanDefinition以HashMap(key,BeanDefinition)的形式放到BeanFatory中,告诉它都有哪些类Bean在容器里。另外可以通过getBean(key)的形式得到BeanDefinition。

2.将bean创建放入工程且为Bean注入属性

第一个方法中的Bean(HelloWorldService)是初始化好之后再set进去的,实际使用中,我们希望容器来管理bean的创建。于是我们将bean的初始化放入BeanFactory中。为了保证扩展性,我们使用Extract Interface的方法,将BeanFactory替换成接口,而使用AbstractBeanFactoryAutowireCapableBeanFactory作为其实现。”AutowireCapable”的意思是“可自动装配的”,为我们后面注入属性做准备。

// 1.初始化beanfactory
BeanFactory beanFactory = new AutowireCapableBeanFactory();

// 2.注入bean
BeanDefinition beanDefinition = new BeanDefinition();
beanDefinition.setBeanClassName("us.codecraft.tinyioc.HelloWorldService");
beanFactory.registerBeanDefinition("helloWorldService", beanDefinition);

// 3.设置属性
PropertyValues propertyValues = new PropertyValues();
propertyValues.addPropertyValue(new PropertyValue("text", "Hello World!"));
beanDefinition.setPropertyValues(propertyValues);

// 4.获取bean
HelloWorldService helloWorldService = (HelloWorldService) beanFactory.getBean("helloWorldService");
helloWorldService.helloWorld();

输出结果:“Hello World!”

分析改进点:

1.容器的选择从BeanFatory()改成它的实现类AutowireCapableBeanFactory()可以参考上一篇博客的类图。它拥有装配属性的作用。

2.在载入BeanDefinition时,不再是创建一个Bean类放入其中,这本身就违背了IOC的理念。而是直接指定了BeanDefinition的数据结构模型。

3.调用AutowireCapableBeanFactory()容器的setPropertyValues方法可以为HelloWorldService类指定的属性text手动赋值,而不需要xml中的配置。

3.读取xml配置来初始化Bean

上面为Bean属性注入的部分比较麻烦,如果属性一多,代码会非常麻烦,这里的BeanDefinition只是一些配置,我们还是用xml来初始化吧。我们定义了BeanDefinitionReader初始化bean,它有一个实现是XmlBeanDefinitionReader

// 1.读取配置
XmlBeanDefinitionReader xmlBeanDefinitionReader = new XmlBeanDefinitionReader(new ResourceLoader());
xmlBeanDefinitionReader.loadBeanDefinitions("tinyioc.xml");

// 2.初始化BeanFactory并注册bean
BeanFactory beanFactory = new AutowireCapableBeanFactory();
for (Map.Entry<String, BeanDefinition> beanDefinitionEntry : xmlBeanDefinitionReader.getRegistry().entrySet()) {
        beanFactory.registerBeanDefinition(beanDefinitionEntry.getKey(), beanDefinitionEntry.getValue());
}

// 3.获取bean
HelloWorldService helloWorldService = (HelloWorldService) beanFactory.getBean("helloWorldService");
helloWorldService.helloWorld();

分析:

1.读取配置的部分封装的东西比较多。核心问题是如何将xml的Bean配置解析成Spring中的Bean类,可以充当BeanDinfition放到容器中充当数据结构模板。首先是使用xmlBeanDefinitionReader类来读取xml,需要调用Resource接口进行I/O操作,另外在loadBeanDefinitions方法中是先将xm转换成document对象,再放到DefinitionBeanReader中解析,后Spring能识别的Bean形式被BeanDefinitionHolder持有,而BeanDefinitionHolder中就有BeanDefinition。这样就可以注册的容器中。

2.注册的过程有两点,其一,注册其实就将BeanDefinitionHolder中的BeanDefinition放到BeanFatory中。其二,就是map的循环,map的底层Entry实现的。

4.使用高级容器:ApplicationContext

现在BeanFactory的功能齐全了,但是使用起来有点麻烦。于是我们引入熟悉的ApplicationContext接口,并在AbstractApplicationContextrefresh()方法中进行bean的初始化工作。

ApplicationContext applicationContext = new ClassPathXmlApplicationContext("tinyioc.xml");
HelloWorldService helloWorldService = (HelloWorldService) applicationContext.getBean("helloWorldService");
helloWorldService.helloWorld();

这种方式是我们最常用也是最好的一种方法了,在项目中经常这样写。一是ApplicationContext 除了实现了BeanFatory接口外,还实现了其他高级接口,提供了更多的特性。二是使用ClassPathXmlApplicationContext指定了Resource接口进行IO操作。将所有的解析过程都封装在了底层,而使用者无需手工操作。

猜你喜欢

转载自blog.csdn.net/mengdonghui123456/article/details/77521573