ioc控制反转
传统创建对象,需要在用到的地方new对象,如果一个类的创建方式发生改变,那么所有调用到的地方都要改动。
spring基于di依赖倒置
的思想,通过ioc控制反转
将bean的创建、初始化、销毁生命周期都交给spring容器,高层不再直接依赖底层
,而是通过第三方获取。调用类需要用到什么对象,只需要通过spring获取。
好处
提高了代码的可维护性
,对象的管理只需要一个地方配置,比如一个被很多类依赖的对象,某个配置项需要变动,那么只需要修改配置这个对象的地方,其他通过spring注入这个对象的都可以直接生效,不用重新设置。
AbstractApplicationContext
的refresh方法
spring启动执行的核心源码,bean注册逻辑
容器加载流程概述
1 预准备阶段,设定容器开始时间,标记容器已开启等
2 加载配置文件,将bean封装成beandefinion,注册到beanfactory(BeanDefinitionRegistry
)
3 设置 BeanFactory 的类加载器,添加几个 特殊的BeanPostProcessor,手动注册几个特殊的 bean。预准备beanfactory阶段。
4 注册BeanFactoryPostProcessor
5 执行BeanFactoryPostProcessor
6 注册BeanPostProcessor
7 初始化当前 ApplicationContext 的 MessageSource
8 初始化事件广播器
9 onfresh方法。子类可以实现这个类,在bean初始化前做一些处理
10 注册监听器(实现了ApplicationListener接口的类)
11 初始化bean,bean的生命周期
12 广播事件,容器初始化完成。
扫描类,为扫描到符合条件的bean创建对象,加入容器
ClassPathScanningCandidateComponentProvider
spring通过该类扫描指定包下的类和配置文件(spring通过dom框架解析xml
),扫描的类可以通过@ComponentScan配置includeFilters包含的类型和excludeFilters排除的类型,该类会通过匹配获得满足的类,通过asm
获取这些类文件的字节码信息,将类的基本信息封装成BeanDefinition
类存储到一个集合中(这时还没创建对象,类还未加载)。
ClassPathBeanDefinitionScanner
该类继承自 ClassPathScanningCandidateComponentProviderd
,除了扫描包之外会对BeanDefinition
做一些加工,设置bean的作用域,设置bean的代理模式(默认是jdk代理,如果被代理类没有实现接口则用cglib
),注册到spring容器。
后置处理器
BeanFactoryPostProcessor - bean工厂后置处理器
作用:实现这个接口,可以让spring在类被加载后,bean被实例化前,修改bean对应类的定义属性
,比如修改bean的作用域、修改类的属性值。
原理:实现工厂后置处理器的类会先被实例化,保存到AbstractApplicationContext
的beanFactoryPostProcessors列表,在创建bean会先执行该列表的执行方法。
例子:通过PropertyPlaceholderConfigurer
进行配置替换,实现了BeanFactoryPostProcessor
.
BeanPostProcessor - bean后置处理器
作用:spring提供的扩展bean的类,在bean被实例化
之后修改bean的内容。可以在后置处理器中,为该bean生成代理对象,在bean的方法前后加上日志等信息。
原理:在实例化后会通过AbstractBeanFactory
维护的beanPostProcessors列表。创建bean后,遍历所有的处理器对bean进行加工。如果符合则会经过该处理器会处理。比如方法上加了事务注解,会通过后置处理器生成代理类,返回通过代理类生成的bean,在方法初始化前后加上事务开启和提交。
运用:可以自定义类实现BeanPostProcessor类,加入spring容器,这样bean的初始化前后就会经过处理器处理。
例子:ApplicationContextAwareProcessor
用于为实现了ApplicationContextAware接口的类设置spring上下文,最先被注入到BeanPostProcessor注册器。
**需要重写两个方法:
postProcessBeforeInitialization方法,在bean被初始化之前调用。初始化前指的是bean实例化之后,如果类实现了InitializingBean接口会调用的afterPropertiesSet的进行初始化、还有init-method的初始化
。
postProcessAfterInitialization
在初始化之后被调用。
一些内置的BeanPostProcessor
CommonAnnotationBeanPostProcessor
支持@Resource注解
RequiredAnnotationBeanPostProcessor
支持@Required注解
AutowiredAnnotationBeanPostProcessor
支持@Autowired注解
PersistenceAnnotationBeanPostProcessor
支持@PersistenceUnit和@PersistenceContext注解
ApplicationContextAwareProcessor
支持注入ApplicationContext等容器对象
BeanDefinitionRegistryPostProcessor
实现该接口,spring在初始化时,会调用该接口,postProcessBeanDefinitionRegistry
方法提供了BeanDefinitionRegistry
参数,通过这个参数可以往spring中添加BeanDefinition
。
后置处理器的注册顺序
调用registerBeanPostProcessors之前就注册的>实现了PriorityOrdered>实现Ordered>没有实现排序接口。(省略了一些,具体看源码)
源码PostProcessorRegistrationDelegate::registerBeanPostProcessors
需要注意的是有一些加工类在开始注册加工类前就先注册了,比如ApplicationContextAware的加工类
和ApplicationListenerDetector
在没注册加工类前就已经加进去了。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-w3tG7Oor-1585213898176)(evernotecid://EA56B0F8-13B5-4B11-8191-1E827D9FC2A6/appyinxiangcom/22897236/ENResource/p22)]
生命周期
执行顺序如下:
- 执行beanFactoryPostProcessor类的postProcessBeanFactory方法,影响bean的实例化。
2.实例化bean
。 - 如果类实现了
BeanNameAware
接口,调用setBeanName设置bean的名字。 - 如果类实现了
BeanClassLoaderAware
接口,调用setBeanClassLoader设置当前bean的类加载器。 - 如果类实现了
BeanFactoryAware
将调用setBeanFactory方法注入beanFactory。 - 遍历所有当前容器中的
beanPostProcessor
,执行beanPostProcessor
的postProcessBeforeInitialization
,在bean开始初始化前修改这个bean的内容。 初始化bean
。如果类实现了InitializingBean
则调用afterPropertiesSet
方法,调用bean声明的init-method
方法,详见下面的初始化对象部分。- 执行
PostProcessor
的postProcessAfterInitialization
方法,在对象初始化之后修改bean的内容。 - bean的销毁,如果类实现了
DisposableBean
的destory方法,会先执行该方法。再执行bean上声明的destroy-method
方法。
提要
- 第三步开始可以参考
AbstractAutowireCapableBeanFactory
类的initializeBean
方法 - 实现beanFactoryPostProcessor、beanPostProcessor的类都会被提前实例化放到容器中。之后所有bean的创建都会经过这个两种处理器处理。
- spring内置很多beanPostProcessor类,比如实现
ApplicationContextAware
可以获取到applicationContext就是通过实现了beanPostProcessor的ApplicationContextAwareProcessor
处理器处理的
初始化对象
spring提供3种对象创建后的初始化方法
执行顺序
- 构造函数
- @PostConstruct注解的方法(jsr250规范约定的注解)
- 实现InitializingBean类的afterPropertiesSet方法
- @bean注解或xml的bean标签上init-method方法
PostConstruct
原理:通过bean后置处理器InitDestroyAnnotationBeanPostProcessor处理
,在初始化之前将类中被@PostConstruct
修饰的方法加入到待初始化的方法集合
中,将@PreDestroy
修饰的方法加到销毁前待执行的方法集合
。(包括父类的方法)。自底向上遍历到object类结束。最后遍历集合中的方法,父类的PostConstruct修饰的方法会优先执行,同个类中多个PostConstruct修饰的方法没有明确的前后顺序。(jsr250约定PostConstruct同个类的方法只能有一个修饰,spring没有遵从)
注意不要在这里依赖实现了ApplicationContextAware设置applicationContext的方法,可能拿不到。都是通过实现BeanPostProcessor
去处理bean的。
InitializingBean
原理:创建bean之后会用instanceof判断当前类是否为InitializingBean的子类,如果是则调用该类的afterPropertiesSet方法。
init-method
原理:判断完InitializingBean后会根据类加载进spring被封装成BeanDefinition
,获取类存储的init-method方法,调用。
源码参考: AbstractAutowireCapableBeanFactory##initializeBean
依赖注入、循环依赖
注入的方式
- 在set方法上注入
- 在字段上注入
- 在构造函数上注入
循环依赖与注入方式
- set方法和在字段上注入spring都能自动解决循环依赖。
- 构造函数上注入不能解决,在编译期间就会报循环依赖错误。
- 非单例Bean的循环依赖是不能解决的。
注入方式选择
- spring官方建议使用构造函数注入,因为可以在编译期就发现循环依赖的问题,且能确保bean加载后是一个完整的bean,即bean依赖的对象是被注入成功的。
- 通过构造函数注入
也并非就一定会编译期就报错循环依赖
,需要相互依赖的双方都通过构造函数注入
。假设类a通过字段注入b、b通过构造函数注入a,则如果加载顺序是先加载a,则注入b时会需要一个a对象,此时a因为非构造依赖b,可以创建一个空对象a赋给b,b创建后再赋给a,则可以创建成功。如果加载顺序反过来则不行。 - Spring不支持依赖注入static静态变量。如果需要注入静态变量,可以通过set方法注入后,手动赋值。
@Autowired
set(A o){
B.a = o;
}
或者另外放一个非静态的注入后再设置过去。
不建议这么使用,静态变量扩大了这个变量的使用范围
。
spring解决循环依赖原理
假设a依赖b
- 先创建空的对象a,
封装对象a的工厂类
,放入singletonFactories(即如果需要获取一个还没初始化的对象,可以通过这个bean工厂获取
)。 - 设置a依赖的对象b时(
设置对象依赖通过Autowired和Resource的后置处理器处理
),先创建对象b,将对象b放入singletonFactories,设置依赖时发现依赖对象a。 - 从singletonFactories中获取到对象a,将对象a设置给对象b,并将对象a放入earlySingletonObjects。
- b创建完成,获取到b对象设给对象a。初始化结束。
循环依赖遇到的坑
类a在初始化后通过applicationContext.getBeansOfType()获取某个类的所有实现类时,因为实现类b最终依赖了类a,导致循环依赖,这个时候如果配置类a懒加载类b,类b会被懒加载成一个NullBean(NullBean会被getBeansOfType忽略掉),然后类a就获取到的实现类就缺少了类b。
改成如果类b是懒加载a,b就能正常创建返回。
存储bean的类 DefaultSingletonBeanRegistry
的关键字段
singletonsCurrentlyInCreation:存放正在创建中的对象
singletonFactories:存放封装了还未初始化对象的对象工厂。
earlySingletonObjects:存放还没初始化的对象
singletonObjects:存放初始化结束的对象
源码参考: AbstractAutowireCapableBeanFactory##createBean
为什么要用singletonFactories?直接把空对象缓存到earlySingletonObjects不可以吗?
因为返回该Factory返回bean的时候,会遍历所有后置处理器中实现了SmartInstantiationAwareBeanPostProcessor
的后置处理器,即让用户可以通过实现该处理器在返回未初始化的bean时做一些修改bean内容的操作,如果直接放到earlySingletonObjects
中就少了这一步。
作用域
创建bean的时候会根据BeanDefinition
先判断作用域是否为单例模式
,不是再判断是否为prototype
模式,再不是则根据当前scope名获取Scope封装类,根据该封装类获取bean。(代码逻辑在AbstractBeanFactory
的doGetBean
方法)
singleton:单例,spring为该类在spring容器中只会为该类创建一个对象。单例用beanName做map的key,对象作为value。DefaultListableBeanFactory
类的
preInstantiateSingletons
负责初始化单例对象,通过调用DefaultSingletonBeanRegistry
的getSingleton
方法获取单例对象,该类通过map类型singletonObjects
字段存储已经生成的单例对象。
prototype
该类型每次客户端获取对象都会重新创建对象,即对象不共享。
request
对象只在当前http请求共享,每次新的请求都会重新创建。
原理:
- spring在开始创建bean之前会往beanFactory注册request、session作用域获取对象的
封装类Scope类的子类
。代码逻辑在(WebApplicationContextUtils
类的registerWebApplicationScopes
方法) - request作用域用封装类的是
RequestScope
类,继承自AbstractRequestAttributesScope
,通过get方法获取对象。该方法通过类的ThreadLocal获取
当前线程
中存储的RequestAttributes
类。request作用域通过RequestAttributes
保存的HttpServletRequest
设置存取属性值。 - RequestContextHolder中存储的
RequestAttributes是什么时候设置进ThreadLocal的?这就涉及到springmvc中的FrameworkServlet
类,该类继承自httpServlet,在接受http请求的时候会将httpServletRequest
封装成ServletRequestAttributes
存放到 RequestContextHolder中。
session
对象只在session共享,不同的session使用不同的bean。
原理:与request相似,不同的是session用的scope是SessionScope,存取对象时会通过ServletRequestAttributes存储的HttpServletRequest获取到session,通过session进行对象存取。
可能存在的问题:如果使用session作用域,在客户端第一次访问时,如果出现并发访问的情况,由于session的生成需要根据cookie携带的sessionId判断是否需要创建
,第一次访问由于请求都没带上sessionId可能会导致两个请求都去创建bean,存放到各自的session对象中。由于最终cookie只有一个sessionId,所以会有一个请求创建的对象丢失。如果对象中包含一个计数器之类的参数,可能会存在计数丢失。
application
将对象存储在ServletContext,在整个web应用程序中是唯一的。存取操作通过ServletContextScope
进行操作,与session方式一样会将该封装类注册到beanFactory。
proxyMode:session、requeset、
application这三种作用域由于并不是在spring容器加载时创建,因此如果单例对象依赖这三种作用域的对象时,是拿不到对象的。因此需要在@scope注解
指定代理模式proxyMode,当单例对象依赖这三种作用域的独享时,会创建依赖的类的代理对象,代理对象会在用户发起请求时再去获取真实的对象。
@Autowired、@Resource
Autowired
按照类型装配bean,如果同个类型有两个bean,需要用@Qualifie执行bean的。由spring提供的注解。
原理:创建对象后,为对象赋值时,通过AutowiredAnnotationBeanPostProcessor
寻找到用@Autowired注解修饰的字段,利用反射通过将实例化好的对象
赋值到对象上。主要逻辑在AbstractAutowireCapableBeanFactory##populateBean
,调用了AutowiredAnnotationBeanPostProcessor##postProcessPropertyValues
.最终是该后置处理器的内部类AutowiredFieldElement
的inject方法负责注入属性,通过beanFactory进行getBean。
[源码解析参考](https://www.nmyswls.com/article/103/1)
Resource
默认按照名字装配bean,如果名字匹配不到则按照类型匹配。由JSR-250规范的注解。如果指定了名字,则只会按照名字装配。
其中ScopeProxyMode.INTERFACES表示使用jdk代理,ScopeProxyMode.TAGER_CLASS表示使用cglib代理,当该被指定为这三种作用域的类不是接口,则只能用cglib。
原理:通过CommonAnnotationBeanPostProcessor
后置处理器处理,与Autowired类似。
BeanFactory FactoryBean
BeanFactory 和 FactoryBean 是不一样的,BeanFactory
是用于生成bean的工厂类,ApplicationContext
就是BeanFactory
的实现类。而FactoryBean
是一种特殊的bean,实现了该接口的的类,spring在创建bean时会进行判断是否为FactoryBean
类,如果是会通过调用该类的getObject
方法获取bean,并将该bean放入spring容器。
通过这一个点,可以实现FactoryBean
接口,在getObject
方法返回一个自定义的代理对象,或者返回另外一个类的实例。比如mybatis的…
如果想要获取FactoryBean本身的bean,只要通过在bean名字前面加上&
,则会返回FactoryBean本身,而不是通过getObject获取bean。比如context.getBean("&beanName"),getBean方法判断包含前缀则会返回bean本身,不包含则通过getObject获取bean。
原理:
- 注入bean时,会把实现了
FactoryBean
的类及上&
前缀注入到bean容器中。源码逻辑在DefaultListableBeanFactory::preInstantiateSingletons
. - 获取bean时,如果是
FactoryBean
类型,判断beanName是否包含&
,包含则返回bean本身,不包含则通过getObject
方法获取。源码逻辑在AbsractBeanFactory::doGetBean
方法,getObjectForBeanInstance
方法**
总结
创建空对象放入缓存工厂(获取一次后放入早期单例缓存) -> 通过Autowired、Resource的后置处理器扫描类中待注入的对象 -> 通过bean工厂获取对象(绕回第一步) -> 通过反射设置对象的值 -> 放入单例对象缓存。
spring使用的设计模式
简单工厂
BeanFactory
。使用ConcurrentHashMap管理spring管理的对象。
- 通过简单工厂来生成bean,避免直接new创建,松散耦合
- 统一管理每个bean的生命周期,在spring启动的每个阶段为bean做一些处理。
工厂方法
FactoryBean
。实现这个接口都要重写getObject()方法,spring的getBean()在获取该bean时,会调用这个bean的getObject方法获取bean.比如spring mybatis的sqlSessionFactoryBean。
通过这个可以自定义bean的生成方式,比如返回代理的bean等。
单例模式
spring默认创建bean的方式都是单里模式,通过双重判断锁生成。
保证bean不会重复创建,并提供全局访问点。
适配器模式
HandlerAdapter
.springmvc handler的适配器(HandlerMethod
)。通过support方法决定是否匹配这个handler,匹配就可以用这个适配器处理handler。
意义:方便外部扩展,外部只要实现一个适配器就可以对特定的HandlerMethod进行适配做处理。也可以自己实现一个handler和对应的
HandlerAdapter。
每个HandlerMethod都对应了一个controller的一个方法。
比如通过@RequestMapping标注的方法都是由RequestMappingHandlerAdapter
进行匹配处理。
装饰器模式
BeanWrapper bean的包装类,spring用这个类的实现类来对bean进行获取和设值。反射调用bean的set get方法设置和获取属性值。
包装类提供了增强功能,提供了统一的操作bean的方法。因为spring支持用xml设置值,spring只知道属性名就可以包装这个bean,对这个bean进行设置。
代理模式
代理类。比如bean添加事务注解时,之所以操作方法会加上事务,就是因为代理对象对bean进行了增强。加上了事务提交等操作。
spring支持cglib和jdk的动态代理。
观察者
ApplicationListener 启动停止进行事件通知
ApplicationListener 监听器
监听spring一些事件,ApplicationContext是事件源,ApplicationEventMulticaster事件广播类
原理是spring在启动过程中,每经历一些阶段就会通过广播类通知所有的监听器,监听器可以在事件进行到某个阶段来做不同的处理。
策略模式
Resource接口是访问资源的接口,定义了访问资源的通用方法。比如UrlResource、InputStreamResource、ByteArrayResource等。
策略模式有利于bean的扩展
模版方法
可以抽出公用代码,子类分别实现。