Spring
概述
- 最受欢迎的企业级 Java 应用程序开发框架
- 一个开源的 Java 平台
- 轻量级的框架,基础版本只有2M左右
- 框架的核心特性是可以用于开发任何 Java 应用程序
三层架构
- 表现层:Web层 MVC是表现层的一个设计模型
- 业务层:Service层
- 持久层:dao层
特性
非侵入式、控制反转(IOC)、依赖注入、面向切面(AOP)、容器、组件化、一站式。。。
参考文章:W3Cschool
体系结构

核心容器
- Spring-core:提供了框架的基本组成部分,包括IoC和依赖注入功能。
- Spring-beans:模块提供 BeanFactory,应用工厂模式。
- context:基于core和beans模块。
- Spring-expression:提供了强大的表达式语言,用于在运行时查询和操作对象图。
完整依赖关系如下图所示:

数据访问/集成
数据访问/集成层包括 JDBC,ORM,OXM,JMS 和事务处理模块,它们的细节如下:
ps:JDBC=Java Data Base Connectivity,ORM=Object Relational Mapping,OXM=Object XML Mapping,JMS=Java Message Service
Web
Web 层由 Web,Web-MVC,Web-Socket 和 Web-Portlet 组成。
- Web 模块提供面向 web 的基本功能和面向 web 的应用上下文,比如多部分(multipart)文件上传功能、使用 Servlet 监听器初始化 IoC 容器等。它还包括 HTTP 客户端以及 Spring 远程调用中与 web 相关的部分。
- Web-MVC 模块为 web 应用提供了模型视图控制(MVC)和 REST Web服务的实现。Spring 的 MVC 框架可以使领域模型代码和 web 表单完全地分离,且可以与 Spring 框架的其它所有功能进行集成。
- Web-Socket 模块为 WebSocket-based 提供了支持,而且在 web 应用程序中提供了客户端和服务器端之间通信的两种方式。
- Web-Portlet 模块提供了用于 Portlet 环境的 MVC 实现,并反映了 spring-webmvc 模块的功能。
Test、其他
…
IoC容器
Spring 容器是 Spring 框架的核心。容器将创建对象,把它们连接在一起,配置它们,并管理他们的整个生命周期从创建到销毁。Spring 容器使用依赖注入(DI)来管理组成一个应用程序的组件。(这些对象被称为 Spring Beans)
在Spring出现之前,通常new(实例化)一个实例是由程序员来完成,而“控制反转”就是将new 实例的操作交给Spring容器来做。(BeanFactory在Spring中就是IoC容器的实际代表者)
Bean定义
bean 是一个被实例化,组装,并通过 Spring IoC 容器所管理的对象。
创建Bean
创建实体类
1
2
3
4
5
6
7
public class PC {
    private String name;
    public PC(){ System.out.println("PC实例化"); }
    public void startUp(){ System.out.println(this.name+"正在启动"); }
    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
}
添加applicationContext.xml文件(该文件名可随便起,一般为applicationContext.xml)
1
2
3
4
5
6
7
8
<?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="pc" class="top.jamartin.spring.PC">
        <property name="name" value="我的电脑" />
    </bean>
</beans>
Spring中两种不同类型的容器
- Spring BeanFactory:给DI(依赖注入)提供最基本的支持。
- Spring ApplicationContext:该容器添加了更多的企业特定的功能,例如从一个属性文件中解析文本信息的能力,发布应用程序事件给感兴趣的事件监听器的能力。
BeanFactory
主要的功能就是为DI(依赖注入)提供支持,该容器接口在org.springframework.beans.factory.BeanFactory中被定义。
ps:在 Spring 中,有大量对 BeanFactory 接口的实现。其中,最常被使用的是 XmlBeanFactory 类。这个容器从一个 XML 文件中读取配置元数据,由这些元数据来生成一个被配置化的系统或者应用。
通过XmlBeanFactory生成工厂:(已过时)
1
2
3
4
5
6
@Test
public void TestXmlBeanFactory(){
    XmlBeanFactory factory = new XmlBeanFactory(new ClassPathResource("applicationContext.xml"));
    PC pc = factory.getBean("pc", PC.class);
    pc.startUp();
}
ApplicationContext
Application Context 是 BeanFactory 的子接口,也被称为 Spring 上下文。
ps:ApplicationContext 包含 BeanFactory 所有的功能,一般情况下,相对于 BeanFactory,ApplicationContext 会更加优秀。当然,BeanFactory 仍可以在轻量级应用中使用,比如移动设备或者基于 applet 的应用程序。
最常被使用的 ApplicationContext 接口实现:
- FileSystemXmlApplicationContext:该容器从 XML 文件中加载已被定义的 bean。在这里,你需要提供给构造器 XML 文件的完整路径。
- ClassPathXmlApplicationContext:该容器从 XML 文件中加载已被定义的 bean。在这里,你不需要提供 XML 文件的完整路径,只需正确配置 CLASSPATH 环境变量即可,因为,容器会从 CLASSPATH 中搜索 bean 配置文件。
- WebXmlApplicationContext:该容器会在一个 web 应用程序的范围内加载在 XML 文件中已被定义的 bean。
这里举例通过ClassPathXmlApplicationContext生成工厂bean。
实体类和applicationContext.xml文件和之前一样,只需要改测试类:
1
2
3
4
5
6
@Test
public void TestClassPathXmlApplicationContext(){
    Application factory = new ClassPathXmlApplicationContext("applicationContext.xml");
    PC pc = factory.getBean("pc", PC.class);
    pc.startUp();
}
结果都是一样的:

