Spring 概述
Spring 概述
在实际企业应用中,绝大部分企业架构都基于Spring框架。它最核心的理念是IoC(控制反转)和AOP(面向切面编程),其中IoC是Spring的基础,而AOP则是其重要的功能,最为典型的当属数据库事务的使用。
Spring历史
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并不想取代那些已有的框架,而是与它们无缝地整合。
Spring特点(策略)
- 轻量:对于POJO的潜力开发,提供轻量级和低侵入的编程,可以通过配置(xml、注解等)来扩展POJO的功能,通过依赖注入的理念去扩展功能;通过接口编程,强调OOD的开发模式理念,降低系统的耦合度,提高系统的可读性和可扩展性。
- 面向切面:Spring提供了面向切面编程的丰富支持,允许通过分离应用的业务逻辑与系统级服务(例如审计(auditing)和事务(transaction)管理)进行内聚性的开发。使得开发人员把精力更加集中于业务开发而非技术本身,也避免了try-catch-finally的滥用。
Spring不排斥各种优秀的开源框架,相反,Spring可以降低各种框架的使用难度,Spring提供了模板类来整合各种优秀框架。
Spring IoC概述
2004年,Martin Fowler探讨了同一个问题,既然IoC是控制反转,那么到底是“哪些方面的控制被反转了呢?经过详细地分析和论证后,他得出了答案:“获得依赖对象的过程被反转了”。控制被反转之后,获得依赖对象的过程由自身管理变为了由IOC容器主动注入。于是,他给“控制反转”取了一个更合适的名字叫做“依赖注入(Dependency Injection)”。他的这个答案,实际上给出了实现IOC的方法:注入。所谓依赖注入,就是由IoC容器在运行期间,动态地将某种依赖关系注入到对象之中。
所以,依赖注入(DI)和控制反转(IoC)是从不同的角度的描述的同一件事情,就是指通过引入IoC容器,利用依赖关系注入的方式,实现对象之间的解耦。
在Spring中,实现控制反转的是IoC容器,其实现方法是依赖注入。Spring会提供IoC容器来管理对应的资源。
控制反转是一个比较抽象的概念,对于初学者不好理解,我们举例说明。
案例 组装电脑
每台电脑由内存、主板、CPU三个基本的电脑元件组成,内存及CPU需要插在主板上才能工作。我们假定有各式各样的主板、内存及CPU可供选择,因此可通过Java接口来抽象各配件的定义。

package com.computer.component;
/**
* 电脑组件接口
* @author Jing61
*/
public interface PCComponent {
String getName(); // 获取组件名称
double getPrice(); // 获取组件价格
String getCompany(); // 获取组件厂家
}
package com.computer.component;
/**
* CPU接口
* @author Jing61
*/
public interface CPU extends PCComponent {
int getSpeed(); // 获取CPU速度
void doInstr(); // 做指令运算
void outResult(); // 输出结果
}
package com.computer.component;
/**
* 内存接口
* @author Jing61
*/
public interface Ram extends PCComponent {
int getSize(); // 获取内存大小
void inData(); // 读数据
void outData(); // 取数据
}
package com.computer.component;
/**
* 主板接口
* @author Jing61
*/
public interface Mainboard extends PCComponent {
void setCpu(CPU cpu);//安装cpu
CPU getCpu();//得到主板上的CPU
void setRam(Ram ram);//安装内存
Ram getRam();//得到主板上的内存
boolean havePower();//是否有电源
void startPower();//开电源
void shutdownPower();//关电源
}
Computer类的设计及实现
每台Computer需要一个主板,每个主板上需要插上CPU及内存条,电脑才能正常工作。

