IOC, Inversion of Control, 中文是 控制反转,还有一个别名是 dependency Injection,依赖注入。
在接触IOC之前,我们需要用到什么对象,就直接在类的构造函数里面新建相应的依赖的类 。 但是,我们每次需要用到什么对象就要主动地去获取,这是否真的有必要呢? 我们其实只需要调用那个依赖对象的提供的一项服务,所以,只要我们需要那个依赖对象时,那个依赖对象就在。 如果有人能在我们需要时将某个依赖对象送过来,为什么我们还需要大费周章的去创建?这就是用到了IOC, 让别人为我们服务!
我们现在把 能提供给我们依赖对象的提供者叫做 IOC Service Provider
。我们需要什么对象,肯定要通知这个IOC容器,那么通知的方式就有一下三种: 构造方法注入
, setter方法注入
,接口注入
IOC Service Provider
知道它需要哪些对象IOC Service Provider
为其注入对象,就必须实现一个接口, IOC Service Provider
通过这些接口了解应该为被注入对象注入什么依赖对象。这种方式比较死板和繁琐,不推荐使用。优缺点:
IOC Service Provider 的职责有如下两个:
Spring 的IOC容器 完成了 IOC Service Provider
的事情
Spring有两种IOC容器,BeanFactory
和ApplicationContext
BeanFactory
,是基础的 IOC容器,提供完整的Ioc服务支持。只有当客户端对象需要访问容器中的某个受管对象时,才对受管对象进行初始化和依赖注入。ApplicationContext
,它继承了 BeanFactory 接口,是相对高级的容器实现。它多提供了 事件发布、国际化信息支持等等。ApplicationContext
管理的对象,都说在容器启动之后,默认全部初始化并绑定完成。顾名思义,就是生产 JavaBean 的工厂。BeanFactory可以完成IOC Service Provider的所有职责,包括业务对象的注册和对象间 的依赖关系绑定。BeanFactory为了能够明确管理各个业务对象及业务对象之间的依赖绑定关系,使用了几种方式来记录这些信息。
我们看到BeanFactory接口的实现类DefaultListableBeanFactory
,它提供了 registerBeanDefinition()
和 我们使用它就可以直接来进行注册和绑定:
我们有如下类要IOC容器来管理:
public class Person {
public String name;
public Integer age;
public Person() {
}
public Person(String name, Integer age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
然后使用 BeanFactory
来管理:
public class test {
public static void main(String[] args) {
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
RootBeanDefinition person1 = new RootBeanDefinition(Person.class);
ConstructorArgumentValues argumentValues1 = new ConstructorArgumentValues();
argumentValues1.addIndexedArgumentValue(0,"john");
argumentValues1.addIndexedArgumentValue(1,18);
person1.setConstructorArgumentValues(argumentValues1);
beanFactory.registerBeanDefinition("person1",person1);
RootBeanDefinition person2 = new RootBeanDefinition(Person.class);
MutablePropertyValues values = new MutablePropertyValues();
values.addPropertyValue(Person.class.getFields()[0].getName(),"ALICE");
values.addPropertyValue(Person.class.getFields()[1].getName(),19);
person2.setPropertyValues(values);
beanFactory.registerBeanDefinition("person2",person2);
Person p1 = (Person) beanFactory.getBean("person1");
System.out.println(p1);
Person p2 = (Person) beanFactory.getBean("person2");
System.out.println(p2);
}
}
这就是直接编码方式的 IOC 职责。
在上面的代码中,出现了这么几个类 DefaultListableBeanFactory
,RootBeanDefinition
我们查看一下类层次:
事实上,观察 一下 BeanFactory接口的源码:
public interface BeanFactory {
Object getBean(String name) throws BeansException;
boolean containsBean(String name);
boolean isSingleton(String name) throws NoSuchBeanDefinitionException;
boolean isPrototype(String name) throws NoSuchBeanDefinitionException;
boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException;
Class<?> getType(String name) throws NoSuchBeanDefinitionException;
String[] getAliases(String name);
}
可以发现,他仅提供了对工厂内的Bean的访问控制。注册Bean进去的方法是由 BeanDefinitionRegistry 接口来提供的:
public interface BeanDefinitionRegistry extends AliasRegistry {
void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
throws BeanDefinitionStoreException;
void removeBeanDefinition(String beanName) throws NoSuchBeanDefinitionException;
}
所以,实现了这两者的DefaultListableBeanFactory
才是一个完整的IOC Service Provider
而 BeanDefinition
这个类我们看代码也很容易看出来, 对于容器中受管对象的每一个实例,使用 BeanDefinition
类对其封装
public class RootBeanDefinition extends AbstractBeanDefinition {
@Nullable
private AnnotatedElement qualifiedElement;
@Nullable
volatile Class<?> resolvedTargetType;
@Nullable
Object[] resolvedConstructorArguments;
}
如上是部分属性,但是已经包括了对应对象的 Class类型,构造方法参数和注解信息
这种方式。只需要读取外部文件,将其转换成 BeanDefinition 即可
Spring对于外部文件的读取,交给了 BeanDefinitionReader
接口
我们现在最常用的就是注解方式,我们使用 @Component
和 @Autowired
注解进行标记, IOC容器就会扫描,然后构建对象并且注入依赖对象
这个接口是 Spring 对基于工厂方法模式实例化对象 的支持,它也是Spring容器提供的一种可以扩展容器对象实例化逻辑的接口, 像 ProxyFactoryBean
就是利用这一点来代理原对象做到了 AOP .
public interface FactoryBean<T> {
T getObject() throws Exception;
Class<?> getObjectType();
default boolean isSingleton() {
return true;
}
}
getObject()
方法会返回该 FactoryBean 生产的 对象实例,我们可以实现该方法以给出自己的对象实例化逻辑,如 ProxyFactoryBean
就是在这个方法中调用AopProxy
类的 方法获取代理对象
getObjectType()
就是返回这个对象的类型
Spring框架提供了一个 BeanFactoryAware 接口,容器在实例化实现了该接口的bean定义的过程中,会自动将容器本身注入该bean,这样,该bean就会持有它处在的容器BeanFactory的引用
这个接口是容器的扩展机制,该机制允许我们在容器实例化对象之前,对注册到容器的 所有 BeanDefinition
所保存的信息做一些修改。
既然要源码解析,那么我们肯定要创建项目,我这里的项目是使用以前创建过的:
创建spring项目
首先,会加载配置的元数据,这个阶段所做的工作是准备性的,更加侧重于对象管理信息的收集,加载的元数据编组为对应的 BeanDefinition
,然后把这些 BeanDefinition
注册到的 BeanDefinitionRegistry
里去。
这一步对应源码中的
new ClassPathXmlApplicationContext()
构造方法中的 refresh()
方法中的
// Tell the subclass to refresh the internal bean factory.
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
我们细细分析一下该方法;
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
refreshBeanFactory();
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
if (logger.isDebugEnabled()) {
logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
}
return beanFactory;
}
通过打断点发现,在到第二行时BeanFactory就已经充满了 BeanDefinition
,所以对象信息的收集应该就在第一个方法里面:
@Override
protected final void refreshBeanFactory() throws BeansException {
try {
//第一步
DefaultListableBeanFactory beanFactory = createBeanFactory();
//第二步
beanFactory.setSerializationId(getId());
//第三步
customizeBeanFactory(beanFactory);
//第四步
loadBeanDefinitions(beanFactory);
synchronized (this.beanFactoryMonitor) {
this.beanFactory = beanFactory;
}
}
}
第一步的话是创建一个空的 BeanFactory
,
第二步是设置序列化ID,应该是和序列化有关系。
第三步是自定义BeanFactory,我们可以进去看看:
protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) {
if (this.allowBeanDefinitionOverriding != null) {
beanFactory.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
}
if (this.allowCircularReferences != null) {
beanFactory.setAllowCircularReferences(this.allowCircularReferences);
}
}
即使看了,也看不懂,不过幸好在这两个方法上有注释说明:
前者的意思是:
设置是否应允许它通过注册来覆盖BeanDefinition,具有相同名称的不同定义,将自动替换前者。否则,将引发异常。这也适用于替代别名。默认值为true
后者的意思是:
设置是否允许bean之间的循环引用-并自动试着解决它们。
循环引用意味着其中一个涉及的bean将接收对另一个尚未完全初始化的bean的引用。这可能会对初始化产生微妙和不太微妙的副作用;不过,它在许多情况下都能正常工作。默认值为“true”。关闭此选项可在遇到异常时引发异常
循环引用,完全不允许它们。
第四步的loadBeanDefinitions(beanFactory);
我们光是看方法名就知道这是这侧我们定义的 Bean到BeanFactory里。
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
beanDefinitionReader.setEnvironment(this.getEnvironment());
beanDefinitionReader.setResourceLoader(this);
beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
initBeanDefinitionReader(beanDefinitionReader);
loadBeanDefinitions(beanDefinitionReader);
}
前面的各种set方法都是配置一些运行环境,这里不用管,其中loadBeanDefinitions()
才是去注册Bean定义的主要方法。
进入该方法:
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
Resource[] configResources = getConfigResources();
if (configResources != null) {
reader.loadBeanDefinitions(configResources);
}
String[] configLocations = getConfigLocations();
if (configLocations != null) {
reader.loadBeanDefinitions(configLocations);
}
}
上面两段意思都一样,先获取XML文件(以文件名方式或者Resource对象的方式),之后用到 XmlBeanDefinitionReader
这个类来解析你写在 XML文件里面的Bean定义, 然后将xml文件信息里的内容加载到容器里面去。
回到refresh() 方法,下一个方法是
prepareBeanFactory(beanFactory);
这个是为了给BeanFactory增加一些内容,比如ApplicationContext比BeanFactory增加的国际化信息支持就是在这个方法中增加的。
再下一个方法是
// Allows post-processing of the bean factory in context subclasses.
postProcessBeanFactory(beanFactory);
我这里没有子类去重写这个方法,参考方法上的注释的话
修改应用程序上下文的内部bean工厂,使其符合标准初始化过程。将加载所有bean定义,但任何bean都不会被实例化。这允许注册特殊的某些ApplicationContext实现中的BeanPostProcessors等。
所以我们可以想到Servlet容器的启动,这个应该是把Servlet容器初始化和Spring容器初始化关联起来,查看它的重写子类,也可以发现这点:
// Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory);
从方法名我们就可以得知,这是执行容器中所有的 BeanFactoryPostProcessor
的实现类的postProcessBeanFactory()
方法
下一个方法是
// Register bean processors that intercept bean creation.
registerBeanPostProcessors(beanFactory);
从该方法名我们就可以得知将BeanPostProcessor
接口的实现类注册到容器中,具体是从容器中找到该接口的实现类,然后将其添加到容器类的实例:
/** BeanPostProcessors to apply in createBean */
private final List<BeanPostProcessor> beanPostProcessors = new ArrayList<>();
到这里面去。
下一个方法是
// Initialize message source for this context.
initMessageSource();
这个是和国际化消息处理相关的操作。
下个方法是
// Initialize event multicaster for this context.
initApplicationEventMulticaster();
这个是初始化事件多播的Bean
/** Helper class used in event publishing */
@Nullable
private ApplicationEventMulticaster applicationEventMulticaster;
通过注释可知,该类是用于事件发布。
下一个是
// Initialize other special beans in specific context subclasses.
onRefresh();
可以重写该方法以添加特定于上下文的刷新工作,在初始化特殊bean时调用,然后实例化单例。
下一个是
// Check for listener beans and register them.
registerListeners();
检查监听器并且注册它们。具体实现是找到容器中的 ApplicationListener 接口的实现类,然后把该类放到
/** Helper class used in event publishing */
@Nullable
private ApplicationEventMulticaster applicationEventMulticaster;
这个集合中去。
下一个是
// Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory);
前面都是准备工作,这个方法就是去实例化容器中所有的对象了,像循环引用的解决也是在这个方法中处理的。
最后的一个方法是
// Last step: publish corresponding event.
finishRefresh();
主要工作是清理缓存。