个人计划
1.常用设计模式-----工厂模式
2.Spring 启动流程学习
上传项目到github
1.git init
2.git add .(注意这个".",是有空格的
3.git commit -m “这里面写你的注释”
4.git remote add origin https://github.com/Suddee/zhj_design_patterns.git
5.git push -u origin master
由于新建的远程仓库是空的,所以要加上-u这个参数。然后进去GitHub test2这个仓库刷新下就会有已经上传的文件夹了。
计划1–工厂模式
学习自添加链接描述,添加链接描述
三种工厂模式的特点:添加链接描述
计划2–Spring 启动流程
学习自添加链接描述
添加链接描述
1.对于一个web应用,部署在web容器tomcat中,web容器提供一个全局的上下文环境–ServletContext(服务器会为每一个工程创建一个对象,这个对象就是ServletContext对象。这个对象全局唯一,而且工程内部的所有servlet都共享这个对象。所以叫全局应用程序共享对象。)为后面的Spring ioc容器提供宿主环境。
2.在web.xml中会提供有contextLoaderListener。在web容器启动时,会触发容器初始化事件,此时contextLoaderListener会监听到这个事件,其contextInitialized方法会被调用,会启动Spring的ioc容器,他对应的Bean定义的配置由web.xml中的context-param标签指定。
3.再 次,contextLoaderListener监听器初始化完毕后,开始初始化web.xml中配置的Servlet,这里是DispatcherServlet,这个servlet实际上是一个标准的前端控制器,用以转发、匹配、处理每个servlet请 求。DispatcherServlet上下文在初始化的时候会建立自己的IoC上下文,用以持有spring mvc相关的bean。在建立DispatcherServlet自己的IoC上下文时,会利用WebApplicationContext.ROOTWEBAPPLICATIONCONTEXTATTRIBUTE 先从ServletContext中获取之前的根上下文(即WebApplicationContext)作为自己上下文的parent上下文。有了这个 parent上下文之后,再初始化自己持有的上下文。这个DispatcherServlet初始化自己上下文的工作在其initStrategies方 法中可以看到,大概的工作就是初始化处理器映射、视图解析等。这个servlet自己持有的上下文默认实现类也是 mlWebApplicationContext。初始化完毕后,spring以与servlet的名字相关(此处不是简单的以servlet名为 Key,而是通过一些转换,具体可自行查看源码)的属性为属性Key,也将其存到ServletContext中,以便后续使用。这样每个servlet 就持有自己的上下文,即拥有自己独立的bean空间,同时各个servlet共享相同的bean,即根上下文(第2步中初始化的上下文)定义的那些 bean。
简而言之:
1.web容器启动时创建ContextLoaderListener,并回调contextInitialized()方法
2.initWebApplicationContext()开始创建并初始化Spring容器。
3.创建WebApplicationContext对象
4.refresh容器,读取xml配置,创建beans
5.spring容器引用保存到ServletContext和ContextLoaderListener中。
bean实例化
学习自添加链接描述
ApplicationContext内置一个BeanFactory对象,作为实际的bean工厂,和Bean相关业务都交给BeanFactory去处理。
定位:spring容器对应的Bean定义的配置都由web.xml的context-param标签来指定。
加载:Spring在初始化容器时,会先解析和加载所有的Bean class,如果符合要求则通过class生成BeanDefinition。
注册:在加载完所有的BeanClass后,存入BeanFactory中,有序的通过BeanDefinition信息实例化Bean,注册就是把BeanName和BeanDefinition作为键值对放在BeanFactory对象的Map中。
BeanFactory在加载一个BeanDefinition(就是一个bean-class)的时候,将相应BeanName存入BeanDefinitionNames( volatile List)属性中,在加载完所有的BeanDefinition后,执行Bean的实例化工作,此时会依据BeanDefinitionNames的顺序来有序实例化Bean,也就是说Spring容器中bean的加载和实例化是有顺序的。近似一致?(不懂)
加载BeanClass过程,零配置下Spring中Bean的加载起始于ConfigurationClassPostProcessor的postProcessBeanDefinitionRegistry(BeanDefinitionRegistry)方法
配置类可以是Spring容器的起始配置类,也可以是通过@ComponentScan扫描得到的类,也可以是通过@Import引入的类。如果这个类上含有@Configuration,@Component,@ComponentScan,@Import,@ImportResource注解中的一个,或者内部含有@Bean标识的方法,那么这个类就是一个配置类,Spring就会按照一定流程去解析这个类上的信息。
在解析的第一步会校验当前类是否已经被解析过了,如果是,那么需要按照一定的规则处理(@ComponentScan得到的Bean能覆盖@Import得到的Bean,@Bean定义的优先级最高)。
如果未解析过,那么开始解析:
1.解析内部类,查看内部类是否应该被定义成一个Bean,如果是,递归解析。
2.解析@PropertySource,也就是解析被引入的Properties文件。
3.解析配置类上是否有@ComponentScan注解,如果有则执行扫描动作,通过扫描得到的Bean Class会被立即解析成BeanDefinition,添加进beanDefinitionNames属性中。之后查看扫描到的Bean Class是否是一个配置类(大部分情况是,因为标识@Component注解),如果是则递归解析这个Bean Class。
4.解析@Import引入的类,如果这个类是一个配置类,则递归解析。
5.解析@Bean标识的方法,此种形式定义的Bean Class不会被递归解析
6.解析父类上的@ComponentScan,@Import,@Bean,父类不会被再次实例化,因为其子类能够做父类的工作,不需要额外的Bean了。
在1,3,4,6中都有递归操作,也就是在解析一个Bean Class A时,发现其上能够获取到其他Bean Class B信息,此时会递归的解析Bean Class B,在解析完Bean Class B后再接着解析Bean Class A,可能在解析B时能够获取到C,那么也会先解析C再解析B,就这样不断的递归解析。
在第3步中,通过@ComponentScan扫描直接得到的Bean Class会被立即加载入beanDefinitionNames中,但@Import和@Bean形式定义的Bean Class则不会,也就是说正常情况下面@ComponentScan直接得到的Bean其实例化时机比其他两种形式的要早。
通过@Bean和@Import形式定义的Bean Class不会立即加载,他们会被放入一个ConfigurationClass类中,然后按照解析的顺序有序排列,就是图片上的 “将配置类有序排列”。一个ConfigurationClass代表一个配置类,这个类可能是被@ComponentScan扫描到的,则此类已经被加载过了;也可能是被@Import引入的,则此类还未被加载;此类中可能含有@Bean标识的方法。
Spring在解析完了所有Bean Class后,开始加载ConfigurationClass。如果这个ConfigurationClass是被Import的,也就是说在加载@ComponentScan时其未被加载,那么此时加载ConfigurationClass代表的Bean Class。然后加载ConfigurationClass内的@Bean方法。
顺序总结:@ComponentScan > @Import > @Bean
Bean Class的结构图如上所示,A是配置类的入口,通过A能直接或间接的引入一个模块。
此时启动Spring容器,将A引入容器内。
如果A是通过@ComponentScan扫描到的,那么此时的加载顺序是:
A > D > F > B > E > G > C
如果A是通过@Import形式引入的,那么此时的加载顺讯是:
D > F > B > E > G > A > C
当然以上仅仅代表着加载Bean Class的顺序,实际实例化Bean的顺序和加载顺序大体相同,但还是会有一些差别。
Spring在通过getBean(beanName)形式实例化Bean时,会通过BeanDefinition去生成Bean对象。在这个过程中,如果BeanDefinition的DependsOn不为空,从字面理解就是依赖某个什么,其值一般是某个或多个beanName,也就是说依赖于其他Bean,此时Spring会将DependsOn指定的这些名称的Bean先实例化,也就是先调用getBean(dependsOn)方法。我们可以通过在Bean Class或者@Bean的方法上标识**@DependsOn**注解,来指定当前Bean实例化时需要触发哪些Bean的提前实例化。
当一个Bean A内部通过@Autowired或者@Resource注入Bean B,那么在实例化A时会触发B的提前实例化,此时会注册A>B的dependsOn依赖关系,实质和@DependsOn一样,这个是Spring自动为我们处理好的。
了解Spring Bean的解析,加载及实例化的顺序机制能够加深对Spring的理解,搭建更优雅简介的Spring框架。