Spring-IOC总结
本文最后更新于:2020年9月10日 下午
1 Spring IOC是什么
1.1 Spring IOC是个什么东西
IOC
是用为用户创建、管理实例对象的。用户需要实例对象时只需要向IOC
容器获取就行了,不用自己去创建,从而达到与具体类解耦。
简单点来讲就是Spring IOC
就是一个Map
集合,对象的名字就是集合中的key
,值就是对应的对象。我们可以通过一个对象的名字到集合中获取对象。
1.2 IOC实现的流程
- Bean定义
Spring`提供了多种方式来配置`Bean`定义,有`Xml`,`JavaConfig
Xml:
<bean id="user" class="com.ranger.bean.User">
<constructor-arg type="String" value="ranger"></constructor-arg>
<constructor-arg ref="cbean"></constructor-arg>
</bean>
<bean id="car" class="com.ranger.bean.Car">
<constructor-arg type="String" value="mazda"></constructor-arg>
</bean>
JavaConfig的方法,这种方式要和注解配合
@Configuration
public class AppConfig {
@Bean
public Service myService() {
return new ServiceImpl();
}
}
- Spring读取Bean的定义
创建SpringIOC
容器的时候指定一个配置文件(xml),或者指定包扫描的路径(JavaConfig)
// 通过指定classpath下的配置文件
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("springcontext.xml");
或者使用JavaConfig
的方式
AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext();
annotationConfigApplicationContext.register(AppConfig.class);
annotationConfigApplicationContext.refresh();
System.out.println(annotationConfigApplicationContext.getBean(User.class));
2. 从入口ApplicationContext开始
大概介绍每个父接口对应的职责
EnvironmentCapable
:获取环境变量相关的参数HierarchicalBeanFactory
:提供父子容器功能ListableBeanFactory
:BeanFactory的实现ApplicationEventPublisher
:事件的发布ResourcePatternResolver
:加载Resource文件MessageSource
:提供国际化功能
ApplicationContext
中定义的方法
ApplicationContext
中定义了自己的几个方法,对这几个方法做简单的介绍:
getApplicationName
:返回容器所属的应用的名称, 默认返回空字符串getAutowireCapableBeanFactory
:暴露当前容器的AutowireCapableBeanFactorygetDisplayName
:返回一个友好的容器名字getId
:返回当前容器的唯一IdgetParant
:返回父容器,没有的话返回nullgetStartupDate
:返回容器第一次加载完成的时间戳
2.1 Application的子类
2.1.1 AbstarctApplicationContext 的子类
我们先从ApplicationContext
后面的子孙开始
从上面的类继承图可以看出,AbastractApplicationContext
后面有两个儿子,分别是 GernericApplicationContext
和AbstractRefreshableApplicationContext
.这两个大儿子分别都持有了BeanFactory
实例,这两个儿子所有的对于BeanDefiniton
的注册,Bean
的实例化都是基于这个BeanFactory
实例的
它们分成了两大派系:
- AbstractRefreshableApplicationContext:这个类在每次调用
refresh
方法的时候都会产生一个新的beanfactory
实例(通常是,但是不是必须的)。这个应用上下文会通过一系列的配置文件去加载BeanDefinition。在调用refresh方法的时候才会创建内部持有的BeanFacoty
实例(可以参见该类中的refreshBeanFactory
方法) - GenericApplicationContext:这个类内部持有唯一的一个
DefaultListableBeanFactory
实例,而且相较于其它ApplicationContext
的实现类,这个类在创建的时候就会有一个BeanFactory的实例,意思就是在refresh方法调用前,内部持有的BeanFactory
实例就已经创建,且这个类从开始到最终都是一个BeanFacoty实例。
GenericApplicationContext
实现了BeanDefinitionRegistry
这个接口,这个接口干啥的呢,看名字是BeanDefinition
的注册什么东东,没错,这个就是用来添加删除BeanDefiniton的。GenericApplicationContext
也还有几个儿子,后面会简单分析一下他们的不同。
2.1.2 AbstractApplicationContext抽象类简单说明
上面都提到了refresh方法,这个方法是AbastractApplicationContext
实现的,用于配置Context.该类使用了模板方法模式,很多方法都留给了子类去实现。
在AbstractApplicationContext
中,实现了大多数ApplicationContext
接口从BeanFactroy
接口继承来的方法。
我们还可以看到,AbstractApplicationContext
的父类是ConfigurableApplicationContext
,这个类提供了配置Context
的方法,还提供了生命周期方法。
3 BeanDefinition的加载
通过上面的分析我们看到,虽然都有一个共同的祖先叫做ApplicationContext
,但是不同的子孙还是有不同的加载BeanDefinition
的方法,但是其它方面都是一样的,SpringIOC
中最重要的方法refresh就在他们共同的老爸AbstractApplicationContext
中。refresh
方法会根据BeanDefinition
来创建Bean
对象(除开lazy loading
)
我们就从我们常见的ClasspathXmlApplicationContext
开始分析:
现在项目中包含上面图中的类和配置,我们编写一个主方法:
public class Application {
public static void main(String[] args) {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("springcontext.xml");
Person person = applicationContext.getBean(Person.class);
System.out.println(person);
}
}
springcontext.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-3.0.xsd"
default-lazy-init="true" default-init-method="" default-destroy-method="">
<bean id="car" class="com.ranger.spring.ioc.bean.Car">
<constructor-arg name="brand" value="mazda"></constructor-arg>
</bean>
<bean id="person" class="com.ranger.spring.ioc.bean.Person">
<property name="car" ref="car"></property>
</bean>
</beans>
调试启动Application
类的main
方法
因为最终BeanDefinition
的注册都是在DefaultListableBeanFactory
中完成的,所以在registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
打断点就能看到下面的调用栈
从线程调用栈的下面向上看。
得到一个基本的流程:
这里面的解析Document
获取BeanDefinition
也比较复杂,如果有兴趣可以去看看。
那么GenericXmlApplicationContext
加载BeanDefinition
的流程是不是也和上面一样呢。
可以看到流程是一样的。
只是GenericXmlApplicationContext
会先调用load
来加载BeanDefinition
,然后调用refresh
完成配置。
而ClasspathXmlApplicationContext
会在refresh
方法调用的时候完成BeanDefinition
的加载。
4 bean工厂-DefaultListableBeanFactory
通过前面的分析我们可以看到,ApplicationContext
的大部分操作其实都是基于DefaultListableBeanFactory
来完成的。
DefaultListableBeanFactory
是BeanFactory
的一个实现类
现在我们来认识一下它:
- 先看看最上面的祖先
BeanFactory
通过读源码的doc,
这个接口是
spring bean
容器的根接口,它有一些为了提供特定功能的子接口ListableBeanFactory
和ConfigurableBeanFactory
实现这个接口的对象持有一系列的
bean definitions
,每个bean definition
都有一个唯一的字符串名字。返回的Bean可以是单例的,也可以是独立的(每次都要创建),具体返回什么类型取决于applicationcontext
的配置。BeanFactory
通过依赖注入来完成配置,通常的手段是用setter
或者constructor
通常情况
BeanFactory
加载的BeanDefinition
保存在一个配置资源中,比如XML文件。但是具体存储在哪儿是没有限制的,比如LDAP,XML,properties
等等。HierarchicalBeanFactory
会先从本上下文找,找不到从父BeanFactory
找,且本工厂实例中的bean会覆盖父工厂BeanFactory
的实现类应该尽可能支持bean
的生命周期方法,比如BeanNameAware
,BeanClassLoaderAware
,等等。对于这些生命周期方法的支持,BeanFacoty没有给出抽象的接口,需要实现类自己去实现
BeanFactory
的源码:
public interface BeanFactory {
// 用来区分FactoryBean和其产生的对象
String FACTORY_BEAN_PREFIX = "&";
// 通过BeanName获取Bean
Object getBean(String name) throws BeansException;
// 通过beanName和bean 的Class类型来获取Bean
<T> T getBean(String name, @Nullable Class<T> requiredType) throws BeansException;
// 增加获取bean的参数
Object getBean(String name, Object... args) throws BeansException;
// 通过类型获取
<T> T getBean(Class<T> requiredType) throws BeansException;
// 和上面一样的道理
<T> T getBean(Class<T> requiredType, Object... args) throws BeansException;
// 判断是否包含某个Bean
boolean containsBean(String name);
// bean是否是单例
boolean isSingleton(String name) throws NoSuchBeanDefinitionException;
// bean是否是prototype
boolean isPrototype(String name) throws NoSuchBeanDefinitionException;
//查询指定了名字的Bean的Class类型是否与指定类型匹配
boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException;
// 同上
boolean isTypeMatch(String name, @Nullable Class<?> typeToMatch) throws NoSuchBeanDefinitionException;
//获取指定名字bean的Class类型
@Nullable
Class<?> getType(String name) throws NoSuchBeanDefinitionException;
// 获取bean的别名
String[] getAliases(String name);
}
BeanFactory
有三个子类接口:ListableBeanFactory
、HierarchicalBeanFactory
和AutowireCapableBeanFactory
,还有一个实现类SimpleJndiBeanFactory
。
这里对于BeanFactory
的体系介绍就先不说了,太多了,单独写吧。
5 后记
ApplicationContext
的体系很大,重要抓住了核心几个比较重要的几个类:AbstractApplicationContext
,以及它的两个大儿子 GenericApplicationContext
和AbstractRefreshableApplicationContext
。大部分功能都在这里面实现了。
两个大儿子生下的儿子都是基于他们做了一些扩展。
阅读代码可以发现,ApplicationContext
很多的方法都留到了子类去实现,这里用到了模板方法设计模式。
最终对于注册BeanDefinition
和基于BeanDefinition
创建bean
实例都是归结到了DefaultListableBeanFactory
中。
前面对 Spring
容器的体系做了整体的了解,接下来会分析bean
的创建。
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!