1.基本概念
1 spring是开源(免费的)的轻量级框架(没有太多依赖)
2 spring核心主要两部分:
(1)aop:面向切面编程,扩展功能不是修改源代码实现
(2)ioc:控制反转, 比如有一个类,在类里面有方法(不是静态的方法),调用类里面的方法,创建类的对象,使用对象调用方法,创建类对象的过程,需要new出来对象,而IOC把对象的创建不是通过new方式实现,而是交给spring配置创建类对象.
3 spring是一站式框架
(1)spring在javaee三层结构中,每一层都提供不同的解决技术
- web层:springMVC
- service层:spring的ioc
- dao层:spring的jdbcTemplate
4 spring版本
(1)hibernate5.x
(2)spring4.x
2.Spring IOC
2.1.具体概念
Ioc—Inversion of Control,即“控制反转”,不是什么技术,而是一种设计思想。在Java开发中,Ioc意味着将你设计好的对象交给容器控制,而不是传统的在你的对象内部直接控制。如何理解好Ioc呢?理解好Ioc的关键是要明确“谁控制谁,控制什么,为何是反转(有反转就应该有正转了),哪些方面反转了”,那我们来深入分析一下:
●谁控制谁,控制什么:传统Java SE程序设计,我们直接在对象内部通过new进行创建对象,是程序主动去创建依赖对象;而IoC是有专门一个容器来创建这些对象,即由Ioc容器来控制对 象的创建;谁控制谁?当然是IoC 容器控制了对象;控制什么?那就是主要控制了外部资源获取(不只是对象包括比如文件等)。
●为何是反转,哪些方面反转了:有反转就有正转,传统应用程序是由我们自己在对象中主动控制去直接获取依赖对象,也就是正转;而反转则是由容器来帮忙创建及注入依赖对象;为何是反转?因为由容器帮我们查找及注入依赖对象,对象只是被动的接受依赖对象,所以是反转;哪些方面反转了?依赖对象的获取被反转了。
IoC 不是一种技术,只是一种思想,一个重要的面向对象编程的法则,它能指导我们如何设计出松耦合、更优良的程序。传统应用程序都是由我们在类内部主动创建依赖对象,从而导致类与类之间高耦合,难于测试;有了IoC容器后,把创建和查找依赖对象的控制权交给了容器,由容器进行注入组合对象,所以对象与对象之间是 松散耦合,这样也方便测试,利于功能复用,更重要的是使得程序的整个体系结构变得非常灵活。
2.2 ioc操作两部分:
(1)ioc的配置文件方式
(2)ioc的注解方式
2.3.低层实现
ioc底层原理使用技术:
(1)xml配置文件
(2)dom4j解决xml
(3)工厂设计模式
(4)反射
低层原理:
最初,对象的创建和方法的调用都直接在servlet 中进行,耦合度太高,如果,对象或则方法名修改则需要修改servlet 中方法。
后来使用工厂模式降低耦合度,servlet 直接调用工厂类来获取对象。仍然存在耦合度,servlet 和工厂类耦合。
最终IOC实现:通过xml 配置文件+工程模式 通过反射机制实现高内聚,低耦合
3.Spring IOC 的实现
BeanFacotry是spring中比较原始的Factory。如XMLBeanFactory就是一种典型的BeanFactory。原始的BeanFactory无法支持spring的许多插件,如AOP功能、Web应用等。
ApplicationContext接口,它由BeanFactory接口派生而来,因而提供BeanFactory所有的功能。ApplicationContext以一种更向面向框架的方式工作以及对上下文进行分层和实现继承。
IoC容器BeanFactory(基本)和ApplicationContext(高级),两个都是接口,需要具体实现类。(常用WebApplicationContext)
IoC容器初始化
servlet容器启动(tomcat),为应用创建一个“全局上下文环境”:ServletContext ;容器调用web.xml中配置的contextLoaderListener(监听器),初始化WebApplicationContext上下文环境(即IoC容器,默认XmlWebApplicationContext),加载context-param指定的配置文件信息到IoC容器中。WebApplicationContext在ServletContext中以键值对的形式保存。此时开始IoC容器的初始化。
IoC容器的初始化由refresh()方法启动(这个方法在IoC具体实现类的构造函数中),启动包括BeanDefinition的Resouce定位(获取资源位置的Resource对象)、载入(解析xml配置文件,获取各个Element信息)和注册(将BeanDefinition放入HashMap中)三个基本过程。
Resource定位
构造IoC容器时,需要指定BeanDefinition的信息来源(如从xml文件中来),而这个信息来源需要封装成spring中的Resource类。Resource是Spring用来封装I/O操作的类。Resource是一个接口,具体实现类如ClassPathResource。通过配置文件指定的文件位置完成BeanDefinition的定位,但具体的数据还没开始读入。
Resouce 资源的读取
Resource只是BeanDefinition位置的封装,具体操作由ResourceLoader完成。这个过程即找到BeanDefinition的位置,为其载入创造I/O操作的条件。
BeanDefinition的载入
把用户定义好的Bean表示成IoC容器内部的数据结构(即BeanDefinition)。BeanDefinition的载入分两部分,首先调用XML解析器得到document对象,但这些document对象没有按照spring的bean规则进行解析。接下来按照spring规则进行解析。
1、构造函数调用refresh(),该方法在synchronized修饰的同步代码块中,保证不能有多个线程初始化。
2、refresh()先调用createBeanFactory()创建容器
3、refresh()再调用loadBeanDefinitions(BeanFactory)方法,先调用ResourceLoader得到Resource[]数组(定位);再调用Reader读取器(如XMLBeanDefinitionReader)读取Resource[]中资源(完成I/O操作,即开始载入),利用回调把结果传入之前建立BeanFactory中.
4、在Reader读取器(如XMLBeanDefinitionReader的loadBeanDefinitions方法)中调用documentLoader(XML解析器)中的方法,获得document对象;
4、在Reader读取器(如XMLBeanDefinitionReader的loadBeanDefinitions方法)再调用processBeanDefinition(传入document的element);
8、 processBeanDefinition (element,BeanDefinition)方法调用parseBeanDefinitionElement()方法,解析具体的元素标签(如、、等),将解析得到的结果设置到BeanDefinitionHolder中(BeanDefinitionHolder是BeanDefinition对象的封装类,封装了BeanDefinition、bean的名字等,用它来完成向IoC容器注册);
**经过对document逐层解析,我们在XML中定义的BeanDefinition就被载入到了IoC容器中。**但是,重要的依赖注入在这个时候还没有发生,现在IoC容器中BeanDefinition存在的还只是一些静态的配置信息。
4.IoC容器的依赖注入
1.概念
DI—Dependency Injection,即“依赖注入”:组件之间依赖关系由容器在运行期决定,形象的说,即由容器动态的将某个依赖关系注入到组件之中。依赖注入的目的并非为软件系统带来更多功能,而是为了提升组件重用的频率,并为系统搭建一个灵活、可扩展的平台。通过依赖注入机制,我们只需要通过简单的配置,而无需任何代码就可指定目标需要的资源,完成自身的业务逻辑,而不需要关心具体的资源来自何处,由谁实现。
理解DI的关键是:“谁依赖谁,为什么需要依赖,谁注入谁,注入了什么”,那我们来深入分析一下:
●谁依赖于谁:当然是应用程序依赖于IoC容器;
●为什么需要依赖:应用程序需要IoC容器来提供对象需要的外部资源;
●谁注入谁:很明显是IoC容器注入应用程序某个对象,应用程序依赖的对象;
●注入了什么:就是注入某个对象所需要的外部资源(包括对象、资源、常量数据)。
IoC和DI由什么关系呢?其实它们是同一个概念的不同角度描述,由于控制反转概念比较含糊(可能只是理解为容器控制对象这一个层面,很难让人想到谁来维护对象关系),所以2004年大师级人物Martin Fowler又给出了一个新的名字:“依赖注入”,相对IoC 而言,“依赖注入”明确描述了“被注入对象依赖IoC容器配置依赖对象”。
2.实现
依赖注入的过程是用户第一次向IoC容器索要Bean时触发的(BeanFactory.getBean(name)),当然也有例外,也就是我们可以在BeanDefinition信息中通过控制lazy-init属性来让容器完成对Bean的预实例化(在初始化过程中完成,即在refresh()(初始化的入口)时调用getBean())。通过依赖注入生成实例。
1、 getBean()方法中,首先判断是否已经创建,且为单例,这种情况不要创建。然后根据Bean的名字获取BeanDefinition,当前取不到就到双亲BeanFactory中取,如果还取不到就顺着双亲链一直向上。
2、 取到BeanDefinition后,获取当前Bean所依赖的所有Bean,并递归调用getBean。
3、 通过createBean创建Bean的实例,最后返回Bean。
4、 createBean()中,调用createBeanInstance()创建Bean(默认使用CGLIB一个常用的字节码生成器类库、或使用JVM的反射);
5、 createBean()中,再调用populateBean()方法,将当前Bean的依赖注入,通过递归调用容器的getBean方法,得到当前Bean的依赖Bean,同时也触发对依赖Bean的创建和注入。