1、IoC是什么
IOC——Inversion of Control,即控制反转,不是什么技术,而是一种设计思想。在Java开发中,IoC意味着将你设计好的对象交给容器控制,而不是传统的直接在你的对象内部控制。
- 谁控制谁,控制什么:传统的JavaSE程序设计,我们直接在对象内部通过new关键字进行对象的创建,是程序主动去创建依赖的对象;而IoC有一个专门的容器来创建对象,即由IoC容器来控制对象的创建;谁控制谁?当然是IoC容器控制对象;控制什么?主要控制了外部资源的获取(不只对象包括比如文件等)。
- 为何是反转,那些方面反转:有反转就有正转,传统应用程序是由我们自己在对象中主动控制直接获取依赖对象,也就是正转;而反转则是由容器帮忙创建及注入依赖对象;为何是反转?因为由容器帮助我们查找及注入依赖对象。对象只是被动的接受依赖对象,所以是反转。
2、DI是什么
DI——Dependency Injection,即依赖注入,是组件之间的依赖关系由容器在运行期决定,形象的说,即由容器动态的将某个依赖关系注入到组件儿中。依赖注入的目的并非为软件系统带来更多的功能,而是为了提升组件的复用性。
- 谁依赖谁:当然是某个IoC容器管理对象依赖于容器,“被注入对象的对象”依赖于“依赖对象”。
- 为什么需要依赖:容器管理对象需要IoC容器来提供对象需要的外部资源。
- 谁注入谁:很明显是IoC容器注入某个对象,也就是注入依赖对象。
- 注入了什么:就是注入某个对象需要的外部资源
IoC和DI其实它们是同一个概念的不同角度描述,“依赖注入”明确描述了“被注入对象依赖IoC容器配置依赖对象”。
3、IoC容器
IoC容器就是拥有依赖注入功能的容器,IoC容器负责实例化,定位,配置应用程序中的对象及监理这些对象之间的依赖。应用程序无需再直接通过new关键字创建对象,应用程序由IoC容器进行组装。在spring中BeanFactory是IoC容器的实际代表者。
4、Bean
由IoC容器管理的那些组成应用程序的对象我们叫它bean,bean是由spring容器初始化,装配及管理的对象,除此之外没有什么不同。IoC容器实例化管理bean需要配置元数据,在spring中由BeanDefinition代表。
5、详解IoC容器
在spring中IoC容器的带包就是org.springframework.beans包中的BeanFactory接口,BeanFactory接口提供了IoC容器最基本的功能;而org.springframework.context包下的ApplicationContext接口扩展了BeanFactory,还提供了与spring aop集
成、国际化处理、事件传播及提供不同层次的Context实现。简单说就是BeanFactory提供了IoC容器最基本的功能,而ApplicationContext则增加了更多企业级功能支持。ApplicationContext完全集成BeanFactory,因而BeanFactory所具有的
语义也适用于ApplicationContext。
容器实现一览:
XMLBeanFactory:BeanFactory实现,提供了基本的IoC功能,可以从classpath或文件系统获取资源:
1 File file = new File("fileSystemConfig.xml"); 2 Resource resource = new FileSystemResource(file); 3 BeanFactory beanFactory = new XmlBeanFactory(resource); 4 5 Resource resource = new ClassPathResource("classpath.xml"); 6 BeanFactory beanFactory = new XmlBeanFactory(resource);
ClassPathXmlApplicationContext:ApplicationContext实现,从classpath获取配置文件:
1 BeanFactory beanFactory = new ClassPathXmlApplicationContext("classpath.xml");
FileSystemXmlApplicationContext:ApplicationContext实现,从文件系统获取配置文件:
1 BeanFactory beanFactory = new FileSystemXmlApplicationContext("fileSystemConfig.xml");
ApplicationContext接口获取bean方法简介:
1 Object getBean(String name) //根据名称返回一个Bean,客户端需要自己进行类型转换; 2 T getBean(String name, Class<T> requiredType) //根据名称和指定的类型返回一个Bean,客户端无需自己进行类型转换,如果类型转换失败,容器抛出异常; 3 T getBean(Class<T> requiredType) //根据指定的类型返回一个 Bean,客户端无需自己进行类型转换,如果没有或有多于一个Bean存在容器将抛出异常; 4 Map<String, T> getBeansOfType(Class<T> type) //根据指定的类型返回一个键值为名字和值为Bean对象的Map,如果没有Bean对象存在则返回空的Map
6、IoC容器如何工作
- 准备配置文件:在配置文件中声明bean定义也就是为bean配置元数据。
- 由IoC容器解析元数据:IoC容器的Bean Reader读取并解析配置文件,根据定义生成BeanDefinition配置元数据对象,IoC容器根据BeanDefinition进行实例化、配置及组装bean。
- 实例化IoC容器:由客户端实例化容器,获取需要的bean。
7、IoC容器配置使用
一般配置文件如下
1 <beans> 2 <import resource="resource1.xml"/> 3 <bean id="bean1" class=""></bean> 4 <bean id="bean2" class=""></bean> 5 <bean name="bean2" class=""></bean> 6 <alias alias="bean3" name="bean2"/> 7 <import resource="resource2.xml"/> 8 </beans>
- bean标签主要用来进行bean定义;
- alias用于定义bean别名;
- import用于导入其他配置文件的bean定义这个是为了加载多个配置文件,当然也可以把这些配置文件构造为一个数组(new String[]{“config1.xml”,“config2.xml”})传给ApplicationContext实现进行多个配置文件加载。
7.1 bean的配置
spring IoC容器目的就是管理bean,这些bean将根据配置文件的定义进行创建,而bean定义在容器内部由BeanDefinition对象表示,该对象主要有以下含义:
。全限定类名:用于定义bean的实现类
。bean行为定义:这些定义了bean在容器中的行为;包括作用域(单例,原型创建)、是否惰性初始化及生命周期
。bean创建方式定义:说明是通过构造器还是工厂方法创建bean
。bean之间关系定义:即对其他bean的引用,也就是依赖关系定义
bean不仅是只能通过配置方式创建,某些SingletonBeanRegistry接口实现类实现也允许将那些非BeanFactory创建、已有的用户对象注册到容器中,这些对象必须是共享的,不过建议采用配置的方式定义。
7.2 bean的命名
每个bean可以有一个或多个id,在这里我们把第一个id称为“标识符”,其余的id称为“别名”;这些id在IoC容器中必须唯一。
。不指定id,只配置必须得全限定类名
1 <bean class="cn.javass.spring.chapter2.helloworld.HelloImpl"/>
1 @Test 2 public void test1() { 3 BeanFactory beanFactory = new ClassPathXmlApplicationContext("chapter2/namingbean1.xml"); 4 //根据类型获取 bean 5 HelloApi helloApi = beanFactory.getBean(HelloApi.class); 6 helloApi.sayHello(); 7 }
。指定id,必须在IoC容器中唯一
1 <bean id="bean" class="cn.javass.spring.chapter2.helloworld.HelloImpl"/>
1 @Test 2 public void test2() { 3 BeanFactory beanFactory = new ClassPathXmlApplicationContext("chapter2/namingbean2.xml"); 4 //根据id获取bean 5 HelloApi bean = beanFactory.getBean("bean", HelloApi.class); 6 bean.sayHello(); 7 }
。指定name,这样name就是标识符,标识符必须在IoC容器中唯一
1 <bean name="bean" class="cn.javass.spring.chapter2.helloworld.HelloImpl"/>
1 @Test 2 public void test3() { 3 BeanFactory beanFactory = new ClassPathXmlApplicationContext("chapter2/namingbean3.xml"); 4 //根据 name 获取 bean 5 HelloApi bean = beanFactory.getBean("bean", HelloApi.class); 6 bean.sayHello();
7}
。指定id和name,id是标识符,name是别名,同样标识符必须在IoC容器中唯一
1 <bean id="bean1" name="alias1" class="cn.javass.spring.chapter2.helloworld.HelloImpl"/> 2 <!-- 如果id和name一样,IoC容器能检测到,并消除冲突 --> 3 <bean id="bean3" name="bean3" class="cn.javass.spring.chapter2.helloworld.HelloImpl"/>
1 @Test 2 public void test4() { 3 BeanFactory beanFactory = new ClassPathXmlApplicationContext("chapter2/namingbean4.xml"); 5 //根据id获取bean 6 HelloApi bean1 = beanFactory.getBean("bean1", HelloApi.class); 7 bean1.sayHello(); 8 //根据别名获取bean 9 HelloApi bean2 = beanFactory.getBean("alias1", HelloApi.class); 10 bean2.sayHello(); 11 //根据id获取bean 12 HelloApi bean3 = beanFactory.getBean("bean3", HelloApi.class); 13 bean3.sayHello(); 14 String[] bean3Alias = beanFactory.getAliases("bean3"); 15 //因此别名不能和id一样,如果一样则由IoC容器负责消除冲突 16 Assert.assertEquals(0, bean3Alias.length); 17 }
。指定多个name,多个name用“,”、“;”、“ ”分割,第一个被作为标识符,其他的被作为别名
。用alias标签指定别名,别名也必须在IoC容器中唯一
注:bean的命名约定
bean的命名必须遵循xml命名规范,但最好符合Java命名规范,由字母、数字、下划线组成驼峰命名。
7.3 实例化bean
传统的应用程序可以通过new关键字和反射方式进行实例化,而spring IoC容器则需要根据bean定义里的配置元数据通过反射机制来创建bean。
。使用构造器实例化bean
- 使用空参构造函数进行定义
1 <bean name="bean1" class="cn.javass.spring.chapter2.HelloImpl2"/>
- 使用有参数构造函数进行定义,constructor-arg标签指定参数值,index表示参数位置,value表示常量值,也可以指定引用,指定引用使用ref来引用另一个bean定义
1 <bean name="bean2" class="cn.javass.spring.chapter2.HelloImpl2"> 2 <!-- 指定构造器参数 --> 3 <constructor-arg index="0" value="Hello Spring!"/> 4 </bean>
1 @Test 2 public void testInstantiatingBeanByConstructor() { 3 //使用构造器 4 BeanFactory beanFactory = new ClassPathXmlApplicationContext("chapter2/instantiatingBean.xml"); 5 HelloApi bean1 = beanFactory.getBean("bean1", HelloApi.class); 6 bean1.sayHello(); 7 HelloApi bean2 = beanFactory.getBean("bean2", HelloApi.class); 8 bean2.sayHello(); 9 }
。使用静态工厂方式实例化bean
1 <!-- 使用静态工厂方法 --> 2 <bean id="bean3" class="cn.javass.spring.chapter2.HelloApiStaticFactory" factory-method="newInstance"> 3 <constructor-arg index="0" value="Hello Spring!"/> 4 </bean>
1 public class HelloApiStaticFactory { 2 //工厂方法 3 public static HelloApi newInstance(String message) { 4 //返回需要的Bean实例 5 return new HelloImpl2(message); 6 } 7 } 8 @Test 9 public void testInstantiatingBeanByStaticFactory() { 10 //使用静态工厂方法 11 BeanFactory beanFactory = new ClassPathXmlApplicationContext("chaper2/instantiatingBean.xml"); 12 HelloApi bean3 = beanFactory.getBean("bean3", HelloApi.class); 13 bean3.sayHello(); 14 }
。使用实例工厂方法实例化bean
1 <!—1、定义实例工厂 Bean --> 2 <bean id="beanInstanceFactory" class="cn.javass.spring.chapter2.HelloApiInstanceFactory"/> 3 <!—2、使用实例工厂 Bean 创建 Bean --> 4 <bean id="bean4" factory-bean="beanInstanceFactory" factory-method="newInstance"> 5 <constructor-arg index="0" value="Hello Spring!"></constructor-arg> 6 </bean>
1 public class HelloApiInstanceFactory { 2 public HelloApi newInstance(String message) { 3 return new HelloImpl2(message); 4 } 5 }
6 7 @Test 8 public void testInstantiatingBeanByInstanceFactory() { 9 //使用实例工厂方法 10 BeanFactory beanFactory = new ClassPathXmlApplicationContext("chapter2/instantiatingBean.xml"); 11 HelloApi bean4 = beanFactory.getBean("bean4", HelloApi.class); 12 bean4.sayHello(); 13 }
通过以上例子我们发现只是配置不一样,从获取方式看完全一样没有任何不同。
至此我们已经讲完了spring IoC容器基础部分,包括IoC容器概念,如何实例化容器,bean配置、命名及实例化,bean获取等等。