深入研究Spring-IoC :容器创建的几种方式_一支支穿云箭的博客-程序员秘密

技术标签: JAVA  spring  实例  IOC创建  

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操作。将所有的解析过程都封装在了底层,而使用者无需手工操作。

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/mengdonghui123456/article/details/77521573

智能推荐

MIT6.828学习之Lab4: Preemptive Multitasking_6.828 lab4_请叫宝宝荡哥的博客-程序员秘密

在这个lab中,你会在多个同时active的用户模式环境中实现preemptive multitasking(抢占多任务处理)在Part A,你会向JOS加入multiprocessor support(多处理器支持),实现round-robin scheduling(轮转调度),并且添加基础的environment management system calls(这个调用能创建和destroy...

CSS3溢出文字省略_css3 字数省略_周围都是小趴菜的博客-程序员秘密

&lt;!DOCTYPE html&gt;&lt;html&gt; &lt;head&gt; &lt;meta charset="utf-8"&gt; &lt;title&gt;文字多余部分隐藏&lt;/title&gt; &lt;style&gt; .content { width: 300px; height: 100px; .

Spring Security 实战干货:路径Uri中的 Ant 风格_程序猿DD_的博客-程序员秘密

点击上方蓝色“程序猿DD”,选择“设为星标”回复“资源”获取独家整理的学习资料!来源 |juejin.im/post/5c6b6b126fb9a04a0c2f024f 1. 前言 我们经常在读到一些文章会遇到uri支持Ant风格 ,而且这个东西在Spring MVC和Spring Security中经常被提及。这到底是什...

小程序中使用async和await_Fighting宁的博客-程序员秘密

小程序原生中使用async 和await 时会报错此时就是因为在.js中使用了async和await 但是小程序原生是不支持的解决方案1:npm init -y 快速创建一个packpage.json文件命令行执行 npm i regenerator-runtime安装regenerator-runtime打开小程序开发者工具,点击右上角的工具选项,点击构建npm,稍等几秒,小程序...

在线免费学习java资源推荐_lvzhou_MadSky的博客-程序员秘密

源:http://www.iteye.com/news/29113 这篇文章将介绍各种各样的网络资源(不包含必读的Java书籍),你可以借助这些资源进行在线学习。 学习Java最好的电子书(PDF) 如果你喜欢阅读,那么通过这些免费的Java电子书可以自学这门语言。大多数在线的电子书都是不断更新且完整的。覆盖了Java的大多数细节。 Oracle(Sun)官方教程:这是Addis...

2023高频前端面试题(含答案)_边关月_的博客-程序员秘密

一、简单页面1、CSS选择器样式优先级2、CSS实现三列布局(左右固定宽度,中间自适应)(1)CSS浮动第一个float:left,第二个float:right,第三个设置margin-left和margin-right(2)绝对定位法第一个定位到left,第二个定位到right,第三个设置margin-left和margin-right(3)flex布局.left{ width:200px; 或者 flex:0 0 200px;}.right{ width:200px;

随便推点

Java学习资源_L-Zhang的博客-程序员秘密

最新的科技一般都是先有英文的,所以英语有多重要可想而知。我的英文很烂,一直想学,从来都是说起来容易,想起来简单,做起来最难。只能强迫自己多看些英语方面的技术网站,技术英语两不误。学习 Java 最好的电子书(PDF)喜欢阅读的可以通过这些免费的 Java 电子进行自学。大多数在线的电子书都是更新的,完整的。覆盖了 Java 的大多数细节。Official Java Tutorial by Orac

android 获取apk的摘要,【Android】获取手机中已安装apk文件信息(PackageInfo、ResolveInfo)(应用图片、应用 ......_mater lai的博客-程序员秘密

众所周知,通过PackageManager可以获取手机端已安装的apk文件的信息,具体代码如下:PackageManagerpackageManager=this.getPackageManager();ListpackageInfoList=packageManager.getInstalledPackages(0);/***查询手机内非系统应用*@paramcontext*@...

PIP 更换国内源, 起飞~_python 换源起飞_x_mm_c的博客-程序员秘密

pip 国内的一些镜像阿里云 http://mirrors.aliyun.com/pypi/simple/中国科技大学 https://pypi.mirrors.ustc.edu.cn/simple/豆瓣(douban) http://pypi.douban.com/simple/清华大学 https://pypi.tuna.tsinghua.edu.cn/simple/中国科学技术大学 http://pypi.mirrors.ustc.edu.cn/simple/修改源方法:临时使用:

NVMe 协议之Multi-Streams和Directives_nvme directives_Lucky_Pai的博客-程序员秘密

Multi-Streams多流多流是企业级SSD的一个新特性,通过使用多流特性,可以显著提高SSD的垃圾回收效率,从而提高性能和延长寿命。在SSD的应用场景中,可能有多个不同的应用在对SSD进行写操作,包括热数据和冷数据,包括随机写数据和顺序写数据,包括大块写数据和小块写数据,如果把这些数据都混在同一个NAND块里面,显然不利于垃圾回收处理,一个很自然的想法是把这些数据进行分类。NVMe协议里面有两种方法可以对数据进行分类,其中一个是通过Dataset Management,另一个就是Multi-S

搭建大型分布式服务(十)Docker搭建开发环境安装Redis___libc_readline_unlocked version glibc_private_hanyi_的博客-程序员秘密

一、本文要点本文将介绍利用docker快速搭建开发环境,搭建Redis实例。系列文章完整目录docker 命令docker 安装redisdocker 常用命令docker 复制文件到宿主机docker 复制文件报错二、开发环境docker 18.09.7, build 2d0083dlinux 64位redis 4.0三、安装docker参考:https://blog.csdn.net/caiwen_5050/article/details/85245106四、安装Red

Qt容器遍历(前后遍历,查找,删除,设值)_qt 适合查找删除的集合_赵玉~想要一个定所的博客-程序员秘密

QT容器Java风格的使用以及基本操作(前后遍历toBack,hasPrevious,previous,toFront,hasNext,next,插入insert,删除remove,设值setValue,查找 findPrevious,findNext)

推荐文章

热门文章

相关标签