Spring-IOC容器的使用

1- IOC的概念

IOC:也即控制反转,DI即依赖注入,控制反转IOC和依赖注入DI其实就是同个概念的两个不同角度的解释。

控制反转的概念

控制反转可以理解为获取依赖对象的控制反转过来。有反转的概念自然就有正转的概念。

若有两个类,类A和类B,若类A依赖与类B,则类A要获取类B的方法,这时我们可以按照传统的JavaSE思想,在A类里,写代码B b = new B();,通过new关键字获取A类的依赖对象,或者通过工厂模式进行获取,当然还有等等其它方法。这些方法,就属于正转的方法,因为A类获取B类就是通过主动的方法进行获取的,统称为正转的方法。
然后,什么是反转呢?既然正转是主动的方式,那么反转就是被动的方式。也即我们要获取某个类的依赖对象,不需要类主动去获取。然后在Spring框架里是怎么实现的呢?在Spring框架里,实现IOC,是通过IOC容器实现的,由IOC容器负责创建和获取依赖对象,对象只是被动地接受依赖对象。
比如我们要在一个客户端类里获取用户信息类,下图给出正转的方式

有了IOC容器之后,获取依赖对象的方式就变成如图所示了


这里写图片描述

IOC是一种很好的解耦合思想,在软件开发中有很好的作用,不仅被应用在JavaEE里,在其它语言里同样适用。


2- Bean的装载与管理

Spring框架中,一旦把一个Bean纳入到Spring IoC容器之中,这个Bean的生命周期就会交由容器进行管理,一般担当管理者角色的是BeanFactory或ApplicationContex

将Bean元数据收集装载

BeanDefinition:用于管理各种对象以及它们之间相互依赖关系的核心数据结构。Resource:用来封装IO操作的类。

收集过程:通过refresh方法启动,包括BeanDefinition和Resource的定位、载入、注册三个基本过程。

  • 定位:即寻找数据的过程,指对BeanDefinition的资源定位,由ResourceLoader通过统一的Resource接口完成。
  • 载入:把用户定义好的Bean表示成IOC容器的内部数据结构,即BeanDefinition(POJO对象在IOC容器中的抽象)
  • 注册:将BeanDefinition注入到HashMap中,IOC容器就是通过这个HahMap来管理数据。
BeanFactory接口

Spring 的容器最基本的接口就是:BeanFactory。BeanFactory 负责配置、创建及管理bean。

BeanFactory接口的方法

调用者只需使用getBean 方法即可获得指定bean的引用,无须关心bean 的实例化过程。即bean 实例的创建过程完全透明。



BeanFactory有很多实现类,通常使用org.springframework.beans.factory.xml.XmlBeanFactory 类。

通过BeanFactory获取Bean

在UserService中注入tool字段,通过BeanFactory来实现获取


UserService实现BeanFactoryAware接口的setBeanFactory方法,Spring会调用setBeanFactory方法将BeanFactory注入到UserService的factory字段(需要注意UserService需要被实例化,可以通过xml来配置bean,也可以通过@component类的注解来将Bean装载到容器实例化),然后就可以通过setBeanFactory获取Bean

ApplicationContext接口

对大部分J2EE 应用而言,获取Bean推荐使用ApplicationContext,因为其是BeanFactory 的子接口,提供了更多面向应用的功能,其常用的实现类是org.springframework.context.support.FileSystemXmlApplicationContext。

通过ApplicationContext获取Bean


和BeanFactory一样,SpringConfigTool类也需要实例化,ApplicationContext才能被注入到context字段上,可以通过XML或者@component类型的注解,将Bean注入。

BeanFactory与ApplicationContext区别
  1. BeanFacotry延迟加载,即只有在使用到某个Bean时(调用getBean()),才对该Bean进行加载实例化,如果Bean的某一个属性没有注入,BeanFacotry加载后,直至第一次使用调用getBean方法才会抛出异常;而ApplicationContext则在初始化自身是检验,这样有利于检查所依赖属性是否注入;所以通常情况下我们选择使用 ApplicationContext。ApplicationContext则会在上下文启动后预载入所有的单实例Bean。通过预载入单实例bean ,确保当你需要的时候,你就不用等待,因为它们已经创建好了。

  2. 一旦单例被实例化就会被放入缓存Bean池,下次获取直接返回,而多例则每一次都创建一个新对象返回,并将控制权移交给用户,由用户管理Bean的生命周期,使用完后需要显示设置为null,帮助回收。BeanFactory只能管理单例Bean,而ApplicationContext也可以管理多例Bean。

  3. BeanFactory和ApplicationContext都支持BeanPostProcessor、BeanFactoryPostProcessor的使用,但两者之间的区别是:BeanFactory需要手动注册,而ApplicationContext则是自动注册。(Applicationcontext比 beanFactory 加入了一些更好使用的功能。而且 BeanFactory的许多功能需要通过编程实现而 Applicationcontext 可以通过配置实现。比如后处理 bean , Applicationcontext 直接配置在配置文件即可而 BeanFactory这要在代码中显示的写出来才可以被容器识别)。

  4. BeanFactory主要是面对与 Spring 框架的基础设施,面对 spring 自己。而Applicationcontext主要面对与 Spring 使用的开发者。基本都会使用 Applicationcontext并非 BeanFactory。

