Spring 是一个开源免费的框架,为了解决企业应用开发的复杂性而创建。Spring 框架是一个轻量级的解决方案,可以一站式地构建企业级应用。
Spring 是模块化的,所以可以只使用其中需要的部分。可以在任何 web 框架上使用控制反转(IoC),也可以只使用 Hibernate 集成代码或 JDBC 抽象层。它支持声明式事务管理、通过RMI 或 web 服务实现远程访问,并可以使用多种方式持久化数据。它提供了功能全面的 MVC 框架,可以透明地集成 AOP 到软件中。
Spring 被设计为非侵入式的,这意味着你的域逻辑代码通常不会依赖于框架本身。在集成层(比如数据访问层),会存在一些依赖同时依赖于数据访问技术和 Spring,但是这些依赖可以很容易地从代码库中分离出来。
Spring 框架是基于 Java 平台的,它为开发 Java 应用提供了全方位的基础设施支持,并且它很好地处理了这些基础设施,所以你只需要关注你的应用本身即可。Spring 可以使用 POJO(普通的 Java 对象,plain old java objects)创建应用,并且可以将企业服务非侵入式地应用到 POJO。这项功能适用于 Java SE 编程模型以及全部或部分的 Java EE。
Spring 体系结构
Core Container 核心容器
Spring 的核心容器是其他模块的基础,由 Beans 模块、Core 核心模块、Context 上下文模块和 Expression Language 表达式语言四个部分组成。
- Beans 模块:提供了 BeanFactory,用于创建和管理对象,Spring 将产生出来的对象称为 Bean。
- Core 核心模块:提供了 Spring 框架的基本组成部分,包括 IoC 和 DI 功能。
- Context 上下文模块:建立在 Core 核心和 Beans 模块的基础之上,它是访问定义和配置任何对象的媒介。ApplicationContext 接口是上下文模块的焦点。
- Expression Language 模块:是运行时查询和操作对象图的强大的表达式语言。
Data Access 模块
Data Access/Integration包括 JDBC、ORM、OXM、JMS 和 Transactions 五个模块,具体介绍如下。
- JDBC 模块:提供了一个 JDBC 的抽象层,大幅度减少了在开发过程中对数据库操作的编码。
- ORM 模块:对流行的 ORM框架管理的 API ,也是我们常用的组件之一,包括 JPA、JDO、Hibernate 和 iBatis 提供了的集成层。
- OXM 模块:提供了一个支持对象/XML 映射的抽象层实现,如 JAXB、Castor、XMLBeans、JiBX 和 XStream。
- JMS 模块:指 Java 消息服务,包含的功能为生产和消费的信息。
- Transactions 事务模块:支持编程和声明式事务管理实现特殊接口类,并为所有的 POJO。
Web 模块
Spring 的 Web 层包括 WebSocket、Servlet、Web 和 Portlet 组件,具体介绍如下。
- ‘WebSocket 模块:提供了客户端和服务器之间的一个持久链接,管理之间数据的发送。
- Servlet模块:包括 Spring 模型—视图—控制器(MVC)实现 Web 应用程序。
- Web 模块:提供了基本的 Web 开发集成特性,例如多文件上传功能、使用的 Servlet 监听器的 IoC 容器初始化以及 Web 应用上下文。
- Portlet 模块:提供了在 Portlet 环境中使用 MVC 的实现,类似 Web-Servlet 模块的功能。
其他模块
Spring的其他模块还有 AOP、Aspects、Instrumentation 以及 Test 模块,具体介绍如下:
- AOP 模块:提供了面向切面编程实现,允许定义方法拦截器和切入点,将代码按照功能进行分离,以降低耦合性。
- Aspects 模块:提供与 AspectJ 的集成,是一个功能强大且成熟的面向切面编程(AOP)框架。
- Instrumentation 模块:提供了类工具的支持和类加载器的实现,可以在特定的应用服务器中使用。
- Test 模块:支持 Spring 组件,使用 JUnit 或 TestNG 框架的测试。
Spring 特点
方便解耦,简化开发
通过 Spring 提供的 IoC 容器,我们可以将对象之间的依赖关系交由 Spring 进行控制,避免硬编码所造成的过度程序耦合。有了 Spring,用户不必再为单实例模式类、属性文件解析等这些很底层的需求编写代码,可以更专注于上层的应用。
AOP 编程的支持
通过 Spring 提供的 AOP 功能,方便进行面向切面的编程,许多不容易用传统 OOP 实现的功能可以通过 AOP 轻松应付。
声明事物的支持
在 Spring 中,我们可以从单调烦闷的事务管理代码中解脱出来,通过声明式方式灵活地进行事务的管理,提高开发效率和质量。
方便程序的测试
可以用非容器依赖的编程方式进行几乎所有的测试工作,在 Spring 里,测试不再是昂贵的操作,而是随手可做的事情。例如:Spring 对 Junit4 支持,可以通过注解方便的测试 Spring 程序。
方便集成各种优秀框架
Spring 不排斥各种优秀的开源框架,相反,Spring 可以降低各种框架的使用难度,Spring 提供了对各种优秀框架(如 Struts,Hibernate、Hessian、Quartz)等的直接支持。
降低 Java EE API 的使用难度
Spring 对很多难用的 Java EE API(如 JDBC,JavaMail,远程调用等)提供了一个薄薄的封装层,通过 Spring 的简易封装,这些 Java EE API 的使用难度大为降低。
Java 源码是经典学习范例
Spring 的源码设计精妙、结构清晰、匠心独用,处处体现着大师对Java设计模式灵活运用以及对 Java 技术的高深造诣。Spring 框架源码无疑是 Java 技术的最佳实践范例。如果想在短时间内迅速提高自己的 Java 技术水平和应用开发水平,学习和研究 Spring 源码将会使你收到意想不到的效果。
Spring IOC 概述
控制反转 IoC(Inversion of Control):是一种设计思想,DI (依赖注入)是实现 IoC 的一种方法,也有人认为 DI 只是 IoC 的另一种说法。没有 IoC 的程序中我们使用面向对象编程对象的创建与对象间的依赖关系完全硬编码在程序中,对象的创建由程序自己控制,控制反转后将对象的创建转移给第三方。
IoC 是指容器控制程序对象之间的关系,而不是传统实现中,由程序代码直接操控。控制权由应用代码中转到了外部容器,控制权的转移就是所谓反转。对于 Spring 而言,就是由 Spring 来控制对象的生命周期和对象之间的依赖关系。IoC 还有另外一个名字——“依赖注入(Dependency Injection)”。从名字上理解,所谓依赖注入,即组件之间的依赖关系由容器在运行期决定,即由容器动态地将某种依赖关系注入到组件之中。
在 Spring 的工作方式中,所有的类都会在 spring 容器中登记,告诉 spring 这是个什么东西,你需要什么东西,然后 spring 会在系统运行到适当的时候,把你要的东西主动给你,同时也把你交给其他需要你的东西。所有的类的创建、销毁都由 spring 来控制,也就是说控制对象生存周期的不再是引用它的对象,而是 spring。对于某个具体的对象而言,以前是它控制其他对象,现在是所有对象都被 spring 控制,所以这叫控制反转。
在系统运行中,动态的向某个对象提供它所需要的其他对象。
依赖注入的思想是通过反射机制实现的,在实例化一个类时,它通过反射调用类中 set 方法将事先保存在 HashMap 中的类属性注入到类中。 总而言之,在传统的对象创建方式中,通常由调用者来创建被调用者的实例,而在 Spring 中创建被调用者的工作由 Spring 来完成,然后注入调用者,即所谓的依赖注入 or 控制反转。
IoC 的优点:降低了组件之间的耦合,降低了业务对象之间替换的复杂性,使之能够灵活的管理对象。
Spring IOC 容器
面向对象最基本的就是对象,我们对于对象的使用都是从 new 开始的。
传统方式
我们需要用到那个类的对象,只需要 new 它就可以产生该类的对象了,然在在通过该对象调用它的属性和方法。例如:
Person guan1 = new Person();
Person guan2 = new Person(1,"关为","程序猿");
类和类之间都是自行产生其他类的对象来进行使用,好处是,使用简单,需要的时候 new 就行,但是缺点就有很多,我用到哪些类都需要自身去创建,依赖度太高,不利于解耦,也有可能多个类都使用一个类的对象,但要产生多个对象,不能“共享”,冗余过多。
就类似关老师是一个快递人员,给同学们送快递,每一个快递都需要给每一个人打电话,等你们全部拿完快递后,我才能走。太麻烦了,为什么我不放到 CaiNiao 驿站呢?
IoC 方式
所有对象的产生都交给了容器来实现,当你需要对象时,在通过容器获取它就行。
在启动 Spring 时读取 applicationContext.xml 文件,将文件中需要产生的对象都产生出来并存放到 Spring IOC 容器中,当需要使用这个对象时,从容器中将它获取出来就行。
IoC 容器的规范
IoC 容器有两种规范:BeanFactory 和 ApplicationContext。
- BeanFactory:BeanFactory 为 IOC 容器提供了基础功能,Spring 文档中提到,当前该类仅仅是为了向后兼容老的版本,除非你有更好的原因否则就应该使用第二种容器。
- ApplicationContext:通过 API 文档可以知道,ApplicationContext 是BeanFactory 的子接口,并且从文档中也可以看到 ApplicaionContext 除了包含有 BeanFactory 的所有功能还支持了更多的功能。
BeanFactory 和 ApplicationContext 多是接口,都是对 IoC 容器提出了规范,对于它们而言是通过对应的实现类去实现,分别有四种实现方式。
FileSystemXmlApplicationContext 实现
加载配置文件的时候采用的是项目的路径。过去比较喜欢使用这种方式,以项目路径为相对路径起始点。
ApplicationContext bean = new FileSystemXmlApplicationContext("ioc/src/main/resources/applicationContext.xml");
ClassPathXmlApplicationContext 实现
加载配置文件的时候根据 ClassPath 位置。maven 项目的标准规范,以 resources 文件夹为相对路径起始点。
ApplicationContext bean = new ClassPathXmlApplicationContext("applicationContext.xml");
XmlWebApplicationContext 实现
在 Web 环境下初始化监听器的时候会加载。很少使用,在 web 项目中启动 tomcat 时加载配置文件,但我们实际上有其他方式来完成,具体内容在Spring MVC 中讲解。
AnnotationConfigApplicationContext 实现
注解方式,零 xml 配置。根据注解的方式启动 Spring 容器。
ApplicationContext bean = new AnnotationConfigApplicationContext("com.dailyblue.java.spring");
Spring IOC 实现方式
我们先以 xml 方式来搭建一个环境,后边在引入注解写法。
xml 方式
1. 引入 ioc 模块的依赖
<!-- 引入了SpringIOC的依赖 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.20</version>
</dependency>
2. 在 resources 文件夹下创建 applicationContext.xml 配置文件
3. 书写需要使用的类
@Data
public class DemoA {
private Integer id;
private String name;
private Character sex;
private Double weight;
public void sleep() {
System.out.println(name + "准备睡觉了!");
}
}
4. 在 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 http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--这个是一个IOC容器 负责产生对象的 管理对象-->
<!--
bean:产生对象
id:产生出来的对象名
class:产生那个类的对象
-->
<bean id="guanwei1" class="com.dailyblue.java.spring.bean.DemoA"></bean>
<!--产生对象并赋值-->
<bean id="guanwei2" class="com.dailyblue.java.spring.bean.DemoA">
<!--给这些属性赋值-->
<!--
name:属性名
value:属性赋予的值
-->
<property name="id" value="1"/>
<property name="name">
<value>关为</value>
</property>
<property name="sex" value="男"/>
<property name="weight" value="75.5"/>
</bean>
</beans>
常见标签 API
<bean> 标签:每一个标签 bean 对应的就一个类对象,通过 id(或者Name)以及 Class 来定位一个类,这边 Class 要求是完全限定类名。
名称 | 解释 |
---|---|
id | 对象名 |
name | 等同于 id,用于指定 bean 的别名 |
class | 对象的完整类路径 |
lazy-init | 用于指定当前 Bean 的初始化时间 若值为 true 表示在初次调用时才会自动创建实例并初始化 false 表示在 IoC 容器创建的时候就会完成 创建和初始化 |
init-method | 用于指定当前 Bean 的初始化方法,在 Bean 实例创建好后,首先会调 用其指定名称的方法 |
destory-method | 用于指定当前 Bean 的销毁方法,在 Bean 即将被销毁之前会自动调用 该属性指定的方法 |
depends-on | 用于指定当前 Bean 的依赖 Bean,强制指定的 Bean 在当前 Bean 初 始化之前先完成初始化 |
abstract | 代表是否是抽象类,值:true|false |
scope | 生命周期作用域: singleton:默认的,单例模式 prototype:多例模式 request:一个 bean 定义作用于 HTTP 的 Request 生命周期;是指每个 HTTP 的 Request 拥有自己的通过一个 bean 定义创建的实例。 仅在 web 中有效 session:一个 bean 定义作用于 HTTP 的 Session 生命周期;是指每 个 HTTP 的 Session 拥有自己的通过一个 bean 定义创建的实例。仅 在 web 中有效 |
<property> 标签:属性标签,负责给对象属性赋值。
名称 | 解释 |
---|---|
name | 属性名 |
value | 通过字符串来指定属性或构造器参数的值 |
ref | 通过使用 ref 标记指定 bean 属性的目标 bean |
<constructor-arg> 标签:构造器标签,负责给对象属性赋值。
名称 | 解释 |
---|---|
index | 构造器参数位置,从0开始 |
name | 属性名 |
value | 通过字符串来指定属性或构造器参数的值 |
ref | 通过使用 ref 标记指定 bean 属性的目标 bean |
type | 当前参数的数据类型 |
注解方式
注解不需要 applicationContext.xml 配置文件,以@注解的方式注册和管理 bean。
@Component
public class GuanWei{
@Resource //byName+byType 首先按照名称查找,如果找到赋值 如果没有找到,按照类型查询
private ZhangSan zhang;
public void print(){
System.out.println("我是关为!");
zhang.print();
}
}
注解 API
一些常用的注解,产生对象,引入依赖,设置配置等。
名称 | 解释 |
---|---|
@Component | 启动 Spring 后,会自动把它转成容器管理的 Bean |
@Repository | 用于对DAO层注解,但是目前该功能与 @Component 相同 |
@Service | 用于对业务层注解,但是目前该功能与 @Component 相同 |
@Controller | 用于对控制层注解,但是目前该功能与 @Component 相同 |
@Scope | 作用域,等同于 XML 中的 scope 写法例如: @Component(“guanwei”) @Scope(“prototype”) |
@Resource | 默认是按照名称来装配注入的,只有当找不到与名称匹配的 bean 才会按 照类型来装配注入 |
@Autowired | 默认是按照类型装配注入的,如果想按照名称来转配注入,则需要结合 @Qualifier 一起使用 |
@Qualifier | @Qualifier("XXX") 中的 XX 是 Bean 的名称,所以 @Autowired 和 @Qualifier 结合使用时,自动注入的策略就从 byType 转变成 byName 了 |
@Configuration | 配置注解,标明当前类是一个配置类 |
@ComponentScan | 配置扫描那些包,一次可以扫描多个包 |
@Bean | 将方法的返回结果存放到 IoC 容器,就是手动产生对象并存放到 IoC 容器中 |
@Lazy | 设置当前类是否懒加载,配合产生对象注解使用 |
Spring IOC 中 bean 的生命周期
Bean 的生命周期包括 Bean 的定义,Bean 的初始化,Bean 的使用,Bean 的销毁。
Bean 的定义:一般 Bean 使用 XML 文件的方式进行定义,定义的时候将 Bean 之间的依赖关系和属性的赋值都进行了定义。
Bean 的初始化:其实 Bean 的初始化包括 Bean 的创建和初始化两个方法,Bean 的创建和初始化一般是同步进行的,Bean 在完成创建后直接就会进行初始化操作,创建的时机与 Bean 的 lazy-init 属性的设置有关。
Bean 的使用:在 web 程序运行期间,发生对某一个 Bean 的调用时,就会使用这个 Bean实例,如果使用编码的方式来获取 Bean 同样也是 Bean 的使用,Bean 的编码使用方式有三种,第一种是使用 BeanWarpper,第二种是使用 BeanFactory,第三种就是使用 ApplicationContext。
Bean 的销毁:Bean 实例在程序退出的时候会进行销毁,而在销毁之前会自动调用 Bean的 destory-method 属性指定名称的方法。
加载 xml 文件后 bean 的加载方式
默认情况下,是勤加载模式,加载 xml 后加载类文件并产生对象,我们也可以设置当前 bean 为懒加载模式,当设置为懒加载后,xml 加载后不会加载类文件,更不会产生对象。
设置懒加载方式
<bean id="da" class="com.dailyblue.java.spring.bean.DemoA" lazy-init="true"/>
// 当懒加载时,DemoA类没有被加载
ApplicationContext beans = new ClassPathXmlApplicationContext("applicationContext.xml");
// 从容器中获取到DemoA类型的对象时才加载类并产生对象
DemoA guanwei = beans.getBean(DemoA.class);
bean 的单例和多例模式
默认情况下,bean 是单例的,你可以设置当前 bean 为多例。
<bean id="da" class="com.dailyblue.java.spring.bean.DemoA" scope="prototype"/>
// da1 是单例模式的,每一次 getBean 时获取的都是同一个对象
DemoA a1 = beans.getBean("da1", DemoA.class); // 从容器中获取到DemoA类型的对象
DemoA a2 = beans.getBean("da1", DemoA.class);
System.out.println(a1 == a2);
// da2 是多例效果的,每一次 getBean 时都是产生对象
DemoA a3 = beans.getBean("da2", DemoA.class);
DemoA a4 = beans.getBean("da2", DemoA.class);
System.out.println(a3 == a4);
getBean 获取对象的方式
从容器中获取对象,我们主要由三种方式
- byName:根据名字获取
- byType:根据类型获取
- byName+byType:根据名字和类型获取
// byName 根据名称获取对象,缺点是返回结果是Object类型的,需要自行强转
Object obj1 = beans.getBean("db");
// byType 根据类型获取对象,缺点是容器中只能有一个DemoB类型的对象
DemoB obj2 = beans.getBean(DemoB.class);
// byName+byType:根据名称和类型获取,缺点是写得多
DemoB obj3 = beans.getBean("db", DemoB.class);