一、Spring简介
1.1、Spring概述
官网地址:https://spring.io
Spring 是最受欢迎的企业级 Java 应用程序开发框架,数以百万的来自世界各地的开发人员使用
Spring 框架来创建性能好、易于测试、可重用的代码。
Spring 框架是一个开源的 Java 平台,它最初是由 Rod Johnson 编写的,并且于 2003 年 6 月首 次在 Apache 2.0 许可下发布。
Spring 是轻量级的框架,其基础版本只有 2 MB 左右的大小。
Spring 框架的核心特性是可以用于开发任何 Java 应用程序,但是在 Java EE 平台上构建 web 应 用程序是需要扩展的。 Spring 框架的目标是使 J2EE 开发变得更容易使用,通过启用基于 POJO 编程模型来促进良好的编程实践。
1.2、Spring家族
项目列表:https://spring.io/projects
1.3、Spring Framework
Spring 基础框架,可以视为 Spring 基础设施,基本上任何其他 Spring 项目都是以 Spring Framework 为基础的。
1.3.1、Spring Framework特性
- 非侵入式:使用 Spring Framework 开发应用程序时,Spring 对应用程序本身的结构影响非常 小。对领域模型可以做到零污染;对功能性组件也只需要使用几个简单的注解进行标记,完全不会 破坏原有结构,反而能将组件结构进一步简化。这就使得基于 Spring Framework 开发应用程序 时结构清晰、简洁优雅。
- 控制反转:IOC——Inversion of Control,翻转资源获取方向。把自己创建资源、向环境索取资源 变成环境将资源准备好,我们享受资源注入。
- 面向切面编程:AOP——Aspect Oriented Programming,在不修改源代码的基础上增强代码功 能。
- 容器:Spring IOC 是一个容器,因为它包含并且管理组件对象的生命周期。组件享受到了容器化 的管理,替程序员屏蔽了组件创建过程中的大量细节,极大的降低了使用门槛,大幅度提高了开发 效率。
- 组件化:Spring 实现了使用简单的组件配置组合成一个复杂的应用。在 Spring 中可以使用 XML 和 Java 注解组合这些对象。这使得我们可以基于一个个功能明确、边界清晰的组件有条不紊的搭 建超大型复杂应用系统。
- 声明式:很多以前需要编写代码才能实现的功能,现在只需要声明需求即可由框架代为实现。
- 一站式:在 IOC 和 AOP 的基础上可以整合各种企业应用的开源框架和优秀的第三方类库。而且 Spring 旗下的项目已经覆盖了广泛领域,很多方面的功能性需求可以在 Spring Framework 的基 础上全部使用 Spring 来实现。
1.3.2、Spring Framework五大功能模块
二、IOC
2.1、IOC容器
2.1.1、IOC思想
IOC:Inversion of Control,翻译过来是反转控制。
① 获取资源的传统方式
自己做饭:买菜、洗菜、择菜、改刀、炒菜,全过程参与,费时费力,必须清楚了解资源创建整个过程 中的全部细节且熟练掌握。
在应用程序中的组件需要获取资源时,传统的方式是组件主动的从容器中获取所需要的资源,在这样的 模式下开发人员往往需要知道在具体容器中特定资源的获取方式,增加了学习成本,同时降低了开发效率。
② 反转控制方式获取资源
点外卖:下单、等、吃,省时省力,不必关心资源创建过程的所有细节。
反转控制的思想完全颠覆了应用程序组件获取资源的传统方式:反转了资源的获取方向——改由容器主 动的将资源推送给需要的组件,开发人员不需要知道容器是如何创建资源对象的,只需要提供接收资源 的方式即可,极大的降低了学习成本,提高了开发的效率。这种行为也称为查找的被动形式。
③ DI
DI:Dependency Injection,翻译过来是依赖注入。
DI 是 IOC 的另一种表述方式:即组件以一些预先定义好的方式(例如:setter 方法)接受来自于容器的资源注入。相对于IOC而言,这种表述更直接。
所以结论是:IOC 就是一种反转控制的思想, 而 DI 是对 IOC 的一种具体实现。
2.1.2、IOC容器在Spring中的实现
Spring 的 IOC 容器就是 IOC 思想的一个落地的产品实现。IOC 容器中管理的组件也叫做 bean。在创建 bean 之前,首先需要创建 IOC 容器。
Spring 提供了 IOC 容器的两种实现方式:
① BeanFactory
这是 IOC 容器的基本实现,是 Spring 内部使用的接口。面向 Spring 本身,不提供给开发人员使用。
② ApplicationContext
BeanFactory 的子接口,提供了更多高级特性。面向 Spring 的使用者,几乎所有场合都使用 ApplicationContext 而不是底层的 BeanFactory。
③ ApplicationContext的主要实现类
2.2、基于XML管理bean
2.2.1、入门案例
① 创建Maven Module
② 引入依赖
<dependencies>
<!-- 基于Maven依赖传递性,导入spring-context依赖即可导入当前所需所有jar包 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.1</version>
</dependency>
<!-- junit测试 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
</dependencies>
③ 创建类HelloWorld
public class HelloWorld {
public void sayHello() {
System.out.println("hello,spring");
}
}
④ 创建Spring的配置文件
⑤ 在Spring的配置文件中配置bean
<!--
配置HelloWorld所对应的bean,即将HelloWorld的对象交给Spring的IOC容器管理
通过bean标签配置IOC容器所管理的bean
属性:
id:设置bean的唯一标识
class:设置bean所对应类型的全类名
-->
<bean id="helloworld" class="com.ssm.spring.pojo.HelloWorld"></bean>
⑥ 创建测试类测试
@Test
public void test() {
//获取IOC容器
ApplicationContext ioc = new ClassPathXmlApplicationContext("applicationContext.xml");
//获取IOC容器中的Bean
HelloWorld helloWorld = (HelloWorld) ioc.getBean("helloWorld");
helloWorld.sayHello();
}
⑦ 思路
⑧ 注意
Spring 底层默认通过反射技术调用组件类的无参构造器来创建组件对象,这一点需要注意。如果在需要 无参构造器时,没有无参构造器,则会抛出下面的异常:
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'helloworld' defined in class path resource [applicationContext.xml]: Instantiation of bean failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.atguigu.spring.bean.HelloWorld]: No default constructor found; nested exception is java.lang.NoSuchMethodException: com.atguigu.spring.bean.HelloWorld.<int>()
2.2.2、获取bean
① 方式一:根据id获取
由于 id 属性指定了 bean 的唯一标识,所以根据 bean 标签的 id 属性可以精确获取到一个组件对象。 上个实验中我们使用的就是这种方式。
② 方式二:根据类型获取
@Test
public void testHelloWorld(){
ApplicationContext ioc = new ClassPathXmlApplicationContext("applicationContext.xml");
HelloWorld helloWorld = (HelloWorld) ioc.getBean(helloWorld.class);
helloWorld.sayHello();
}
③ 方式三:根据id和类型
@Test
public void test() {
//获取IOC容器
ApplicationContext ioc = new ClassPathXmlApplicationContext("applicationContext.xml");
//获取IOC容器中的Bean
HelloWorld helloWorld = (HelloWorld) ioc.getBean("helloWorld", helloWorld.class);
helloWorld.sayHello();
}
④ 注意
当根据类型获取bean时,要求IOC容器中指定类型的bean有且只能有一个 当IOC容器中一共配置了两个:
<bean id="helloWorldOne" class="com.atguigu.spring.bean.HelloWorld"></bean>
<bean id="helloWorldTwo" class="com.atguigu.spring.bean.HelloWorld"></bean>
根据类型获取时会抛出异常:
org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'com.atguigu.spring.bean.HelloWorld' available: expected single matching bean but found 2: helloworldOne,helloworldTwo
⑤ 扩展
如果组件类实现了接口,根据接口类型可以获取 bean 吗?
可以,前提是bean唯一
如果一个接口有多个实现类,这些实现类都配置了 bean,根据接口类型可以获取 bean 吗?
不行,因为bean不唯一
⑥ 结论
根据类型来获取bean时,在满足bean唯一性的前提下,其实只是看:『对象 instanceof 指定的类 型』的返回结果,只要返回的是true就可以认定为和类型匹配,能够获取到。
2.2.3、依赖注入之setter注入
① 创建学生类Student
② 配置bean时为属性赋值
<bean id="studentTwo" class="com.ssm.spring.pojo.Student">
<!-- property标签:通过组件类的setXxx()方法给组件对象设置属性 -->
<!-- name属性:指定属性名(这个属性名是getXxx()、setXxx()方法定义的,和成员变量无关)
-->
<!-- value属性:指定属性值 -->
<property name="sid" value="1001"></property>
<property name="sname" value="张三"></property>
<property name="age" value="23"></property>
<property name="sex" value="男"></property>
</bean>
③ 测试
@Test
public void testDI() {
//获取IOC容器
ApplicationContext ioc = new ClassPathXmlApplicationContext("spring-ioc.xml");
//获取IOC容器中的Bean
Student studentTwo = ioc.getBean("studentTwo", Student.class);
System.out.println(studentTwo);
}
2.2.4、依赖注入之构造器注入
① 在Student类中添加有参构造
② 配置bean
<bean id="studentThree" class="com.ssm.spring.pojo.Student">
<constructor-arg value="1002"></constructor-arg>
<constructor-arg value="李四"></constructor-arg>
<constructor-arg value="33"></constructor-arg>
<constructor-arg value="女"></constructor-arg>
</bean>
注意:
constructor-arg 标签还有两个属性可以进一步描述构造器参数:
- index属性:指定参数所在位置的索引(从0开始)
- name属性:指定参数名
③ 测试
@Test
public void testDI