spring1--IOC

一、IOC

0、概述

1)别名:依赖注入(DI)。
控制被反转之后,获得依赖对象的过程由自身管理变为了由IOC容器主动注入
在这里插入图片描述
https://www.jianshu.com/p/9fe5a3c25ab6

2) IOC的优点是什么?
应用的代码量降到最低。
容易测试,单元测试不再需要单例和JNDI查找机制。
最小的代价和最小的侵入性使松散耦合
饿汉式初始化和懒加载。

3)原理:工厂模式加反射机制。

1、IOC容器介绍

1)BeanFactory 是 Spring 框架的基础设施,面向 Spring 本身;
2)ApplicationContext 面向使用 Spring 框架的开发者。

对比

A、依赖及功能
BeanFactory:是Spring里面最底层的接口,包含了各种Bean的定义,读取bean配置文档,管理bean的加载、实例化,控制bean的生命周期,维护bean之间的依赖关系。

ApplicationContext接口作为BeanFactory的派生,还提供了更完整的框架功能:
继承MessageSource,因此支持国际化。
统一的资源文件访问方式。
提供在监听器中注册bean的事件。
同时加载多个配置文件。
载入多个(有继承关系)上下文 ,使得每一个上下文都专注于一个特定的层次,比如应用的web层。

B、加载方式
BeanFactroy采用的是延迟加载形式来注入Bean的,即只有在使用到某个Bean时(调用getBean()),才对该Bean进行加载实例化。
ApplicationContext,它是在容器启动时,一次性创建了所有的Bean。这样,在容器启动时,我们就可以发现Spring中存在的配置错误,这样有利于检查所依赖属性是否注入。 ApplicationContext启动后预载入所有的单实例Bean,通过预载入单实例bean ,确保当你需要的时候,你就不用等待,因为它们已经创建好了。

C、创建方式
BeanFactory通常以编程的方式被创建
ApplicationContext还能以声明的方式创建,如使用ContextLoader。

	public static void main(String[] args) throws Exception {
		//1 从类路径中加载XML配置文件
        ClassPathXmlApplicationContext classPathXmlApplicationContext =
                new ClassPathXmlApplicationContext("beans.xml");
         //2 从文件系统路径下加载XML配置文件
        FileSystemXmlApplicationContext fileSystemXmlApplicationContext =
                new FileSystemXmlApplicationContext("file:/src/main/resources/beans.xml");
        //3 一个标注@Configuration注解的POJO         
        AnnotationConfigApplicationContext annotationConfigApplicationContext =
                new AnnotationConfigApplicationContext(Config.class);
    }

3)WebApplicationContext

继承了ApplicationContext接口,是ApplicationContext的扩展
增加了WEB应用特性,还可以视图解析、主题解析、映射,通过ServletContext与servlet关联 
WebApplicationContext被绑定在ServletContext上(通过ContextLoaderListener绑定),可以通过RequestContextUtils获取

web容器启动原理:Spring应用的IOC容器通过tomcat的Servlet或Listener监听启动加载;Spring MVC的容器由DispatchServlet作为入口加载;Spring容器是Spring MVC容器的父容器

2、常用注解

1)@Configuration 用于标注配置类
配置 类组件之间无依赖关系用Lite模式加速容器启动过程,减少判断
配置类组件之间有依赖关系,方法会被调用得到之前单实例组件,用Full模式

 * 1、配置类里面使用@Bean标注在方法上给容器注册组件,默认也是单实例的
 * 2、配置类本身也是组件
 * 3、proxyBeanMethods:代理bean的方法
 *      Full(proxyBeanMethods = true)、【保证每个@Bean方法被调用多少次返回的组件都是单实例的】
 *      Lite(proxyBeanMethods = false)【每个@Bean方法被调用多少次返回的组件都是新创建的】
 *      组件依赖必须使用Full模式默认。其他默认是否Lite模式

