执行链的执行是按照配置文件的先后顺序执行的。当执行到handle的时候,会去请求合适的HandleAdapter去执行,而不是自己执行。执行之后返回一个ModelAndView对象。之后在请求一个ViewResolver来解析ModelAndView对象得到View对象,这个对象可以用于解析和渲染得到最终的数据。
ore Container(核心容器)包含Core、Beans、Context和Expression Language模块。Core和Beans模块是框架的基础部分,提供IoC(控制反转)和依赖注入特性。此处涉及到的基础概念是BeanFactory,其提供对于Factory模式的经典实现来消除对程序性单例模式的需要,并真正的允许从程序逻辑中分离出依赖关系和配置。
Data Access/Integration包含JDBC、ORM、OXM、JMX和Transaction模块。
JDBC模块提供一个JDBC抽象层,可以完全消除冗长的JDBC编码和解析数据库厂商特有的错误代码。此模块包含了Spring对JDBC数据访问进行封装的所有类。
ORM模块为流行的对象-关系映射API,如JPA、JDO、Hibernate、iBatis等,提供了一个交互层。利用ORM封装包,可以混合使用所有Spring提供的特性进行O/R映射。
OXM模块提供了一个对Object/XML映射实现的抽象层,Object/XML映射实现包括JAXB、Castor、XMLBeans、JiBX和XStream。
JMS(Java Messaging Service)包含了一些制造和消息消息的特性。
Transaction模块支持编程和声明性的事物管理,这些事物必须实现特定的接口,并且对所有的POJO适用。
Web上下文模块建立在应用程序上下文模块之上,为基于Web的应用程序提供上下文。同时,该模块还简化了处理多部分请求以及将请求参数绑定到域对象的工作。
Web模块提供了基础的面向Web的集成特性。例如,多文件上传、使用servlet listeners初始化IoC容器以及一个面向Web的应用上下文,还包含了Spring远程支持中Web的相关部分。
Web-Servlet模块(web.servlet.jar)包含了Spring的model-view-controller(MVC)实现。Spring的MVC框架使得模型范围内的代码和web forms之间能够清楚地分离开来,并与Spring框架的其他特性集成在一起。
Web-Struts模块提供了对Struts的支持,使得类在Spring应用中能够与一个典型的Struts Web层集成在一起。(PS:此支持在Spring3.0中是deprecated的)。
Web-Portlet模块提供了用于Portlet环境和Web-Servlet模块的MVC的实现。
AOP模块提供了一个符合AOP联盟标准的面向切面编程的实现,可以定义例如方法拦截器和切点,从而将逻辑代码分开,降低耦合性。利用source-level的元数据功能,还可以将各种行为信息合并到代码中。通过配置管理特性,该模块直接将面向切面的编程集成到框架中,进而是框架管理的对象支持AOP。同时,该模块为基于Spring的应用程序中的对象提供了事务管理服务。通过使用Spring AOP,不用依赖EJB组件,就可以将声明性事务管理集成到应用程序中。
Aspects提供了对AspectJ的集成支持。
Instrumentation提供了class instrumentation支持和classloader实现,进而可以在特定的应用服务器上使用。
beanfactory and applicationcontext的区别
IoC: 控制反转,是一种思想,表示将由程序员自动创建对象的权限交由spring框架来管理。当使用spring 的时候,对象的创建和其生命周期的维护全权交由spring来做,我们只要给出依赖的配置即可。
Spring IoC容器的设计主要基于下面的两个主要的接口
其中,ApplicationContext是BeanFactory的派生接口,但是它实现了更多,更全面的功能,也就是一种更加高级的接口,最常用的一个实现类就是ClassPathXMLApplicationContex类。因此,在绝大部分的时候使用ApplicationContext。
BeanFactory: 是Spring中最底层的接口,只提供了最简单的IoC功能,负责配置,创建和管理bean。在应用中,一般不使用 BeanFactory,而推荐使用ApplicationContext
ApplicationContext:
虽然 Spring IoC 容器的生成十分的复杂,但是大体了解一下 Spring IoC 初始化的过程还是必要的。这对于理解 Spring 的一系列行为是很有帮助的。
使用任何一个类,都需要先定义,声明,初始化这三个步骤。
Resource 定位 Spring IoC 容器先根据开发者的配置,进行资源的定位,在 Spring 的开发中,通过 XML 或者注解都是十分常见的方式,定位的内容是由开发者提供的。
BeanDefinition 的载入 这个时候只是将 Resource 定位到的信息,保存到 Bean 定义(BeanDefinition)中,此时并不会创建 Bean 的实例
BeanDefinition 的注册 这个过程就是将 BeanDefinition 的信息发布到 Spring IoC 容器中 注意:此时仍然没有对应的 Bean 的实例。
最后我们简单说说IoC是如何实现的。想象一下如果我们自己来实现这个依赖注入的功能,我们怎么来做? 无外乎:
读取标注或者配置文件,看看JuiceMaker依赖的是哪个Source,拿到类名 使用反射的API,基于类名实例化对应的对象实例 将对象实例,通过构造函数或者 setter,传递给 JuiceMaker
如果使用ApplicationContext,如果配置的bean是singleton,那么不管你有没有或想不想用它,它都会被实例化。好处是可以预先加载,坏处是浪费内存。
BeanFactory,当使用BeanFactory实例化对象时,配置的bean不会马上被实例化,而是等到你使用该bean的时候(getBean)才会被实例化。好处是节约内存,坏处是速度比较慢。多用于移动设备的开发。
没有特殊要求的情况下,应该使用ApplicationContext完成。因为BeanFactory能完成的事情,ApplicationContext都能完成,并且提供了更多接近现在开发的功能。
Spring上下文中的Bean也类似,【Spring上下文的生命周期】
其实在初始化之前的很多东西很像是一个声明,注册的一个过程,使得spring 可以对此bean进行必要的控制的过程。
1:先通过扫描指定包路径下的spring注解,比如@Component、@Service、@Lazy @Sope等spring识别的注解或者是xml配置的属性(通过读取流,解析成Document,Document)然后spring会解析这些属性,将这些属性封装到BeanDefintaion这个接口的实现类中.
比如这个配置Bean,spring也会将className、scope、lazy等这些属性装配到PersonAction对应的BeanDefintaion中.具体采用的是BeanDefinitionParser接口中的parse(Element element, ParserContext parserContext)方法,该接口有很多不同的实现类。通过实现类去解析注解或者xml然后放到BeanDefination中,BeanDefintaion的作用是集成了我们的配置对象中的各种属性,重要的有这个bean的ClassName,还有是否是Singleton、对象的属性和值等(如果是单例的话,后面会将这个单例对象放入到spring的单例池中)。spring后期如果需要这些属性就会直接从它中获取。然后,再注册到一个ConcurrentHashMap中,在spring中具体的方法就是registerBeanDefinition(),这个Map存的key是对象的名字,比如Person这个对象,它的名字就是person,值是BeanDefination,它位于DefaultListableBeanFactory类下面的beanDefinitionMap类属性中,同时将所有的bean的名字放入到beanDefinitionNames这个list中,目的就是方便取beanName;
spring的bean生命周期其实最核心的分为4个步骤,只要理清三个关键的步骤,其他的只是在这三个细节中添加不同的细节实现,也就是spring的bean生明周期:
实例化和初始化的区别:实例化是在jvm的堆中创建了这个对象实例,此时它只是一个空的对象,所有的属性为null。而初始化的过程就是讲对象依赖的一些属性进行赋值之后,调用某些方法来开启一些默认加载。比如spring中配置的数据库属性Bean,在初始化的时候就会将这些属性填充,比如driver、jdbcurl等,然后初始化连接
AbstractAutowireCapableBeanFactory.doCreateBean中会调用createBeanInstance()方法,该阶段主要是从beanDefinitionMap循环读取bean,获取它的属性,然后利用反射(core包下有ReflectionUtil会先强行将构造方法setAccessible(true))读取对象的构造方法(spring会自动判断是否是有参数还是无参数,以及构造方法中的参数是否可用),然后再去创建实例(newInstance) 复制代码
初始化主要包括两个步骤,一个是属性填充,另一个就是具体的初始化过程
PopulateBean()会对bean的依赖属性进行填充,@AutoWired注解注入的属性就发生这个阶段,假如我们的bean有很多依赖的对象,那么spring会依次调用这些依赖的对象进行实例化,注意这里可能会有循环依赖的问题。后面我们会讲到spring是如何解决循环依赖的问题 复制代码
初始化的过程包括将初始化好的bean放入到spring的缓存中、填充我们预设的属性进一步做后置处理等 复制代码
在Spring将所有的bean都初始化好之后,我们的业务系统就可以调用了。而销毁主要的操作是销毁bean,主要是伴随着spring容器的关闭,此时会将spring的bean移除容器之中。此后spring的生命周期到这一步彻底结束,不再接受spring的管理和约束。 复制代码
spring单例对象的初始化大略分为三步:
从上面讲述的单例bean初始化步骤我们可以知道,循环依赖主要发生在第一、第二步。也就是构造器循环依赖和field循环依赖。 接下来,我们具体看看spring是如何处理三种循环依赖的。
这三级缓存的作用分别是: singletonFactories : 进入实例化阶段的单例对象工厂的cache (三级缓存) earlySingletonObjects :完成实例化但是尚未初始化的,提前暴光的单例对象的Cache (二级缓存) singletonObjects:完成初始化的单例对象的cache(一级缓存)
通过查询第三级缓存可以知道哪些对象其实已经创建,可以依赖了,尽管此时还没有完成初始化的整个流程。这样就可以提前终止了依赖圈。
这样做有什么好处呢?让我们来分析一下“A的某个field或者setter依赖了B的实例对象,同时B的某个field或者setter依赖了A的实例对象”这种循环依赖的情况。A首先完成了初始化的第一步,并且将自己提前曝光到singletonFactories中,此时进行初始化的第二步,发现自己依赖对象B,此时就尝试去get(B),发现B还没有被create,所以走create流程,B在初始化第一步的时候发现自己依赖了对象A,于是尝试get(A),尝试一级缓存singletonObjects(肯定没有,因为A还没初始化完全),尝试二级缓存earlySingletonObjects(也没有),尝试三级缓存singletonFactories,由于A通过ObjectFactory将自己提前曝光了,所以B能够通过ObjectFactory.getObject拿到A对象(虽然A还没有初始化完全,但是总比没有好呀),B拿到A对象后顺利完成了初始化阶段1、2、3,完全初始化之后将自己放入到一级缓存singletonObjects中。此时返回A中,A此时能拿到B的对象顺利完成自己的初始化阶段2、3,最终A也完成了初始化,进去了一级缓存singletonObjects中,而且更加幸运的是,由于B拿到了A的对象引用,所以B现在hold住的A对象完成了初始化。