您的位置:首页 > 编程语言 > Java开发

Spring IOC 原理

2019-09-10 23:18 2626 查看
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。 本文链接:https://blog.csdn.net/smartbetter/article/details/100672806

IOC(Inversion of Control)即控制反转,可以说是 Spring 最核心的部分,IOC 是一种思想,使得开发者从繁琐的对象交互中解脱出来,进而专注对象本身,更进一步突出面向对象。了解 IOC,需要先了解下依赖注入(Dependency Inversion,DI)。

依赖注入就是把底层类作为参数传递给上层类,实现上层对下层的 “控制”。

依赖注入的方式:

  • Set 注入;
  • 接口注入;
  • 构造方法注入;
  • 注解注入。

下面看下依赖倒置原则、IOC、依赖注入(DI)、IOC 容器的关系:

正式依赖倒置原则的指导,才有了 IOC 的思路,而实现 IOC 离不开依赖注入(DI)的支撑,Spring 框架基于 IOC 提出了 IOC 容器的概念。对于 IOC 来说,最重要的就是容器了,容器管理着 Bean 的生命周期,控制着 Bean 依赖注入。

IOC 容器的优势:

  • 避免在各处使用 new 来创建类,并且可以做到统一维护。

下面来看下 Spring IOC 容器。

Spring 启动的时候,会读取应用程序提供的 Bean 配置信息(XML Config、Java Config、注解 @Autowired),并在 Spring 容器中生成一份 Bean 定义注册表,然后根据这张注册表去实例化 Bean,装配好 Bean 之间的依赖关系,为上层提供准备就绪的运行环境。

Spring 提供一个配置文件描述 Bean 之间的依赖关系,利用 Java 语言的反射功能,实例化 Bean,并建立 Bean 之间的依赖关系。

Spring IOC 支持以下功能:

  • 依赖注入;
  • 依赖检查;
  • 自动装配;
  • 支持集合;
  • 指定初始化方法和销毁方法;
  • 支持回调方法(需要实现 Spring 接口,略带侵入性,谨慎使用)。

Spring IOC 容器的核心接口:

  • BeanFactory;
  • ApplicationContext。

为了进一步分析 BeanFactory 和 ApplicationContext,需要先弄清楚 BeanDefinition 接口:

BeanDefinition 接口主要是用来描述 Bean 的定义的。

<!-- 事务配置 -->
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="mobileDataSource"/>
</bean>
@Service
public class MyService {
@Autowired
private ConnectionSettings connection;
//...
}

Spring 容器在启动的时候,会将 XML Config、Java Config 或者注解里的 Bean 的定义解析成 Spring 内部的 BeanDefinition。

第二个需要了解的是 BeanDefinitionRegistry 接口,BeanDefinitionRegistry 接口提供了向 IOC 容器注册 BeanDefinition 对象的方法。

BeanFactory 是 Spring 框架最核心的接口,它提供了 IOC 的配置机制,包含了 Bean 的各种定义,便于实例化 Bean,BeanFactory 实例化 Bean 的时候会建立 Bean 之间的依赖关系。除此之外,BeanFactory 还包含了 Bean 生命周期的控制。

package org.springframework.beans.factory;
public interface BeanFactory {
Object getBean(String name) throws BeansException;
boolean containsBean(String name);
boolean isSingleton(String name) throws NoSuchBeanDefinitionException;
// 省略部分代码...
}

由于 BeanFactory 的功能还不够强大,所以 Spring 在 BeanFactory 的基础上还设计了一个更为高级的接口 ApplicationContext,ApplicationContext 是 BeanFactory 的子接口之一。

package org.springframework.context;
import org.springframework.beans.factory.HierarchicalBeanFactory;
import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.core.env.EnvironmentCapable;
import org.springframework.core.io.support.ResourcePatternResolver;

public interface ApplicationContext extends EnvironmentCapable, ListableBeanFactory,
HierarchicalBeanFactory, MessageSource, ApplicationEventPublisher, ResourcePatternResolver {
String getApplicationName();
ApplicationContext getParent();
// 省略部分代码...
}
package org.springframework.beans.factory;
public interface ListableBeanFactory extends BeanFactory {
boolean containsBeanDefinition(String beanName);
int getBeanDefinitionCount();
String[] getBeanDefinitionNames();
// 省略部分代码...
}

BeanFactory 和 ApplicationContext 的比较:

  • BeanFactory 是 Spring 框架的基础设施,面向 Spring 本身;
  • ApplicationContext 面向使用 Spring 框架的开发者;
  • BeanFactory 可以理解为 “发动机”,而 ApplicationContext 可以理解为 “汽车”。

ApplicationContext 的功能:

  • 继承 BeanFactory 接口:能够管理、装配 Bean;
  • 继承 ResourcePatternResolver 接口:能够加载资源文件;
  • 继承 MessageSource 接口:能够实现国际化等功能;
  • 继承 ApplicationEventPublisher 接口:能够注册监听器,实现监听机制。

Java Config 中,使用注解 @Configuration 可以将 @Bean 注解的方法返回的实例注入到 Spring IOC 容器中。

@Configuration
public class ApplicationConfig {
@Bean(name = "person")
public Person initPerson() {
return new Person(100L, "Jack"); // id, name
}
}

Bean 注入的原理?

下来分析一下容器的创建、配置和 getBean() 两个源码。

Spring IOC 容器创建好之后就会调用 refresh() 方法。

  1. Spring IOC 的 refresh() 源码解析

AbstractApplicationContext 是 ApplicationContext 接口的实现类。

