栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Spring进阶(二)IOC高级应用及源码深度剖析

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Spring进阶(二)IOC高级应用及源码深度剖析

一. Spring IOC基础知识

Spring核心之IOC--快速入门_舞鹤白沙编码日志-CSDN博客

Spring核心之IOC--相关API_舞鹤白沙编码日志-CSDN博客

Spring核心之IOC--配置文件开发_舞鹤白沙编码日志-CSDN博客

Spring核心之IOC--注解开发_舞鹤白沙编码日志-CSDN博客

 (一)BeanFactory与ApplicationContext区别

     BeanFactory是Spring框架中IoC容器的顶层接⼝,它只是⽤来定义⼀些基础功能,定义⼀些基础规范,⽽ApplicationContext是它的⼀个⼦接⼝,所以ApplicationContext是具备BeanFactory提供的全部功能的。

     通常,我们称BeanFactory为SpringIOC的基础容器,ApplicationContext是容器的⾼级接⼝,⽐
BeanFactory要拥有更多的功能,⽐如说国际化⽀持和资源访问(xml,java配置类)等等。

 (二)启动IOC容器的方式 1. Java环境下启动IoC容器

(1)xml方式(两种,推荐第一种)

ClassPathXmlApplicationContext:从类的根路径下加载配置⽂件(推荐)

FileSystemXmlApplicationContext:从磁盘路径上加载配置⽂件(不推荐)

// 通过读取classpath下的xml文件来启动容器(xml模式SE应用下推荐)
        ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");

        // 不推荐使用
        //ApplicationContext applicationContext1 = new FileSystemXmlApplicationContext("文件系统的绝对路径");


        // 第一次getBean该对象
        Object accountPojo = applicationContext.getBean("accountPojo");

        AccountDao accountDao = (AccountDao) applicationContext.getBean("accountDao");

Spring之applicationContext.xml代码示例_舞鹤白沙编码日志-CSDN博客

(2)纯注解方式
AnnotationConfigApplicationContext:纯注解模式下启动Spring容器

        // 通过读取classpath下的xml文件来启动容器(xml模式SE应用下推荐)
        ApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringConfig.class);

        AccountDao accountDao = (AccountDao) applicationContext.getBean("accountDao");

        System.out.println(accountDao);

 Spring之配置类代码示例_舞鹤白沙编码日志-CSDN博客

2. Web环境下启动IoC容器

(1)从xml启动容器



 Archetype Created Web Application
 
 
 contextConfigLocation
 classpath:applicationContext.xml
 
 
 
 org.springframework.web.context.ContextLoaderListener
 

 (2)从配置类启动容器



 Archetype Created Web Application
 
 
 contextClass
 org.springframework.web.context.support.AnnotationConfigWebAppli
cationContext
 

 
 contextConfigLocation
 com.lagou.edu.SpringConfig
 
 
 
 org.springframework.web.context.ContextLoaderListener
 
(三)纯XML模式、半XML半注解模式及纯注解模式代码示例

这里将用银行转账案例来演示纯XML模式、半XML半注解模式及纯注解模式

1. 纯XML模式(不常用)

银行转账案例-Spring纯XML模式代码示例_舞鹤白沙编码日志-CSDN博客

2. 半XML半注解模式

外部引用的包采用XML方式,自定义类采用注解方式

银行转账案例-Spring半XML半注解模式代码示例_舞鹤白沙编码日志-CSDN博客

3. 纯注解模式

银行转账案例-Spring纯注解模式代码示例_舞鹤白沙编码日志-CSDN博客

二. Spring IOC高级特性 (一)lazy-Init 延迟加载

Spring高级特性之lazy-Init 延迟加载_舞鹤白沙编码日志-CSDN博客

(二)FactoryBean 和 BeanFactory

FactoryBean 和 BeanFactory_舞鹤白沙编码日志-CSDN博客

(三)后置处理器

Spring高级特效之后置处理器_舞鹤白沙编码日志-CSDN博客

三. Spring IOC源码深度剖析 (一)Spring IOC容器体系介绍

       IoC容器是Spring的核⼼模块,是抽象了对象管理、依赖关系管理的框架解决⽅案。Spring 提供了很多的容器,其中 BeanFactory 是顶层容器(根容器),不能被实例化,它定义了所有 IoC 容器 必须遵从的⼀套原则,具体的容器实现可以增加额外的功能,⽐如我们常⽤到的ApplicationContext,其下更具体的实现如 ClassPathXmlApplicationContext 包含了解析 xml 等⼀系列的内容,AnnotationConfigApplicationContext 则是包含了注解解析等⼀系列的内容。Spring IoC 容器继承体系⾮常聪明,需要使⽤哪个层次⽤哪个层次即可,不必使⽤功能⼤⽽全的。

BeanFactory 顶级接⼝⽅法栈如下:

 BeanFactory 容器继承体系:

 通过其接⼝设计,我们可以看到我们⼀贯使⽤的 ApplicationContext 除了继承BeanFactory的⼦接⼝,还继承了ResourceLoader、MessageSource等接⼝,因此其提供的功能也就更丰富了。

 下⾯我们以 ClasspathXmlApplicationContext 为例,深⼊源码分析,以探明 IoC 容器的初始化流程。

Spring之IoC 容器源码分析_舞鹤白沙编码日志-CSDN博客

(二)Spring IoC容器初始化主流程

