spring源码+步骤
2023-04-14 10:25:24 0 举报
AI智能生成
登录查看完整内容
spring源码+步骤分析
作者其他创作
大纲/内容
1. 读取配置文件的文件名,可以有多个配置文件,解析的时候循环解析,把文件名记录到 容器 ApplicationContext中
可以自己写一个enviroment 继承 AbstractEnvironment(默认是StandardEnvironment对象),上下文context 有 setEnviroment方法的,并重写里面的 customizePropertySources(MutablePropertySources propertySources)方法,让容器获取根据 自定义的 环境属性(默认是获取systemEnvironment和systemProperties)。AbstractEnvironment的另一个方法就是setRequiredProperties(String... requiredProperties),提供给用户设定必要的环境属性,用户可以设置 当前环境必须包含 什么样的条件才能允许启动,context.getEnvironment().setRequiredProperties();来设置
2. 校验环境条件 prepareRefresh()
对容器做好上面的预设后,就开始解析配置文件了。解析是通过XmlBeanDefinitionReader进行的。(还有一个读取 properties文件的PropertiesBeanDefinitionReader,基本不用)给XmlBeanDefinitionReader提供3样条件1. 容器:BeanDefinitionRegistry 就是容器对象(在XmlBeanDefinitionReader眼中,这个BeanDefinitionRegistry命名就是把beanDefinition注册到哪里的意思);2. 资源解析器:新建的,把当前的上下文Context作为构造参数创建的3. 当前的上下文Context:用来接收外部定义参数的吧4. 运行环境变量:暂时不知道作用再设置是否使用XML验证的开关,默认是开启的,不用管,通过 context.setValidating();方法设置
进入XmlBeanDefinitionReader解析正式环节。先决定使用哪种配置文件进行解析,在创建上下文的时候,有几种构造方法,一种是传入classpath路径下的配置文件,保存在String[] configLocations中一种就是上图的方式,保存在 Resource[] configResources中我是使用第一种构造方法的,这里分析第一种,其实两种都是使用同一套解析逻辑的。可能有多个配置文件,所以会循环解析,通过上下文的getResources(配置文件)方法(该方法默认是从classpath*:读的)拿到配置文件信息封装在 Resource对象中。 拿到Resource之后,会进入一个检查机制(目的是不能重复解析),先把当前要解析的Resource记录到set中,解析完成之后就会删除,完成一个解析周期(set为空就是正常),倘若检查到 set不为空,说明存在重复解析问题了。(最终都是使用Resource的子类直接获取inputStream, ex: new ClassPathResource(\"路径\
解析Document对象要用到 BeanDefinitionDocumentReader(默认是DefaultBeanDefinitionDocumentReader)把XmlBeanDefinitionReader 封装到 XmlReaderContext中,在这个DocumentReader中,直接获取<beans>标签节点Element(包含其子标签所有信息),构造 BeanDefinitionParserDelegate (作用是把 <beans>标签的属性值 封装到BeanDefinitionParserDelegate的DocumentDefaultsDefinition成员变量中,并且在BeanDefinitionParserDelegate中规定了xml的schema 是 \"http://www.springframework.org/schema/beans\",在解析子节点的时候,会根据子节点的document对象中的namespaceURI的值来判断是否属于spring 的 schema还是自定义的schema)<beans>标签有哪些属性呢看上图
解析完<beans>标签的属性后,到最重要的一个属性 profile( 用来区分不同环境的配置),通过context.getEnvironment().setActiveProfiles(profile属性的值);选择哪个环境的配置,假设有两个环境配置(分别A环境,B环境),但在初始化容器的时候指定了C环境,在解析profile属性此刻,就会去找环境C的配置,找不到就直接会返回,结束整个解析过程了。
获取resource属性值的配置文件路径,如果路径包含类似 \"${user.dir}\"这种字符,会先获取系统环境变量去替换,然后判断是绝对路径还是相对路径,绝对路径,直接获取XmlReadContext中的XmlBeanDefinitionReader 去解析读取配置文件存入到容器中;相对路径,获取对应的ClassPathResource,获取XmlReadContext中的XmlBeanDefinitionReader 去解析读取配置文件存入到容器中;最后触发import的处理事件时,创建了ImportDefinition对象(默认没有实现)
解析<import>标签
解析<alias>标签
通过BeanDefinitionParserDelegate进行解析,获取 id和 name 属性的值,根据这两个属性值判断是否之前已经有同名的bean被解析过了,直接抛异常(定义两个id 或者 name 一样的bean);继续解析,把 id 属性作为beanName 创建一个新对象(BeanEntry)存入 stack中(利用 stack 后进先出特性,来排队解析吧,解析完一个就出队一个),获取class 和 parent 属性值,调用font color=\"#e74f4c\
解析<bean>标签完成后,继续解析其所有的子标签,<meta>标签,获取key 和 value 值,用BeanMetadataAttribute进行封装,让后再设置到GenericBeanDefinition中;<lookup-method>(标签有两种用法 https://blog.csdn.net/aqin1012/article/details/127472455)1. 分别获取这个父类的两个不同的子类的实例2. 每次获取到的都是同一个类的不同实例用LookupOverride进行封装,让后再设置到GenericBeanDefinition中;<replaced-method>标签(作用是 当调用 serveiceA中的getA()方法时,通过 此标签的设置,在调用方法前进行拦截,把对方法的调用转移到调用别的方法)(https://www.cnblogs.com/jiafa/p/13769365.html 注意:替换的类必须要实现spring 提供的 MethodReplacer接口)用ReplaceOverride进行封装,让后再设置到GenericBeanDefinition中;<constructor-arg>标签,获取name 、index 、value 、ref 、type 的属性值,因为该标签还有其他子标签 <list> <ref> <bean><value><props><null><set><map><array><idref>,需要解析这些标签获取对应值,最后也设置进GenericBeanDefinition中;b style=\
上面只是读取配置文件的属性和值,封装起来而已,如果其中某些属性是自定义的属性名,则再进一步按照 自定义的NamespaceHandler去处理。再把BeanDefinitionHolder 存入 容器中之前,先判断 容器是否已经存在此 beanName 的BeanDefinitionHolder,如果存在,则判断allowBeanDefinitionOverriding 开关是否开启,关闭代表不允许覆盖,直接抛异常。最后发送一个事件
解析<bean>标签
解析<bean>标签containingBean参数是什么鬼?
解析<bean>标签下的子标签
解析<beans>标签
进入解析符合spring标签schema的方法,逐一解析上图的标签
3. obtainFreshBeanFactory()创建实现beanFactory接口的核心容器(DefaultListableBeanFactory,简称 容器)并且将配置文件信息转为beanDefinition存入beanDefinitionMap中,创建容器时,会把原来的删除重新建一个。
为什么上下文通过getResource就能读到配置文件呢?通过统一资源加载器ResourceLoader进行加载(作用是根据资源类型选择不用的资源类而已),其中资源统一称为Resource,不同类型的资源都有属于自己的Resource类,如(FileSystemResource、ClassPathResource...)
主要是设置了BeanExpressionResolverPropertyEditorRegistrarApplicationContextAwareProcessor忽略了ResourceLoaderAwareApplicationEventPublisherAwareMessageSourceAwareApplicationContextAwareEnvironmentAware
4. prepareBeanFactory(beanFactory) 对容器做一些扩展设置
5. postProcessBeanFactory(beanFactory);这是一个扩展点,允许自定义的context 去继承 applicationContext来实现这个方法,对beanFactory作一些自定义操作,默认是没有实现的
7. registerBeanPostProcessors(beanFactory);给beanFactory 设置BeanPostProcessor(下面简称 bean处理器),根据BeanPostProcessor.class 类 去容器拿bean处理器,拿到bean处理器后进行类,实现了PriorityOrdered 接口的归一类, 排序后添加到容器中实现了MergedBeanDefinitionPostProcessor接口的归一类,排序后添加到容器中实现了Ordered接口的归一类,排序后添加到容器中其余归一类,不用排序,直接添加到容器中最后再添加一个 ApplicationListenerDetector bean处理器到容器
8. initMessageSource()
9. initApplicationEventMulticaster()创建事件广播器,默认SimpleApplicationEventMulticaster 存入容器中
10. onRefresh()这是一个扩展方法,留给子类去实现,默认没有实现。作用是提前初始化一些特定的bean
11. registerListeners() 注册监听器,获取上下文中提前注册的监听器 和 实现了ApplicationListener接口的监听器,注册到广播器中。然后看看有没前期需要激活的事件,把事件激活,让监听器去处理(为每个监听器独立开启一个线程去处理事件)。通过 context.publishEvent(事件类型) 进行广播,实际调用的是 getApplicationEventMulticaster().multicastEvent(earlyEvent);
factoryBean实例化
getBean(beanName);处理一下beanName ,去除\"&\"(factorybean情况),如果存在别名,则使用别名
普通bean实例化
如果有配置 beanName 是 \"conversionService\"的bean,则先实例化并且放入容器中,这个 bean 是用来进行对象属性类型转换的,如 将 字符串转 Long 等。如果有LoadTimeWeaverAware 的 bean 定义,则提前实例化该bean。冻结所有bean定义,不允许修改 (DefaultListableBeanFactory.configurationFrozen = true)。进入实例化阶段。1. 从容器中拿到解析好的bean定义信息(beanDefinition),根据beanDefinition 判断只实例化 单例 非抽象的bean;2. 判断这个beanDefinition 是 factoryBean 还是 普通bean
12. finishBeanFactoryInitialization(beanFactory) 实例化bean
外框
实现流程
BeanFactoryPostProcessor对象是 BeanFactory
BeanPostProcessor对象是Bean
在 StandardEnviromnent中包含 java环境变量和系统变量
占位符方式:${环境变量}.xml
直接配置方式:application.xml
配置方式
xml配置
@Configuration
阶段一:处理配置文件名
创建DefaultListableBeanFactory
创建XmlBeanDefinitionReader实例,并通过其提供的功能进行配置信息读取和转换
ResourcePatternResolver接口的getResources(location)方法将配置文件转成Resource对象
Resource对象的getInputStream()方法获取输入流inputStream
InputSource包装inputStream
将InputSource转化为Document
XmlBeanDefinitionReader加载多个配置文件
<import>
<alias>
<bean>
<beans>
其他标签
BeanDefinitionDocumentReader读取Document文件
用BeanDefinitionHolder包装BeanDefinition
读取配置文件信息,转成BeanDefinition
将BeanDefinition注册到BeanFactory中
阶段二:创建bean工厂
ApplicationContextAwareProcessor
LoadTimeWeaverAwareProcessor
ApplicationListenerDetector
设置BeanPostProcessor
EnvironmentAware
EmbeddedValueResolverAware
ResourceLoaderAware
ApplicationEventPublisherAware
MessageSourceAware
ApplicationContextAware
ApplicationStartupAware
忽略某些接口
BeanFactory.class
ResourceLoader.class
ApplicationEventPublisher.class
ApplicationContext.class
注册一些依赖
阶段三:预设bean工厂
执行BeanFactoryPostProcessors
阶段四:对bean工厂进行扩展
阶段五:预设BeanPostProcessor
实例化FactoryBean
实例化非FactoryBean
singletonObjects
earlySingletonObjects
singletonFactories
先依次从缓存中获取,有就直接返回
再到父容器找(有父容器并且当前容器没有该bean定义),有就重新调用doGetBean方法
拿到BeanDefinition,先创建depends-on 所设置的bean
看看一级缓存有木有,有就返回
拿到class对象,prepareMethodOverrides(方法重写处理,有待分析)
通过调用InstantiationAwareBeanPostProcessor类的方法直接返回一个代理对象,就是打断下面的实例化过程,一般不会这么做
如果有用factroy-method属性,则会调用对应的静态工厂方法进行构造
再处理自动装配
调用无参构造器创建一个无状态对象(反射方式)
调用MergedBeanDefinitionPostProcessor类的方法对BeanDefinition进行修改,一般不这么做
创建一个bean的beanFactory放入singletonFactories缓存中,供发生循环依赖时使用
单例
多例
其他
按照作用域不同进行实例化
再对bean进行执行一次 beanPostProcessor 处理
执行doGetBean方法
实例化bean
根据BeanDefinition拿到属性名和值(hasPropertyValues())
如果有实现InstantiationAwareBeanPostProcessors接口的类(AutowiredAnnotationBeanPostProcessor),如果是基于注解的自动装配,就会调用其postProcessPropertyValues()方法进行注入
获取属性类型转换器 getCustomTypeConverter
RuntimeBeanReference
RuntimeBeanNameReference
BeanDefinitionHolder
BeanDefinition
DependencyDescriptor
ManagedArray
ManagedList
ManagedSet
ManagedMap
ManagedProperties
TypedStringValue
NullBean
基本数据类型
创建BeanDefinitionValueResolver根据属性值类型选择对应处理操作
获取到属性值后,进行类型转换处理
将属性值赋给bean bw.setPropertyValues()
对bean填充属性
最终都是通过beanFactory.resolveDependency()进行依赖注入,拿到属性名 和 set方法待用,获取属性类型根据类型(String、Array、Collection、Map、引用类)进行不同的操作,对于引用类需要实例化(重新执行getBean方法),最后把得到的对象或者值 添加到 PropertyValues 属性列表中
处理实现Aware接口的类,为对应属性赋值
初始化前,执行BeanPostProcessor接口方法postProcessBeforeInitialization
执行初始化方法(如果有设置)
初始化后,执行BeanPostProcessor接口方法postProcessAfterInitialization
把bean添加到一级缓存中
初始化bean
阶段六:创建bean
ApplicationContext
ioc
spring 如何处理两个 id 相同的bean定义?
什么是ioc?
ioc原理是什么?
什么是ioc容器?有哪些?
beanFactory和applicationContext区别?
bean的生命周期是?
什么是依赖注入?
有哪些依赖注入的方式?
spring是如何加载配置文件的?
beanFactory和factoryBean的区别?
涉及哪些设计模式?
bean解析过程是否有钩子函数?
依赖注入的方式?
创建bean的方式有哪几种?
bean的范围?
如何解决循坏依赖?
问题
MethodInterceptor
BeanPostProcessor
MethodMatcher
Pointcut
PointcutAdvisor
ProxyFactoryBean:使用spring的AOP,通过spring xml配置
ProxyFactory:使用spring的AOP,通过注解方式配置
AspectJProxyFactory:使用aspectJ的AOP应用,集成aspectJ和spring的作用
分类
创建ProxyFactory 代理工厂
预设ProxyFactory
<aop:config proxy-target-class=\"false\"></aop:config>
默认:JdkDynamicAopProxy
<aop:config proxy-target-class=\"true\"></aop:config>
ObjenesisCglibAopProxy
创建AopProxy接口实现类
生成代理类
创建代理类getProxy
执行 AspectJAwareAdvisorAutoProxyCreator的postProcessAfterInitialization方法
aop
执行数据库操作要么全部成功,要么全部失败
A:原子性 Atomicity
执行完数据操作后,数据从一个状态转为另一个状态,保证在业务意义上是正确的
C:一致性 Consistency
mysql 默认 可重复读;oracle 默认 读已提交
多个事务之间互不干扰
事务A读取了事务B更新的数据,然后B回滚操作,那么A读取到的数据是脏数据
脏读
事务A多次读取同一数据,事务B在事务A多次读取的过程中,对数据作了修改并提交,导致事务A多次读取同一数据时,结果不一致
解决不可重复度需要 锁行
不可重复读
在同一个事务中,前后两次查询相同范围的时候,得到的结果不一致
解决幻读需要 锁表
幻读
事务并发带来的问题
I:隔离性 Isolation
数据被永久保存
D:持久性 Durability
ACID
事务
autowire详解: http://c.biancheng.net/spring/autowire-xml.htmlno:不采用自动装配功能,只能使用<ref> 设置;byName:如果类A中包含一个成员变量类B,则会根据成员变量名去容器查找匹配的bean进行注入;byType:会根据成员变量的类型去匹配合适的bean进行注入constructor:和byType差不多,这是应对构造函数注入的情况,通过类B和类C去构造类A,则会根据类B、类C类型去容器找匹配的bean进行构造注入defalut:表示按照<beans default-autowire>的配置
factory-method详解:目的是返回自定义的bean,如果该工厂方法返回的bean和当前类不一样,哪怕bean定义的是当前类,都会返回工厂方法返回的bean 对象(实质是替代class属性)。对于一些复杂的,需要预先设定状态的类,推荐用这种方式进行定义。注意:自动装配在此时会失效,但可以通过<constructor-arg>来进行传参
factory-bean详解:与factory-method不同,factory-method是在同一个类中创建对象,而factory-bean是用工厂模式创建对象,其指定一个工厂bean,通过factory-bean指定工厂bean,再通过factory-method绑定工厂bean中创建实例的方法,绑哪个方法就返回哪个方法创建的bean
spring
0 条评论
回复 删除
下一页