Spring IoC 容器与beans简介
IOC全称,the Inversion of Control,即控制反转。
IOC又称为 dependency injection(DI),即依赖注入。
IoC是一个对象定义依赖的过程。即在对象构造或通过工厂方法返回之后,将对象运作时需要用到的其他对象,通过构造器参数、工厂方法参数、对象中的属性设置进对象实例之中。
IoC是一个基础性的反转,是将bean对自身的实例化、bean对自身上的依赖通过使用类构造或服务定位模式进行的定位过程进行了反转。
org.springframework.beans与org.springframework.context包时Spring框架IoC容器的基础。
BeanFactory接口提供了管理一切类型对象的高级配置机制。
ApplicationContext是BeanFactory的子接口。它简单集成了AOP特性、信息资源处理、事件发布以及应用层特定contexts,例如WebApplicationContext。
BeanFactory提供了配置框架以及基础功能。
ApplicationContext添加了更多的企业特性功能。
Spring中,构成了应用的骨架并且被Spring IoC容器管理的对象被称为beans。
bean被IoC容器实例化、封装并且管理。
beans以及它们之间的依赖被容器配置元数据反映。
容器概览
ApplicationContext代表容器,并且负责管理beans的实例化、配置与装配。
容器通过读取配置元数据获取实例化、配置、装配对象的指示。
配置元数据通过XML、Java注解或Java代码表示。
Spring中内置了多个ApplicationContext的实现。
单独应用通常使用ClassPathXmlApplication或FileSystemXmlApplicationContext。
过去使用xml来作为定义配置元数据的形式时,可以指示容器使用java注解或代码作为元数据格式来使xml配置尽可能减少。
在多数情况下,显式使用代码实例化一个或者多个IoC容器并不是必需的。eg:web应用只需要在web.xml中编写少量代码。
Spring的运行原理
在ApplicationContext创建并且实例化之后,通过应用中类与配置元数据的结合,得到一个完整配置且可运行的系统或应用。
配置元数据
配置元数据表示了作为一个应用开发者如何告知Spring容器去实例化、配置和包装应用中的对象。
Spring配置中至少包含一个bean的定义。
基于xml的配置元数据通过在顶层<beans/>元素下的<bean>元素定义bean。
java配置通常@Configuration注解的class下的@Bean注解的方法定义bean。
基于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.xsd">
<bean id="..." class="...">
<!-- collaborators and configuration for this bean go here -->
</bean>
<!-- more bean definitions go here -->
</beans>
ps:id属性是用来标识单独bean定义,class属性使用类的完整类名来定义bean的类型。
ps:id的值引用协作对象。
实例化容器
ApplicationContext context = new ClassPathXmlApplicationContext("services.xml", "daos.xml");
组合基于xml的配置元数据
<beans>
<import resource="aaa.xml"/>
</beans>
note:载入与此配置文件位于同一个文件夹下的aaa.xml文件中的bean定义。
ps:import元素使用的是相对路径。
ps:建议不要使用../引用父目录中的文件,这样做会产生一个与应用外文件的依赖。特别是,不建议应用在classpath URL中。
ps:在使用绝对路径时,建议通过${}占位符间接使用绝对路径。
使用容器
ApplicationContext是一个用于维护不同beans以及它们之间依赖的注册的高级工厂接口。
使用T getBean(String name, Class<T> requiredType)方法检索获取bean。
理想化情况下,最好不要使用getBean()方法,这样就可以避免与Spring API的依赖。
Bean概览
在容器之中,bean的定义被表示为BeanDefinition对象。
BeanDefinition中包含有完整类名、bean的行为配置(scope、lifecycle callbacks等)、运作时需要的其他对象的引用(这些引用又称,合作者或依赖)、新创建对象的一些配置(eg:用于使用bean的连接的数量,这些bean用于管理连接池或池限制)。
元数据会被转化成一个由bean定义构成的属性集合。
bean definitions中包含了如何创建一个bean的信息,ApplicationContext实现允许在容器外创建的对象登记。
通过ApplicationContext的getBeanFactory()获取BeanFactory的实现DefaultListableBeanFactory。
DefaultListableBeanFacory通过registerSingleton()与registerBeanDefinition()注册。
注册bean 元数据与单例时越早越好,这样容器就可以在自动装配等阶段推出它们。
当重载已存在的元数据与单例时,官方不推荐注册新bean,因为可能导致并发错误。
命名beans
每一个bean都具有一个或多个标识符。标识符需要在持有bean的容器中保持唯一。
一个bean通常只有一个标识符,如果需要一个以上,额外的标识符被看作是别名。
给予xml的配置元数据中,使用id或name属性来指定beans的标识符。
id属性用于精确指定一个id。
name属性用于引入别名,可以使用逗号、分号或空格进行分隔,用于指定多个别名。
如果没有显示指定一个id或name,那么容器会自动为bean生成一个唯一名称。
不显示提供一个名称的动机一般与自动装配协作者或内部bean有关。
如果想要通过bean的名字引用bean,需要使用ref元素或Service Locator。
bean的命名习惯通常为以小写字母开头的驼峰命名法,eg:accountService。
当组建扫描机制扫描classpath,Spring将使用上述命名法为没有命名的组件命名。当未命名的组件类名大于一个字符且第一与第二个字母都是大写时,Spring将保留原始名称。这与java.beans.Introspector.decapitalize定义的规则相同。
可以在bean的定义位置之外设置bean的别名,基于xml的配置元数据,使用alias元素实现
<alias name="fromName" alias="toName"/>
note:位于相同容器的bean的名称为fromName,在使用了这个别名定义之后,通过toName来引用这个bean。
实例化beans
bean的定义本质上来说是一个用于创建对象的食谱。
基于xml的配置元数据使用bean元素的class属性指定需要实例化的对象类型。
class属性(即BeanDefinition的Class属性)往往是必须的。
使用Class属性的way
在bean被容器自身直接调用其构造器创建时,指定被创建的bean类。
指定包含用于创建对象的用于静态工厂方法的实际类。
class属性使用静态内部类
com.example.Foo$Bar
note:com.example包下的Foo类中定义的静态内部类的名字。
使用构造器实例化
使用构造器方法创建bean时,所有的一般类都可以被Spring使用,并且都可以被Spring兼容。
Spring IoC容器事实上几乎可以管理任何类,并不仅限于正确的JavaBeans。
JavaBeans,只有一个默认无参构造器,并为属性配置合适的getter、setter。
用于指定bean类的基于xml的配置元数据如下:
<bean id="exampleBean" class="examples.ExampleBean"/>
or
<bean name="aaa" class="examples.ExampleBean"/>
ps:使用构造器实例化。
使用静态工厂方法实例化
当使用静态工厂方法实例化时,使用class属性指定包含静态工程方法的类,使用factory-method属性指定工厂方法的名称。
<bean id="aaaBbb" class="aaa.AaaBbb" factory-method="getInstance"/>
package aaa;
public class AaaBbb {
public static AaaBbb getInstance() {
return new AaaBbb();
}
}
使用实例工厂方法实例化
实例工厂方法通过调用一个来自容器的已经存在的bean的非静态方法来创建一个新bean。
使用这个机制时,class属性不要设置,factory-bean指明包含工厂方法的bean,factory-method指明工厂方法的名字。
<bean id="serviceLocator" class="examples.DefaultServiceLocator"/>
<bean id="clientService" factory-bean="serviceLocator" factory-method="getClientServiceInstance"/>
package examples;
public class DefaultServiceLocator {
private static ClientService clientService = new ClientServiceImpl();
public ClientService getClientServiceInstance() {
return clientService;
}
}
note:使用了单例模式。
ps:一个factory bean内可以有多个工厂方法,从而被多个bean的factory-bean引用。
ps:factory bean引用一个已经存在于容器中的bean。与之相对,FactoryBean引用了一个特定的FactoryBean。