BeanFactory & ApplicationContext
IoC(DI) IoC is also known as dependency injection (DI). It is a process whereby objects define their dependencies, that is, the other objects they work with, only through constructor arguments, arguments to a factory method, or properties that are set on the object instance after it is constructed or returned from a factory method.
Spring通过一个配置文件描述Bean与Bean之间的依赖关系,利用Java语言的反射功能实例化Bean并建立Bean之间的依赖关系。同时,Spring还提供了Bean实例缓存、声明周期管理、Bean实例代理、事件发布、资源装载等高级服务。
org.springframework.beans
和org.springframework.context
包是IoC容器的基础。BeanFactory
是Spring框架最核心的接口,它提供了高级Ioc的配置机制。ApplicationContext
是BeanFactory
的子接口,提供了更多面向应用的功能。一般称BeanFactory
为IoC容器,而ApplicationContext
为应用上下文,详细介绍参考这里。
BeanFactory
Spring 定义的Bean比JavaBean的定义更宽泛,可以说所有可以被Spring容器实例化并管理的Java类都可以成为Bean。BeanFactory
的类体系结构如下:
ApplicationContext
ApplicationContext
的主要实现类是ClassPathXmlApplicationContext
和FileSystemXmlApplicationContext
,前者默认从类路径加载配置文件,后者默认从文件系统加载配置文件。
ApplicationContext
还扩展了很多有用的接口:
ApplicationEventPublisher
——让容器拥有发布应用上下文事件的功能,包括容器启动事件、关闭事件。实现ApplicationListener
事件监听接口的Bean可以接收到容器的事件,并对事件进行相应处理。在ApplicationContext
的实现类AbstractApplicationContext
中存在一个ApplicationEventMulticaster
,它负责保存所有的监听器,以便在容器产生上下文事件时通知这些监听者。MessageResource
——用于国际化ResourcePatternResolver
——可以通过带前缀的Ant风格的资源文件路径装载Spring的配置文件。LifeCycle
——该接口提供了start()
和stop()
两个方法,主要用于控制异步处理过程。
ConfigurableApplicationContext
新增了两个主要的方法:refresh()
和close()
,让ApplicationContext
具有启动、刷新和关闭应用上下文的能力。
ApplicationContext
的初始化和BeanFactory
有一个重大区别:BeanFactory
在初始化容器时,并未实例化Bean,直到第一次访问某个Bean时才实例化目标Bean;ApplicationContext
则在初始化应用上下文时就实例化所有的单例Bean。
Spring为基于注解类的配置提供了专门的ApplicationContext
实现类:AnnotationConfigApplicationContext
。
WebApplicationContext
WebApplicationContext
允许从相对于Web根目录的路径中加载配置文件完成初始化工作。从WebApplicationContext
中可以获取ServletContext
的引用,整个WebApplicationCOntext
对象作为属性放置在ServletContext
中,以便Web应用环境可以访问Spring应用上下文。
Bean
在容器内部,bean的定义通过BeanDefinition
对象表示,BeanDefinition
对象主要包括的bean的元数据如下:
- 类的全限定名——实际的类名
- bean的行为配置,如bean的作用域、生命周期回调函数等
- 对其它bean的引用,这些引用也称为合作者或依赖
- 其它设置信息
基于JavaConfig的容器配置
Spring IoC通过配置元数据的方式告诉Spring容器实例化、配置并装配对象。配置元数据主要分两种:
1. XML模式。XML模式实现了配置文件和代码的分离,开发者编写的是最纯粹的POJO。缺点是配置文件冗长,不易维护。
2. JavaConfig。JavaConfig通过注解的形式来实现通过Java代码配置容器的目的。
Spring 还支持混用这两种配置方式,本文主要以JavaConfig配置为主。
当@Bean
方法不是声明在@Configuration
类之中时,这种bean会被通过轻量级模式处理,在这种情况下,@Bean
方法不应该调用其它的@Bean
方法。只有@Configuration
类中的@Bean
方法才能调用其它@Bean
方法,这样能避免同一个@Bean
方法被多次调用,防止出现意想不到的bug。参考这里
在JavaConfig中,最核心的是@Configuration
类和@Bean
方法。
@Beean
注解用于表明被注解的方法会实例化、配置并且初始化一个能被Spring IoC容器管理的新对象。这个注解与XML配置的<bean/>
效果相同。@Bean
主要用于@Configuration
类中,原因如上所述。@Configuration
注解用于表明被注解的类主要是作为bean definition的源文件而存在的,而且@Configuration
还允许内部bean之间的依赖,只需要调用其它的@Bean
方法即可。
通过AnnotationConfigApplicationContext实例化Spring Container
AnnotationConfigApplicationContext
能同时接受@Configuration
和@Component
以及JSR-330元数据注解作为输入。
当@Configuration
类作为输入时,@Configuration
类自身会被作为bean definition被注册,所有@Bean
方法也会作为bean definition注册。
简单的构造器
以@Configuration
类作为输入
public static void main(String[] args) {
ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
MyService myService = ctx.getBean(MyService.class);
myService.doStuff();
}
以@Component
或JSR-330注解的类作为输入
public static void main(String[] args) {
ApplicationContext ctx = new AnnotationConfigApplicationContext(MyServiceImpl.class, Dependency1.class, Dependency2.class);
MyService myService = ctx.getBean(MyService.class);
myService.doStuff();
}
通过register(Class< ? >…)方法以编程的方式构建容器
public static void main(String[] args) {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.register(AppConfig.class, OtherConfig.class);
ctx.register(AdditionalConfig.class);
ctx.refresh();
MyService myService = ctx.getBean(MyService.class);
myService.doStuff();
}
通过scan(String…)方法实现组件扫描
public static void main(String[] args) {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.scan("com.acme");
ctx.refresh();
MyService myService = ctx.getBean(MyService.class);
}
通过AnnotationConfigWebApplicationContext支持Web Application
当配置Spring ContextLoaderListener
servlet监听器, Spring DispatcherServlet
等组件时会用到这个实现。关于Spring MVC的配置请移步这里。
获取生命周期回调
所有带有@Bean
的类定义都支持常规的声明周期回调函数,还能使用JSR-250注解。
组合基于Java的配置类
通过@Import
注解实现:
@Configuration
public class ConfigA {
public @Bean A a() { return new A(); }
}
@Configuration
@Import(ConfigA.class)
public class ConfigB {
public @Bean B b() { return new B(); }
}