3- Bean的生命周期,以及作用域

Bean的加载(生命周期)
以BeanFactory为例
  1. 实例化一个Bean(由BeanFactory读取Bean定义文件,并生成各个实例)--也就是我们常说的new;

  2. 按照Spring上下文对实例化的Bean的属性值进行设置--也就是IOC注入;

  3. 如果这个Bean已经实现了BeanNameAware接口,会调用它实现的setBeanName(String)方法,此处传递的就是Spring配置文件中Bean的id值

  4. 如果这个Bean已经实现了BeanFactoryAware接口,会调用它实现的setBeanFactory(setBeanFactory(BeanFactory)传递的是Spring工厂自身(可以用这个方式来获取其它Bean,只需在Spring配置文件中配置一个普通的Bean就可以);

  5. 如果这个Bean已经实现了ApplicationContextAware接口,会调用setApplicationContext(ApplicationContext)方法,传入Spring上下文(同样这个方式也可以实现步骤4的内容,但比4更好,因为ApplicationContext是BeanFactory的子接口,有更多的实现方法);

  6. 如果这个Bean关联了BeanPostProcessor接口,将会调用postProcessBeforeInitialization(Object obj, String s)方法

初始化之前构造注入依赖之后执行

  1. 如果Bean类实现了org.springframework.beans.factory.InitializingBean接口,则执行其afterPropertiesSet()方法。

一般是用来在实例构造完成后,实例的应该注入的依赖属性已经完成注入,需要对不需要注入的实例属性进行自定义初始化配置;当然也可指定一个init-method方法完成初始化,两者的作用一样,都是完成用户自定义初始化。对于单例,如果bean实现了InitializingBean接口则afterPropertiesSet方法只会被调用一次;否则每次创建bean时afterPropertiesSet方法都会被重新调用。

  1. 如果Bean在Spring配置文件中配置了init-method属性会自动调用其配置的初始化方法。

或者使用@PostConstruct后构造注解来将任意方法名称的方法标明为一个init-method方法

  1. 如果这个Bean关联了BeanPostProcessor接口,将会调用postProcessAfterInitialization(Object obj, String s)方法;

注:以上工作完成以后就可以应用这个Bean了,如果这个Bean是一个Singleton的,则将这准备就绪的Bean放入Spring缓存池中,已被下次使用,所以一般情况下我们调用同一个id的Bean会是在内容地址相同的实例,当然在Spring配置文件中也可以配置非Singleton,此时不会将Bean放入缓存池中,而是直接将控制权交给Bean的使用者。

  1. 当Bean不再需要时,会经过清理阶段,如果Bean实现了DisposableBean这个接口,会调用那个其实现的destroy()方法;

  2. 最后,如果这个Bean的Spring配置中配置了destroy-method属性,会自动调用其配置的销毁方法。

可以使用@PreDestroy前销毁注解,标明一个方法为destroy-method

说明如果Bean是由BeanFactory管理,则没有第五步,同样如果由ApplicationContext管理,则没有第四步。

Bean的作用域Scope

通过@Scope注解来标注Bean的作用域

示例:


Scope的类型:

主要分为单例(singleton)和多例(除了singleton外的Bean)

  1. singleton:单例模式(如果不指定Scope,默认是singleton
  2. Prototype:原型模式
  3. request:对于 每次http 请求,bean都会产生一个新的实例,只有在web 应用中才会生效
  4. session 对于 每次HttpSession时会产生一个新的实例,只有在web 应用中才会生效
  5. global session ,只有在portlet context中有效

比较常用到的是singleton和prototype,java在创建实例,要进行内存申请,销毁实例时,要垃圾回收,这些都会导致系统开销的增加,因此prototype作用域的bean在创建、销毁代价会比较大,而singleton的bean实例,一旦创建,就可以重复使用

单例Bean的线程安全问题

在spring中,singleton属性默认是true,只有设定为false,对单线程的程序说并不会有什么问题,但对于多线程的程序,就必须注意安全(Thread-safe)的议题,防止多个线程同时存取共享资源所引发的数据不同步问题。

如果多线程都会并发的修改Bean的实例字段时,一定要进行同步,如果只是只读共享域则不用关心。为了防止修改共享字段,可以使用不可变对象,或者直接让Bean无状态(没有任何字段)

Prototype的Bean的使用

多例Bean通过BeanFactory和ApplicationContext 的getBean方法获取,每次返回的都是新创建的Bean;当然单例也可以使用上面方法获得,但是每次返回都是同一个Bean。

4- bean的注入方式(DI)

  1. XML配置方式
  1. 注解方式

采用注解的方式,比起xml配置方式更加简洁,不然也要注意耦合方面
除了@Component外,Spring提供了3个功能基本和@Component等效的注解,分别对应于用于对DAO,Service,和Controller进行注解。

  • 1:@Repository 用于对DAO实现类进行注解。
  • 2:@Service 用于对业务层注解,但是目前该功能与 @Component 相同。
  • 3:@Constroller用于对控制层注解,但是目前该功能与 @Component 相同。
  1. Aotowired自动装配方式
  • 实现自动转配需要两个步骤:
  • 组件扫描(component scanning):Spring会自动发现应用上下文中所创建的bean
  • 自动装配(autowiring):Spring自动满足bean之间的依赖



    @Autowired,该注解的作用是:可以对成员变量、方法和构造函数进行注解,来完成自动装配的工作,通俗来说就是会根据类型从容器中自动查到到一个Bean给bookDAO字段。@Autowired是根据类型进行自动装配的,如果需要按名称进行装配,则需要配合@Qualifier。另外可以使用其它注解,
    @ Resource :等同于@Qualifier
    @Inject:等同于@ Autowired。
    @Service用于注解业务层组件(我们通常定义的service层就用这个)
    @Controller用于注解控制层组件(如struts中的action)
    @Repository用于注解数据访问组件,即DAO组件
    @Component泛指组件,当组件不好归类的时候,我们可以使用这个注解进行注解。

装配注解的使用

装配注解主要有:@Autowired、@Qualifier、@Resource,它们的特点是:

@Resource

默认是按照名称来装配注入的,只有当找不到与名称匹配的bean才会按照类型来装配注入;

@Autowired

默认是按照类型装配注入的,如果想按照名称来转配注入,则需要结合@Qualifier一起使用;

@Resource

注解是又J2EE提供,而@Autowired是由spring提供,故减少系统对spring的依赖建议使用@Resource的方式;如果Maven项目是1.5的JRE则需换成更高版本的。

@Resource和@Autowired都可以书写注解在字段或者该字段的setter方法之上

@Autowired

可以对成员变量、方法以及构造函数进行注释,而 @Qualifier 的注解对象是成员变量、方法入参、构造函数入参。

@Qualifier("XXX")

中的 XX是 Bean 的名称,所以 @Autowired 和 @Qualifier 结合使用时,自动注入的策略就从 byType 转变成 byName 了。

@Autowired 注释进行自动注入时

Spring 容器中匹配的候选 Bean 数目必须有且仅有一个,通过属性required可以设置非必要。

@Resource装配顺序

  1. 如果同时指定了name和type,则从Spring上下文中找到唯一匹配的bean进行装配,找不到则抛出异常
  2. 如果指定了name,则从上下文中查找名称(id)匹配的bean进行装配,找不到则抛出异常
  3. 如果指定了type,则从上下文中找到类型匹配的唯一bean进行装配,找不到或者找到多个,都会抛出异常
  4. 如果既没有指定name,又没有指定type,则自动按照byName方式进行装配;如果没有匹配,则回退为一个原始类型进行匹配,如果匹配则自动装配;

参考链接
http://uule.iteye.com/blog/2094609
http://www.blogjava.net/wmcoo/articles/334044.html
http://www.cnblogs.com/xiaoxi/p/5846416.html
http://blog.csdn.net/titilover/article/details/6736556
http://www.jianshu.com/p/9a8a89c1cfd1

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,524评论 5 460
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,869评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,813评论 0 320
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,210评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,085评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,117评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,533评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,219评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,487评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,582评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,362评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,218评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,589评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,899评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,176评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,503评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,707评论 2 335

推荐阅读更多精彩内容