Spring简介
Spring是一个轻量级Java开发框架,由Rod Johnson创建,目的是为了解决企业级应用开发的业务逻辑层和其他各层的耦合问题。它是一个JavaSE/JavaEE分层的full-stack(一站式)轻量级开源框架,为开发Java应用程序提供全面的基础架构支持。
Spring负责基础架构,因此Java开发者可以专注于应用程序的开发。
Spring的起源
Spring起源于2002年Rod Johnson写的一本书《Expert One-on-One J2EE》,书里介绍了Java企业应用程序开发情况,并指出Java EE和EJB组件框架中的一些主要缺陷,还提出了一个基于普通的Java类和依赖注入的更简单的解决方案。在书中,他展示了如何在不使用EJB的情况下构建高质量、可扩展性的在线预留座位系统。为构建应用程序,他编写了超过30,000行的基础结构代码,项目中的根包命名为com.interface21,这是Spring的前身,所以框架最初的名字叫interface21。
2003 年 Rod Johnson 和同伴在此框架的基础上开发了一个全新的框架命名为 Spring,据 Rod Johnson 介绍 Spring 是传统 J2EE 新的开始,随后 Spring 发展进入快车道。
Spring的优点
- 方便解耦,简化开发:Spring就是一个大工厂,可以将所有对象创建和依赖的关系维护,交给Spring管理。
- AOP编程的支持:Spring提供面向切面编程,可以方便的实现对程序进行权限拦截、运行监控等功能。
- 声明式事务的支持:只需要通过配置就可以完成对事务的管理,而无需手动编程。
- 方便程序的测试:Spring对Junit4支持,可以通过注解方便的测试Spring程序。
- 方便集成各种优秀框架:Spring不排斥各种优秀的开源框架,其内部提供了对各种优秀框架的直接支持(如:Struts、Hibernate、MyBatis等)。
- 降低JavaEE API的使用难度:Spring对JavaEE开发中非常难用的一些API(JDBC、JavaMail、远程调用等),都提供了封装,使这些API应用难度大大降低。
Spring框架可以说是当前Java世界中最为成功的框架,在企业实际应用中,大部分的企业架构都基于Spring框架。Spring的成功来自于理念,而不是技术,最核心的理念是控制反转(IOC/DI)和面向切面编程(AOP),以及声明式事务。其中IOC是spring的基础,AOP则是其重要的功能,最为典型的当属数据库事务的使用。
spring框架已经融入了J2EE开发的各个领域,不论是数据访问层,还是控制层,又或是表现层,全都可以看到spring的身影。
Spring体系结构
Spring框架至今已集成了20多个模块,这些模块分布在以下模块中:
- 核心容器(Core Container)
- 数据访问/集成(Data Access/Integration)层
- Web层
- AOP(Aspect Oriented Programming)模块
- 植入(Instrumentation)模块
- 消息传输(Messaging)
- 测试(Test)模块
核心容器
Spring的核心容器是其他模块建立的基础,有Spring-core、Spring-beans、Spring-context、Spring-context-support和Spring-expression(Spring表达式语言)等模块组成。
- Spring-core模块:提供了框架的基本组成部分,包括控制反转(Inversion of Control,IOC)和依赖注入(Dependency Injection,DI)功能。
- Spring-beans模块:提供了BeanFactory,是工厂模式的一个经典实现,Spring将管理对象称为Bean。
- Spring-context模块:建立在Core和Beans模块的基础之上,提供一个框架式的对象访问方式,是访问定义和配置的任何对象的媒介。ApplicationContext接口是Context模块的焦点。
- Spring-expression模块:提供了强大的表达式语言去支持运行时查询和操作对象图。这是对JSP2.1规范中规定的统一表达式语言(Unified EL)的扩展。该语言支持设置和获取属性值、属性分配、方法调用、访问数组、集合和索引器的内容、逻辑和算术运算、变量命名以及从Spring的IOC容器中以名称检索对象。它还支持列表投影、选择以及常用的列表聚合。
AOP和Instrumentation
- Spring-aop模块:提供了一个符合AOP要求的面向切面的编程实现,允许定义方法拦截器和切入点,将代码按照功能进行分离,以便干净地解耦。
- Spring-aspects模块:提供了与AspectJ的集成功能,AspectJ是一个功能强大且成熟的AOP框架。
- Spring-instrument模块:提供了类植入(Instrumentation)支持和类加载器的实现,可以在特定的应用服务器中使用。
消息
Spring4.0以后新增了消息(Spring-messaging)模块,该模块提供了对消息传递体系结构和协议的支持。
数据访问/集成
数据访问/集成层由JDBC、ORM、OXM、JMS和事务模块组成。
- Spring-jdbc模块:提供了一个JDBC的抽象层,消除了烦琐的JDBC编码和数据库厂商特有的错误代码解析。
- Spring-orm模块:为流行的对象关系映射(Object-Relational Mapping)API提供集成层,包括JPA和Hibernate。使用Spring-orm模块可以将这些O/R映射框架与Spring提供的所有其他功能结合使用,例如声明式事务管理功能。
- Spring-oxm模块:提供了一个支持对象/XML映射的抽象层实现,例如JAXB、Castor、JiBX和XStream。
- Spring-jms模块(Java Messaging Service):指Java消息传递服务,包含用于生产和使用消息的功能。自Spring4.1以后,提供了与Spring-messaging模块的集成。
- Spring-tx模块(事务模块):支持用于实现特殊接口和所有POJO(普通Java对象)类的编程和声明式事务管理。
Web
Web层由Spring-web、Spring-webmvc、Spring-websocket和Portlet模块组成。
- Spring-web模块:提供了基本的Web开发集成功能,例如多文件上传功能、使用Servlet监听器初始化一个IOC容器以及Web应用上下文。
- Spring-webmvc模块:也称为Web-Servlet模块,包含用于web应用程序的Spring MVC和REST Web Services实现。Spring MVC框架提供了领域模型代码和Web表单之间的清晰分离,并与Spring Framework的所有其他功能集成。
- Spring-websocket模块:Spring4.0以后新增的模块,它提供了WebSocket和SocketJS的实现。
- Portlet模块:类似于Servlet模块的功能,提供了Portlet环境下的MVC实现。
测试
Spring-test模块支持使用JUnit或TestNG对Spring组件进行单元测试和集成测试。
IoC/DI:控制反转/依赖注入
控制反转是软件设计大师 Martin Fowler在 2004 年发表的《Inversion of Control Containers and the Dependency Injection pattern》提出的。这篇文章系统阐述了控制反转的思想,提出了控制反转有依赖查找和依赖注入实现方式。<br /> 控制反转是一种通过描述(XML或注解)并通过第三方去产生或获取特定对象的方式。使用控制反转带来的最大好处就是降低对象之间的耦合。<br /> 程序中对象的产生是基于IoC容器,而不是开发者主动的行为。开发者主动创建的模式,责任归于开发者,在使用IoC容器被动创建的模式下,责任归于IoC容器。基于这样的被动形式,我们就说对象被控制反转了。<br /> spring支持XML和注解两种方式实现IoC。
spring:XML实现
入门案例
需求:搭建spring IoC环境,使用spring容器创建并管理类的对象。
spring的不同模块具备不同的功能,因此,具体的spring环境搭建,应该根据当前开发的应用使用到的功能来决定,将某个模块的jar包导入项目即可使用该模块对应的功能。
步骤1:创建maven项目,引入对应依赖。
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.8.RELEASE</version>
</dependency>
步骤2:创建实体类src/com/woniuxy/domain/Subject.java
public class Subject implements Serializable {
}
步骤3:创建spring核心配置文件src/applicationContext.xml
<?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
https://www.springframework.org/schema/beans/spring-beans.xsd">
<!--
bean标签对应于实体类对象,在java程序中可以通过容器的getBean(id属性值)获取实体类对象
id属性是实体类对象的唯一标识
class属性是通过反射创建对象时的全限定类名
-->
<bean id="subject" class="com.woniuxy.domain.Subject"></bean>
</beans>
注:
标签中的属性部分无需手动配置,可在官网:https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#spring-core中找到相应内容复制即可。
步骤4:创建测试类src/com/woniuxy/test/Test.java
public class Test {
public static void main(String[] args) {
//1、读取配置文件,生成spring容器
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
//2、根据bean标签id获取spring容器管理的bean实例
Subject sub = (Subject) ac.getBean("subject");
System.out.println(sub);
}
}
运行测试类后,可在控制台看到对应Subject实例信息被打印,在测试类中并没有手动创建Subject类实例,而是通过spring容器获取的Subject类对象,由此可知,是spring容器创建并管理了Subject类对象。
获取spring容器的三种方式
在入门案例的测试类中,先获取spring容器ApplicationContext后,再调用其方法获取其创建和管理的对象。ApplicationContext是BeanFactory接口的子接口之一,它对BeanFactory的功能做了很多有用的扩展,绝大部分情况下会使用ApplicationContext作为IoC容器。
BeanFacotry是spring中比较原始的Factory。如XMLBeanFactory就是一种典型的BeanFactory。原始的BeanFactory无法支持spring的许多插件,如AOP功能、Web应用等。
```java
ClassPathResource classPathResource = new ClassPathResource("applicationContext.xml");
XmlBeanFactory xmlBeanFactory = new XmlBeanFactory(classPathResource);
System.out.println(xmlBeanFactory.getBean("subject"));
ApplicationContext接口,它由BeanFactory接口派生而来,ApplicationContext包含BeanFactory的所有功能,通常建议比BeanFactory优先。
![](https://cdn.nlark.com/yuque/0/2021/png/2901531/1609844744308-d53a3a4f-776d-4ab9-99bd-0ee30b118f2e.png#align=left&display=inline&height=591&margin=%5Bobject%20Object%5D&originHeight=591&originWidth=1380&size=0&status=done&style=none&width=1380)
**BeanFactory和ApplicationContext的区别**:
- BeanFactory创建对象是延迟加载的,即什么时候调用对象,什么时候创建;
- ApplicationContext创建对象则更智能,会根据对象的单例还是多例,来选择是否延迟加载(单例立即加载,多例延迟加载);<br />**创建ApplicationContext容器的三种方式**:
- 解析类路径下的XML文件创建(ClassPathXmlApplicationContext),要求配置文件必须存在于类路径下;
- 解析系统文件路径下的XML文件创建(FileSystemXmlApplicationContext),配置文件可以在系统的任意路径中;
- 解析注解创建(AnnotationConfigApplicationContext);
> 一般来说,不建议使用FileSystemXmlApplicationContext,因为系统路径的权限是否开放决定了是否能够访问到该路径下的文件。当权限没有开放时,由于访问不到该路径,因此加载不到配置文件,会出现异常。
<a name="dc991e5f"></a>
#### 控制反转的三种方式
<a name="70e98516"></a>
##### 使用构造方法创建
入门案例中使用的就是构造方法创建,使用的是默认的无参构造。
<a name="07e218ff"></a>
##### 使用工厂创建
在实际应用开发过程中,会使用到很多的第三方类库,第三方类库中的类都是class字节码文件,无法通过修改源码的形式提供构造方法,此时类的对象一般情况下是通过工厂模式来提供的,spring核心容器也提供了根据工厂模式来创建和管理类的方法。
<a name="6f40b538"></a>
###### 使用实例工厂创建
需求:模拟实例工厂创建。
步骤1:<br /> 创建Subject子类:src/com/woniuxy/domain/PhysicsSubject.java
```java
public class PhysicsSubject extends Subject implements Serializable {
}
创建Subject子类:src/com/woniuxy/domain/LiteratureSubject.java
public class LiteratureSubject extends Subject implements Serializable {
}
步骤2:创建工厂类src/com/woniuxy/factory/SubjectFactory.java
public class SubjectFactory {
//工厂方法,根据sub_no生产不同的Subject子类
public Subject getSubject(Integer sub_no){
switch (sub_no) {
case 1:
return new PhysicsSubject();
case 2:
return new LiteratureSubject();
default:
return null;
}
}
}
步骤2:修改核心配置文件src/applicationContext.xml
<?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
https://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 创建工厂实例 -->
<bean id="subjectFactory" class="com.woniuxy.factory.SubjectFactory"></bean>
<!--
使用工厂创建Subject类的实例
factory-bean属性指定创建当前类的实例使用哪个工厂
factory-method属性指定创建当前类的实例使用的是工厂的哪个方法
-->
<bean id="subject" factory-bean="subjectFactory" factory-method="getSubject">
<!-- 使用constructor-arg为工厂方法传递参数 -->
<constructor-arg name="sub_no" value="2"></constructor>
</bean>
</beans>
使用静态工厂创建
需求:模拟静态工厂创建。
步骤1:修改工厂类src/com/woniuxy/factory/SubjectFactory.java
public class SubjectFactory {
//将原本的工厂方法修改成静态方法
public static Subject getSubject(Integer sub_no){
switch (sub_no) {
case 1:
return new PhysicsSubject();
case 2:
return new LiteratureSubject();
default:
return null;
}
}
}
步骤2:修改核心配置文件src/applicationContext.xml
<?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
https://www.springframework.org/schema/beans/spring-beans.xsd">
<!--
创建Subject类实例
class属性指定当前实例由哪个类来创建
factory-method属性指定创建类实例的方法
-->
<bean id="subject" class="com.woniuxy.factory.SubjectFactory"
factory-method="getSubject">
<!-- 使用constructor-arg为工厂方法传递参数 -->
<constructor-arg name="sub_no" value="2"></constructor>
</bean>
</beans>
spring依赖注入的三种方式
spring依赖注入有以下三种方式:
- 构造器注入:依赖于类的构造方法实现,构造方法可有参也可无参;
- setter注入:依赖于类的setter方法实现,灵活且可读性高,这是spring中最主流的注入方式;
- 接口注入:当注入的资源并非来自本系统,而是来自于系统外部,比如数据库连接资源在Tomcat下配置,并通过JNDI的方式去获取,此时数据库资源就属于外部资源,可以使用接口注入方式获取它;
接口注入模式因为具备侵入性,它要求组件必须与特定的接口相关联,因此并不被看好,实际使用有限,了解即可。
构造器注入
需求:使用带参构造为spring管理的Subject实例注入相应属性值。
步骤1:修改src/com/woniuxy/domain/Subject.java
public class Subject implements Serializable {
private Integer subNo;
private String subName;
public Subject(Integer subNo, String subName) {
this.subNo = subNo;
this.subName = subName;
}
/*toString*/
}
步骤2:修改核心配置文件src/applicationContext.xml
<?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
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="subject" class="com.woniuxy.domain.Subject">
<constructor-arg name="subNo" value="1"></constructor>
<constructor-arg name="subName" value="HTML"></constructor>
</bean>
</beans>
标签属性详解
- type:为构造方法中的某个类型的参数赋值;
- index:指定为构造方法中某个索引的参数赋值,索引从0开始计数;
- name:指定为构造方法中某个名称为name属性取值的参数赋值,一般使用该方式;
- value:被赋值的数据。
- ref:指定为构造方法中某个参数赋值,该值的类型不是java基本数据类型和String类型,而是一个在spring容器中已被注册的bean;
setter注入
需求:使用setter方法为spring管理的Subject实例注入相应属性值。
步骤1:修改src/com/woniuxy/domain/Subject.java
package com.woniuxy.domain;
import java.io.Serializable;
public class Subject implements Serializable {
private Integer subNo;
private String subName;
public Integer getSubNo() {
return subNo;
}
public void setSubNo(Integer subNo) {
this.subNo = subNo;
}
public String getSubName() {
return subName;
}
public void setSubName(String subName) {
this.subName = subName;
}
/*toString*/
}
步骤2:修改核心配置文件src/applicationContext.xml
<?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
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="subject" class="com.woniuxy.domain.Subject">
<property name="subNo" value="1"></property>
<property name="subName" value="html"></property>
</bean>
</beans>
与 类似,但没有type和index属性。