一、IOC概述
IOC(Inversion of Control 控制反转) 是对象通过构造器参数、工厂方法参数、属性来定义所需的依赖,并且在容器创建对象的时候为其注入这些依赖的过程。这与对象自己调用构造来控制依赖项的实例化和位置的过程相反。
这块主要关注两个相关的包:org.springframework.beans
和 org.springframework.context
。 其中org.springframework.beans.factory.BeanFactory
接口提供了可以管理任意类型对象的配置机制,org.springframework.context.ApplicationContext
是在此基础上扩展了企业特定功能(如更好的集成AOP、用于国际化的消息资源处理、事件发布、应用层特定上下文)的子接口。
在Spring中,被IOC容器所管理(实例化、装配等)的应用中的对象称为Bean。容器的配置元数据会反映Bean之间的依赖关系。
二、IOC容器
实际上,我们可以将org.springframework.context.ApplicationContext
理解成就是IOC容器。我们基于XML、注解、Java代码这些不同方式告诉容器我们的配置元数据,它会通过读取这些配置元数据来初始化、配置和组装bean。相当于我们只提供一堆食材和一套配方,容器来做成一桌菜。
三、Bean
我们在配置元数据中对每个bean的定义最终在容器内部都会对应一个BeanDefinition
对象。BeanDefinition
是对bean的抽象,用来描述bean,其属性中会包括要定义的bean的全限定类名、bean的行为(作用域,回调等)、所需依赖等。我们可以把容器当成一个map,key是bean的id,value则是对应的BeanDefinition
。
除此之外,某些ApplicationContext
的具体实现也允许注册容器外的对象。如AbstractApplicationContext#getBeanFactory()
会返回ConfigurableListableBeanFactory
类型对象,其实现类DefaultListableBeanFactory
中的registerSingleton(..)
和registerBeanDefinition(..)
方法就是提供给用户注册外部对象的。
注意:
基于Java配置时,通过带@Bean注解的方法声明的容器中的bean的类型是依据方法返回值类型强限定的:如果方法返回值类型为接口类型,即使方法返回了具体的实现类,实际在容器中的bean也是接口类型,在其他地方注入时,也不能依据此方法注入实现类。
public interface FileInfo {
}
public class FileInfoImpl implements FileInfo {
}
@Configuration
public class GenerateFileInfo {
@Bean
public FileInfo generateFile(){
return new FileInfoImpl();
}
}
Bean的命名:
容器中的Bean必须能唯一标识。在基于XML的配置中通过<bean/>
的id
属性来标识,也可以通过name
属性来指定多个名称。如果不指定bean的id
和name
,容器会自动生成一个名称,一般是类名首字母小写。Bean的名称约定使用驼峰格式。在复杂系统中,如果多个子系统相对同一个bean有自己的命名,可以通过<alias/>
来指定别名,如:
<alias name="fromName" alias="toName" />
Bean的实例化:
在基于XML的配置中,<bean/>
必须包含class
属性。一种情况,容器会使用该class
属性通过反射调用构造器来实例化;另一种情况通过class
指定包含静态工厂方法的类,容器会通过静态方法实例化bean。注意对于静态内部类,需使用“com.example.OuterClassName$InnerClassName”
格式来指定class
属性。
1. 构造器初始化
只需要在XML的<bean/>
中通过class
属性告诉要实例化的bean的全限定类名,容器就能通过反射调用构造方法来创建Bean实例;
<bean id="example" class=com.examples.Example" />
2. 静态工厂方法实例化
在XML的<bean/>
中通过class
属性指定静态工厂方法所在类,通过factory-method
属性指定方法名,容器就能调用此方法实例化Bean。生成的Bean的类型取决于方法的返回值类型。
<bean id="clientService" class="examples.ClientService" factory-method="createInstance"/>
3.非静态工厂方法实例化
在XML的<bean/>
中通过factory-bean
属性指定容器中已经存在的bean,通过factory-method
属性指定这个bean对应的class中的工厂方法名。
<bean id="serviceLocator" class="com.example.ServiceLocator"/>
<bean id="clientService" factory-bean="serviceLocator" factory-method="createClientServiceInstance" />
“factory bean” VS FactoryBean
"factory bean"
是容器中的一个bean,我们能通过这个bean中定义的工厂方法来穿件其他的bean。而FactoryBean
是Spring指定的能用来创建bean的一个工厂类。下面演示FactoryBean
的用法:
@Component
public interface Book{
}
public class BookA implements Book{
}
public class BookB implements Book{
}
@Component
public class BookFactory implements FactoryBean<Book>, EnvironmentAware {
private Environment env;
@Override
public Book getObject(){
String type = env.getProperty("type");
if("A".equals(type)){
return new BookA();
}
return new BookB();
}
@Override
public Class<?> getObjectType() {
return Book.class;
}
@Override
public boolean isSingleton() {
return false;
}
@Override
public void setEnvironment(Environment environment) {
this.env = environment;
}
}
@Component
public class TestBook {
@Autowired
private Book book;
@PostConstruct
public void getBook(){
System.out.println(book);
}
}