Spring——IOC & DI
IOC(Inversion of Control):反转控制
在应用程序中的组件需要获取资源时,传统的方式是组件主动的从容器中获取所需 要的资源,在这样的模式下开发人员往往需要知道在具体容器中特定资源的获取方式,
增加了学习成本,同时降低了开发效率。
反转控制的思想完全颠覆了应用程序组件获取资源的传统方式:反转了资源的获取 方向——改由容器主动的将资源推送给需要的组件,开发人员不需要知道容器是如何创 建资源对象的,只需要提供接收资源的方式即可,极大的降低了学习成本,提高了开发 的效率。这种行为也称为查找的被动形式。
DI(Dependency Injection):依赖注入
IOC 的另一种表述方式:即组件以一些预先定义好的方式(例如:setter 方法)接受 来自于容器的资源注入。相对于 IOC 而言,这种表述更直接。
IOC 容器在 Spring 中的实现
1)在通过 IOC 容器读取 Bean 的实例之前,需要先将 IOC 容器本身实例化。
2)Spring 提供了 IOC 容器的两种实现方式
① BeanFactory:IOC 容器的基本实现,是 Spring 内部的基础设施,是面向Spring 本身的,不是提供给开发人员使用的。
② ApplicationContext:BeanFactory 的子接口,提供了更多高级特性。面向 Spring 的 使 用 者 , 几 乎 所 有 场 合 都 使 用 ApplicationContext 而 不 是 底 层 的 BeanFactory
ApplicationContext 的主要实现类
1) ClassPathXmlApplicationContext:对应类路径下的 XML 格式的配置文件
2) FileSystemXmlApplicationContext:对应文件系统中的 XML 格式的配置文件
3) 在初始化时就创建单例的 bean,也可以通过配置的方式指定创建的 Bean 是多实例的。
ConfigurableApplicationContext
1) 是 ApplicationContext 的子接口,包含一些扩展方法
2) refresh()和 close()让 ApplicationContext 具有启动、关闭和刷新上下文的能力。
WebApplicationContext
1) 专门为 WEB 应用而准备的,它允许从相对于 WEB 根目录的路径中完成初始化工作
给 bean 的属性赋值
1 依赖注入的方式
1). 通过 bean 的 **setXxx()**方法赋值
Hello World 中使用的就是这种方式
2). 通过 bean 的构造器赋值
Spring 自动匹配合适的构造器
通过索引值指定参数位置
通过类型区分重载的构造器
2 p 名称空间
为了简化 XML 文件的配置,越来越多的 XML 文件采用属性而非子元素配置信息。Spring 从 2.5 版本开始引入了一个新的 p 命名空间,可以通过元素属性的方式配置 Bean 的属性。 使用 p 命名空间后,基于 XML 的配置方式将进一步简化。
3 可以使用的值
1). 字面量
可以使用字符串表示的值,可以通过 value 属性或 value 子节点的方式指定
基本数据类型及其封装类、String 等类型都可以采取字面值注入的方式
若字面值中包含特殊字符,可以使用<![CDATA[]]>把字面值包裹起来
2). null 值
子节点
3). 给 bean 的级联属性赋值 在name中直接 . 即可
4). 外部已声明的 bean 使用ref属性
5). 内部 bean
当 bean 实例仅仅给一个特定的属性使用时,可以将其声明为内部 bean。内部bean 声明直接包含在或元素里,不需要设置任何 id 或 name 属性 内部 bean 不能使用在任何其他地方
4 集合属性
在 Spring 中可以通过一组内置的 XML 标签来配置集合属性,例如:,或。
4.1 数组和 List
配置 java.util.List 类型的属性,需要指定标签,在标签里包含一些元素。这些标签 可以通过指定简单的常量值,通过指定对其他 Bean 的引用。通过 指定内置 bean 定义。通过指定空元素。甚至可以内嵌其他集合。
数组的定义和 List 一样,都使用元素。
配置 java.util.Set 需要使用标签,定义的方法与 List 一样。
4.2 Map
Java.util.Map 通过标签定义,标签里可以使用多个作为子标签.每个条目包含一个键和一个值。
必须在标签里定义键。 因为键和值的类型没有限制,所以可以自由地为它们指定、、或 元素。 可以将 Map 的键和值作为的属性定义:简单常量使用 key 和 value 来定义;bean 引用通过 key-ref 和 value-ref 属性定义。
4.3 集合类型的 bean
如果只能将集合对象配置在某个 bean 内部,则这个集合的配置将不能重用。我们需要 将集合 bean 的配置拿到外面,供其他 bean 引用。 配置集合类型的 bean 需要引入 util 名称空间
5 FactoryBean
2.5.1 FactoryBean
Spring 中有两种类型的 bean,一种是普通 bean,另一种是工厂 bean,即 FactoryBean。
工厂 bean 跟普通 bean 不同,其返回的对象不是指定类的一个实例,其返回的是该工 厂 bean 的 getObject 方法所返回的对象。
工厂 bean 必须实现 org.springframework.beans.factory.FactoryBean 接口。
6 bean 的高级配置
6.1 配置信息的继承
Spring 允许继承 bean 的配置,被继承的 bean 称为父 bean。继承这个父 bean 的 bean 为子 bean .子 bean 从父 bean 中继承配置,包括 bean 的属性配置 子 bean 也可以覆盖从父 bean 继承过来的配置
补充说明
父 bean 可以作为配置模板,也可以作为 bean 实例。若只想把父 bean 作为模板,可以 设置的 abstract 属性为 true,这样 Spring 将不会实例化这个 bean
如果一个 bean 的 class 属性没有指定,则必须是抽象 bean 并不是元素里的所有属性都会被继承。比如:autowire,abstract 等。
也可以忽略父 bean 的 class 属性,让子 bean 指定自己的类,而共享相同的属性配置。 但 此时 abstract 必须设为 true
6.2 bean 之间的依赖
有的时候创建一个 bean 的时候需要保证另外一个 bean 也被创建,这时我们称前面的bean 对后面的 bean 有依赖。例如:要求创建 Employee 对象的时候必须创建 Department。
这里需要注意的是依赖关系不等于引用关系,Employee 即使依赖 Department 也可以不 引用它。
7 bean 的作用域****★
在 Spring 中,可以在元素的 scope 属性里设置 bean 的作用域,以决定这个 bean 是单实例的还是多实例的。
默认情况下,Spring 只为每个在 IOC 容器里声明的 bean 创建唯一一个实例,整个 IOC 容器范围内都能共享该实例:所有后续的 getBean()调用和 bean 引用都将返回这个唯一的 bean 实例。该作用域被称为 singleton,它是所有 bean 的默认作用域。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZX8NAiJL-1586866765091)(images/image-20200404184034041.png)]
当 bean 的作用域为单例时,Spring 会在 IOC 容器对象创建时就创建 bean 的对象实例。 而当 bean 的作用域为 prototype 时,IOC 容器在获取 bean 的实例时创建 bean 的实例对象。
8 bean 的生命周期
- Spring IOC 容器可以管理 bean 的生命周期,Spring 允许在 bean 生命周期内特定的时间 点执行指定的任务。
- Spring IOC 容器对 bean 的生命周期进行管理的过程:
① 通过构造器或工厂方法创建 bean 实例
② 为 bean 的属性设置值和对其他 bean 的引用
③ 调用 bean 的初始化方法
④ bean 可以使用了
⑤ 当容器关闭时,调用 bean 的销毁方法
- 在配置 bean 时,通过 init-method 和 destroy-method 属性为 bean 指定初始化和销毁 方法
- bean 的后置处理器
① bean 后置处理器允许在调用初始化方法前后对 bean 进行额外的处理
② bean 后置处理器对 IOC 容器里的所有 bean 实例逐一处理,而非单一实例。其典型 应用是:检查 bean 属性的正确性或根据特定的标准更改 bean 的属性。
③ bean 后置处理器时需要实现接口: org.springframework.beans.factory.config.BeanPostProcessor。
在初始化方法被调用前 后,Spring 将把每个 bean 实例分别传递给上述接口的以下两个方法:
●postProcessBeforeInitialization(Object, String)
●postProcessAfterInitialization(Object, String)
- 添加 bean 后置处理器后 bean 的生命周期
①通过构造器或工厂方法创建 bean 实例
②为 bean 的属性设置值和对其他 bean 的引用
③将 bean 实例传递给 bean 后置处理器的 postProcessBeforeInitialization()方法
④调用 bean 的初始化方法
⑤将 bean 实例传递给 bean 后置处理器的 postProcessAfterInitialization()方法
⑥bean 可以使用了
⑦当容器关闭时调用 bean 的销毁方法
9 引用外部属性文件
当 bean 的配置信息逐渐增多时,查找和修改一些 bean 的配置信息就变得愈加困难。这 时可以将一部分信息提取到 bean 配置文件的外部,以 properties 格式的属性文件保存起来, 同时在 bean 的配置文件中引用 properties 属性文件中的内容,从而实现一部分属性值在发 生变化时仅修改 properties 属性文件即可。这种技术多用于连接数据库的基本信息的配置。
9.1 直接配置
<!-- 直接配置 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="user" value="root"/>
<property name="password" value="root"/>
<property name="jdbcUrl" value="jdbc:mysql:///test"/>
<property name="driverClass" value="com.mysql.jdbc.Driver"/>
</bean>
9.2 使用外部的属性文件
1. 创建 properties 属性文件
2. 引入 context 名称空间
**3.**指定 properties 属性文件的位置
**4.**从 properties 属性文件中引入属性值
10 自动装配
10.1 自动装配的概念
手动装配:以 value 或 ref 的方式明确指定属性值都是手动装配。
自动装配:根据指定的装配规则,不需要明确指定,Spring 自动将匹配的属性值注入bean 中。
10.2 装配模式
根据类型自动装配:将类型匹配的 bean 作为属性注入到另一个 bean 中。若 IOC 容器中 有多个与目标 bean 类型一致的 bean,Spring 将无法判定哪个 bean 最合适该属性,所 以不能执行自动装配
根据名称自动装配:必须将目标 bean 的名称和属性名设置的完全相同
通过构造器自动装配:当 bean 中存在多个构造器时,此种自动装配方式将会很复杂。不推荐使用。
10.3 选用建议
相对于使用注解的方式实现的自动装配,在 XML 文档中进行的自动装配略显笨拙,在项目中更多的使用注解的方式实现。
11 SpEL
11.1 简介
Spring Expression Language,Spring 表达式语言,简称 SpEL。支持运行时查询并可以操作对象图。和 JSP 页面上的 EL 表达式、Struts2 中用到的 OGNL 表达式一样,SpEL 根据 JavaBean 风 格的 getXxx()****、****setXxx()****方法定义的属性访问对象图,完全符合我们熟悉的操作习惯。
11.2 基本语法
SpEL 使用**#{…}**作为定界符,所有在大框号中的字符都将被认为是 SpEL 表达式。
2.11.3 使用字面量
整数:
小数:
科学计数法:
String 类型的字面量可以使用单引号或者双引号作为字符串的定界符号
<property name=“name” value="#{‘Chuck’}"/>
<property name=“name” value=’#{“Chuck”}’/>
Boolean:
11.4 引用其他 bean
<property name="detp" value="#{dept}"/>
11.5 引用其他 bean 的属性值作为自己某个属性的值
<property name="deptName" value="#{dept.deptName}"/>
11.6 调用非静态方法
<property name="salayOfYear" value="#{salaryGenerator.getSalaryOfYear(5000)}"/>
11.7 调用静态方法
<property name="circle" value="#{T(java.lang.Math).PI*20}"/>
11.8 运算符
算术运算符:+、-、*、/、%、^
字符串连接:+
比较运算符:<、>、==、<=、>=、lt、gt、eq、le、ge
逻辑运算符:and, or, not, |
三目运算符:判断条件?判断结果为 true 时的取值:判断结果为 false 时的取值
正则表达式:matches
12 通过注解配置 bean
12.1 概述
相对于 XML 方式而言,通过注解的方式配置 bean 更加简洁和优雅,而且和 MVC 组件 化开发的理念十分契合,是开发中常用的使用方式。
12.2 使用注解标识组件
普通组件:@Component 标识一个受 Spring IOC 容器管理的组件
持久化层组件:@Repository 标识一个受 Spring IOC 容器管理的持久化层组件
业务逻辑层组件:@Service 标识一个受 Spring IOC 容器管理的业务逻辑层组件
表述层控制器组件:@Controller 标识一个受 Spring IOC 容器管理的表述层控制器组件
组件命名规则
①默认情况:使用组件的简单类名首字母小写后得到的字符串作为 bean 的 id
②使用组件注解的 value 属性指定 bean 的 id
注意:事实上 Spring 并没有能力识别一个组件到底是不是它所标记的类型,即使将 @Respository 注解用在一个表述层控制器组件上面也不会产生任何错误,所以 @Respository、@Service、@Controller 这几个注解仅仅是为了让开发人员自己 明确 当前的组件扮演的角色。
12.3 扫描组件
组件被上述注解标识后还需要通过 Spring 进行扫描才能够侦测到。
- 指定被扫描的 package
<context:component-scan base-package=*"com.atguigu.component"*/>
- 详细说明
①base-package 属性指定一个需要扫描的基类包,Spring 容器将会扫描这个基类包及其 子包中的所有类
②当需要扫描多个包时可以使用逗号分隔。
③如果仅希望扫描特定的类而非基包下的所有类,可使用 resource-pattern 属性过滤特 定的类,示例:
④包含与排除
●context:include-filter子节点表示要包含的目标类
注意:通常需要与 use-default-filters 属性配合使用才能够达到“仅包含某些 组件”这样的效果。即:通过将 use-default-filters 属性设置为 false, 禁用默认过滤器,然后扫描的就只是 include-filter 中的规则指定的 组件了。
●context:exclude-filter子节点表示要排除在外的目标类
●component-scan 下可以拥有若干个 include-filter 和 exclude-filter 子节点
●过滤表达式
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-y2DzIV85-1586866765094)(images/image-20200404185733737.png)]
JAR 包
必须在原有 JAR 包组合的基础上再导入一个:spring-aop-4.0.0.RELEASE.jar
12.4 组件装配
-
需求
Controller 组件中往往需要用到 Service 组件的实例,Service 组件中往往需要用到 Repository 组件的实例。Spring 可以通过注解的方式帮我们实现属性的装配。 -
实现依据
在指定要扫描的包时,context:component-scan 元素会自动注册一个 bean 的后置处 理器:AutowiredAnnotationBeanPostProcessor 的实例。该后置处理器可以自动装配标记 了**@Autowired**、@Resource 或@Inject 注解的属性。 -
@Autowired 注解
①根据类型实现自动装配。
②构造器、普通字段(即使是非 public)、一切具有参数的方法都可以应用@Autowired 注解
③默认情况下,所有使用@Autowired 注解的属性都需要被设置。当 Spring 找不到匹 配的 bean 装配属性时,会抛出异常。
④若某一属性允许不被设置,可以设置@Autowired 注解的 required 属性为 false
⑤默认情况下,当 IOC 容器里存在多个类型兼容的 bean 时,Spring 会尝试匹配 bean 的 id 值是否与变量名相同,如果相同则进行装配。如果 bean 的 id 值不相同,通 过类 型的自动装配将无法工作。此时可以在@Qualifier 注解里提供 bean
的名称。Spring 甚至允许在方法的形参上标注@Qualifiter 注解指定注入 bean 的名称。
⑥@Autowired 注解也可以应用在数组类型的属性上,此时 Spring 将会把所有匹配的 bean 进行自动装配。
⑦@Autowired 注解也可以应用在集合属性上,此时 Spring 读取该集合的类型信息, 然后自动装配所有与之兼容的 bean。
⑧@Autowired 注解用在 java.util.Map 上时,若该 Map 的键值为 String,那么 Spring 将自动装配与值类型兼容的 bean 作为值,并以 bean 的 id 值作为键。
-
@Resource
@Resource 注解要求提供一个 bean 名称的属性,若该属性为空,则自动采用标注处的 变量或方法名作为 bean 的名称。 -
@Inject
@Inject 和@Autowired 注解一样也是按类型注入匹配的 bean,但没有 reqired 属性。
总结
//ioc是一个容器,帮我们管理所有的组件;
//1、依赖注入;@Autowired;自动赋值
//2、某个组件要使用Spring提供的更多(IOC、AOP)必须加入到容器中;
//体会:
//1、容器启动。创建所有单实例bean
//2、autowired自动装配的时候,是从容器中找这些符合要求的bean
//3、ioc.getBean("bookServlet");也是从容器中找到这个bean;
//4、容器中包括了所有的bean;
//5、调试spring的源码,容器到底是什么?其实就是一个map;
//6、这个map中保存所有创建好的bean,并提供外界获取功能...
//7、探索,单实例的bean都保存到哪个map中了。【源码-扩展】
//8、源码调试的思路;
// 从helloworld开始的;给helloworld每一个关键步骤打上断点。进去看里面都做了什么工作?
// 怎么知道哪些方法都是干什么的
// 1、翻译这个方法是干什么?
// 2、放行这个方法,看控制台,看debug的每一个变量的变化
// 3、看方法注释;
// 学到的一点:1)、规范注释, 2)、规范方法名和类名;