package com.computer.computer;
import com.computer.component.Mainboard;
/**
* 电脑类
* @author Jing61
*/
public class Computer {
private Mainboard mainboard;
public void setMainboard(Mainboard mainboard) {
this.mainboard = mainboard;
}
public void doWork() {
System.out.println("开始工作");
for (int i = 0; i < 10; i++) {
System.out.println("第" + i + "次工作");
}
System.out.println("工作结束");
}
public void start() {
mainboard.startPower();
}
public void shutdown() {
mainboard.shutdownPower();
}
public double getPrice() {
return mainboard.getPrice() + mainboard.getCpu().getPrice() + mainboard.getRam().getPrice();
}
public String getSetting() {
String ret = "电脑组成如下!主板:" + mainboard.getName() + ",CPU:" + mainboard.getCpu().getName() + ",内存:" + mainboard.getRam().getName() + "\n";
ret += "这个配置的价格为:"+getPrice();
return ret;
}
}
主板MainBoard的设计及实现
在现实中,有很多种类型的主板,只要这些主板符合我们的标准,就能接到我们的电脑中来用。本例提供了AUSUBoard及IntelBoard两种类别的主板。

package com.computer.component;
public class AbstractMainboard implements Mainboard {
private CPU cpu;
private Ram ram;
private boolean power;
public void setCpu(CPU cpu) {
this.cpu=cpu;
}
public CPU getCpu() {
return cpu;
}
public void setRam(Ram ram) {
this.ram=ram;
}
public Ram getRam() {
return ram;
}
public boolean havePower() {
return power;
}
public void startPower() {
power=true;
}
public void shutdownPower() {
power=false;
}
}
package com.computer.component;
public class AUSUBoard extends AbstractMainboard {
public String getName() {
return "AUSU主板";
}
public double getPrice() {
return 3000.00;
}
public String getCompany() {
return "SUSU公司";
}
}
package com.computer.component;
public class IntelBoard extends AbstractMainboard {
public String getName() {
return "Intel主板";
}
public double getPrice() {
return 3500.00;
}
public String getCompany() {
return "Intel公司";
}
}
CPU的设计及实现
在现实中,有很多种类型的CPU可供使用,只要这些CPU符合我们的标准,就能接到我们的电脑中来用。本例提供了AMDCpu及IntelCPU两种类别的CPU。

package com.computer.component;
public class IntelCPU implements CPU {
public int getSpeed() {
return 1700;
}
public void doInstr() {
System.out.println("做指令运算");
}
public void outResult() {
}
public String getName() {
return "IntelCPU";
}
public double getPrice() {
return 2500;
}
public String getCompany() {
return "Intel公司";
}
}
package com.computer.component;
public class AMDCpu implements CPU {
public int getSpeed() {
return 1500;
}
public void doInstr() {
System.out.println("做指令运算");
}
public void outResult() {
}
public String getName() {
return "AMD CPU";
}
public double getPrice() {
return 1800;
}
public String getCompany() {
return "AMD公司";
}
}
内存的设计及实现
本例我们也只提供两款可供选择的内存。分别是Kingmax的内存,以及Kingstone的内存。

