Spring介绍
什么是spring
参考百度百科:https://baike.baidu.com/item/spring/85061
Spring框架是一个开放源代码的J2EE应用程序框架,由Rod Johnson发起,是针对bean的生命周期进行管理的轻量级容器(lightweight container)。 Spring解决了开发者在J2EE开发中遇到的许多常见的问题,提供了功能强大IOC、AOP及Web MVC等功能。Spring可以单独应用于构筑应用程序,也可以和Struts、Webwork、Tapestry等众多Web框架组合使用,并且可以与 Swing等桌面应用程序AP组合。因此, Spring不仅仅能应用于JEE应用程序之中,也可以应用于桌面应用程序以及小应用程序之中。Spring框架主要由七部分组成,分别是 Spring Core、 Spring AOP、 Spring ORM、 Spring DAO、Spring Context、 Spring Web和 Spring Web MVC。
耦合和内聚介绍
耦合性(Coupling),也叫耦合度,是对模块间关联程度的度量。
在软件工程中,耦合指的就是就是对象之间的依赖性。对象之间的耦合越高,维护成本越高。因此对象的设计应使类和构件之间的耦合最小。软件设计中通常用耦合度和内聚度作为衡量模块独立程度的标准。划分模块的一个准则就是高内聚低耦合。
内聚标志一个模块内各个元素彼此结合的紧密程度,它是信息隐蔽和局部化概念的自然扩展。内聚是从功能角度来度量模块内的联系,一个好的内聚模块应当恰好做一件事。
内聚和耦合是密切相关的,同其他模块存在高耦合的模块意味着低内聚,而高内聚的模块意味着该模块同其他模块之间是低耦合。在进行软件设计时,应力争做到高内聚,低耦合
Spring体系结构
Spring核心概念介绍
IoC(核心中的核心):Inverse of Control,控制反转。对象的创建权力由程序反转给Spring框架。
AOP:Aspect Oriented Programming,面向切面编程。在不修改目标对象的源代码情况下,增强IoC容器中Bean的功能。
DI:Dependency Injection,依赖注入。在Spring框架负责创建Bean对象时,动态的将依赖对象注入到Bean组件中!
Spring容器:指的就是IoC容器。
Spring IoC
1. 什么是IoC容器
所谓的IoC容器就是指的Spring中Bean工厂里面的Map存储结构(存储了Bean的实例)。
2. Spring框架中的工厂有哪些
ApplicationContext接口
实现ApplicationContext接口的工厂,可以获取到容器中具体的Bean对象BeanFactory工厂(是Spring框架早期的创建Bean对象的工厂接口)
实现BeanFactory接口的工厂也可以获取到Bean对象其实通过源码分析,不管是BeanFactory还是ApplicationContext,其实最终的底层BeanFactory都是DefaultListableBeanFactory
3. ApplicationContext和BeanFactory的区别
创建Bean对象的时机不同:
BeanFactory采取延迟加载,第一次getBean时才会初始化Bean。
ApplicationContext是加载完applicationContext.xml时,就创建具体的Bean对象的实例。(只对BeanDefition中描述为是单例的bean,才进行饿汉式加载)
如何创建Web环境中的IoC容器
1. ApplicationContext接口常用实现类
ClassPathXmlApplicationContext:
它是从类的根路径下加载配置文件,推荐使用这种。
FileSystemXmlApplicationContext:
它是从磁盘路径上加载配置文件,配置文件可以在磁盘的任意位置。
AnnotationConfigApplicationContext:
当我们使用注解配置容器对象时,需要使用此类来创建 spring 容器。它用来读取注解。
2. Java应用中创建IoC容器
ApplicationContext context = new ClassPathXmlApplicationContext(xml路径);
3. Web应用中创建IoC容器:(重点)
web.xml中配置ContextLoaderListener接口,并配置ContextConfigLocation参数
web容器启动之后加载web.xml,此时加载ContextLoaderListener监听器(实现了ServletContextListener接口,该接口的描述请见下面《三类八种监听器》)
ContextLoaderListener监听器会在web容器启动的时候,触发contextInitialized()方法。
contextInitialized()方法会调用initWebApplicationContext()方法,该方法负责创建Spring容器(DefaultListableBeanFactory)。
【Web三类八种监听器】:
***监听域对象的生命周期:
*ServletContextListener:
*创建:服务器启动
*销毁:服务器正常关闭
*spring ContextLoaderListener(服务器启动时负责加载Spring配置文件)
*HttpSessionListener
*创建:第一次访问request.getHttpSession();
*销毁:调用invalidate();非法关闭;过期
*ServletRequestListener
*创建:每一次访问
*销毁:响应结束
***监听域对象的属性:(添加、删除、替换)
* ServletContextAttributeListener
* HttpSessionAttributeListener
* ServletRequestAttributeListener
***监听HttpSession中JavaBean的改变:
* HttpSessionBindingListener(HttpSession和JavaBean对象的绑定和解绑)
* HttpSessionActivationListener(HttpSession的序列化,活化、钝化)
4. 创建spring IoC容器源码分析
web服务器(tomcat)启动会加载web.xml(启动ContextLoaderListener监听器)
调用ContextLoaderListener类的contextInitialized方法,创建Web环境中的Spring上下文对象
调用ContextLoader类的initWebApplicationContext方法 -- 初始化spring中的web容器
继续调用ContextLoader类的configureAndRefreshWebApplicationContext方法,该方法中调用最终初始化Bean的refresh方法。
spring容器真正初始化的方法就是refresh方法,不管是web环境还是java环境都是这个方法
创建Spring容器图示
spring容器初始化源码分析
主流程
1. 入口
ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
2. ClassPathXmlApplicationContext中,重载构造方法会进入下面的方法,而后进入refresh方法
3. AbstractApplicationContext的refresh方法:初始化spring容器的核心代码
@Override public void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { // 1、Prepare this context for refreshing. prepareRefresh(); //创建DefaultListableBeanFactory(真正生产和管理bean的容器) //加载BeanDefition并注册到BeanDefitionRegistry //通过NamespaceHandler解析自定义标签的功能(比如:context标签、aop标签、tx标签) //2、Tell the subclass to refresh the internal bean factory. ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); //3、 Prepare the bean factory for use in this context. prepareBeanFactory(beanFactory); try { //4、 Allows post-processing of the bean factory in context subclasses. postProcessBeanFactory(beanFactory); //实例化并调用实现了BeanFactoryPostProcessor接口的Bean //比如:PropertyPlaceHolderConfigurer(context:property-placeholer) //就是此处被调用的,作用是替换掉BeanDefinition中的占位符(${})中的内容 //5、 Invoke factory processors registered as beans in the context. invokeBeanFactoryPostProcessors(beanFactory); //创建并注册BeanPostProcessor到BeanFactory中(Bean的后置处理器) //比如:AutowiredAnnotationBeanPostProcessor(实现@Autowired注解功能) // RequiredAnnotationBeanPostProcessor(实现@d注解功能) //这些注册的BeanPostProcessor //6、 Register bean processors that intercept bean creation. registerBeanPostProcessors(beanFactory); //7、 Initialize message source for this context. initMessageSource(); //8、 Initialize event multicaster for this context. initApplicationEventMulticaster(); //9、 Initialize other special beans in specific context subclasses. onRefresh(); //10、 Check for listener beans and register them. registerListeners(); //创建非懒加载方式的单例Bean实例(未设置属性) //填充属性 //初始化实例(比如调用init-method方法) //调用BeanPostProcessor(后置处理器)对实例bean进行后置处理 //11、 Instantiate all remaining (non-lazy-init) singletons. finishBeanFactoryInitialization(beanFactory); //12、 Last step: publish corresponding event. finishRefresh(); } catch (BeansException ex) { if (logger.isWarnEnabled()) { logger.warn("Exception encountered during context initialization - " + "cancelling refresh attempt: " + ex); } // Destroy already created singletons to avoid dangling resources. destroyBeans(); // Reset 'active' flag. cancelRefresh(ex); // Propagate exception to caller. throw ex; } finally { // Reset common introspection caches in Spring's core, since we // might not ever need metadata for singleton beans anymore... resetCommonCaches(); } } }
图示:
创建BeanFactory流程分析
获取BeanFactory的子流程
1. 入口
主流程的第二步的obtainFreshBeanFactory()方法
//2、Tell the subclass to refresh the internal bean factory.
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
2. 进入AbstractApplicationContext类,调用refreshBeanFactory方法
3. 进入AbstractRefreshableApplicationContext类,调用createBeanFactory方法
加载解析BeanDefinition的子流程(loadDefinitions方法)
1. 入口
上图中第3步的AbstractRefreshableApplicationContext类的refreshBeanFactory方法中:loadBeanDefinitions方法
此处依次调用多个类的loadBeanDefinitions方法(AbstractXmlApplicationContext -> AbstractBeanDefinitionReader -> XmlBeanDefinitionReader),一直调用到XmlBeanDefinitionReader 类的doLoadBeanDefinitions方法
2. 进入AbstractXmlApplicationContext,调用loadBeanDefinitions方法
继续调用 loadBeanDefinitions方法
3. 进入AbstractBeanDefinitionReader,调用loadBeanDefinitions
4. 进入XmlBeanDefinitionReader,调用loadBeanDefinitions
找到doLoadBeanDefinitions方法
5. 对于doLoadDocument方法不是我们关注的重点,我们进入到该类的registerBeanDefinitions方法看看
6. 此处有两个地方是我们关注的:一个createRederContext方法,一个是DefaultBeanDefinitionDocumentReader类的registerBeanDefinitions方法,先进入createRederContext方法看看
至此,14个NamespaceHandlerResolver初始化成功。
7. 然后回到第5步,进入DefaultBeanDefinitionDocumentReader类的registerBeanDefinitions方法
8. 继续进入到该类的doRegisterBeanDefinitions方法,这是真正干活的方法
9. 继续进入到该类的parseBeanDefinitions方法
10. 两种解析方案,先看parseDefaultElement方法
重点看BeanDefinitionParserDelegate类的parseCustomElement方法
(AOP标签、tx标签的解析都是在该步骤中完成的)
11. 进入DefaultNamespaceHandlerResolver类的resolve方法
12. namespaceHandler.init();这个方法是很重要。
它实现了自定义标签到处理类的注册工作,不过NamespaceHandler是一个接口,具体的init方法需要不同的实现类进行实现。
我们通过AopNamespaceHandler了解一下init的作用,其中aop:config标签是由ConfigBeanDefinitionParser类进行处理:
13. 至此,我们了解到了xml中的aop标签都是由哪些类进行处理的了。不过init方法只是注册了标签和处理类的对应关系,那么什么时候调用处理类进行解析的呢?
我们再回到BeanDefinitionParserDelegate类的parseCustomElement方法,找到最后一行的parse方法
14. 我们看到,最后一行执行了parse方法,那么parse方法,在哪呢?
我们需要到NamespaceHandlerSupport类中去看看,它是实现NamespaceHandler接口的,并且AopNamespaceHandler是继承了NamespaceHandlerSupport类,那么该方法也会继承到AopNamespaceHandler类中。
至此,整个XML文档的解析工作,包括bean标签以及自定义标签如何解析为BeanDefinition信息的过程,我们已经了解了。
后续具体想了解哪个自定义标签的处理逻辑,可以自行去查找xxxNamespaceHandler类进行分析。
图示
创建Bean流程分析
1. 子流程入口
主流程第11步,即AbstractApplicationContext类的refresh方法
//创建非懒加载方式的单例Bean实例(未设置属性)
//填充属性
//初始化实例(比如调用init-method方法)
//调用BeanPostProcessor(后置处理器)对实例bean进行后置处理
//11、 Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory);
2. 进入此类的finishBeanFactoryInitialization方法,在最后一行找到preInstantiateSingletons方法
3. 进入DefaultListableBeanFactory类的preInstantiateSingletons方法
4. 继续跟踪下去,我们进入到了AbstractBeanFactory类的doGetBean方法,找到核心方法
5. 进入到AbstractAutowireCapableBeanFactory类的createBean方法
6. 进入此类的doCreateBean方法,有两个关注点
7. 看看initializeBean方法是如何调用BeanPostProcessor的,因为这个牵扯到我们对于AOP动态代理的理解
至此,如何创建Bean,以及AOP在哪产生代理的步骤,已经分析