Spring IoC原理理解
2017-12-21 16:43
281 查看
IoC与DI
Java IoC简单例子
IoC源码
DI,Dependency Injection,依赖注入。在系统运行中,动态的向某个对象提供它所需要的其他对象。
控制的什么被反转了?就是:获得依赖对象的方式反转了。
那么IoC是如何做的呢?有点类似婚介所,一个婚介所有很多男男女女的个人信息,我可以向婚介所提出要求,比如长得像高圆圆,身材像林熙蕾等等,然后婚介所就会按照我们的要求,提供符合要求的一个mm信息,我们只需要去和mm谈朋友就好了。如果婚介所给我们的人选不符合要求,我们就提出不交往。整个过程不再由我自己来控制,而是由婚介所类控制中间环节。Spring开发也是如此,所有的类都会在Spring容器中登记,告诉Spring你是什么,你需要什么,然后Spring会在系统运行到适当的时候,把你需要的东西主动给你,同时也把你交给其他需要的你的类。所有类的创建、销毁都由Spring来控制,也就是说控制对象生存周期不再是引用它的对象,而是Spring。对于某个对象而言,以前是它控制其他对象,现在是所有对象都被spring控制,所以就叫控制反转。
接着创建动物实现类Cat
然后创建配置文件applicationContext.xml以及配置Cat类
最后创建测试类TestAnimal
执行main方法控制台结果
I am kitty!
(1)FileSystemXmlApplicationContext继承自AbstractXmlApplicationContext;
(2)继承AbstractRefreshableConfigApplicationContext;
(3)继承AbstractRefreshableApplicationContext;
(4)继承AbstractApplicationContext;
(5)实现接口ConfigurableApplicationContext;
(6)继承接口ApplicationContext;
(7)继承接口ListableBeanFactory,HierarchicalBeanFactory;
(8)继承接口BeanFactory。
由此可以看出,接口BeanFactory就是Spring源码的入口。
接下来关注FileSystemXmlApplicationContext类构造方法
然后调用带有多个参数的构造方法
S1.调用父类容器的构造方法为容器设置好Bean资源加载器
super(parent);
S1-1最终调用AbstractApplicationContext类构造方法AbstractApplicationContext,代码如下:
this.parent = parent;复制对象
this.resourcePatternResolver = getResourcePatternResolver();解析资源模式
this.environment = createEnvironment();创建环境参数
S1-1-1其实getResourcePatternResolver()方法就是保存了resourceLoader对象,后续解析资源路径会用到的
S1-1-1-1实现类PathMatchingResourcePatternResolver的构造方法代码如下:
S1-1-2加载环境配置createEnvironment()方法
类StandardEnvironment 继承了AbstractEnvironment 类,代码如下:
AbstractEnvironment类的构造方法加载了log信息设置,代码如下:
在这里整个S1流程走完了,整理一下步骤:
(1)调用父类构造方法;
(2)保存了一个ApplicationContext容器对象
(3)保存了一个ResourceLoader对象
(4)加载配置参数
S2.告诉读取器Bean定义资源文件的定位路径
setConfigLocations(configLocations);
调用了父类AbstractRefreshableConfigApplicationContext的setConfigLocations方法,代码如下:
然后调用了resolvePath方法,代码如下:
接下来调用了接口PropertyResolver的resolveRequiredPlaceholders方法,
实现类AbstractPropertyResolver实现了该方法,代码如下:
创建了占位符帮助对象createPlaceholderHelper,然后执行了解析占位符方法doResolvePlaceholders,代码如下:
具体的解析代码就不展开了。
S3.刷新
refresh();
调用了父类AbstractApplicationContext的refresh()方法,代码如下:
S3-1准备这个上下文刷新。
prepareRefresh();
调用了
Java IoC简单例子
IoC源码
IoC与DI
IoC,Inversion of Control,控制倒转。这是spring的核心,贯穿始终。所谓IoC,对于spring框架来说,就是由spring来负责控制对象的生命周期和对象间的关系。DI,Dependency Injection,依赖注入。在系统运行中,动态的向某个对象提供它所需要的其他对象。
控制的什么被反转了?就是:获得依赖对象的方式反转了。
IoC举例
如何找女朋友的?常见的情况是,我们到处去看哪里有长得漂亮身材又好的mm,然后打听她们的兴趣爱好、微信号、qq号、电话号等等,想办法认识她们,投其所好送其所要,然后嘿嘿……这个过程是复杂深奥的,我们必须自己设计和面对每个环节。传统的程序开发也是如此,在一个对象中,如果要使用另外的对象,就必须得到它(自己new一个,或者从JNDI中查询一个),使用完之后还要将对象销毁(比如Connection等),对象始终会和其他的接口或类藕合起来。那么IoC是如何做的呢?有点类似婚介所,一个婚介所有很多男男女女的个人信息,我可以向婚介所提出要求,比如长得像高圆圆,身材像林熙蕾等等,然后婚介所就会按照我们的要求,提供符合要求的一个mm信息,我们只需要去和mm谈朋友就好了。如果婚介所给我们的人选不符合要求,我们就提出不交往。整个过程不再由我自己来控制,而是由婚介所类控制中间环节。Spring开发也是如此,所有的类都会在Spring容器中登记,告诉Spring你是什么,你需要什么,然后Spring会在系统运行到适当的时候,把你需要的东西主动给你,同时也把你交给其他需要的你的类。所有类的创建、销毁都由Spring来控制,也就是说控制对象生存周期不再是引用它的对象,而是Spring。对于某个对象而言,以前是它控制其他对象,现在是所有对象都被spring控制,所以就叫控制反转。
Java IoC简单例子
首先创建动物接口Animalpackage com.feiniu.springframework.test; public interface Animal { public void say(); }
接着创建动物实现类Cat
package com.feiniu.springframework.test; public class Cat implements Animal{ private String name; @Override public void say() { System.out.println("I am " + name + "!"); } public String getName() { return name; } public void setName(String name) { this.name = name; } }
然后创建配置文件applicationContext.xml以及配置Cat类
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:task="http://www.springframework.org/schema/task" xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-3.1.xsd" default-autowire="byName"> <bean id="animal" class="com.feiniu.springframework.test.Cat"> <property name="name" value="kitty" /> </bean> </beans>
最后创建测试类TestAnimal
package com.feiniu.springframework.test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.FileSystemXmlApplicationContext; public class TestAnimal { public static void main(String[] args) { ApplicationContext context = new FileSystemXmlApplicationContext( "applicationContext.xml"); Animal animal = (Animal) context.getBean("animal"); animal.say(); } }
执行main方法控制台结果
I am kitty!
IoC源码
我们来看看控制台输出的I am kitty背后spring究竟做了哪些事情。作为开发人员一般很熟悉applicationContext.xml,关于测试类TestAnimal有使用了FileSystemXmlApplicationContext,那么我们就从这里入手,源码调用类步骤如下:(1)FileSystemXmlApplicationContext继承自AbstractXmlApplicationContext;
(2)继承AbstractRefreshableConfigApplicationContext;
(3)继承AbstractRefreshableApplicationContext;
(4)继承AbstractApplicationContext;
(5)实现接口ConfigurableApplicationContext;
(6)继承接口ApplicationContext;
(7)继承接口ListableBeanFactory,HierarchicalBeanFactory;
(8)继承接口BeanFactory。
由此可以看出,接口BeanFactory就是Spring源码的入口。
接下来关注FileSystemXmlApplicationContext类构造方法
public FileSystemXmlApplicationContext(String configLocation) throws BeansException { this(new String[] {configLocation}, true, null); }
然后调用带有多个参数的构造方法
public FileSystemXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent) throws BeansException { //S1.调用父类容器的构造方法为容器设置好Bean资源加载器 super(parent); //S2.告诉读取器Bean定义资源文件的定位路径 setConfigLocations(configLocations); //S3.刷新 if (refresh) { refresh(); } }
S1.调用父类容器的构造方法为容器设置好Bean资源加载器
super(parent);
S1-1最终调用AbstractApplicationContext类构造方法AbstractApplicationContext,代码如下:
public AbstractApplicationContext(ApplicationContext parent) { this.parent = parent; this.resourcePatternResolver = getResourcePatternResolver(); this.environment = createEnvironment(); }
this.parent = parent;复制对象
this.resourcePatternResolver = getResourcePatternResolver();解析资源模式
this.environment = createEnvironment();创建环境参数
S1-1-1其实getResourcePatternResolver()方法就是保存了resourceLoader对象,后续解析资源路径会用到的
protected ResourcePatternResolver getResourcePatternResolver() { return new PathMatchingResourcePatternResolver(this); }
S1-1-1-1实现类PathMatchingResourcePatternResolver的构造方法代码如下:
public PathMatchingResourcePatternResolver(ResourceLoader resourceLoader) { Assert.notNull(resourceLoader, "ResourceLoader must not be null"); this.resourceLoader = resourceLoader; }
S1-1-2加载环境配置createEnvironment()方法
protected ConfigurableEnvironment createEnvironment() { return new StandardEnvironment(); }
类StandardEnvironment 继承了AbstractEnvironment 类,代码如下:
public class StandardEnvironment extends AbstractEnvironment { public static final String SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME = "systemEnvironment"; public static final String SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME = "systemProperties"; @Override protected void customizePropertySources(MutablePropertySources propertySources) { propertySources.addLast(new MapPropertySource(SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME, getSystemProperties())); propertySources.addLast(new SystemEnvironmentPropertySource(SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, getSystemEnvironment())); } }
AbstractEnvironment类的构造方法加载了log信息设置,代码如下:
public AbstractEnvironment() { String name = getClass().getSimpleName(); if (this.logger.isDebugEnabled()) { this.logger.debug(format("Initializing new %s", name)); } customizePropertySources(this.propertySources); if (this.logger.isDebugEnabled()) { this.logger.debug(format( "Initialized %s with PropertySources %s", name, this.propertySources)); } }
在这里整个S1流程走完了,整理一下步骤:
(1)调用父类构造方法;
(2)保存了一个ApplicationContext容器对象
(3)保存了一个ResourceLoader对象
(4)加载配置参数
S2.告诉读取器Bean定义资源文件的定位路径
setConfigLocations(configLocations);
调用了父类AbstractRefreshableConfigApplicationContext的setConfigLocations方法,代码如下:
public void setConfigLocations(String[] locations) { if (locations != null) { Assert.noNullElements(locations, "Config locations must not be null"); this.configLocations = new String[locations.length]; for (int i = 0; i < locations.length; i++) { //将字符串解析为路径 this.configLocations[i] = resolvePath(locations[i]).trim(); } } else { this.configLocations = null; } }
然后调用了resolvePath方法,代码如下:
/** * 解决给定的路径,用相应的替换占位符系统属性值,如有必要。应用于配置位置。 */ protected String resolvePath(String path) { return this.getEnvironment().resolveRequiredPlaceholders(path); }
接下来调用了接口PropertyResolver的resolveRequiredPlaceholders方法,
实现类AbstractPropertyResolver实现了该方法,代码如下:
public String resolveRequiredPlaceholders(String text) throws IllegalArgumentException { if (strictHelper == null) { strictHelper = createPlaceholderHelper(false); } return doResolvePlaceholders(text, strictHelper); }
创建了占位符帮助对象createPlaceholderHelper,然后执行了解析占位符方法doResolvePlaceholders,代码如下:
private String doResolvePlaceholders(String text, PropertyPlaceholderHelper helper) { return helper.replacePlaceholders(text, new PlaceholderResolver() { public String resolvePlaceholder(String placeholderName) { return getProperty(placeholderName); } }); }
具体的解析代码就不展开了。
S3.刷新
refresh();
调用了父类AbstractApplicationContext的refresh()方法,代码如下:
public void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { // Prepare this context for refreshing. // 准备这个上下文刷新。 prepareRefresh(); // Tell the subclass to refresh the internal bean factory. // 告诉子类刷新内部的bean工厂。 ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // Prepare the bean factory for use in this context. // 准备在这种情况下使用的bean工厂。 prepareBeanFactory(beanFactory); try { // Allows post-processing of the bean factory in context subclasses. // 允许在上下文子类中对bean工厂进行后处理。 postProcessBeanFactory(beanFactory); // Invoke factory processors registered as beans in the context. // 在上下文中调用注册为bean的工厂处理器。 invokeBeanFactoryPostProcessors(beanFactory); // Register bean processors that intercept bean creation. // 注册拦截bean创建的bean处理器。 registerBeanPostProcessors(beanFactory); // Initialize message source for this context. // 初始化此上下文的消息源。 initMessageSource(); // Initialize event multicaster for this context. // 初始化此上下文的事件多播器。 initApplicationEventMulticaster(); // Initialize other special beans in specific context subclasses. // 在特定的上下文子类中初始化其他特殊的bean。 onRefresh(); // Check for listener beans and register them. // 检查监听器bean并注册它们。 registerListeners(); // Instantiate all remaining (non-lazy-init) singletons. // 实例化所有剩下的(非惰性初始化)单例。 finishBeanFactoryInitialization(beanFactory); // Last step: publish corresponding event. // 最后一步:发布相应的事件。 finishRefresh(); } catch (BeansException ex) { if (logger.isWarnEnabled()) { logger.warn("Exception encountered during context initialization - " + "cancelling refresh attempt: " + ex); } // Destroy already created singletons to avoid dangling resources. // 销毁已经创建的单例资源。 destroyBeans(); // Reset 'active' flag. // 重置“active”标志为false。 cancelRefresh(ex); // Propagate exception to caller. // 传播异常给调用者。 throw ex; } } }
S3-1准备这个上下文刷新。
prepareRefresh();
调用了
/** * 准备这个上下文进行刷新,设置其启动日期和活动标志以及执行任何财产来源的初始化。 */ protected void prepareRefresh() { this.startupDate = System.currentTimeMillis(); synchronized (this.activeMonitor) { this.active = true; } if (logger.isInfoEnabled()) { logger.info("Refreshing " + this); } // 在上下文环境中初始化任何占位符属性源 initPropertySources(); // 验证标记为必需的所有属性是可解析的 // 请参阅ConfigurablePropertyResolver#setRequiredProperties getEnvironment().validateRequiredProperties(); }
/** * 用实际实例替换任何存根属性源。 * @请参阅 org.springframework.core.env.PropertySource.StubPropertySource * @请参阅 org.springframework.web.context.support.WebApplicationContextUtils#initServletPropertySources */ protected void initPropertySources() { // For subclasses: do nothing by default. // 对于子类:默认情况下不做任何事情。 }
相关文章推荐
- 通俗化理解Spring3 IoC的原理和主要组件(spring系列知识二总结)
- 初步理解spring ioc原理(读完可自己实现依赖注入部分的spring框架)
- 最好理解的: spring ioc原理讲解,强烈推荐!
- spring学习总结 - ioc原理及ID理解
- Spring的IoC原理(通俗理解)
- SPRING原理解析-Ioc容器初始化
- spring ioc annotation 理解
- 深入理解 Spring 事务原理
- 转:Spring技术内幕——深入解析Spring架构与设计原理(二)IOC实现原理
- Spring Ioc 原理小结
- 番外 01:Spring IoC 实现原理简析,Java的反射机制,通过类名创建对象
- 深入理解Spring的两大特征(IOC和AOP)
- 谈谈对Spring IOC的理解
- JAVA中的一些概念, IOC DI spring 讲的很好很容易理解
- spring四种依赖注入方式 ( 依赖注入DI+ 控制反转IOC的原理)
- spring ioc原理(看完后大家可以自己写一个spring)
- 谈谈对Spring IOC的理解
- 【spring】--IoC 之理解
- 【Spring】Spring的IOC(控制反转)/DI(依赖注入)原理(一):用到“反射”编程
- 谈谈对Spring IOC的理解