package org.springframework.context.support;
public abstract class AbstractApplicationContext extends DefaultResourceLoader
implements ConfigurableApplicationContext, DisposableBean {
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// 准备刷新上下文
prepareRefresh();
// 获取BeanFactory实例
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// 对BeanFactory进行相关设置(设置ClassLoader用来加载Bean、设置表达式解析器等), 为后续使用做准备
prepareBeanFactory(beanFactory);
try {
// 在BeanFactory进行相关设置后需要做的逻辑, 方法体为空, 不同的Spring容器会重写它
postProcessBeanFactory(beanFactory);
// 这个方法比较重要, 调用工厂后处理器, 处理各类Bean标签, 扫描Bean文件, 并解析成一个个的Bean
invokeBeanFactoryPostProcessors(beanFactory);
// 注册实现了BeanPostProcessors接口的Bean
registerBeanPostProcessors(beanFactory);
// 初始化国际化相关属性
initMessageSource();
// 初始化事件广播器, 事件广播器用于事件发布
initApplicationEventMulticaster();
// 模板方法, 方法体为空, 不同的Spring容器会重写它
onRefresh();
// 注册事件监听器
registerListeners();
// 实例化所有已经被注册但未被实例化的Bean(排除懒加载Bean)
finishBeanFactoryInitialization(beanFactory);
// 做一些初始化生命周期处理器等事情
finishRefresh();
} catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
destroyBeans();
cancelRefresh(ex);
throw ex;
} finally {
resetCommonCaches();
}
}
}
// 省略部分代码...
}

refresh() 方法:

  • 为 IOC 容器以及 Bean 的生命周期管理提供条件;
  • 刷新 Spring 上下文信息,定义整个 Spring 上下文加载的流程。
  1. Spring IOC 的 getBean() 源码解析

AbstractBeanFactory 是 BeanFactory 接口的实现类。

package org.springframework.beans.factory.support;
public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport implements ConfigurableBeanFactory {
@Override
public Object getBean(String name) throws BeansException {
return doGetBean(name, null, null, false);
}
protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
// 获取beanName
final String beanName = transformedBeanName(name);
Object bean;
// 根据beanName获取共享的实例
Object sharedInstance = getSingleton(beanName);
if (sharedInstance != null && args == null) {
// 尝试从缓存或者实例工厂中获取实例
bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
} else {
if (isPrototypeCurrentlyInCreation(beanName)) { throw new BeanCurrentlyInCreationException(beanName); }
// 尝试从ParentBeanFactory中获取Bean实例
BeanFactory parentBeanFactory = getParentBeanFactory();
if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
//...
}
if (!typeCheckOnly) { markBeanAsCreated(beanName); }
try {
// 将父类的属性合并到子类里
final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
checkMergedBeanDefinition(mbd, beanName, args);
// 获取依赖关系
String[] dependsOn = mbd.getDependsOn();
// 如果存在依赖, 递归实例化依赖的Bean
if (dependsOn != null) {
for (String dep : dependsOn) { //...
}
}
// 判断Bean的作用域是否是单例的, 如果是单例就去看先前有没有创建过这个Bean, 如果有就直接返回, 没有就创建一个
if (mbd.isSingleton()) {
//...
} else if (mbd.isPrototype()) { // 如果作用域是Prototype, 就new一个Bean实例出来
//...
} else { //...
}
} catch (BeansException ex) {
cleanupAfterBeanCreationFailure(beanName);
throw ex;
}
}
// 类型检查
if (requiredType != null && !requiredType.isInstance(bean)) {
//...
}
return (T) bean;
}
protected Object getObjectForBeanInstance(
Object beanInstance, String name, String beanName, @Nullable RootBeanDefinition mbd) {
//...
Object object = null;
if (mbd == null) {
// 从缓存中获取实例
object = getCachedObjectForFactoryBean(beanName);
}
if (object == null) {
//...
boolean synthetic = (mbd != null && mbd.isSynthetic());
// 从实例工厂中获取实例
object = getObjectFromFactoryBean(factory, beanName, !synthetic);
}
return object;
}
}

getBean() 方法的代码逻辑:

  • 转换 beanName;
  • 尝试从缓存中加载实例;
  • 实例化Bean;
  • 检查 parentBeanFactory;
  • 初始化依赖的 Bean;
  • 创建 Bean。

下面看两个常见的面试问题:

  1. Spring Bean 的作用域?
  • singleton:Spring 的默认作用域,容器里拥有唯一的 Bean 实例;
  • prototype:针对每个 getBean() 请求,容器都会创建一个 Bean 实例;

如果是 Web 容器,还支持以下三种作用域:

  • request:会为每个 http 请求创建一个 Bean 实例;
  • session:会为每个 session 创建一个 Bean 实例;
  • globalSession:会为每个全局 http session 创建一个 Bean 实例,该作用域仅对 Portlet 有效。
  1. Spring Bean 的生命周期?

容器创建之后会解析并创建出来 Bean,Spring Bean 的生命周期是由容器来管理的。

Bean 创建的过程:

  • 实例化 Bean;
  • Aware (注入 Bean ID、BeanFactory 和 AppCtx),Aware 接口是为了能够感知到自身的属性;
  • 调用 BeanPostProcessor 的前置初始化方法 postProcessBeforeInitialization();
  • 如果实现了 InitializingBean 接口,则会调用 InitializingBean 的 afterPropertiesSet() 方法,做一些属性被自定义后的事情;
  • 调用 Bean 自身定义的 init() 方法;
  • 调用 BeanPostProcessor 的后置初始化方法 postProcessAfterInitialization();
  • Bean 的初始化完毕。

Bean 销毁的过程:

  • 如果 Bean 实现了 DisposableBean 接口,则会调用 destroy() 方法;
  • 如果Bean 自身定义了 destroy-method 属性,则会调用其定义的销毁方法;
  • Bean 的销毁完毕。

#2.AOP原理

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: