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接口来抽象各配件的定义。

image

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及内存条,电脑才能正常工作。

image

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两种类别的主板。

image

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。

image

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的内存。

image

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的工作原理等非常了解。

image

基于上面的实例,可以基本上把控制反转定义为:控制反转是一种通过描述(在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能通过接口而不是类促进好的编程习惯,降低开发难度。

image

posted @ 2025-12-18 09:39  Jing61  阅读(3)  评论(0)    收藏  举报