Spring的IoC容器
Spring的IoC容器是Spring的核心,IOC(Inversion of Control )控制反转的。IoC和DI(Dependency Injection)是一个意思,也就是说对象定义它们的依赖(也就是和它们一起干活的其他对象),Spring容器会把这些依赖给注入进去。具体来讲,我们可以通过xml文件、注解和java代码的形式来定义对象和它们之间的依赖,Spring会自动帮我们把它们解析成BeanDefinition、注册BeanDefinition,然后将BeanDefinition实例化,填充属性并注入依赖,调用生命周期方法等,最终生成Bean,从而供用户使用。
Spring容器的工作原理
我们有一些POJO(示例代码中的A和B),还有一些配置信息,这些配置信息可以是xml文件、注解、java代码(示例代码中的AppConfig类)。我们把这两坨扔给Spring容器(示例代码中的AnnotationConfigApplicationContext),Spring容器帮我们实例化并装配好,我们使用时直接从Spring获取即可,例如从Spring容器中获取A实例applicationContext.getBean(A.class)。
public class A {
}
public class B {
}
@Configuration
public class AppConfig {
@Lazy
@Bean
@DependsOn("b")
public A a() {
System.out.println("Generate bean a");
return new A();
}
@Scope("prototype")
@Bean
public B b() {
System.out.println("Generate bean b");
return new B();
}
}
public class Main {
public static void main(String[] args) {
AnnotationConfigApplicationContext configApplicationContext =
new AnnotationConfigApplicationContext(AppConfig.class);
A a = applicationContext.getBean(A.class);
B b = applicationContext.getBean(B.class);
}
}
核心概念介绍
那么问题来了,Spring是怎么把我们的POJO变成Spring管理的对象的呢?这里涉及到Spring中的一些概念,包括Bean、BeanDefinition、BeanFactory、ApplicationContext、BeanDefinitionRegistry、SingletonBeanRegistry、BeanDefinitionReader、BeanFactoryPostProcessor、BeanPostProcessor
-
Bean
我们这里说的Bean是Spring中的Bean,而不是普通的JavaBean。Spring中的Bean是Spring IoC容器管理的对象,Bean不是普通的java对象,Bean具有生命周期,即创建、初始化和销毁。Spring的容器就是用来管理Bean的。 -
BeanDefinition
在Java中我们使用Class来描述类或接口,但是Class不能描述一个类是否要懒加载、自动装配模式等属性,所以在Spring中使用BeanDefinition来描述Bean实例,BeanDefinition包括Bean属于哪个Class、Bean的Name、Scope、Constructor arguments、Properties、Autowiring mode、Lazy initialization mode、Initialization method、Destruction method。 -
BeanFactory
BeanFactory是访问Spring Bean容器的最基本的接口,BeanFactory是所有应用组件(ApplicationComponent)的注册中心,为所有的组件提供集中化的配置。BeanFactory接口的实现类需要满足一定要求:
1 应该持有一系列的BeanDefination,通过BeanDefination,容器可以返回独立的或共享的实例。
2 应该尽可能地支持标准的Bean生命周期接口,即Bean的初始化、创建和销毁。
我们可以看到BeanFactory中提供了许多getBean的方法,此外还提供了判断一个Bean是否是单例、原型等方法。 -
ApplicationContext
ApplicationContext是BeanFactory的扩展,是为一个应用提供配置的中心接口,主要提供以下能力;
1 通过继承ListableBeanFactory和HierarchicalBeanFactory接口,可以访问BeanFactory中的应用组件(也就是Bean);
2 通过继承ResourceLoader接口,可以加载资源,例如classPath资源、文件系统资源等;
3 通过继承ApplicationEventPublisher接口,可以向注册的Listener发布事件;
4 通过继承MessageSource,可以解析消息,支持国际化;
5 支持从parent context继承,例如WebApplicationContext。public interface ApplicationContext extends EnvironmentCapable, ListableBeanFactory, HierarchicalBeanFactory, MessageSource, ApplicationEventPublisher, ResourcePatternResolver
-
BeanDefinitionRegistry
BeanFactory接口只提供了访问Bean的方法,而BeanDefinitionRegistry则提供了注册BeanDefinition的方法。DefaultListableBeanFactory和GenericApplicationContext都实现了BeanDefinitionRegistry接口。 -
SingletonBeanRegistry
SingletonBeanRegistry接口提供了注册单实例Bean的能力,ConfigurableBeanFactory继承了该接口。
注意这个接口是用来向Spring容器中添加单实例Bean的,而BeanDefinitionRegistry则是向Spring容器中添加BeanDefinition的。 -
BeanDefinitionReader
BeanDefinitionReader指定了loadBeanDefinition方法,从而根据传入的String或Resource类型参数加载BeanDefinition。需要注意的是一个BeanDefinition的读取器不必非要实现这个接口,例如AnnotationConfigApplicationContext中的AnnotatedBeanDefinitionReader就没有实现该接口。 -
BeanFactoryPostProcessor
BeanFactoryPostProcessor是Spring提供的一个扩展点,主要是用来修改ApplicationContext中的BeanDefinition而不是Bean实例。
BeanFactoryPostProcessor的一个重要的子接口是BeanDefinitionRegistryPostProcessor,BeanDefinitionRegistryPostProcessor允许在BeanFactoryPostProcessor处理BeanDefinition之前注册一些BeanDefinition。
BeanDefinitionRegistryPostProcessor目前唯一的实现是ConfigurationClassPostProcessor,这个类非常重要,主要是用来处理@Configuration标注的配置类的,它处理@Component、@Import、@Bean等注解,将用户基于注解和java代码定义的类和类之间的依赖关系转化成BeanDefinition。我们会在之后的文章中对ConfigurationClassPostProcessor进行专门的介绍。 -
BeanPostProcessor
BeanPostProcessor也是Spring提供的一个扩展点,主要是在Bean实例化前后执行回调,我们之后会单独介绍该接口。
接口层次
下面介绍一下BeanDefinition、BeanFactory、ApplicationContext接口的层次。我们可以看到Spring中接口和类的层次是相当复杂的,这是因为Spring把能分开的都分开了,体现了单一职责的原则。子接口或子类可以组合这些接口,从而实现丰富的功能。
BeanDefinition的层次
-
BeanDefinition
BeanDefinition用来描述Bean实例,包括属性值、构造器参数值以及它的具体实现提供的额外的信息。通过BeanFactoryPostProcessor可以对BeanDefinition的属性值和其他元数据信息进行修改。 -
AnnotatedBeanDefinition
AnnotatedBeanDefinition是BeanDefinition的直接子接口,主要是可以获取到AnnotationMetadata,AnnotationMetadata接口主要提供对一个类上注解的访问,例如getAnnotationTypes、getAnnotatedMethods等。 -
AbstractBeanDefinition
AbstractBeanDefinition是实现了BeanDefinition接口的抽象类,提取出了GenericBeanDefinition、RootBeanDefinition以及ChildBeanDefinition的公共属性,例如beanClass、scope、abstractFlag、lazyInit、autowireMode、dependsOn、primary、initMethodName、destroyMethodName等。 -
GenericBeanDefinition
GenericBeanDefinition是AbstractBeanDefinition的子类,它是BeanDefinition接口的标准实现。通常使用GenericBeanDefinition是为了注册用户可见的BeanDefinition,这些BeanDefinition可能会进一步被PostProcessor处理,也可能被重新设置parentName。Spring 2.5之后,通过编程的方式注册BeanDefinition的推荐方式是使用GenericBeanDefinition,因为可以通过setParentName方法动态地设置父依赖 -
RootBeanDefinition
RootBeanDefinition代表MergedBeanDefinition,一个RootBeanDefinition可能是由多个有继承关系的BeanDefinition创建而来的,RootBeanDefinition本质上是Spring的BeanFactory运行期间统一的BeanDefinition视图。 -
ChildBeanDefinition
ChildBeanDefinition使得Bean可以从他的parent继承一些属性,包括构造器参数值、属性值、重载父类方法、init和destroy方法、静态工厂方法。对于depends on、autowire mode、 dependency check、singleton、lazy init则会使用自己设置的值。使用RootBeanDefinition和ChildBeanDefinition可以表达预先确定好的父子关系。 -
ScannedGenericBeanDefinition
ScannedGenericBeanDefinition是GenericBeanDefinition的子类,使用ASM ClassReader解析.class文件,从而获取到相关的元信息。 -
AnnotatedGenericBeanDefinition
AnnotatedGenericBeanDefinition也是GenericBeanDefinition的子类,在将@Configuration标注的类(配置类)转化成BeanDefinition时,使用的就是AnnotatedGenericBeanDefinition。 -
ConfigurationClassBeanDefinition
ConfigurationClassBeanDefinition是ConfigurationClassBeanDefinitionReader的内部类,ConfigurationClassBeanDefinition表示BeanDefinition是从Configuration Class(配置类)中创建的而不是基于其他的配置源。 -
ClassDerivedBeanDefinition
ClassDerivedBeanDefinition是GenericApplicationContext的内部类,在GenericApplicationContext#registerBean方法中会把beanClass转化成ClassDerivedBeanDefinition。ClassDerivedBeanDefinition beanDefinition = new ClassDerivedBeanDefinition(beanClass);
BeanFactory的层次
-
BeanFactory
BeanFactory接口是Spring IoC容器最基本的接口,主要提供各种getBean的方法,从而可以从Spring容器中获取到Bean。注意,BeanFactory只提供了获取Bean的接口,而没有提供注册Bean的接口。注册Bean的接口是BeanDefinitionRegistry,也就是说注册和获取Bean的接口是隔离的。 -
ListableBeanFactory
ListableBeanFactory是BeanFactory的直接子接口,主要是提供“批量”的能力,例如批量获取BeanName和Bean实例。 -
HierarchicalBeanFactory
HierarchicalBeanFactory是BeanFactory的直接子接口,主要提供支持父子容器的能力,即可以获取当前容器的父容器。 -
AutowireCapableBeanFactory
AutowireCapableBeanFactory是BeanFactory的直接子接口,主要提供自动注入的能力。 -
ConfigurableBeanFactory
ConfigurableBeanFactory提供配置BeanFactory的一系列方法,包括setBeanClassLoader、addBeanPostProcessor、registerAlias等。 -
ConfigurableListableBeanFactory
ConfigurableListableBeanFactory除了ConfigurableBeanFactory提供的配置方法,ConfigurableListableBeanFactory还提供分析和改变BeanDefinition、预实例化单实例Bean的方法。 -
AbstractBeanFactory
AbstractBeanFactory是BeanFactory的抽象基类,提供了基本的实现 -
DefaultListableBeanFactory
DefaultListableBeanFactory是
ConfigurableListableBeanFactory和BeanDefinitionRegistry接口的默认实现,在AnnotationConfigApplicationContext中会用到。
DefaultListableBeanFactory中的beanDefinitionMap就是放BeanDefinition的。
/** Map of bean definition objects, keyed by bean name. */
private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);
ApplicationContext的层次
-
ApplicationContext
ApplicationContext是配置一个应用的中心接口。 -
ConfigurableApplicationContext
ApplicationContext接口中大多数方法都是getBean,ConfigurableApplicationContext则提供了一些设置 applicationContext的方法,例如setXXX addXXX registerXXX等。 -
WebApplicationContext
WebApplicationContext为web应用提供配置,在ApplicationContext的基础上增加了getServletContext方法,可以获取到标准的javax.servlet.ServletContext。 -
AbstractApplicationContext
AbstractApplicationContext是ApplicationContext的抽象实现,提供了通用能力,使用模板方法设计模式,使其子类实现一些具体的逻辑。
ApplicationContext需要检测定义在BeanFactory中的其他Bean,因此AbstractApplicationContext中注册了包括BeanFactoryPostProcessors、BeanPostProcessors以及ApplicationListeners接口的一些实现类对应的Bean -
GenericApplicationContext
GenericApplicationContext内部持有一个DefaultListableBeanFactory
并且没有强制指定BeanDefinition的类型。此外,实现了BeanDefinitionRegistry接口,从而可以注册BeanDefinition。
其他ApplicationContext的实现在每次调用refresh方法时,会创建一个新的BeanFactory,GenericApplicationContext只能调用refresh方法一次,多次调用会抛异常。另外,我们常用的基于注解的AnnotationConfigApplicationContext是GenericApplicationContext的子类。Exception in thread "main" java.lang.IllegalStateException: GenericApplicationContext does not support multiple refresh attempts: just call 'refresh' once
-
AbstractRefreshableApplicationContext
AbstractRefreshableApplicationContext支持多次调用refresh方法,每次会创建一个新的BeanFactory实例。AbstractRefreshableApplicationContext的子类需要实现loadBeanDefinitions方法,从而在每次调用refresh方法时可以将BeanDefinition加载到DefaultListableBeanFactory中。 -
AnnotationConfigApplicationContext
AnnotationConfigApplicationContext支持以@Configuration标注的类、@Component和JSR-330的@Inject注解作为BeanDefinition源,同时也支持直接注册类register(Class…)以及基于classPath扫描scan(String…)的方式注册类。
如果有多个@Configuration标注的类,那么后面的类中定义的@Bean方法会覆盖前面的类。利用这点可以通过增加额外的@Configuration类来覆盖某些BeanDefinition。