02-Spring3 IoC
一、IoC
IoC—Inversion of Control,即“控制反转”,不是什么技术,而是一种设计思想。不创建对象,但是描述创建他们的方式。在代码中不直接与对象和服务连接,但在配置文件中描述哪一个组件需要哪一项服务,IoC容器负责将这些联系在一起。
IoC设计模式重点关注组件的依赖性、配置以及生命周期。当然IoC也适用于简单类,而不只是组件。除了具有“Dependency Injection"(依赖注入)的昵称外,IoC还有另一个称呼,即Hollywood原则("Don't call me,I'll call you,请不要调用我,我将调用你),Ioc设计模式实现了“面向接口编程,而不是实现”的原则
IoC原理是基于OO设计原则的The Hollywood Principle:Don't call us, we'll call you(别找我,我会来找你的)。也就是说,所有的组件都是被动的(Passive),所有的组件初始化和调用都由容器负责。组件处在一个容器当中,由容器负责管理。简单的来讲,就是由容器控制程序之间的关系,而非传统实现中,由程序代码直接操控。这也就是所谓“控制反转”的概念。控制权由应用代码中转到了外部容器,控制权的转移,是所谓反转。即不用new了而是通过Spring容器依赖注入完成或者说当某个java对象需要(依赖)另一个java对象时,不是自身直接创建依赖对象,而是由实现IoC的容器(如Spring框架的IoC容器)来创建,并将它注入需要这个依赖对象的java对象中。
依赖注入:是指在程序运行过程中,如果需要调用另一个对象协助时,无须在代码中创建被调用者,而是依赖于外部的注入,Spring的依赖注入对调用者和被调用者几乎没有任何要求,完全支持对POJO之间的依赖关系的管理。
Spring采用动态及灵活的方式来管理各种对象,使对象与对象之间的具体实现相互透明
二、Hello IoC
下面我们来一个Hello World by Spring
1、 准备Jar包
核心jar包:spring-framework-3.0.5.RELEASE-with-docs.zip中dist目录查找如下jar包
org.springframework.asm-3.0.5.RELEASE.jar org.springframework.core-3.0.5.RELEASE.jar org.springframework.beans-3.0.5.RELEASE.jar org.springframework.context-3.0.5.RELEASE.jar org.springframework.expression-3.0.5.RELEASE.jar |
依赖Jar包:spring-framework-3.0.5.RELEASE-dependencies.zip中查找如下依赖jar包
|
接口:
package com.iflytek.demo; public interface HelloIoC { public void sayHello(); }
实现:
package com.iflytek.demo; public class HelloIoCImpl implements HelloIoC { @Override public void sayHello() { System.out.println("Hello SpringIoC"); } }
现在接口和实现都已经实现了,下面来看Spring IoC容器如何来管理它,这就需要配置文件,让IoC容器知道要管理哪些对象helloioc.xml(resources目录下)
<?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:context="http://www.springframework.org/schema/context" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd"> <!-- id 表示你这个组件的名字,class表示组件类 --> <bean id="hello" class="com.iflytek.demo.HelloIoCImpl" /> </beans>
Ok,下面我们实例化一个IoC容器,然后从容器中获取需要的对象,然后调用接口完成我们需要的功能。
package com.iflytek.test; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import com.iflytek.demo.HelloIoC; public class HelloIoCTest { @Test public void testSayHello() { //1、读取配置文件实例化一个IoC容器 ApplicationContext context = new ClassPathXmlApplicationContext("helloioc.xml"); //2、从容器中获取Bean,注意此处完全“面向接口编程,而不是面向实现” HelloIoC helloIoC = context.getBean("hello", HelloIoC.class); //3、执行业务逻辑 helloIoC.sayHello(); } }
三、理解IoC容器
OK,我们来看看IoC容器,在Spring IoC容器的代表是org.springframework.beans包中的BeanFactory接口,BeanFactory接口提供了IoC容器最基本功能,而org.springframework.context包下的ApplicationContext接口扩展了BeanFactory,还提供了与Spring AOP集成、国际化处理、事件传播及提供不同层次的context实现(如针对web应用的WebApplicationContext)。简单说,BeanFactory提供了IoC容器最基本功能,而ApplicationContext则增加了更多支持企业级功能支持。ApplicationContext完全继承BeanFactory,因而BeanFactory所具有的语义也适用于ApplicationContext。
容器实现一览:
• XmlBeanFactory:BeanFactory实现,提供基本的IoC容器功能,可以从classpath或文件系统等获取资源;
(1) File file = new File("fileSystemConfig.xml");
Resource resource = new FileSystemResource(file);
BeanFactory beanFactory = new XmlBeanFactory(resource);
(2) Resource resource = new ClassPathResource("classpath.xml");
BeanFactory beanFactory = new XmlBeanFactory(resource);
• ClassPathXmlApplicationContext:ApplicationContext实现,从classpath获取配置文件;
BeanFactory beanFactory = new ClassPathXmlApplicationContext("classpath.xml");
• FileSystemXmlApplicationContext:ApplicationContext实现,从文件系统获取配置文件。
BeanFactory beanFactory = new FileSystemXmlApplicationContext("fileSystemConfig.xml");
@Test public void testXmlBeanFactoryBaseOnFileSystem() { // 1.准备配置文件,从文件系统获取配置文件,默认是相对路径,可以指定绝对路径 File file = new File("./resources/helloioc.xml"); Resource resource = new FileSystemResource(file); // 2.初始化容器 BeanFactory beanFactory = new XmlBeanFactory(resource); // 2、从容器中获取Bean HelloIoC helloIoC = beanFactory.getBean("hello", HelloIoC.class); // 3、执行业务逻辑 helloIoC.sayHello(); } @Test public void testXmlBeanFactoryBaseOnClassPath() { // 1.准备配置文件,从当前类加载路径中获取配置文件 Resource resource = new ClassPathResource("helloioc.xml"); // 2.初始化容器 BeanFactory beanFactory = new XmlBeanFactory(resource); // 2、从容器中获取Bean HelloIoC helloIoC = beanFactory.getBean("hello", HelloIoC.class); // 3、执行业务逻辑 helloIoC.sayHello(); } @Test public void testClassPathXmlApplicationContext() { // 1.准备配置文件,从当前类加载路径中获取配置文件 // 2.初始化容器 BeanFactory beanFactory = new ClassPathXmlApplicationContext( "helloioc.xml"); // 2、从容器中获取Bean HelloIoC helloIoC = beanFactory.getBean("hello", HelloIoC.class); // 3、执行业务逻辑 helloIoC.sayHello(); } @Test public void testFileSystemApplicationContext() { // 1.准备配置文件,从文件系统获取配置文件,默认是相对路径,可以指定绝对路径 // 2.初始化容器 BeanFactory beanFactory = new FileSystemXmlApplicationContext( "./resources/helloioc.xml"); // 2、从容器中获取Bean HelloIoC helloIoC = beanFactory.getBean("hello", HelloIoC.class); // 3、执行业务逻辑 helloIoC.sayHello(); }
下面我们来看看IoC容器是如何工作的,以XML为例
1、 准备配置文件:在配置文件中声明Bean定义,即为Bean配置元数据
2、 由IoC容器进行解析元数据:IoC容器的Bean Reader读取并解析配置文件,根据定义生成BeanDefinition配置元数据对象,IoC容器根据BeanDifinition进行实例化,配置及组装Bean。
3、 实例化IoC容器:由客户端实例化容器,获取需要的Bean
四、IoC配置
XML配置结构
<beans> <import resource=”resource1.xml”/> <bean class="org.lxh.spring.StoneAxe" id="stoneAxe"></bean> <bean class="org.lxh.spring.SteelAxe" id="steelAxe"></bean> <bean class="org.lxh.spring.UserAxePerson" id="userAxePerson"> <property name="axe"> <!-- 或<property name="axe" ref="steelAxe"> --> <!-- 指将哪个bean赋给name的(这里引用了其他的bean),对与local则是针对与当前的xml --> <ref local="steelAxe"/> </property><!-- 这里的name是跟setName中的name有关系而不跟字段有关 --> </bean> </beans>
标签说明:
1、beans:Spring配置文件的根元素
2、bean:定义bean,
id:指定整个Bean中的唯一标识,不能和其他bean重复。
class:指定该Bean实例的实现类(全限定名)。
3、property:向Spring容器注入的属性
name:为该Bean指定一到多个别名。多个别名可以用“,”和“;”分割。
ref:指定将哪个bean注入给bean,该值与对应bean的id值相同才可注入
4、autowire:指定该Bean的属性的装配方式。
5、scope:指定该Bean的存在。
6、init-method:指定该Bean的初始化方法。
7、destroy-method:指定该Bean的销毁方法。
8、abstract:指定该Bean是否为抽象的。如果是抽象的,则spring不为它创建实例。
9、parent:指定该Bean的父类标志或别名。
10、import用于导入其他配置文件的Bean定义,这是为了加载多个配置文件,当然也可以把这些配置文件构造为一个数组(new String[] {“config1.xml”, config2.xml})传给ApplicationContext实现进行加载多个配置文件,那一个更适合由用户决定;这两种方式都是通过调用Bean Definition Reader 读取Bean定义,内部实现没有任何区别。<import>标签可以放在<beans>下的任何位置,没有顺序关系。
Bean的配置
Spring IoC容器目的就是为了管理Bean,这些Bean将根据配置文件中的Bean定义进行创建,而Bean定义在容器内部由BeanDefinition对象表示,该定义主要包含以下信息
全限定类名(FQN):用于定义Bean的实现类;
Bean行为定义:这些定义了Bean在容器中的行为;包括作用域(单例、原型创建)、是否惰性初始化及生命周期等;
Bean创建方式定义:说明是通过构造器还是工厂方法创建Bean;
Bean之间关系定义:即对其他bean的引用,也就是依赖关系定义,这些引用bean也可以称之为同事bean 或依赖bean,也就是依赖注入。
Bean定义只有“全限定类名”在当使用构造器或静态工厂方法进行实例化bean时是必须的,其他都是可选的定义。难道Spring只能通过配置方式来创建Bean吗?回答当然不是,某些SingletonBeanRegistry接口实现类实现也允许将那些非BeanFactory创建的、已有的用户对象注册到容器中,这些对象必须是共享的,比如使用DefaultListableBeanFactory 的registerSingleton() 方法。不过建议采用元数据定义。
Bean的命名
每个Bean可以有一个或多个id(或称之为标识符),在这里我们把第一个id称为“标识符”,其余id叫做“别名”。
1、不指定id,只配置必须的全限定类名,由IoC容器为其生成一个标识;
HelloIoC helloIoC = beanFactory.getBean(HelloIoC.class);
2、指定id,必须在Ioc容器中唯一;
HelloIoC helloIoC = beanFactory.getBean("id", HelloIoC.class);
3、指定name,这样name就是“标识符”,必须在Ioc容器中唯一;
HelloIoC helloIoC = beanFactory.getBean("name", HelloIoC.class);
4、指定id和name,id就是标识符,而name就是别名,必须在Ioc容器中唯一;
HelloIoC helloIoC = beanFactory.getBean("id", HelloIoC.class); HelloIoC helloIoC = beanFactory.getBean("alias", HelloIoC.class); String[] bean3Alias = beanFactory.getAliases("alias");
5、指定多个name,多个name用“,”、“;”、“ ”分割,第一个被用作标识符,其他的(alias1、alias2、alias3)是别名,所有标识符也必须在Ioc容器中唯一;
6、使用<alias>标签指定别名,别名也必须在IoC容器中唯一;
<bean name="bean" class="com.iflytek.demo.HelloIoCImpl"/> <alias alias="alias1" name="bean"/> <alias alias="alias2" name="bean"/>
实例化Bean
在传统应用程序中可以通过new和反射方式进行实例化Bean,而Spring IoC容器则需要根据Bean定义里的配置元数据使用反射机制来创建Bean。在Spring IoC容器中根据Bean定义创建。
1、 构造方法创建,Spring IoC容器即能使用默认空构造器也能使用有参数构造器
无参:
<bean id="hello" class="com.iflytek.demo.HelloIoCImpl" />
有参:
public class HelloIoCImpl02 implements HelloIoC02 { private String str; public HelloIoCImpl02(String str) { this.str = str; } @Override public void sayHello02() { System.out.println(str); } }
<!-- id 表示你这个组件的名字,class表示组件类 --> <bean id="hello" class="com.iflytek.demo.HelloIoCImpl" /><!-- 无参 --> <bean id="hello02" class="com.iflytek.demo02.HelloIoCImpl02"> <!-- 指定构造器参数 --> <constructor-arg index="0" value="Hello IoC by xdwang !" /> </bean>
2、 使用静态工厂方式实例化Bean,使用这种方式除了指定必须的class属性,还要指定factory-method属性来指定实例化Bean的方法,而且使用静态工厂方法也允许指定方法参数
public class HelloIoCStaticFactory { // 工厂方法 public static HelloIoCImpl02 newInstance(String str) { // 返回需要的Bean实例 return new HelloIoCImpl02(str); } }
<!-- 使用静态工厂方法 --> <bean id="statichello" class="com.iflytek.demo02.HelloIoCStaticFactory" factory-method="newInstance"> <constructor-arg index="0" value="Hello IoC by xdwang in static!"/> </bean>
3、使用实例工厂方法实例Bean,使用这种方式不能指定class属性,此时必须使用factory-bean属性来指定工厂Bean,factory-method属性指定实例化Bean的方法,而且使用实例工厂方法允许指定方法参数,方式和使用构造器方式一样
public class HelloIoCInstanceFactory { public HelloIoCImpl02 newInstance(String str) { return new HelloIoCImpl02(str); } }
<!-- 1、定义实例工厂Bean --> <bean id="beanInstanceFactory" class="com.iflytek.demo02.HelloIoCInstanceFactory"/> <!-- 2、使用实例工厂Bean创建Bean --> <bean id="beanFactory" factory-bean="beanInstanceFactory" factory-method="newInstance"> <constructor-arg index="0" value="Hello IoC by xdwang in factory!"/> </bean>
部分转自开涛博客。