Spring IOC 原理解析

文章目录

  • IOC 概念
  • IOC Service Provider
  • Spring 的 IOC Service Provider
    • BeanFactory
        • 直接编码方式
            • 示例:
        • 外部配置文件方式
        • 注解方式
      • FactoryBean:
  • BeanFactoryAware 接口
  • BeanFactoryPostProcessor 接口
  • Spring IOC 源码解析:
      • loadBeanDefinitions(beanFactory) 方法
  • refresh() 方法

IOC 概念

IOC, Inversion of Control, 中文是 控制反转,还有一个别名是 dependency Injection,依赖注入。
在接触IOC之前,我们需要用到什么对象,就直接在类的构造函数里面新建相应的依赖的类 。 但是,我们每次需要用到什么对象就要主动地去获取,这是否真的有必要呢? 我们其实只需要调用那个依赖对象的提供的一项服务,所以,只要我们需要那个依赖对象时,那个依赖对象就在。 如果有人能在我们需要时将某个依赖对象送过来,为什么我们还需要大费周章的去创建?这就是用到了IOC, 让别人为我们服务!

我们现在把 能提供给我们依赖对象的提供者叫做 IOC Service Provider。我们需要什么对象,肯定要通知这个IOC容器,那么通知的方式就有一下三种: 构造方法注入setter方法注入接口注入

  1. 构造方法注入
    就是被注入对象可以通过在构造方法中声明依赖对象的参数列表,来让 IOC Service Provider知道它需要哪些对象
  2. setter方法注入
    当前对象只需要为其依赖对象所对应的属性添加setter方法,Service Provider`就可以通过setter方法将相应的依赖对象设置到被注入对象中
  3. 接口注入
    被注入对象如果要 IOC Service Provider为其注入对象,就必须实现一个接口, IOC Service Provider通过这些接口了解应该为被注入对象注入什么依赖对象。这种方式比较死板和繁琐,不推荐使用。

优缺点:

  1. 构造方法注入
    优点是对象在构造完成之后就进入了就绪状态,缺点是依赖对象太多的话构造方法参数列表太长,不易维护。
  2. setter方法注入
    优点是setter方法在描述性上比构造方法好一点,也可以解决循环依赖的问题,但是对象无法在构造完成后马上进入就绪状态。

IOC Service Provider

IOC Service Provider 的职责有如下两个:

  1. 业务对象的构建管理
    业务对象不需要关心所依赖的对象如何构建如何取得,所以IOC Service Provider 需要将对象的构建逻辑从客户端对象那里剥离出来。
  2. 业务对象间的依赖绑定
    IOC Service Provider 通过上一步构建和管理的所有业务对象,以及各个业务对象可以识别的依赖关系,将这些对象注入绑定,然后使他们可以处于就绪状态。

Spring 的 IOC Service Provider

Spring 的IOC容器 完成了 IOC Service Provider的事情
Spring有两种IOC容器,BeanFactoryApplicationContext

  • BeanFactory,是基础的 IOC容器,提供完整的Ioc服务支持。只有当客户端对象需要访问容器中的某个受管对象时,对受管对象进行初始化和依赖注入
  • ApplicationContext,它继承了 BeanFactory 接口,是相对高级的容器实现。它多提供了 事件发布、国际化信息支持等等。ApplicationContext管理的对象,都说在容器启动之后,默认全部初始化并绑定完成

BeanFactory

顾名思义,就是生产 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
我们查看一下类层次:
Spring IOC 原理解析_第1张图片
事实上,观察 一下 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接口

Spring IOC 原理解析_第2张图片
可以看到,现在已经不支持Properties的读取方式了

注解方式

我们现在最常用的就是注解方式,我们使用 @Component@Autowired注解进行标记, IOC容器就会扫描,然后构建对象并且注入依赖对象

FactoryBean:

这个接口是 Spring 对基于工厂方法模式实例化对象 的支持,它也是Spring容器提供的一种可以扩展容器对象实例化逻辑的接口, 像 ProxyFactoryBean就是利用这一点来代理原对象做到了 AOP .

public interface FactoryBean<T> {
	T getObject() throws Exception;
	Class<?> getObjectType();
	default boolean isSingleton() {
		return true;
	}
}

getObject() 方法会返回该 FactoryBean 生产的 对象实例,我们可以实现该方法以给出自己的对象实例化逻辑,如 ProxyFactoryBean就是在这个方法中调用AopProxy类的 方法获取代理对象
getObjectType() 就是返回这个对象的类型

BeanFactoryAware 接口

Spring框架提供了一个 BeanFactoryAware 接口,容器在实例化实现了该接口的bean定义的过程中,会自动将容器本身注入该bean,这样,该bean就会持有它处在的容器BeanFactory的引用

BeanFactoryPostProcessor 接口

这个接口是容器的扩展机制,该机制允许我们在容器实例化对象之前,对注册到容器的 所有 BeanDefinition所保存的信息做一些修改。

Spring IOC 源码解析:

既然要源码解析,那么我们肯定要创建项目,我这里的项目是使用以前创建过的:
创建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里。

loadBeanDefinitions(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() 方法

回到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容器初始化关联起来,查看它的重写子类,也可以发现这点:

Spring IOC 原理解析_第3张图片
然后再回到 refresh()方法,下一个方法是:

// 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();

主要工作是清理缓存。

你可能感兴趣的:(Java,spring,java,后端)