2)@Scope:设置组件作用域 1.prototype:多例的2.singleton:单例的(默认值)
prototype:多实例的:ioc容器启动并不会去调用方法创建对象放在容器中。
每次获取的时候才会调用方法创建对象;
singleton:单实例的(默认值):ioc容器启动会调用方法创建对象放到ioc容器中。
以后每次获取就是直接从容器(map.get())中拿,
request:同一次请求创建一个实例
session:同一个session创建一个实例

3)@**Conditional({Condition})😗*按照一定的条件进行判断,满足条件给容器中注册Bean,传入Condition数组,,使用时需自己创建类继承Condition然后重写match方法
@Value:给属性赋值,也可以使用SpEL和外部文件的值
@PropertySource:读取外部配置文件中的k/v保存到运行环境中,结合@value使用,或使ConfigurableEnvironment获取
@Profile:结合@Bean使用,默认为default环境,可以通过命令行参数来切换环境

3、组件注册、赋值、注入

3.1 向容器注册组件四种方式

在这里插入图片描述

3.2、组件赋值

在这里插入图片描述

3.3、自动注入

@Autowried 装配优先级如下:
使用按照类型去容器中找对应的组件
按照属性名称去作为组件id去找对应的组件
@Qualifier:指定默认的组件,结合@Autowried使用
–标注在构造器:spring创建对象调用构造器创建对象
–标注在方法上:
@Primary:spring自动装配的时候,默认首先bean,配合@Bean使用
@Resource(JSR250):jsr规范:按照组件名称进行装配
@Inject(JSR330):jsr规范和@Autowired功能一致,不支持require=false;

Autowried原理:
@Autowire由AutowiredAnnotationBeanPostProcessor完成
执行过程:
postProcessAfterInstantiation方法执行,直接return null。
postProcessPropertyValues方法执行,主要逻辑在此处理。

4、springBean

4.1作用域

singleton : bean在每个Spring ioc 容器中只有一个实例。
prototype:一个bean的定义可以有多个实例。
request:每次http请求都会创建一个bean,该作用域仅在基于web的Spring ApplicationContext情形下有效。
session:在一个HTTP Session中,一个bean定义对应一个实例。该作用域仅在基于web的Spring ApplicationContext情形下有效。
global-session:在一个全局的HTTP Session中,一个bean定义对应一个实例。该作用域仅在基于web的Spring ApplicationContext情形下有效。

4.2生命周期

bean创建—初始化----销毁的过程
容器管理bean的生命周期;
我们可以自定义初始化和销毁方法;
容器在bean进行到当前生命周期的时候来调用我们自定义的初始化和销毁方法

  • 构造(对象创建)

  •  单实例:在容器启动的时候创建对象
    
  •  多实例:在每次获取的时候创建对象
    

    BeanPostProcessor.postProcessBeforeInitialization

  • 初始化:init-method

  •  对象创建完成,并赋值好,调用初始化方法。。。
    

    BeanPostProcessor.postProcessAfterInitialization

  • 销毁:destroy-method

  •  单实例:容器关闭的时候
    
  •  多实例:容器不会管理这个bean;容器不会调用销毁方法;
    

4.3 Spring框架中的单例bean是线程安全的吗?

不是,Spring框架中的单例bean不是线程安全的。
spring 中的 bean 默认是单例模式,spring 框架并没有对单例 bean 进行多线程的封装处理。

实际上大部分时候 spring bean 无状态的(比如 dao 类),所有某种程度上来说 bean 也是安全的,但如果 bean 有状态的话(比如 view model 对象),那就要开发者自己去保证线程安全了,最简单的就是改变 bean 的作用域,把“singleton”变更为“prototype”,这样请求 bean 相当于 new Bean()了,所以就可以保证线程安全了。

有状态就是有数据存储功能。
无状态就是不会保存数据。
Spring对一些Bean中非线程安全状态采用ThreadLocal进行处理,解决线程安全问题。

5、流程总括

1、Spring容器在启动的时候,先会保存所有注册进来的Bean的定义信息;

1)、xml注册bean;
2)、注解注册Bean;@Service、@Component、@Bean、xxx

在上面解析完配置文件或注解后:
BeanFactory后置处理器:管理我们的bean工厂内所有的beandefinition(未实例化)数据,可以随心所欲的修改属性。