Bean的作用域
作用域的属性为scope
| 作用域 | 描述 | 
|---|---|
| singleton | 在spring IoC容器仅存在一个Bean实例,Bean以单例方式存在,默认值 | 
| prototype | 每次从容器中调用Bean时,都返回一个新的实例,即每次调用getBean()时,相当于执行newXxxBean() | 
| request | 每次HTTP请求都会创建一个新的Bean,该作用域仅适用于WebApplicationContext环境 | 
| session | 同一个HTTP Session共享一个Bean,不同Session使用不同的Bean,仅适用于WebApplicationContext环境 | 
| global-session | 一般用于Portlet应用环境,该作用域仅适用于WebApplicationContext环境 | 
举例:
将刚刚的applicationContext.xml文件中的bean标签更改一下
1
2
3
4
<bean id="pc" class="top.jamartin.spring.PC" 
      scope="singleton">
    <property name="name" value="我的电脑" />
</bean>
在测试类中多new一个实例
1
2
3
4
5
6
7
8
@Test
public void TestScope(){
    ApplicationContext factory = new ClassPathXmlApplicationContext("applicationContext.xml");
    PC pc = factory.getBean("pc", PC.class);
    pc.startUp();
    PC pc2 = factory.getBean("pc",PC.class);
    pc2.startUp();
}
PC只实例化了一次

再将 “singleton” 改为 ”prototype” 重新测试一下:

Bean的生命周期
首先,在PC类中添加两个方法:init()和destory()
1
2
3
4
5
6
7
8
9
public class PC {
    ...
    public void init(){
        System.out.println("init");
    }
    public void destory(){
        System.out.println("destory");
    }
}
然后更改配置文件
1
2
3
4
5
<bean id="pc" class="top.jamartin.spring.PC" 
      scope="singleton"
      init-method="init" destory-method="destory" >
    <property name="name" value="我的电脑" />
</bean>
在测试类中实例化一个PC
1
2
3
4
5
6
@Test
public void TestScope(){
    ApplicationContext factory = new ClassPathXmlApplicationContext("applicationContext.xml");
    PC pc = factory.getBean("pc", PC.class);
    pc.startUp();
}
依赖注入
Spring 框架的核心功能之一就是通过依赖注入的方式来管理Bean之间的依赖关系。
基于构造函数的依赖注入
还是通过PC来举例子:
一个PC(电脑)一般需要配一些硬件设备才能使用,例如:鼠标键盘、硬盘等。
先给PC加一个HardWare(硬盘)
1
2
3
4
5
6
7
8
public class HardWare {
    private String name;
    public void read(){ System.out.println("读取数据"); }
    public void write(){ System.out.println("写数据"); }
    public HardWare() { System.out.println("HardWare has constructor"); }
    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
}
然后在PC中引入HardWare
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class PC {
    private String name;
    //引入硬盘
    private HardWare hd;
   	public PC(HardWare hd){
        System.out.println("Inside HardWare contructor");
        this.hd = hd;
    }
    
    public PC(){ System.out.println("PC实例化"); }
    public void startUp(){ 
        System.out.println(this.name+"正在启动"); 
        hd.read();
    }
    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
}
编辑测试类:
1
2
3
4
5
public void TestConstruct(){
    ApplicationContext factory = new ClassPathXmlApplicationContext("applicationContext.xml");
    PC pc = factory.getBean(PC.class);
    pc.startUp();
}
运行测试类:

可以看到,硬盘实例化后,被注入到了PC中,这就叫做依赖注入。
基于设值函数的依赖注入
以刚刚的例子,硬盘类不变,PC类更为如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//PC类
public class PC {
    private String name;
    //引入硬盘
    private HardWare hd;
    
    public HardWare getHd() {
        return hd;
    }
    public void setHd(HardWare hd) {
        this.hd = hd;
        System.out.println("Setter HardWare")
    }
    public PC(){ System.out.println("PC实例化"); }
    public void startUp(){ 
        System.out.println(this.name+"正在启动"); 
        hd.read();
    }
    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
}
运行测试类:

