1.基础知识
Spring有两个核心功能,分别是ioc和aop,其中ioc是控制反转,aop是切面编程。
在ioc中,还有一个名次叫DI,也就是依赖注入。嗯,好像IOC和DI是指同一个,好像又感觉他俩不是同一个。
具体的区别是:IOC是DI的原理。依赖注入是向某个类或方法注入一个值,其中所用到的原理就是控制反转。
所以说到操作层面的时候用DI,原理层的是说IOC,下文亦同。
对于DI最新使用方法,一般都是Java注解去标识。但是用这种方式去看源码,好像不太适合。用XML的方式,根据一个demo来进行源码的阅读,比较适合。
2.demo代码
BeanService:
public interface BeanService {
String getName();
}
BeanServiceImpl:
public class BeanServiceImpl implements BeanService {
public String getName() {
return "----------------------------------------:I am Bean";
}
}
applicationContext.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.xsd <!--ioc源码-->
<bean id="beanService" class="com.example.demo.ershi.IocSource.BeanServiceImpl"/> </beans>
test代码:
public class BeanTest {
public static void main(String[] args) {
// 加载xml配置
ApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContextF.xml"); // IOC获取Bean
BeanService beanService = context.getBean(BeanService.class); System.out.println(beanService.getName());
}
}
测试结果:
22:01:07.069 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'beanService'
22:01:07.069 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating instance of bean 'beanService'
22:01:07.069 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Eagerly caching bean 'beanService' to allow for resolving potential circular references
22:01:07.069 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Finished creating instance of bean 'beanService'
22:01:07.070 [main] DEBUG org.springframework.context.support.ClassPathXmlApplicationContext - Unable to locate LifecycleProcessor with name 'lifecycleProcessor': using default [org.springframework.context.support.DefaultLifecycleProcessor@67205a84]
22:01:07.070 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Returning cached instance of singleton bean 'lifecycleProcessor'
22:01:07.071 [main] DEBUG org.springframework.core.env.PropertySourcesPropertyResolver - Could not find key 'spring.liveBeansView.mbeanDomain' in any property source
22:01:07.072 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Returning cached instance of singleton bean 'beanService'
----------------------------------------:I am Bean
3.源码阅读
3.1 Bean的含义
前置先解释下这个Bean的含义,因为会贯穿整个流程。
通俗地讲,Bean就是IOC的容器。如上面的例子,将BeanService注册到Spring里,那么BeanService就是Spring的里面的一个Bean。Demo里面context.getName()
就是从Spring中取出这个Bean,完成控制反转的。
所以我们的重点就是要看看Spring到底是怎么生成管理这些Bean的。
3.2 ClassPathXmlApplicationContext
启动类中,加载配置的ClassPathXmlApplicationContext
肯定就是完成IOC的核心。不知道它到底是怎么做的,怎么入手呢?
先来看看它的类
先分析下这个类图:
-
ClassPathXmlApplicationContext
类是AbstractApplicationContext
抽象类的子类 -
AbstractApplicationContext
类是ApplicaionContext
接口的实现。 -
ApplicaionContext
接口集合了非常多的内容,其中和IOC比较相关的就是ListableBeanFactory
接口和HierarchicalBeanFactory
接口 -
ListableBeanFactory
接口和HierarchicalBeanFactory
接口是继承BeanFactory
从此分析可以看出,ClassPathXmlApplicationContext
是什么,了解下ApplicaionContext
;它怎么和IOC有关,要了解BeanFactory
。
所以后面我们先来看看ApplicaionContext
与BeanFactory
。
3.3 ApplicationContext
/**
*提供应用程序配置的*接口
*当应用程序运行时,这是只读的,但如果实现支持,则可以重新加载
*用于访问应用程序组件的Bean工厂方法
*以通用方式加载文件资源的能力
*拥有向注册侦听器发布事件的能力
*解析消息的能力,支持国际化
*为了让BeanFactory拥有生命周期,实现了一些类
*/
public interface ApplicationContext extends EnvironmentCapable, ListableBeanFactory, HierarchicalBeanFactory,
MessageSource, ApplicationEventPublisher, ResourcePatternResolver { /**
* 返回application context唯一的id
String getId(); /**
*返回上下文中已经部署的应用程序的名称
*/
String getApplicationName(); /**
* 返回一个展示名称
*/
String getDisplayName(); /**
* 程序第一次加载时返回时间.
*/
long getStartupDate(); /**
* 返回父应用程序
*/
ApplicationContext getParent(); /**
* Expose AutowireCapableBeanFactory functionality for this context.
*/
AutowireCapableBeanFactory getAutowireCapableBeanFactory() throws IllegalStateException; }
ApplicationContext
从该接口的注解描述可知,ApplicationContext是整个项目的配置,Spring项目在启动或运行的时候都需要依赖到它。
其中Bean管理相关的则是ListableBeanFactory
和HierarchicalBeanFactory
。
3.4 BeanFactory
ListableBeanFactory
和HierarchicalBeanFactory
都是继承BeanFactory
的。
/**
*用于访问SpringBean容器的*接口
*根据bean的定义,工厂将返回包含对象的独立实例或者单例的对象
*HierarchicalBeanFactory是一个分层的Bean,如果实现了这个接口,所有方法都会经过父类的工厂。
* ListableBeanFactory这个接口是要实现预先加载Bean的配置,生成好实例,直接管理Bean的实例,而不是来一个请求,生成一个。
*/
public interface BeanFactory { /**
* 用来获得实例的引用,并且区分FactoryBean区分。
* 如果使用bean的名字myJndiObject获取FactoryBean,返回的是一个工厂,而不是工厂的实例;如果需要获得工厂实例,需要转义。
*/
String FACTORY_BEAN_PREFIX = "&"; /**
* 根据bean的名称,获取指定的bean实例,该实例可以是共享的,也可以是独立的.
*/
Object getBean(String name) throws BeansException; /**
* 根据bean的名称,获取指定的bean实例,该实例可以是共享的,也可以是独立的.并且增加了一个类型的检验。
*/
<T> T getBean(String name, Class<T> requiredType) throws BeansException; Object getBean(String name, Object... args) throws BeansException; /**
* 根据给定类型返回匹配的bean实例.
*/
<T> T getBean(Class<T> requiredType) throws BeansException; <T> T getBean(Class<T> requiredType, Object... args) throws BeansException; /**
* 检查spring的bean容器中是否包含有该bean
*/
boolean containsBean(String name); /**
* 判断bean的作用域是否是singleton
*/
boolean isSingleton(String name) throws NoSuchBeanDefinitionException; /**
* 判断bena的作用域是否是prototype
*/
boolean isPrototype(String name) throws NoSuchBeanDefinitionException; /**
* 检查给定名称的bean是否和指定类型匹配.更确却的说是通过检查给定的bean,返回指定类型的目标对象
*/
boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException; boolean isTypeMatch(String name, Class<?> typeToMatch) throws NoSuchBeanDefinitionException; /**
* 获取给定名称的bean的class类型
*/
Class<?> getType(String name) throws NoSuchBeanDefinitionException; /**
* 获取给定bean名称的别名,如果根据别名检索,将会获得原始bean名称。
*
String[] getAliases(String name); } BeanFactory
BeanFactory
3.5初始化IOC容器
从ClassPathXmlApplicationContext
的构造函数看,最核心的就是refresh()
函数,其他只是设一些值。
而这个refresh()
是调用父类AbstractApplicationContext
中的refresh()
。
根据它的注解可知它是加载刷新了整个context,并且加载所有Bean定义和创建对应的单例。
ClassPathXmlApplicationContext中refresh()方法:
public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)
throws BeansException { super(parent);
setConfigLocations(configLocations);
if (refresh) {
refresh();
}
}
AbstractApplicationContext中refresh()方法:
public void refresh() throws BeansException, IllegalStateException {
Object var1 = this.startupShutdownMonitor;
//启动监控标识,并且同步此对象,防止同一时间有多个
//线程加载
synchronized(this.startupShutdownMonitor) {
//初始化一些上下文参数
this.prepareRefresh();
//创建一个BeanFactory,进去后只有两个方法this.refreshBeanFactory();
//return this.getBeanFactory();
//具体的实现在它的父类AbstractRefreshableApplicationContext中 ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();
//为 此beanFactory初始化一些组件,比如:ClassLoadder等等
this.prepareBeanFactory(beanFactory); try {
//获取容器级别的后处理器,允许上下文的子类中对
//beanFactory进行后处理,在应用上下文内部beanFactory
//初始化之后可以修改beanFactory,此时所有的BeanDefinittions都
//已经被加载,但未被实例化,具体的实现在AbstractRefreshableWebApplicationContext
this.postProcessBeanFactory(beanFactory);
/**在装配完成配置后执行这些后处理器,这里涉及到一些接口
我们在开发时可以实现这些接口扩展功能,例如:
InstantiationAwareBeanPostProcessor包含两个方法
一个是在实例化前调用,一个在实例化后,初始化前调用
可以用来做特殊作用,例如代理等等
DestructionAwareBeanPostProcessor在销毁前调用
*/
this.invokeBeanFactoryPostProcessors(beanFactory);
/**
把所有的bean的后处理器排序,在bean实例化后调用
*/
this.registerBeanPostProcessors(beanFactory);
//初始化国际化信息资源
this.initMessageSource();
//初始化事件多播组件,Event触发时由Multicaster
//通知ApplicationListener
this.initApplicationEventMulticaster();
//空方法由子类扩展,可以在实例化bean之前
//做一些ApplicationContext相关的操作
this.onRefresh();
//注册事件监听器
this.registerListeners();
//单例模式的bean实例 化,初始化等等完成
this.finishBeanFactoryInitialization(beanFactory);
//applicationContext刷新完成后的处理,
//例如生命周期监听器的回调,广播通知等
this.finishRefresh();
} catch (BeansException var9) {
if(this.logger.isWarnEnabled()) {
this.logger.warn("Exception encountered during context initialization - cancelling refresh attempt: " + var9);
}
//如果加载失败,则清理环境相关的信息
this.destroyBeans();
//把applicationContext的active设置成false
this.cancelRefresh(var9);
throw var9;
} finally {
//清理一些缓存
this.resetCommonCaches();
} }
}
refresh()
里面有许多步骤,重点看下obtainFreshBeanFactory()
(重新获取一个BeanFactory)。
它里面有个核心的方法refreshBeanFactory()
obtainFreshBeanFactory:
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
refreshBeanFactory();
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
if (logger.isDebugEnabled()) {
logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
}
return beanFactory;
}
obtainFreshBeanFactory
AbstractRefreshableApplicationContext.refreshBeanFactory():
@Override
protected final void refreshBeanFactory() throws BeansException {
if (hasBeanFactory()) {
//如果存在就销毁重建
destroyBeans();
closeBeanFactory();
}
try { //创建一个DefaultListableBeanFactory作为Bean的
//管理工厂类
DefaultListableBeanFactory beanFactory = createBeanFactory();
//加载自定义的beanFactory
beanFactory.setSerializationId(getId());
customizeBeanFactory(beanFactory); //加载beanDefinition,关系这个类加载的东西比较多
//可以单分析
loadBeanDefinitions(beanFactory);
synchronized (this.beanFactoryMonitor) {
this.beanFactory = beanFactory;
}
}
catch (IOException ex) {
throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
}
}
refreshBeanFactory
如果已有BeanFactory,先删除所有Bean,然后关闭BeanFactory。
然后创建一个新的ListableBeanFactory
,上面说到这个工厂里会预先加载所有的Bean。
最后核心的就是loadBeanDefinitions(beanFactory)
,它是加载Bean的定义。实现交给了子类。
AbstractXmlApplicationContext.loadBeanDefinitions方法:
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
// Create a new XmlBeanDefinitionReader for the given BeanFactory.
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory); // Configure the bean definition reader with this context's
// resource loading environment.
beanDefinitionReader.setEnvironment(this.getEnvironment());
beanDefinitionReader.setResourceLoader(this);
beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this)); // Allow a subclass to provide custom initialization of the reader,
// then proceed with actually loading the bean definitions.
initBeanDefinitionReader(beanDefinitionReader);
loadBeanDefinitions(beanDefinitionReader);
}
loadBeanDefinitions
最后一行调用的方法:
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);
}
}
loadBeanDefinitions
用的是XmlBeanDefinitionReader
直接读配置文件加载Bean Definition(Bean定义)到BeanFactory。它里面一步步把xml的配置文件拆解读取,把一个个Bean Definition加载到BeanFactory里。
至此,已经有用一个加载好Bean Definition的BeanFactory了。
3.6 依赖注入
回到启动类中,看看怎么从context中获取bean的。
// IOC获取Bean
BeanService beanService = context.getBean(BeanService.class);
是根据类去拿bean的,当然也可以根据id。
其对应的源码实现,在DefaultListableBeanFactory
中,上文有说到对应的BeanFactory选型。
是根据类去拿bean的,当然也可以根据id。
其对应的源码实现,在DefaultListableBeanFactory
中,上文有说到对应的BeanFactory选型。
DefaultListableBeanFactory.getBean()
@Override
public <T> T getBean(Class<T> requiredType, Object... args) throws BeansException {
NamedBeanHolder<T> namedBean = resolveNamedBean(requiredType, args);
if (namedBean != null) {
return namedBean.getBeanInstance();
}
BeanFactory parent = getParentBeanFactory();
if (parent != null) {
return parent.getBean(requiredType, args);
}
throw new NoSuchBeanDefinitionException(requiredType);
}
getBean
NamedBeanHolder
是里面包含一个实例化的对象,和bean的名字。resolveNamedBean()
是怎么拿出Bean的关键。
一步步Debug,可以看到,它是遍历BeanFactory里面维护的beanDefinitionNames和manualSingletonNames成员变量,找出命中的beanName返回。
resolveNamedBean(requiredType,args)➡️getBeanNamesForType(requiredType)➡️getBeanNamesForType(type,true,true)➡️doGetBeanNamesForType(ResolvableType.forRawClass(type),includeNonSingletons, allowEagerInit)
DefaultListableBeanFactory.
doGetBeanNamesForType():
private String[] doGetBeanNamesForType(ResolvableType type, boolean includeNonSingletons, boolean allowEagerInit) {
List<String> result = new ArrayList<String>(); // Check all bean definitions.
for (String beanName : this.beanDefinitionNames) {
// Only consider bean as eligible if the bean name
// is not defined as alias for some other bean.
if (!isAlias(beanName)) {
try {
RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
// Only check bean definition if it is complete.
if (!mbd.isAbstract() && (allowEagerInit ||
((mbd.hasBeanClass() || !mbd.isLazyInit() || isAllowEagerClassLoading())) &&
!requiresEagerInitForType(mbd.getFactoryBeanName()))) {
// In case of FactoryBean, match object created by FactoryBean.
boolean isFactoryBean = isFactoryBean(beanName, mbd);
BeanDefinitionHolder dbd = mbd.getDecoratedDefinition();
boolean matchFound =
(allowEagerInit || !isFactoryBean ||
(dbd != null && !mbd.isLazyInit()) || containsSingleton(beanName)) &&
(includeNonSingletons ||
(dbd != null ? mbd.isSingleton() : isSingleton(beanName))) &&
isTypeMatch(beanName, type);
if (!matchFound && isFactoryBean) {
// In case of FactoryBean, try to match FactoryBean instance itself next.
beanName = FACTORY_BEAN_PREFIX + beanName;
matchFound = (includeNonSingletons || mbd.isSingleton()) && isTypeMatch(beanName, type);
}
if (matchFound) {
result.add(beanName);
}
}
}
catch (CannotLoadBeanClassException ex) {
if (allowEagerInit) {
throw ex;
}
// Probably contains a placeholder: let's ignore it for type matching purposes.
if (this.logger.isDebugEnabled()) {
this.logger.debug("Ignoring bean class loading failure for bean '" + beanName + "'", ex);
}
onSuppressedException(ex);
}
catch (BeanDefinitionStoreException ex) {
if (allowEagerInit) {
throw ex;
}
// Probably contains a placeholder: let's ignore it for type matching purposes.
if (this.logger.isDebugEnabled()) {
this.logger.debug("Ignoring unresolvable metadata in bean definition '" + beanName + "'", ex);
}
onSuppressedException(ex);
}
}
} // Check manually registered singletons too.
for (String beanName : this.manualSingletonNames) {
try {
// In case of FactoryBean, match object created by FactoryBean.
if (isFactoryBean(beanName)) {
if ((includeNonSingletons || isSingleton(beanName)) && isTypeMatch(beanName, type)) {
result.add(beanName);
// Match found for this bean: do not match FactoryBean itself anymore.
continue;
}
// In case of FactoryBean, try to match FactoryBean itself next.
beanName = FACTORY_BEAN_PREFIX + beanName;
}
// Match raw bean instance (might be raw FactoryBean).
if (isTypeMatch(beanName, type)) {
result.add(beanName);
}
}
catch (NoSuchBeanDefinitionException ex) {
// Shouldn't happen - probably a result of circular reference resolution...
if (logger.isDebugEnabled()) {
logger.debug("Failed to check manually registered singleton with name '" + beanName + "'", ex);
}
}
} return StringUtils.toStringArray(result);
}
doGetBeanNamesForType
然后拿着这个beanName去找具体的bean实例。这里的代码比较长,在AbstractBeanFactory
里面的doGetBean()
中实现。
大意是先尝试去找手动添加bean的单例工厂里找有没有对应的实例,没有的话就往父类beanFactory里面找,最后没有的话就生成一个。
spring中一个bean是如何加载和如何注入大致如此。
Spring源码——IOC控制反转的更多相关文章
-
零基础带你看Spring源码——IOC控制反转
本章开始来学习下Spring的源码,看看Spring框架最核心.最常用的功能是怎么实现的. 网上介绍Spring,说源码的文章,大多数都是生搬硬推,都是直接看来的观点换个描述就放出来.这并不能说有问题 ...
-
Spring源码-IOC部分-容器初始化过程【2】
实验环境:spring-framework-5.0.2.jdk8.gradle4.3.1 Spring源码-IOC部分-容器简介[1] Spring源码-IOC部分-容器初始化过程[2] Spring ...
-
Spring学习之Ioc控制反转(1)
开始之前: 1. 本博文为原创,转载请注明出处 2. 作者非计算机科班出身,如有错误,请多指正 ---------------------------------------------------- ...
-
Spring学习之Ioc控制反转(2)
开始之前: 1. 本博文为原创,转载请注明出处 2. 作者非计算机科班出身,如有错误,请多指正 ---------------------------------------------------- ...
-
Spring框架之IOC(控制反转)
[TOC] 第一章Spring框架简介 IOC(控制反转)和AOP(面向方面编程)作为Spring框架的两个核心,很好地实现了解耦合.所以,简单来说,Spring是一个轻量级的控制反转(IoC)和面向 ...
-
Spring框架中IoC(控制反转)的原理(转)
原文链接:Spring框架中IoC(控制反转)的原理 一.IoC的基础知识以及原理: 1.IoC理论的背景:在采用面向对象方法设计的软件系统中,底层实现都是由N个对象组成的,所有的对象通过彼此的合作, ...
-
Spring源码-IOC部分-Spring是如何解决Bean循环依赖的【6】
实验环境:spring-framework-5.0.2.jdk8.gradle4.3.1 Spring源码-IOC部分-容器简介[1] Spring源码-IOC部分-容器初始化过程[2] Spring ...
-
Spring源码-IOC部分-容器简介【1】
实验环境:spring-framework-5.0.2.jdk8.gradle4.3.1 Spring源码-IOC部分-容器简介[1] Spring源码-IOC部分-容器初始化过程[2] Spring ...
-
Spring源码-IOC部分-Xml Bean解析注册过程【3】
实验环境:spring-framework-5.0.2.jdk8.gradle4.3.1 Spring源码-IOC部分-容器简介[1] Spring源码-IOC部分-容器初始化过程[2] Spring ...
随机推荐
-
尝试解析js面试题(一)【转发】
解析: 1.Foo.getName(); //2 1)结果执行的是Foo对象的一个叫做getName()的属性,而1.4.5中的getName都是作为函数存在,所以可以排除1.4.5 2)剩下两个中, ...
-
Double的精度问题
/** * 自定义Math工具类 * */ public class MyMathTools { /** * 提供精确的小数位四舍五入处理. * * @param v * 需要四舍五入的数字 * @p ...
-
新版startssl 免费SSL证书申请 (实测 笔记 https http2 必要条件)
简单说明: 目前多个大型网站都实现全站HTTPS,而SSL证书是实现HTTPS的必要条件之一. StartSSL是StartCom公司旗下的.提供免费SSL证书服务并且被主流浏览器支持的免费SSL.包 ...
-
从反编译的角度去观察C#6.0
1. 自动属性初始化 (Initializers for auto-properties) 1.1 C#6.0 之前的写法 public class FirstExperience { private ...
-
VS调试时监视上一个错误代码和错误的文本描述
以前我都是用GetLastError()然后在MSDN里面查错误原因的.现在才知道有很简便的方法: 在Watch窗口选择一行,然后输入$err,hr
-
Android Weekly Notes Issue #254
Android Weekly Issue #254 April 23rd, 2017 Android Weekly Issue #254 本期内容包括: 如何用Kotlin写一个Gradle Plug ...
-
IE低版本兼容的感悟
2017-04-09 曾经折磨一代人的兼容问题,如今也在同样折磨着我们,明明可以做JS判断来避免对ie低版本的兼容,但是却还是耐心的做着兼容,你可能会问这是为什么, 我们调的不是兼容,是整整一代人的情 ...
-
食物链-HZUN寒假集训
食物链 总时间限制: 1000ms 内存限制: 65536kB 描述 动物王国中有三类动物A,B,C,这三类动物的食物链构成了有趣的环形.A吃B, B吃C,C吃A. 现有N个动物,以1-N编号.每个动 ...
-
[WC2018]州区划分
[WC2018]州区划分 注意审题: 1.有序选择 2.若干个州 3.贡献是州满意度的乘积 枚举最后一个州是哪一个,合法时候贡献sum[s]^p,否则贡献0 存在欧拉回路:每个点都是偶度数,且图连通( ...
-
python manage.py syncdb Unknown command: &#39;syncdb&#39;问题解决方法
在django1.9后的版本中,python manage.py syncdb命令修改为python manage.py migrate,执行正常. 选择sqlite可视化sqlitestudio-3 ...