SSM项目从零开始到入门039-Spring的IoC 容器

1.什么是IoC ?

     控制反转(Inversion of Control,英文缩写为IoC)把创建对象的权利交给框架,是框架的重要特征,并非面向对象编程的专用术语。它包括依赖注入(Dependency Injection,简称DI)和依赖查找(Dependency Lookup)。

2.IoC最大的好处是什么?

因为把对象生成放在了XML里定义,所以当我们需要换一个实现子类将会变成很简单(一般这样的对象都是实现于某种接口的),只要修改XML就可以了,这样我们甚至可以实现对象的热插拔(有点像USB接口和SCSI硬盘了)。

3.IoC最大的缺点是什么?

(1)生成一个对象的步骤变复杂了(事实上操作上还是挺简单的),对于不习惯这种方式的人,会觉得有些别扭和不直观。

(2)对象生成因为是使用反射编程,在效率上有些损耗。但相对于IoC提高的维护性和灵活性来说,这点损耗是微不足道的,除非某对象的生成对效率要求特别高。

(3)缺少IDE重构操作的支持,如果在Eclipse要对类改名,那么你还需要去XML文件里手工去改了,这似乎是所有XML方式的缺陷所在。

4.IoC容器是什么?

IoC 容器是 Spring 框架的核心。容器将创建对象,把它们连接在一起,配置它们,并管理他们的整个生命周期从创建到销毁。Spring 容器使用依赖注入(DI)来管理组成一个应用程序的组件。这些对象被称为 Spring Beans,通过阅读配置元数据提供的指令,容器知道对哪些对象进行实例化,配置和组装。配置元数据可以通过 XML,Java 注释或 Java 代码来表示。下图是 Spring 如何工作的高级视图。 Spring IoC 容器利用 Java 的 POJO 类和配置元数据来生成完全配置和可执行的系统或应用程序。

5.Spring有几种容器?

1.Spring BeanFactory 容器

它是最简单的容器,给 DI 提供了基本的支持,它用 org.springframework.beans.factory.BeanFactory 接口来定义。BeanFactory 或者相关的接口,如 BeanFactoryAware,InitializingBean,DisposableBean,在 Spring 中仍然存在具有大量的与 Spring 整合的第三方框架的反向兼容性的目的。

在 Spring 中,有大量对 BeanFactory 接口的实现。其中,最常被使用的是 XmlBeanFactory 类。这个容器从一个 XML 文件中读取配置元数据,由这些元数据来生成一个被配置化的系统或者应用。

在资源宝贵的移动设备或者基于 applet 的应用当中, BeanFactory 会被优先选择。否则,一般使用的是 ApplicationContext,除非你有更好的理由选择 BeanFactory。

上篇文章中的关于Hello world的实现就是利用了Spring Beanfactory容器。

第一步利用框架提供的 XmlBeanFactory() API 去生成工厂 bean 以及利用 ClassPathResource() API 去加载在路径 CLASSPATH 下可用的 bean 配置文件。XmlBeanFactory() API 负责创建并初始化所有的对象,即在配置文件中提到的 bean。


第二步利用第一步生成的 bean 工厂对象的 getBean() 方法得到所需要的 bean。 这个方法通过配置文件中的 bean ID 来返回一个真正的对象,该对象最后可以用于实际的对象。一旦得到这个对象,就可以利用这个对象来调用任何方法。


2.Spring ApplicationContext 容器

    Application Context 是 spring 中较高级的容器。和 BeanFactory 类似,它可以加载配置文件中定义的 bean,将所有的 bean 集中在一起,当有请求的时候分配 bean。 它增加了企业所需要的功能,比如,从属性文件从解析文本信息和将事件传递给所指定的监听器。这个容器在 org.springframework.context.ApplicationContext interface 接口中定义。

    ApplicationContext 包含 BeanFactory 所有的功能,一般情况下,相对于 BeanFactory,ApplicationContext 会被推荐使用。BeanFactory 仍然可以在轻量级应用中使用,比如移动设备或者基于 applet 的应用程序。
