target
了解什么是Spring
了解Spring产生的背景
了解Spring的生态体系
掌握如何编写一个Spring项目
掌握控制反转和依赖注入的思想
1. Spring概述
1.1 Spring简介
Spring是Java EE编程领域的一个轻量级开源框架,该框架由一个叫Rod Johnson的程序员在 2002 年最早提出并随后创建,是为了解决企业级编程开发中的复杂性,实现敏捷开发的应用型框架 。 Spring是一个开源容器框架,它集成各类型的工具,通过核心的Bean factory实现了底层的类的实例化和生命周期的管理。在整个框架中,各类型的功能被抽象成一个个的 Bean,这样就可以实现各种功能的管理,包括动态加载和切面编程。 Spring是独特的,因为若干个原因:
它定位的领域是许多其他流行的framework没有的。Spring致力于提供一种方法管理你的业务对象。
Spring是全面的和模块化的。Spring有分层的体系结构,这意味着你能选择使用它孤立的任何部分,它的架构仍然是内在稳定的。例如,你可能选择仅仅使用Spring来简单化JDBC的使用,或用来管理所有的业务对象。
它的设计从底部帮助你编写易于测试的代码。Spring是用于测试驱动工程的理想的framework。
Spring对你的工程来说,它不需要一个以上的framework。Spring是潜在地一站式解决方案,定位于与典型应用相关的大部分基础结构。它也涉及到其他framework没有考虑到的内容。
1.2 背景
Rod Johnson在2002年编著的《Expert one on one J2EE design and development》一书中,对Java EE 系统框架臃肿、低效、脱离现实的种种现状提出了质疑,并积极寻求探索革新之道。以此书为指导思想,他编写了interface21框架,这是一个力图冲破J2EE传统开发的困境,从实际需求出发,着眼于轻便、灵巧,易于开发、测试和部署的轻量级开发框架。Spring框架即以interface21框架为基础,经过重新设计,并不断丰富其内涵,于2004年3月24日,发布了1.0正式版。同年他又推出了一部堪称经典的力作《Expert one-on-one J2EE Development without EJB》,该书在Java世界掀起了轩然大波,不断改变着Java开发者程序设计和开发的思考方式。在该书中,作者根据自己多年丰富的实践经验,对EJB的各种笨重臃肿的结构进行了逐一的分析和否定,并分别以简洁实用的方式替换之。至此一战功成,Rod Johnson成为一个改变Java世界的大师级人物。
传统J2EE应用的开发效率低,应用服务器厂商对各种技术的支持并没有真正统一,导致J2EE的应用没有真正实现Write Once及Run Anywhere的承诺。Spring作为开源的中间件,独立于各种应用服务器,甚至无须应用服务器的支持,也能提供应用服务器的功能,如声明式事务、事务处理等。
Spring致力于J2EE应用的各层的解决方案,而不是仅仅专注于某一层的方案。可以说Spring是企业应用开发的“一站式”选择,并贯穿表现层、业务层及持久层。然而,Spring并不想取代那些已有的框架,而是与它们无缝地整合。
EJB是重量级的,比如说你想做个事,一共有5步, 5步都必须去做,其中做完之后,有很多事没用的,但是你不写不行,会报错 ,所以开发成本比较大,维护成本大。
到底轻量级是哪些东西呢?首先是依赖资源少,我这个模块要启动了,和你一点关系都没有,不依赖别的模块。然后是消耗资源少,比如 window启动某个软件的时候,会告诉你,给我腾1个g的内存,否则我不启动。而Spring不是,他消耗的资源非常少。
Rod Johnson当初的描述如下(节选 Rod Johnson于2005年出版的 expertone- on-one 2EE Development without EJB,当时的 Java EE按Sun公司的标准命名称为2EE):
We believe that
- J2EE should be easier to use.
- It is best to program to interfaces, rather than classes. Spring reduces the complexity cost of using interfaces to zero
- Javabean offers a great way of configuring applications
- OO design is more important than any implemention technology, such as 2EE
- Checked exceptions are overused in java. A platform should not force you to catch exceptions you are unlikely to recover from
- Testability is essential and a platform such as spring should help make your code easie to test
We aim that
- Spring should be a pleasure to use.
- Your application codes should not depend on spring apis
- Spring should not compete with good exsiting solutions, but should foster Integration
针对 Rod Johnson的观点,简要分析:
这段话中谈及的 J2EE 更容易使用是针对EB而言的,因为EJB容器十分复杂,难以使用,当时使用EJB2的时候需要很多配置文件。
基于接口的编程是一种理念,强调OOD的设计理念,比技术实现更为重要,因为实现可以多样化,但是如果没有一个好的设计理念,那么代码可读性就会变差,从而导致后期难以开发、维护和扩展。
与此同时,他也指出了当时Java开发的通病一一大量使用 try.. catch. finally,因为大量的数据库操作都需要用try. catch. finally.去控制业务逻辑,这往往被大部分程序员滥用,导致代码非常复杂,而 Spring改造了它们。
由于使用EJB需要从EB容器中获得服务,所以测试人员只能不断地部署和配置。对于测试而言,有时候一个对象比较复杂,它往往需要由其他对象作为属性组成。比如一套餐具,由碟子、碗、筷子、勺子和杯子组成,测试人员就想测试一下勺子好不好用,但是不得不需要自己构建碟子、筷子、勺子和杯子这套餐具,然后才能测试勺子。这显然存在很大的弊病,
在当时的Java技术中,很多框架都是侵入性的,也就是必须使用当前框架所提供的类库,才能实现功能,这样会造成应用对框架的依赖。
Spring技术不是为了取代现有技术(当时的 Struts、 Hibernate、EJB、JDO等),而是提供更好的整合模板使它们能够整合到 Spring技术上来
同时 Rod Johnson也指出了 Spring的一些优势。
1.3 框架特征
轻量——从大小与开销两方面而言Spring都是轻量的。完整的Spring框架可以在一个大小只有1MB多的JAR文件里发布。并且Spring所需的处理开销也是微不足道的。此外,Spring是非侵入式的:典型地,Spring应用中的对象不依赖于Spring的特定类。
控制反转——Spring通过一种称作控制反转(IoC)的技术促进了低耦合。当应用了IoC,一个对象依赖的其它对象会通过被动的方式传递进来,而不是这个对象自己创建或者查找依赖对象。你可以认为IoC与JNDI相反——不是对象从容器中查找依赖,而是容器在对象初始化时不等对象请求就主动将依赖传递给它。它的底层设计模式采用了工厂模式,所有的 Bean 都需要注册到Bean工厂中,将其初始化和生命周期的监控交由工厂实现管理。程序员只需要按照规定的格式进行Bean开发,然后利用XML文件进行bean 的定义和参数配置,其他的动态生成和监控就不需要调用者完成,而是统一交给了平台进行管理。 [4] 控制反转是软件设计大师 Martin Fowler在 2004 年发表的”Inversion of Control Containers and the Dependency Injection pattern”提出的。这篇文章系统阐述了控制反转的思想,提出了控制反转有依赖查找和依赖注入实现方式。控制反转意味着在系统开发过程中,设计的类将交由容器去控制,而不是在类的内部去控制,类与类之间的关系将交由容器处理,一个类在需要调用另一个类时,只要调用另一个类在容器中注册的名字就可以得到这个类的实例,与传统的编程方式有了很大的不同,“不用你找,我来提供给你”,这就是控制反转的含义 [5] 。
面向切面——Spring提供了面向切面编程的丰富支持,允许通过分离应用的业务逻辑与系统级服务(例如审计(auditing)和事务(transaction)管理)进行内聚性的开发。应用对象只实现它们应该做的——完成业务逻辑——仅此而已。它们并不负责(甚至是意识)其它的系统级关注点,例如日志或事务支持。
容器——Spring包含并管理应用对象的配置和生命周期,在这个意义上它是一种容器,你可以配置你的每个bean如何被创建——基于一个可配置原型(prototype),你的bean可以创建一个单独的实例或者每次需要时都生成一个新的实例——以及它们是如何相互关联的。然而,Spring不应该被混同于传统的重量级的EJB容器,它们经常是庞大与笨重的,难以使用。
框架——Spring可以将简单的组件配置、组合成为复杂的应用。在Spring中,应用对象被声明式地组合,典型地是在一个XML文件里。Spring也提供了很多基础功能(事务管理、持久化框架集成等等),将应用逻辑的开发留给了你。
MVC——Spring的作用是整合,但不仅仅限于整合,Spring 框架可以被看做是一个企业解决方案级别的框架。客户端发送请求,服务器控制器(由DispatcherServlet实现的)完成请求的转发,控制器调用一个用于映射的类HandlerMapping,该类用于将请求映射到对应的处理器来处理请求。HandlerMapping 将请求映射到对应的处理器Controller(相当于Action)在Spring 当中如果写一些处理器组件,一般实现Controller 接口,在Controller 中就可以调用一些Service 或DAO 来进行数据操作 ModelAndView 用于存放从DAO 中取出的数据,还可以存放响应视图的一些数据。 如果想将处理结果返回给用户,那么在Spring 框架中还提供一个视图组件ViewResolver,该组件根据Controller 返回的标示,找到对应的视图,将响应response 返回给用户。
所有Spring的这些特征使你能够编写更干净、更可管理、并且更易于测试的代码。它们也为Spring中的各种模块提供了基础支持。
1.4 Spring体系结构
Spring框架是一个分层结构,它包含一系列的功能要素并被分为大约20个模块。
其中:
-
Core Container:核心容器
Beans:管理bean
Core:核心
Context:上下文(配置文件)
Expression Language:SpEl表达式
AOP:切面编程
Aspects:Aop框架
-
Data Access/integration:数据访问/整合
JDBC:jdbcTemplate数据库开发
ORM:整合hibernate、mybatis
Transactions:事务管理(tx)
-
Web
Web:web开发
Struts:整合Struts框架
Servlet:服务连接器
2. 第一个Spring项目
2.1 下载 Jar
解压缩后,在lib文件夹下有好多jar包。
2.2 增加插件
为了编写时有一些提示、自动生成一些配置信息,可以给eclipse增加支持spring的插件:spring tool suite ,注意要下载和eclipse对应的版本。
在eclipse中,Help--> Eclipse Marketplace ,会弹出一个install的对话框:
然后一直下一步,接受,按照步骤装就行。最后记得重启eclipse。
注意:需要选择sts3的相关版本,sts4无法生成spring配置文件。
还有一个更好的方法,直接下载一个软件"STS",会集成所有spring所有功能。
STS = Spring所有插件 + eclipse
2.3 开发spring程序
新建一个Java项目:Spring-01
① 导入jar
我们基础开发需要4+1个jar:
spring-beans-4.3.9.RELEASE.jar
spring-context-4.3.9.RELEASE.jar
spring-core-4.3.9.RELEASE.jar
spring-expression-4.3.9.RELEASE.jar
commons-logging-1.2.jar:第三方提供日志jar包
将上面5个jar复制,粘贴到src目录下,然后全部选中,右键Build Path-->add to build path
② 创建实体类
首先新建一个Student类:
package com.lee.spring.bean;
public class Student {
private int no;
private String name;
private int age;
// getter、setter、构造、toString省略
}
在写一个测试方法:
package com.lee.spring.test;
public class TestDemo {
public static void main(String[] args) {
Student student = new Student(1001, "张三", 23);
System.out.println(student);
}
}
以前我们创建对象是这样去做的。
现在用ioc来创建一个对象:
③ 编写配置文件
在src下右键 --> 新建 --> Other,
在弹出的对话框中起一个名字,一般叫:applicationContext .xml,单击finish。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="stu" class="com.lee.spring.bean.Student">
<property name="no" value="2002"></property>
<property name="name" value="李四"></property>
<property name="age" value="24"></property>
</bean>
</beans>
④ 测试
TestDemo.java:
package com.lee.spring.test;
public class TestDemo {
@Test
public void test01() {
// 1. 获取上下文对象:Context
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
// 2. 获取bean对象
Student student = (Student)context.getBean("stu");
System.out.println(student);
}
}
输出:Student [no=2002, name=李四, age=24]
到此为止,我们就已经实现了第一个spring程序。来看一下和传统方式有什么不一样的地方:
不需要new对象
省略了对象属性的赋值
对象创建不是new的,而是用配置文件创建的,配置文件其实就是spring的IOC容器。
创建好对象后,赋值操作也是在配置文件中操作的。既然IOC容器已经为我们把这一切都做好了,所以直接从容器里面拿对象就可以了。
2.4 解释配置文件
bean标签:表示创建一个对象。
id:表示创建对象的名字。
class:表示实体类的全类名。
property:用来给对象赋值。
其中:
<bean id="stu" class="com.lee.entity.Student">
就相当于
Student stu = new Student();
需要注意:
<bean id="stu" class="com.lee.entity.Student">
调用了Student的无参构造,当我们把无参构造删除时,bean标签就会报错。bean的id就是对象名
3. 控制反转和依赖注入
3.1 IoC(控制反转)
首先想说说IoC(Inversion of Control,控制反转)。这是spring的核心,贯穿始终。所谓IoC,对于spring框架来说,就是由spring来负责控制对象的生命周期和对象间的关系。
这是什么意思呢,举个简单的例子,我们是如何找女朋友的?常见的情况是,我们到处去看哪里有长得漂亮身材又好的mm,然后打听她们的兴趣爱好、qq号、电话号………,想办法认识她们,投其所好送其所要,然后……这个过程是复杂深奥的,我们必须自己设计和面对每个环节。传统的程序开发也是如此,在一个对象中,如果要使用另外的对象,就必须得到它(自己new一个,或者从JNDI中查询一个),使用完之后还要将对象销毁(比如Connection等),对象始终会和其他的接口或类藕合起来。
那么IoC是如何做的呢?有点像通过婚介找女朋友,在你和女朋友之间引入了一个第三方机构:婚姻介绍所。婚介管理了很多男男女女的资料,我可以向婚介提出一个列表,告诉它我想找个什么样的女朋友,比如长得像贾玲,身材像贾玲,唱歌像岳云鹏,幽默程度像贾玲之类的,然后婚介就会按照我们的要求,提供一个mm,我们只需要去和她谈恋爱、结婚就行了。简单明了,如果婚介给我们的人选不符合要求,我们就会抛出异常。整个过程不再由我自己控制,而是有婚介这样一个类似容器的机构来控制。Spring所倡导的开发方式就是如此,所有的类都会在spring容器中登记,告诉spring你是个什么东西,你需要什么东西,然后spring会在系统运行到适当的时候,把你要的东西主动给你,同时也把你交给其他需要你的东西。所有的类的创建、销毁都由 spring来控制,也就是说控制对象生存周期的不再是引用它的对象,而是spring。对于某个具体的对象而言,以前是它控制其他对象,现在是所有对象都被spring控制,所以这叫控制反转。
3.2 DI(依赖注入)
IoC的一个重点是在系统运行中,动态的向某个对象提供它所需要的其他对象。这一点是通过DI(Dependency Injection,依赖注入)来实现的。
比如对象A需要操作数据库,以前我们总是要在A中自己编写代码来获得一个Connection对象,有了 spring我们就只需要告诉spring,A中需要一个Connection,至于这个Connection怎么构造,何时构造,A不需要知道。在系统运行时,spring会在适当的时候制造一个Connection,然后像打针一样,注射到A当中,这样就完成了对各个对象之间关系的控制。A需要依赖 Connection才能正常运行,而这个Connection是由spring注入到A中的,依赖注入的名字就这么来的。
那么DI是如何实现的呢? Java 1.3之后一个重要特征是反射(reflection),它允许程序在运行的时候动态的生成对象、执行对象的方法、改变对象的属性,spring就是通过反射来实现注入的。
理解了IoC和DI的概念后,一切都将变得简单明了,剩下的工作只是在spring的框架中堆积木而已。