Spring:IoC-DI
spring笔记(一)
什么是spring?
spring是一个以ioc(inversion of control-控制反转)和aop(aspect oriented programming-面向切面编程)为内核的开源框架。
那么什么是框架呢?框架能做什么?为什么要使用框架?框架其实就是能完成一定功能的半成品。框架能够帮助我们完成的是:项目的整体框架、一些基础功能、规定了类和对象如何创建,如何协作等,当我们开发一个项目时,框架帮助我们完成了一部分功能,我们自己再完成一部分,那这个项目就完成了。
spring框架包结构
使用spring框架进行开发时,需要用到spring框架和第三方依赖包。
框架包:
以spring 4.3.16为例。下载之后,解压,最终的目录结构图如下:
其中,libs目录下包含60个jar文件,这些jar文件分为3类:
a、以release.jar结尾的是spring框架class文件的压缩包
b、以release-javadoc.jar结尾的是spring框架的api文档的压缩包
c、以release-sources.jar结尾的是spring框架源文件的压缩包
整个spring由20个模块组成,该目录下为每个模块都提供了这三种压缩包。初次之外,在libs目录中,还有4个spring的基础包,分别对应spring核心容器的4个模块:
spring-core-4.3.16.release.jar:包含spring框架的基本核心工具类,spring其他组件都要用到这个包里面的类。
spring-beans-4.3.16.release.jar:所有基于spring框架的应用都要用到的jar包,包含访问配置文件、创建和管理bean以及进行ioc或者di操作相关的所有类。
spring-context-4.3.16.release.jar:spring提供了在基础ioc功能上的扩展服务,还提供了许多企业级服务的支持,如任务调度、jndi定位、ejb集成、远程访问、缓存、邮件服务以及各种视图层框架的封装等。
spring-expression-4.3.16.release.jar:定义了spring的表达式语言。
第三方依赖包:
在使用spring进行开发时,spring的核心容器还需要依赖commons-logging的jar包。
什么是控制反转-ioc?
学习ioc之前,要先懂什么是依赖?
应用程序在没有引入ioc容器之前,对象a依赖对象b,那么a对象实例化或者运行到某一个点的时候,自己就必须去主动创建对象b或者使用已经创建好的对象b,如果没有对象b,对象a就无法使用,就是a has b的关系。此时,我们就可以理解为对象a对b对象产生了依赖。
从上面的依赖关系可以看出,创建对象的过程是主动的:缺对象b马上new一个,也就是说我需要啥自己去创建一个;而将对象引入ioc容器之后呢?对象a和对象b就失去了直接联系,当a对象实例化或者运行到某个点需要对象b,ioc容器就是主动创建一个对象b给到a需要的地方。此时,对象a获取对象b的过程就是被动获取的,也就是把原来由自己创建对象的权力交给了ioc容器,此时对象的创建都由ioc容器来管理,这就是控制反转。你也可以这么理解:是的,你想抽烟!没结婚之前,想抽什么买什么,因为钱在自己手里。结婚之后呢?工资到哪里去了呢?很明显要上交给老婆大人,那你此时想抽烟就比较麻烦了,你该怎么做呢?是不是应该请求老婆大人给钱买烟呢?只有老婆大人给了你钱,你才能买到烟...这个权力的颠倒过程就是控制反转的过程。
ioc在角色扮演上等价于一个第三方,我更习惯于理解成为一个代理商。为什么这么说呢?因为ioc实现了对具有依赖关系的对象之间的解耦!来,看图:
从上面的图中可以看出,ioc做了什么事情呢?ioc它把各个对象类封装之后,再通过ioc容器来关联这些对象。这样一来,对象与对象之间的关系就通过ioc容器来进行联系,而对象与对象之间没有什么直接联系。怎么去理解呢?假如工厂支持diy笔记本电脑,你是消费者,你想diy笔记本电脑,就要直接联系工厂,那么此时你和工厂就产生了依赖关系,也就是你与工厂有了耦合。而ioc扮演的角色就是第三方的代理商,就是告诉你:你不用直接去联系工厂了,你告诉我需要什么,由我来把你的需求告诉工厂。然后代理商拿到按照你需求diy的笔记本电脑,然后注入给你。来看图:
什么是di-依赖注入?
di(dependency inject)依赖注入:由ioc容器在运行期间动态的将某种依赖关系注入对象之中。也就是将对象b注入(赋值)给对象a的成员变量。
怎么去理解di和ioc之间的联系呢?依赖注入( di) 和控制反转( ioc) 是对同一件事情的不同描述, 从某个方面讲, 就是它们描述的角度不同。 依赖注入是从应用程序的角度描述, 即应用程序依赖容器创建并注入它所需要的外部资源; 而控制反转是从容器的角度描述, 即容器控制应用程序, 由容器反向地向应用程序注入应用程序所需要的外部资源。 这里所说的外部资源可以是外部实例对象, 也可以是外部文件、对象等。
ioc和di带来的益处?
1.可维护性比较号,便于单元测试、调试程序和争端故障。代码中的每一个class都可以单独测试,彼此之间互不影响,只要保证自身的功能无误即可,这就是组件之间低耦合或者无耦合带来的好处。
2.每个开发团队的成员都只需要关注自己要实现的业务逻辑,完全不用关心其他人的工作进展,因为你的任务跟别人没有任何关系,你的任务可以单独测试,不用依赖于别人的组件,再也不会扯不清责任了。所以,在一个大中型项目中,团队成员分工明确、责任明晰,很容易将一个大的任务划分为细小的任务,开发效率和产品质量必将得到大幅度的提高。
3.可复用性好,我们可以把具有普遍性的常用组件独立出来,反复应用到项目中的其他部分,或者是其他项目,当然这也是面向对象的基本特征。显然,ioc更好地贯彻了这个原则,提高了模块的可复用性。符合接口标准的实现都可以插接到支持此标准的模块中。
4.生成对象的方式转为外置方式,就是把对象生成放在配置文件中进行定义。这样,当我们更换一个实现子类将会变得很简单,只要修改配置文件就可以了,完全具有热插拔的特性。
ioc和di的实现
spring框架的主要功能是通过器核心容器来实现的。spring框架提供的两种核心容器分别是:beanfactory(来源与org.springframework.beans.factory的主要接口)和applicationcontext(来源于org.springframework.context的主要接口)。
spring ioc框架的主要组件由beans、配置文件applicationcontext.xml、beanfactory接口及其相关类、applicationcontext接口及其相关类。
1.beans是指项目中提供业务功能的bean,即容器要管理的bean。bean就是一个常见的javabean、java类。
2.在spring中对bean的管理是在配置文件中进行的。在spring容器内编辑配置文件管理bean又被称为bean的装配,实际上装配就是告诉容器需要哪些bean,以及容器是如何使用ioc来将他们配合起来的。
其中,配置文件包含bean的id、类、属性及其值,包含一个<beans>元素和数个<bean>子元素。spring ioc框架可以根据bean的id从bean配置文件中取得该bean类,并生成该类的一个实例对象,继而从配置文件中获得该对象的属性和值。
<!--将指定类配置给spring,让spring创建其对象的实例--> <bean id="chinese" class="com.soai.model.chinese"> <!--property元素用来指定需要容器注入的属性,name指定其属性值为 language,ref指向需要向language属性注入的id,即注入对象“english”,该对象由english实体类生成 --> <property name="language" ref="english" /> </bean> <--配置另外一个bean--> <bean id="english" class="com.soai.model.english">
3.beanfactory采用了工厂设计模式,即bean容器模式,负责读取bean的配置文件,管理对象的生成、加载,维护bean对象与bean对象之间的依赖关系,负责bean的生命周期。对于简单的应用程序来说,使用beanfactory就已经足够管理bean了,在对象的管理上可以获得许多便利性。
常用的方法:
getbean(string name):根据bean的id生成该bean的对象。
getbean(string name,class requiredtype):根据bean的id和相应类生成该bean的对象。
4.applicationcontext接口提供高级功能的容器,基本功能与beanfactory很相似,
但它还有以下功能:
a.提供访问资源文件更方便的方法。
b.支持国际化消息。
c.提供文字消息解析的方法。
d.可以发布事件,对事件感兴趣的bean可以接收到这些事件。
applicationcontext常用的实现类:
1.classpathxmlapplicationcontext:读取classpath中的资源(从类路径中的xml文件中加载上下文定义的信息)
applicationcontext context= new filesystemxmlapplicationcontext(" d:/ applicationcontext. xml ");
2.filesystemxmlapplicationcontext:读取指定路径的资源(从文件系统中的xml文件中加载上下文中定义的信息)
applicationcontext context= new classpathxmlapplicationcontext(" applicationcontext. xml ");
3.xmlwebapplicationcontext:需要在web环境下才能运行(从web系统中的xml文件中加载上下文中定义的信息)
xmlwebapplicationcontext ac = new xmlwebapplicationcontext(); // 这时并没有初始化容器 ac.setservletcontext(servletcontext); // 需要指定servletcontext对象 ac.setconfiglocation("/web-inf/applicationcontext.xml"); // 指定配置文件路径,开头的斜线表示web应用的根目录 ac.refresh(); // 初始化容器
beanfactory和applicationcontext
spring ioc容器的设计主要基于以下两个接口:beanfactory和applicationcontext,其中applicationcontext是beanfactory的子接口之一,换句话说:beanfactory是spring ioc容器所定义的最底层接口,只提供了最简单的ioc功能,负责配置,创建和管理bean.而applicationcontext是其*的接口之一,并对beanfactory功能做了许多的扩展,所以在绝大多数情况下,都会使用applicationcontext作为spring ioc容器.
di(依赖注入)的实现方式
注入:bean实例默认在调用类的无参构造器创建对象后,就要对bean实例的属性进行初始化。这个初始化过程是由容器自动完成的,称为注入。
di注入的实现方式通常有2种:
1.设置注入:spring调用类的setter方法,通过set方法完成属性赋值
a.简单类型
<bean id="bean别名" class="类全限定名称"> <property name="类属性名" ref="引用类型属性值"/> </bean>
b.引用类型
<--ref作为属性--> <bean id="bean别名" class="类全限定名称"> <property name="类属性名" ref="引用类型属性值"/> </bean> <--ref作为子标签--> <bean id="bean别名" class="类全限定名称"> <property name="类属性名"> <ref="引用类型属性值"/> </property> </bean>
c.其他类型
2.构造注入:spring调用类的有参构造器,通过构造方法给属性赋值,每个参数代表着一个依赖
a.使用name完成构造注入
<constructor-age name="myage" value="22"/>
b.使用index构造器,参数位置从0开始
<constructor-age index="0" value="..."/>
c.省略index属性,必须按照构造器的参数位置(顺序)来复制
<constructor-age value="...">