package com.computer.component;
public class KingstoneRam implements Ram {
public int getSize() {
return 512;
}
public void inData() {
//读入数据
}
public void outData() {
//输出数据
}
public String getName() {
return "Kingstone内存";
}
public double getPrice() {
return 200;
}
public String getCompany() {
return "Kingstone公司";
}
}
package com.computer.component;
public class KingmaxRam implements Ram {
public int getSize() {
return 512;
}
public void inData() {
//读入数据
}
public void outData() {
//输出数据
}
public String getName() {
return "Kingmax内存";
}
public double getPrice() {
return 300;
}
public String getCompany() {
return "Kingmax公司";
}
}
传统的组装电脑方法
传统的情况下,要组装一台电脑,需要在代码中分别new出每一个电脑组件的实例,再把他们组装到电脑上面,再运行程序。
package com.computer.computer;
import com.computer.component.*;
public class ClientOldDemo {
public static void main(String[] args) {
CPU cpu=new IntelCPU();
Ram ram=new KingmaxRam();
Mainboard myMainboard=new IntelBoard();
myMainboard.setCpu(cpu);
myMainboard.setRam(ram);
Computer computer=new Computer();
computer.setMainboard(myMainboard);
//执行computer的doWork方法,使得commputer开始工作
computer.doWork();
//输出电脑的配置信息
System.out.println(computer.getSetting());
}
}
用Spring组装电脑
在Spring中,它会认为一切java类都是资源,而资源都是Bean,容纳这些Bean的是Spring所提供的IoC容器,所以Spring是一种基于Bean的编程。
<?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="intelCpu" class="com.computer.component.IntelCPU" />
<bean id="admCpu" class="com.computer.component.AMDCpu" />
<bean id="kingstoneRam" class="com.computer.component.KingstoneRam" />
<bean id="kingmaxRam" class="com.computer.component.KingmaxRam" />
<bean id="intelBoard" class="com.computer.component.IntelBoard" >
<property name="cpu" ref="intelCpu" />
<property name="ram" ref="kingstoneRam" />
</bean>
<bean id="ausuBoard" class="com.computer.component.AUSUBoard" >
<property name="cpu" ref="admCpu" />
<property name="ram" ref="kingmaxRam" />
</bean>
<bean id="computer" class="com.computer.computer.Computer" >
<property name="mainboard" ref="intelBoard" />
</bean>
<bean id="myMainboard" class="com.computer.component.IntelBoard">
<property name="ram" >
<bean class="com.computer.component.KingmaxRam"/>
</property>
<property name="cpu" >
<bean class="com.computer.component.IntelCPU"/>
</property>
</bean>
<bean id="myComputer" class="com.computer.computer.Computer">
<property name="mainboard" ref="myMainboard"/>
</bean>
</beans>
在配置文件中,通过<bean>标签来定义了一个id为myMainboard的主板,一个id为myComputer的电脑。通过<bean>标签下的<property>分别设置了主板上选择的具体的内存Ram及CPU,另外还通过myComputer的<property>标签设置了mainboard属性为myMainboard。
使用Spring组装好的电脑
package com.computer;
import com.computer.computer.Computer;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class ClientDemo {
public static void main(String[] args) {
var context = new ClassPathXmlApplicationContext("spring-beans.xml");
var computer = context.getBean("computer", Computer.class);
computer.doWork();
System.out.println(computer.getSetting());
}
}
发布电脑组装程序(让用户自己组装电脑==>修改xml文件即可,无需修改代码)
ClientDemo相当于电脑的最终使用者,一个不会组装电脑的用户,其甚至不知道电脑由哪些部件组成,他需要的仅仅是一个完整可以正常工作的电脑;而负责维护Spring配置文件者,相当于组装电脑的一般技术人员,他不需要很高深的技术,不需要知道怎么样生产一块主板、生产一块CPU,只需要知道什么是主板,什么是CPU,什么是Ram,并且知道CPU及Ram应该插放在什么位置,知道主板怎么安装即可。而实现CPU接口、Mainboard接口及Ram内存的类编写人员,如编写InterCPU这个类的人员,就相当CPU厂家的一线专业技术工程师,他们需要对微电子、数据电路等很多非常专业的技术,他们需要对CPU的工作原理等非常了解。

基于上面的实例,可以基本上把控制反转定义为:控制反转是一种通过描述(在java中可以是xml或者注解)并通过第三方(spring容器)去产生或获取特定对象的方式。
正如被动创建的电脑,是通过下面的xml代码描述所得到的:
<bean id="myComputer" class="edu.uestc.avatar.Computer">
<property name="mainboard" ref="myMainboard"/>
</bean>
在spring中实现控制反转的是IoC容器,其实现方法是依赖注入。Spring会提供IoC容器来管理对应的资源,正如上面示例中的电脑和主板资源,他们形成依赖注入的关系。其中主板用到CPU和RAM两个资源。
为什么要使用Spring
在项目中引入spring立即可以带来下面的好处:
- 降低组件之间的耦合度,实现软件各层之间的解耦,有利于测试。
- 可以使用容器提供的众多服务,如:事务管理服务、消息服务等等。当我们使用容器管理事务时,开发人员就不再需要手工控制事务,也不需处理复杂的事务传播。
- 容器提供单例模式支持,开发人员不再需要自己编写实现代码。
- 容器提供了AOP技术,利用它很容易实现如权限拦截、运行期监控等功能。
- 容器提供的众多辅作类,使用这些类能够加快应用的开发,如:JdbcTemplate、HibernateTemplate。
- Spring对于主流的应用框架提供了集成支持,如:集成Hibernate、JPA、Struts等,这样更便于应用的开发。
- Spring能通过接口而不是类促进好的编程习惯,降低开发难度。


浙公网安备 33010602011771号