2、Spring容器会合适的时机创建这些Bean

1)、用到这个bean的时候;利用getBean创建bean;创建好以后保存在容器中;
2)、统一创建剩下所有的bean的时候;finishBeanFactoryInitialization();

3、后置处理器;BeanPostProcessor

1)每一个bean创建完成,都会使用各种后置处理器进行处理;来增强bean的功能;
AutowiredAnnotationBeanPostProcessor:处理自动注入
AnnotationAwareAspectJAutoProxyCreator:来做AOP功能;
xxx…
增强的功能注解:
AsyncAnnotationBeanPostProcessor

2)注意:
postprocessor的创建和执行时机不是一个概念。
创建是在bean创建之前很早就有了,执行时机一般是在bean创建(构造,值空)并且赋值之后的初始化(重新赋值)前后。
但是也有在创建以前就执行的:【InstantiationAwareBeanPostProcessor】

4、事件驱动模型;

	ApplicationListener;事件监听;
	ApplicationEventMulticaster;事件派发:

ApplicationListener 是 Spring 实现事件机制的核心接口,属于观察者设计模式,一般配合 ApplicationEvent 使用。在 Spring 容器启动过程中,会在相应的阶段通过 ApplicationContext 发布 ApplicationEvent 事件,之后所有的 ApplicationListener 会被回调,根据事件类型,执行不同的操作。

5、容器加载Bean原理图

在这里插入图片描述
1、ResourceLoader从存储介质中加载Spring配置信息,并使用Resource表示这个配置文件的资源;
2、BeanDefinitionReader读取Resource所指向的配置文件资源,然后解析配置文件。配置文件中每一个解析成一个BeanDefinition对象,并保存到BeanDefinitionRegistry中;
3、容器扫描BeanDefinitionRegistry中的BeanDefinition,使用Java的反射机制自动识别出Bean工厂后处理器(实现BeanFactoryPostProcessor接口)的Bean,然后调用这些Bean工厂后处理器对BeanDefinitionRegistry中的BeanDefinition进行加工处理。
4.Spring容器从BeanDefinitionRegistry中取出加工后的BeanDefinition,并调用InstantiationStrategy着手进行Bean实例化的工作;
BeanDefinition 主要是用来描述 Bean,其存储了 Bean 的相关信息,Spring 实例化 Bean 时需读取该 Bean 对应的 BeanDefinition。BeanDefinition 整体可以分为两类,一类是描述通用的 Bean,还有一类是描述注解形式的 Bean。一般前者在 XML 时期定义 <bean‘> 标签以及在 Spring 内部使用较多,而现今我们大都使用后者,通过注解形式加载 Bean。
5.在实例化Bean时,Spring容器使用BeanWrapper对Bean进行封装,BeanWrapper提供了很多以Java反射机制操作Bean的方法,它将结合该Bean的BeanDefinition以及容器中属性编辑器,完成Bean属性的设置工作;
6.利用容器中注册的Bean后处理器(实现BeanPostProcessor接口的Bean)对已经完成属性设置工作的Bean进行后续加工,直接装配出一个准备就绪的Bean。

二、容器创建过程–扩展原理

https://www.cnblogs.com/loongk/tag/Spring/

A、BeanFactory标准初始化

1、prepareRefresh()刷新前的预处理;

	1)、initPropertySources()初始化一些属性设置;子类自定义个性化的属性设置方法;
	2)、getEnvironment().validateRequiredProperties();检验属性的合法等
	3)、earlyApplicationEvents= new LinkedHashSet<ApplicationEvent>();保存容器中的一些早期的事件;

2、obtainFreshBeanFactory();获取BeanFactory(BeanDefinitionReader解析BeanDefinition

	1)、refreshBeanFactory();刷新【创建】BeanFactory;
			创建了一个this.beanFactory = new DefaultListableBeanFactory();
			设置id;
	2)、getBeanFactory();返回刚才GenericApplicationContext创建的BeanFactory对象;
	3)、将创建的BeanFactory【DefaultListableBeanFactory】返回;

