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