注入集合
在上面的例子中介绍了如何注入 Beans,但是如果想要传递多个值,例如List
、Set、Map等,该怎么做呢?(Spring 提供了四种类型的集合的配置元素)
| 元素 | 描述 | 
|---|---|
| <list> | 它有助于连线,如注入一列值,允许重复。 | 
| <set> | 它有助于连线一组值,但不能重复。 | 
| <map> | 它可以用来注入名称-值对的集合,其中名称和值可以是任何类型。 | 
| <props> | 它可以用来注入名称-值对的集合,其中名称和值都是字符串类型。 | 
例如:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package com.tutorialspoint;
import java.util.*;
public class JavaCollection {
   private List addressList;
   private Set addressSet;
   private Map addressMap;
    
   public void setAddressList(List addressList) {
      this.addressList = addressList;
   }
   public List getAddressList() {
      System.out.println("List Elements :"  + addressList);
      return addressList;
   }
   public void setAddressSet(Set addressSet) {
      this.addressSet = addressSet;
   }
   public Set getAddressSet() {
      System.out.println("Set Elements :"  + addressSet);
      return addressSet;
   }
   public void setAddressMap(Map addressMap) {
      this.addressMap = addressMap;
   }
   public Map getAddressMap() {
      System.out.println("Map Elements :"  + addressMap);
      return addressMap;
   }
}
配置文件applicationContext.xml:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<bean id="js" class="top.jamartin.JavaSet">
	<!-- results in a setAddressList(java.util.List) call -->
    <property name="addressList">
    <list>
        <value>INDIA</value>
        <value>Pakistan</value>
        <value>USA</value>
        <value>USA</value>
    </list>
    </property>
	<!-- results in a setAddressSet(java.util.Set) call -->
    <property name="addressSet">
        <set>
            <value>INDIA</value>
            <value>Pakistan</value>
            <value>USA</value>
            <value>USA</value>
        </set>
    </property>
    <!-- results in a setAddressMap(java.util.Map) call -->
    <property name="addressMap">
        <map>
            <entry key="1" value="INDIA"/>
            <entry key="2" value="Pakistan"/>
            <entry key="3" value="USA"/>
            <entry key="4" value="USA"/>
        </map>
    </property>
</bean>
编写测试类:
1
2
3
4
5
6
7
8
@Test
public void TestCollection(){
    ApplicationContext factory = new ClassPathXmlApplicationContext("applicationContext.xml");
    JavaSet js = factory.getBean(JavaSet.class);
    System.out.println(js.getAddressList());
    System.out.println(js.getAddressMap());
    System.out.println(js.getAddressSet());
}
执行测试类:

Spring Beans 自动装配
自动装配ByName
还是PC机的例子:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//PC类
public class PC {
    private String name;
    //引入硬盘
    private HardWare hd;
    
    public HardWare getHd() {
        return hd;
    }
    public void setHd(HardWare hd) {
        this.hd = hd;
        System.out.println("Setter HardWare")
    }
    public PC(){ System.out.println("PC实例化"); }
    public void startUp(){ 
        System.out.println(this.name+"正在启动"); 
        hd.read();
    }
    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
}
如果需要通过ByName自动装配,则配置文件中pc 就需要添加一个autowire属性:
<beans>
    <bean id="pc" class="top.jamartin.PC" autowire="byName">
        <property name="name" value="我的电脑" />
    </bean>
    <bean id="hardware" class="top.jamartin.HardWare" />
</beans>
ByName : 会自动在 beans.xml(容器)的上下文中查找 和自己对象中 set方法 set后面的值对应的 bean 的 id或name。
需要确保配置文件下有一个bean中的id/name与调用Bean的属性相同。
自动装配ByType
<beans>
    <bean id="pc" class="top.jamartin.PC" autowire="byType">
        <property name="name" value="我的电脑" />
    </bean>
    <bean id="HardWare" class="top.jamartin.HardWare" />
</beans>
byType:会自动在beans.xml(容器)上下文中查找,和自己 对象的属性类型 相同的 bean。
由构造函数自动装配
在PC机中加入带有硬盘参数的构造函数:
1
2
3
4
5
6
7
8
9
//PC类
public class PC {
    ...
    private HardWare hd;
    public PC(HardWare hd){
        this.hd = hd;
    }
    ...
}
如果需要通过Constructor自动装配,则配置文件中pc 就需要添加一个autowire属性:
<beans>
    <bean id="pc" class="top.jamartin.PC" autowire="constructor">
        <constructor-arg value="Generic Text Editor"/>
    </bean>
    <bean id="HardWare" class="top.jamartin.HardWare" />
</beans>