由上分析可知,Spring IoC 容器初始化的关键环节就在 AbstractApplicationContext#refresh() ⽅法中,我们查看 refresh ⽅法来俯瞰容器创建的主体流程,主体流程下的具体⼦流程我们后⾯再来讨论。

	public Collection> getApplicationListeners() {
		return this.applicationListeners;
	}

	@Override
	public void refresh() throws BeansException, IllegalStateException {
		// 对象锁加锁
		synchronized (this.startupShutdownMonitor) {
			
			prepareRefresh();

			
			ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

			
			prepareBeanFactory(beanFactory);

			try {
				
				postProcessBeanFactory(beanFactory);

				
				invokeBeanFactoryPostProcessors(beanFactory);

				
				registerBeanPostProcessors(beanFactory);

				
				initMessageSource();

				
				initApplicationEventMulticaster();

				
				onRefresh();

				
				registerListeners();

				
				finishBeanFactoryInitialization(beanFactory);

				
				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.
				cancelRefresh(ex);

				// Propagate exception to caller.
				throw ex;
			}

			finally {
				// Reset common introspection caches in Spring's core, since we
				// might not ever need metadata for singleton beans anymore...
				resetCommonCaches();
			}
		}
	}
(三)Spring IoC容器初始化主流程中部分子流程剖析

接上部分代码中refresh()方法的执行主流程,下面就其中部分重要的子流程进行深入剖析:

1.  第二步:获取BeanFactory;默认实现是DefaultListableBeanFactory,加载BeanDefition 并注册到 BeanDefitionRegistry。

Spring IOC容器初始化之获取BeanFactory、BeanDefinition加载解析及注册子流程剖析_舞鹤白沙编码日志-CSDN博客

2. 第十一步: 初始化所有剩下的非懒加载的单例bean,初始化创建非懒加载方式的单例Bean实例(未设置属性)填充属性,初始化方法调用(比如调用afterPropertiesSet方法、init-method方法)
调用BeanPostProcessor(后置处理器)对实例bean进行后置处理

(四) lazy-init 延迟加载机制原理

普通 Bean 的初始化是在容器启动初始化阶段执⾏的,⽽被lazy-init=true修饰的 bean 则是在从容器⾥第⼀次进⾏context.getBean() 时进⾏触发。Spring 启动的时候会把所有bean信息(包括XML和注解)解析转化成Spring能够识别的BeanDefinition并存到Hashmap⾥供下⾯的初始化时⽤,然后对每个BeanDefinition 进⾏处理,如果是懒加载的则在容器初始化阶段不处理,其他的则在容器初始化阶段进⾏初始化并依赖注⼊。

以下是: DefaultListableBeanFactory类的preInstantiateSingletons方法
@Override
	public void preInstantiateSingletons() throws BeansException {
		if (logger.isTraceEnabled()) {
			logger.trace("Pre-instantiating singletons in " + this);
		}

		// Iterate over a copy to allow for init methods which in turn register new bean definitions.
		// While this may not be part of the regular factory bootstrap, it does otherwise work fine.
		// 所有bean的名字
		List beanNames = new ArrayList<>(this.beanDefinitionNames);

		// Trigger initialization of all non-lazy singleton beans...
		// 触发所有非延迟加载单例bean的初始化,主要步骤为getBean
		for (String beanName : beanNames) {
			// 合并父BeanDefinition对象
			// map.get(beanName)
			RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
			if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
				if (isFactoryBean(beanName)) {
					Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);
					// 如果是FactoryBean则加&
					if (bean instanceof FactoryBean) {
						final FactoryBean factory = (FactoryBean) bean;
						boolean isEagerInit;
						if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
							isEagerInit = AccessController.doPrivileged((PrivilegedAction)
											((SmartFactoryBean) factory)::isEagerInit,
									getAccessControlContext());
						}
						else {
							isEagerInit = (factory instanceof SmartFactoryBean &&
									((SmartFactoryBean) factory).isEagerInit());
						}
						if (isEagerInit) {
							getBean(beanName);
						}
					}
				}
				else {
					// 实例化当前bean
					getBean(beanName);
				}
			}
		}

      对于被修饰为lazy-init的bean Spring 容器初始化阶段不会进⾏ init 并且依赖注⼊,当第⼀次
进⾏getBean时候才进⾏初始化并依赖注⼊。
      对于⾮懒加载的bean,getBean的时候会从缓存⾥头获取,因为容器初始化阶段 Bean 已经
初始化完成并缓存了起来。

(五)循环依赖问题

Spring IoC循环依赖问题_舞鹤白沙编码日志-CSDN博客

四. 扩展知识点-源码的阅读 (一)源码阅读

好处:提⾼培养代码架构思维、深⼊理解框架

原则:

         定焦原则:抓主线
         宏观原则:站在上帝视⻆,关注源码结构和业务流程(淡化具体某⾏代码的编写细节)

读源码的⽅法和技巧:

        断点(观察调⽤栈)
        反调(Find Usages)在idea中读到某个方法时,鼠标右键选FindUsages去找哪里调用了该方法
        经验(spring框架中doXXX,做具体处理的地⽅)

(二)Spring源码构建 1. 下载源码(github)

spring-5.1.x_ch(含文档):   8888

2. 安装gradle 5.6.3(类似于maven) Idea 2019.1 Jdk 11.0.5

Gradle 安装配置详解_舞鹤白沙编码日志-CSDN博客

3. 导⼊(耗费⼀定时间)

Spring源码的导入_舞鹤白沙编码日志-CSDN博客

4. 编译⼯程

顺序:core-oxm-context-beans-aspects-aop

⼯程—>tasks—>compileTestJava

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/694890.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号