最常被使用的 ApplicationContext 接口实现:
FileSystemXmlApplicationContext:该容器从 XML 文件中加载已被定义的 bean。在这里,你需要提供给构造器 XML 文件的完整路径
ClassPathXmlApplicationContext:该容器从 XML 文件中加载已被定义的 bean。在这里,你不需要提供 XML 文件的完整路径,只需正确配置 CLASSPATH 环境变量即可,因为,容器会从 CLASSPATH 中搜索 bean 配置文件。
WebXmlApplicationContext:该容器会在一个 web 应用程序的范围内加载在 XML 文件中已被定义的 bean。

   我们已经在 Spring的Hello World 章节中看到过 ClassPathXmlApplicationContext 容器,并且,在基于 spring 的 web 应用程序这个独立的章节中,我们讨论了很多关于 XmlWebApplicationContext。

所以,接下来,让我们看一个关于 FileSystemXmlApplicationContext 的例子。

实现FileSystemXmlApplicationContext 只需要修改测试类中的方法:

@Test
	public void testHelloWorld2() {
		ApplicationContext context = new FileSystemXmlApplicationContext(
				"F:/MyEclipseProject003/spring01/src/main/resources/beans.xml");
	      HelloWorld obj = (HelloWorld) context.getBean("helloWorld");
	    System.out.println(obj.getMessage());
	}

在主程序当中,我们需要注意以下两点:
第一步生成工厂对象。加载完指定路径下 bean 配置文件后,利用框架提供的 FileSystemXmlApplicationContext API 去生成工厂 bean。FileSystemXmlApplicationContext 负责生成和初始化所有的对象,比如,所有在 XML bean 配置文件中的 bean。
第二步利用第一步生成的上下文中的 getBean() 方法得到所需要的 bean。 这个方法通过配置文件中的 bean ID 来返回一个真正的对象。一旦得到这个对象,就可以利用这个对象来调用任何方法。

Spring Bean 定义

被称作 bean 的对象是构成应用程序的支柱也是由 Spring IoC 容器管理的。bean 是一个被实例化,组装,并通过 Spring IoC 容器所管理的对象。这些 bean 是由用容器提供的配置元数据创建的,例如,已经在先前章节看到的,在 XML 的表单中的 定义。
bean 定义包含称为配置元数据的信息,下述容器也需要知道配置元数据:
1.如何创建一个 bean
2.bean 的生命周期的详细信息
3.bean 的依赖关系
上述所有的配置元数据转换成一组构成每个 bean 定义的下列属性。

属性描述
class这个属性是强制性的,并且指定用来创建 bean 的 bean 类。
name这个属性指定唯一的 bean 标识符。在基于 XML 的配置元数据中,你可以使用 ID 和/或 name 属性来指定 bean 标识符。
scope这个属性指定由特定的 bean 定义创建的对象的作用域,它将会在 bean 作用域的章节中进行讨论。
constructor-arg它是用来注入依赖关系的,并会在接下来的章节中进行讨论。
properties它是用来注入依赖关系的,并会在接下来的章节中进行讨论。
autowiring mode它是用来注入依赖关系的,并会在接下来的章节中进行讨论。
lazy-initialization mode延迟初始化的 bean 告诉 IoC 容器在它第一次被请求时,而不是在启动时去创建一个 bean 实例。
initialization 方法在 bean 的所有必需的属性被容器设置之后,调用回调方法。它将会在 bean 的生命周期章节中进行讨论。
destruction 方法当包含该 bean 的容器被销毁时,使用回调方法。它将会在 bean 的生命周期章节中进行讨论。

Spring 配置元数据


Spring IoC 容器完全由实际编写的配置元数据的格式解耦。有下面三个重要的方法把配置元数据提供给 Spring 容器:

1.基于 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"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">

   <bean id="helloWorld" class="com.lcx.spring.HelloWorld">
       <property name="message" value="Hello World!"/>
   </bean>

</beans>

2.基于注解的配置

新建HelloWorld .java类  然后添加@Component注解

package com.lcx.spring;

import org.springframework.stereotype.Component;

@Component("hello")
public class HelloWorld {
	
	private String message = "你好!";

	public String getMessage() {
		return message;
	}

	public void setMessage(String message) {
		this.message = message;
	}
}

这个等价于

<bean id="hello" class="com.lcx.spring.HelloWorld"/>  

