Spring概述
Spring框架可以说是java世界最为成功的框架,在实际企业应用中,绝大部分企业架构都基于Spring框架。它的成功来自于理念,它最核心的理念是IoC(控制反转)和AOP(面向切面编程),其中IoC是Spring的基础,而AOP则是其重要的功能,最为典型的当属数据库事务的使用。可以说Spring框架已经融入到java EE开发的各个领域。
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容器来管理对应的资源。
控制反转是一个比较抽象的概念,对于初学者不好理解,我们举例说明。
案例:使用Spring组装电脑
每台电脑由内存、主板、CPU三个基本的电脑元件组成,内存及CPU需要插在主板上才能工作。我们假定有各式各样的主板、内存及CPU可供选择,因此可通过Java接口来抽象各配件的定义。
//PCComponent接口 public interface PCComponent { String getName();//名称 double getPrice();//价格 String getCompany();//厂家 } //CPU public interface CPU extends PCComponent{ int getSpeed();//处理器速度 void doInstr(); //做指令运算 void outResult();//输出结果 } //Ram public interface Ram extends PCComponent{ int getSize();//内存大小 void inData();//读数据 void outData();//取数据 } //主板 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及内存条,电脑才能正常工作。
//电脑类 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<100;i++) System.out.print(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; ret="电脑组成如下!主板:"+mainboard.getName()+",CPU:"+mainboard.getCpu().getName()+",内存:"+mainboard.getRam().getName()+"\n"; ret+="这个配置的价格为:"+getPrice(); return ret; } }
主板MainBoard的设计及实现
在现实中,有很多种类型的主板,只要这些主板符合我们的标准,就能接到我们的电脑中来用。本例提供了AUSUBoard及IntelBoard两种类别的主板。
// 主板 公共特性AbstractMainboard public abstract 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; } } // AUSUBoard public class AUSUBoard extends AbstractMainboard { public String getName() { return "AUSU主板"; } public double getPrice() { return 3000.00; } public String getCompany() { return "SUSU公司"; } } //IntelBoard 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两种类别的主板。
// IntelCPU 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公司"; } } // AMDCpu 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的内存。
// KingmaxRam 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公司"; } } // KingstoneRam 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公司"; } }
传统的组装电脑方法
传统的情况下,要组装一台电脑,需要在代码中分别new出每一个电脑组件的实例,再把他们组装到电脑上面,再运行程序。代码如下:
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"?> <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd"> <beans> <bean id="myMainboard" class="edu.uestc.avatar.bean.IntelBoard"> <property name="ram" ><bean class="edu.uestc.avatar.bean.KingmaxRam"/>
</property> <property name="cpu" ><bean class="edu.uestc.avatar.bean.IntelCPU"/></property>
</bean>
<bean id="myComputer" class="edu.uestc.avatar.Computer">
<property name="mainboard" ref="myMainboard"/>
</bean>
</beans>
在配置文件中,通过<bean>标签来定义了一个id为myMainboard的主板,一个id为myComputer的电脑。通过<bean>标签下的<property>分别设置了主板上选择的具体的内存Ram及CPU,另外还通过myComputer的<property>标签设置了mainboard属性为myMainboard。
使用组装好的电脑
public class ClientDemo { public static void main(String[] args) { //定义Spring Bean工厂资源文件 Resource res=new ClassPathResource("computer-beans.xml"); //创建一个Bean工厂 BeanFactory factory=new XmlBeanFactory(res); //从Bean工厂中取出一个名为myComputer的Computer对象 Computer computer=(Computer)factory.getBean("myComputer"); //执行computer的doWork方法,使得commputer开始工作 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能通过接口而不是类促进好的编程习惯,降低开发难度。