3、prepareBeanFactory(beanFactory);BeanFactory 进行功能增强;

	1)、设置BeanFactory的类加载器、支持表达式解析器...
	2)、添加部分BeanPostProcessor【ApplicationContextAwareProcessor】
	3)、设置忽略的自动装配的接口EnvironmentAware、EmbeddedValueResolverAware、xxx;
	4)、注册可以解析的自动装配;我们能直接在任何组件中自动注入:
			BeanFactory、ResourceLoader、ApplicationEventPublisher、ApplicationContext
	5)、添加BeanPostProcessor【ApplicationListenerDetector】
	6)、添加编译时的AspectJ;
	7)、给BeanFactory中注册一些能用的组件;
		environment【ConfigurableEnvironment】、
		systemProperties【Map<String, Object>】、
		systemEnvironment【Map<String, Object>】

4、postProcessBeanFactory(beanFactory);后置处理 beanFactory,交由子类实现

B、BeanFactoryPostProcessor执行和BeanPostProcessor创建

5、invokeBeanFactoryPostProcessors(beanFactory);调用已注册的 BeanFactoryPostProcessor
BeanFactoryPostProcessor:BeanFactory的后置处理器
两个接口:BeanDefinitionRegistryPostProcessor–》BeanFactoryPostProcessor

6、registerBeanPostProcessors(beanFactory); 注册 BeanPostProcessor,仅仅是注册

	不同接口类型的BeanPostProcessor;在Bean创建前后的执行时机是不一样的
	BeanPostProcessor、
	DestructionAwareBeanPostProcessor、
	InstantiationAwareBeanPostProcessor、
	SmartInstantiationAwareBeanPostProcessor、
	MergedBeanDefinitionPostProcessor【internalPostProcessors】
		
	1)、获取所有的 BeanPostProcessor;后置处理器都默认可以通过PriorityOrdered、Ordered接口来执行优先级
	2)、先注册PriorityOrdered优先级接口的BeanPostProcessor;
		把每一个BeanPostProcessor;添加到BeanFactory中
		beanFactory.addBeanPostProcessor(postProcessor);
	3)、再注册Ordered接口的
	4)、最后注册没有实现任何优先级接口的
	5)、最终注册MergedBeanDefinitionPostProcessor;
	6)、注册一个ApplicationListenerDetector;来在Bean创建完成后检查是否是ApplicationListener,如果是
		applicationContext.addApplicationListener((ApplicationListener<?>) bean);

7、initMessageSource();初始化MessageSource组件(做国际化功能;消息绑定,消息解析);

	1)、获取BeanFactory
	2)、看容器中是否有id为messageSource的,类型是MessageSource的组件
		如果有赋值给messageSource,如果没有自己创建一个DelegatingMessageSource;
			MessageSource:取出国际化配置文件中的某个key的值;能按照区域信息获取;
	3)、把创建好的MessageSource注册在容器中,以后获取国际化配置文件的值的时候,可以自动注入MessageSource;
		beanFactory.registerSingleton(MESSAGE_SOURCE_BEAN_NAME, this.messageSource);	
		MessageSource.getMessage(String code, Object[] args, String defaultMessage, Locale locale);

8、initApplicationEventMulticaster();初始化事件广播器;

	1)、获取BeanFactory
	2)、从BeanFactory中获取applicationEventMulticaster的ApplicationEventMulticaster;
	3)、如果上一步没有配置;创建一个SimpleApplicationEventMulticaster
	4)、将创建的ApplicationEventMulticaster添加到BeanFactory中,以后其他组件直接自动注入

9、onRefresh();留给子类实现的模板方法

	1)、子类重写这个方法,在容器刷新的时候可以自定义逻辑;

10、registerListeners();注册事件监听器

	1)、从容器中拿到所有的ApplicationListener
	2)、将每个监听器添加到事件派发器中;
		getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);

C、Bean创建流程