然后在src/main/resources新建applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:context="http://www.springframework.org/schema/context" xmlns:p="http://www.springframework.org/schema/p"
	xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
	http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
	http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
	http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.0.xsd">
	
	<!-- 开启的扫描包  -->
	<context:component-scan base-package="com.lcx.spring" />

</beans>
如此的话,我们便不在需要在XML当中显式使用bean来进行bean的配置。Spring容器在初始化的时候便会自动扫描base-package所指定的包以及子包下面的所有class文件。所有标注为@Component的类将被自动注册为bean。


如果我们的com.lcx.spring路径下有多个类,而我们只想要特定的类,在context中可以使用resource-pattern来过滤出特定的类。

<context:component-scan base-package="com.lcx.spring" resource-pattern="pojo/*.class"/>  
默认情况下加载的是package下的*.class即扫描全部类,在使用了resource-pattern之后,则只扫描package下的pojo子包下的所有类。

使用resource-pattern并不能提供给我们完善的功能,比如我们有时候并不想将某个目录下全部加载,这个时候得使用过滤子元素的方法。

<context:component-scan base-package="com.lcx.spring">  
   <context:include-filter type="regex" expression="com.lcx.spring.*"/>  
   <context:exclude-filter type="aspectj" expression="com.lcx.spring..*Controller+"/>  
</context:component-scan>  
其中:
include-filter表示要包含的目标类,
exclude-filter表示要排除在外的目标类

一个component-scan标签下可以有多个include-filter和exclude-filter。


3.基于 Java 的配置

基于Java类定义Bean配置元数据,其实就是通过Java类定义Spring配置元数据,且直接消除XML配置文件。
首先让我们看一下基于Java类如何定义Bean配置元数据,具体步骤如下:
使用@Configuration注解需要作为配置的类,表示该类将定义Bean的元数据
使用@Bean注解相应的方法,该方法名默认就是Bean的名称,该方法返回值就是Bean的对象。

AnnotationConfigApplicationContext或子类进行加载基于java类的配置

首先我们编写HelloWorld.java类

package com.lcx.spring;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class HelloWorld {
	
	private String message = "你好!";

	@Bean
	public String getMessage() {
		return message;
	}

	public void setMessage(String message) {
		this.message = message;
	}
}

通过@Configuration注解的类将被作为配置类使用,表示在该类中将定义Bean配置元数据,且使用@Configuration注解的类本身也是一个Bean。其中Configuration中的参数值即为该bean的名称。

通过@Bean注解配置类中的相应方法,则该方法名默认就是Bean名,该方法返回值就是Bean对象,并定义了Spring IoC容器如何实例化、自动装配、初始化Bean逻辑,具体使用方法如下:

@Bean(name={}, autowire=Autowire.NO, initMethod="",  destroyMethod="") 

其中name为bean的名称,可以有多个,autowire为是否自动装配,默认值为NO,initMethod为bean的初始化方法,destoryMethod为bean的销毁方法。


然后定义测试类
@Test
	public void testHelloWorld3() {
		AnnotationConfigApplicationContext ctx =  
		        new AnnotationConfigApplicationContext(HelloWorld.class);  
		HelloWorld obj = (HelloWorld) ctx.getBean("helloWorld");
	    System.out.println(obj.getMessage());
	}


总结:不同配置方式比较


使用场景:

基于XML的配置主要使用场景:第三方类库,如DataSource、JdbcTemplate等;命名空间,如aop、context等;
基于注解的配置主要使用场景:Bean的实现类是当前项目开发的,可直接在Java类中使用注解配置

基于Java类的配置主要使用场景:对于实例化Bean的逻辑比较复杂,则比较适合用基于Java类配置的方式

在日常的开发中我们主要是使用XML配置和注解配置方式向结合的开发方式,一般不推荐使用基于Java类的配置方式。


Spring Bean 作用域

当在 Spring 中定义一个 bean 时,你必须声明该 bean 的作用域的选项。例如,为了强制 Spring 在每次需要时都产生一个新的 bean 实例,你应该声明 bean 的作用域的属性为 prototype。同理,如果你想让 Spring 在每次需要时都返回同一个bean实例,你应该声明 bean 的作用域的属性为 singleton。

Spring 框架支持以下五个作用域,如果你使用 web-aware ApplicationContext 时,其中三个是可用的。