详情 https://www.cnblogs.com/loongk/p/12563304.html
在这里插入图片描述
12、finishRefresh();完成刷新过程,发布应用事件

	1)、initLifecycleProcessor();初始化和生命周期有关的后置处理器;LifecycleProcessor
		默认从容器中找是否有lifecycleProcessor的组件【LifecycleProcessor】;如果没有new DefaultLifecycleProcessor();
		加入到容器;
		
		写一个LifecycleProcessor的实现类,可以在BeanFactory
			void onRefresh();
			void onClose();	
	2)、	getLifecycleProcessor().onRefresh();
		拿到前面定义的生命周期处理器(BeanFactory);回调onRefresh();
	3)、publishEvent(new ContextRefreshedEvent(this));发布容器刷新完成事件;
	4)、liveBeansView.registerApplicationContext(this);

三、spring原理补充

1、spring解决循环依赖

以类A,B互相依赖注入为例
(简而言之,是在doCreateBean的时候,检查是否需要提前曝光,避免循环依赖)

1.根据类A的名称先从singletonObjects获取Bean实例,发现获取不到,就通过doGetBean方法开始创建Bean的流程。
2.根据A的名称找到对应的BeanDefinition,通过doCreateBean()方法创建对象,先确定类A的构造函数,然后选择一个实例化策略去实例化类A。
3.判断容器是否允许循环依赖,如果允许循环依赖,就创建一个ObjectFactory类并实现ObjectFactory接口的唯一的一个方法getObject()用于返回类A。然后将该ObjectFactory添加到singletonFactories中。
4.调用populateBean()为类A进行属性赋值,发现需要依赖类B,此时类B尚未创建,启动创建类B的流程。
	1)根据类B的名称先从singletonObjects获取Bean实例,发现获取不到,就开始通过doGetBean方法开始创建Bean的流程
	2)找到类B对应的BeanDefinition,确认B的构造函数,然后实例化B。
	3)判断容器是否允许循环依赖,创建一个ObjectFactory并实现getObject()方法,用于返回类B,并添加到singletonFactories中。
	4)调用populateBean()为类B进行属性赋值,发现需要依赖类A,调用getSingleton方法获取A:
	A现在已存在于singletonFactories中,getSingleton将A从singletonFactories方法中移除并放入earlySingletonObjects中。
	5)调用getSingleton()方法获取B:getSingleton将A从singletonFactories方法中移除并放入earlySingletonObjects中。
	6)调用initializeBean初始化bean,最后将新创建出来的类B保存到singletonObjects中
5.调用getSingleton()方法获取A,这时A已在earlySingletonObjects中了,就直接返回A
6.调用initializeBean初始化bean,最后将新创建出来的类B保存到singletonObjects中。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Spring-IOC是Spring框架的核心部分之一,它是一种设计模式,全称为Inversion of Control(控制反转)。它通过将对象的创建、依赖关系的管理和对象的生命周期交给Spring容器来实现,从而降低了组件之间的耦合度,提高了代码的可重用性和可维护性。Spring-IOC的实现主要依靠Spring容器,Spring容器是Spring框架的核心,它负责创建、管理和装配Bean对象,其中Bean是Spring框架中最基本的组件。 Spring-IOC的实现主要有两种方式:BeanFactory和ApplicationContext。其中,BeanFactory是Spring-IOC的基本实现,而ApplicationContext是BeanFactory的子接口,提供了更多高级特性。ApplicationContext是Spring框架中最常用的IOC容器,它除了提供BeanFactory的所有功能外,还提供了更多的企业级特性,例如AOP、事务管理、国际化、事件传播等。 下面是一个简单的Spring-IOC的例子,假设我们有一个UserService接口和一个UserServiceImpl实现类,我们可以通过Spring-IOC容器来创建和管理UserServiceImpl对象: 1.定义UserService接口和UserServiceImpl实现类 ```java public interface UserService { void addUser(User user); } @Service public class UserServiceImpl implements UserService { @Override public void addUser(User user) { // 添加用户的具体实现 } } ``` 2.在Spring配置文件中配置UserService实例 ```xml <bean id="userService" class="com.example.service.UserServiceImpl"/> ``` 3.在代码中获取UserService实例并使用 ```java ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); UserService userService = context.getBean("userService", UserService.class); User user = new User(); userService.addUser(user); ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值