作用域描述
singleton该作用域将 bean 的定义的限制在每一个 Spring IoC 容器中的一个单一实例(默认)。
prototype该作用域将单一 bean 的定义限制在任意数量的对象实例。
request该作用域将 bean 的定义限制为 HTTP 请求。只在 web-aware Spring ApplicationContext 的上下文中有效。
session该作用域将 bean 的定义限制为 HTTP 会话。 只在web-aware Spring ApplicationContext的上下文中有效。
global-session该作用域将 bean 的定义限制为全局 HTTP 会话。只在 web-aware Spring ApplicationContext 的上下文中有效。
singleton 作用域(默认):

如果作用域设置为 singleton,那么 Spring IoC 容器刚好创建一个由该 bean 定义的对象的实例。该单一实例将存储在这种单例 bean 的高速缓存中,以及针对该 bean 的所有后续的请求和引用都返回缓存对象。

 <bean id="helloWorld" class="com.lcx.spring.HelloWorld" scope="singleton">
       <property name="message" value="Hello World!"/>
   </bean> 

编辑对应的测试类:

@Test
	public void testHelloWorld() {
		 ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext-beans.xml");
	     HelloWorld objA = (HelloWorld) context.getBean("helloWorld");
	     System.out.println(objA.getMessage());
	     objA.setMessage("我是A");
	     HelloWorld objB = (HelloWorld) context.getBean("helloWorld");
	     System.out.println(objB.getMessage());
	}


prototype 作用域

如果作用域设置为 prototype,那么每次特定的 bean 发出请求时 Spring IoC 容器就创建对象的新的 Bean 实例。一般说来,满状态的 bean 使用 prototype 作用域和没有状态的 bean 使用 singleton 作用域。

 <bean id="helloWorld" class="com.lcx.spring.HelloWorld" scope="prototype">
       <property name="message" value="Hello World!"/>
   </bean> 

依旧是前面的测试类:

@Test
	public void testHelloWorld() {
		 ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext-beans.xml");
	     HelloWorld objA = (HelloWorld) context.getBean("helloWorld");
	     System.out.println(objA.getMessage());
	     objA.setMessage("我是A");
	     HelloWorld objB = (HelloWorld) context.getBean("helloWorld");
	     System.out.println(objB.getMessage());
	}


Bean 的生命周期

     理解 Spring bean 的生命周期很容易。当一个 bean 被实例化时,它可能需要执行一些初始化使它转换成可用状态。同样,当 bean 不再需要,并且从容器中移除时,可能需要做一些清除工作。尽管还有一些在 Bean 实例化和销毁之间发生的活动,但是本节将只讨论两个重要的生命周期回调方法,它们在 bean 的初始化和销毁的时候是必需的。

初始化和销毁是通过接口InitializingBean与DisposableBean实现的

package com.lcx.spring;

import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;

public class HelloWorld implements InitializingBean,DisposableBean  {
	
	private String message = "你好!";

	public String getMessage() {
		return message;
	}

	public void setMessage(String message) {
		this.message = message;
	}

	@Override
	public void afterPropertiesSet() throws Exception {
		//初始化工作可以在本方法中执行
		
	}

	@Override
	public void destroy() throws Exception {
		//结束工作可以在本方法中执行
		
	}
}

当我们想在配置文件中去配置:

为了定义安装和拆卸一个 bean,我们只要声明带有 init-method 和/或 destroy-method 参数的 。
init-method 属性指定一个方法,在实例化 bean 时,立即调用该方法。
destroy-method 指定一个方法,只有从容器中移除 bean 之后,才能调用该方法。

<bean id="helloWorld" class="com.lcx.spring.HelloWorld" scope="prototype"  init-method="init" destroy-method="destroy">
       <property name="message" value="Hello World!"/>
   </bean>
package com.lcx.spring;


public class HelloWorld{
	
	private String message = "你好!";

	public String getMessage() {
		return message;
	}

	public void setMessage(String message) {
		this.message = message;
	}

	public void init() throws Exception {
		//初始化工作可以在本方法中执行
		System.out.println("初始化工作执行!");
	}
	public void destroy() throws Exception {
		//结束工作可以在本方法中执行
		System.out.println("销毁工作执行!");
	}
}



  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

心歌技术

打赏不能超过你的早餐钱!!!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值