Spring IOC
# Spring IOC
Ioc—Inversion of Control,即“控制反转”,不是什么技术,而是一种设计思想
Ioc 在开发中,无需自行实例对象,而是有 Spring Ioc容器 创建。Spring容器会负责控制程序之间的关系,而不是由代码直接控制,因此,控制权由 Spring容器 接管,控制权发生了反转,是Ioc设计思想
Spring 提供了两种 IoC 容器,分别为 BeanFactory 和 ApplicationContext
# 对象说明和获取
# BeanFactory
BeanFactory接口 是一个管理Bean的工厂, 也是最顶层的接口 , 定义了Ioc容器的基本功能规范 . 它主要根据xml配置文件中的定义Bean进行管理,主要负责初始化各种Bean对象
可进行 延迟加载 , 他们会在调用的时候进行加载对象
// 加载 xml文件
BeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource("application.xml"));
beanFactory.getBean("...");
BeanFactory有以下三个重要子接口 :
- ListableBeanFactory接口 : Bean可序列化
- HierarchicalBeanFactory接口 : Bean有继承关系 , 每个Bean有父Bean
- AutowireCapableBeanFactory接口 : Bean的自动装配规则
# ApplicationContext
ApplicationContext 是 BeanFactory 的子接口,也是 应用上下文。支持了 BeanFactory 的所有功能
ApplicationContext接口有两个常用的 实现类,主要用于加载配置文件的
ClassPathXmlApplicationContext 通过 类路径上下文加载
==new ClassPathXmlApplicationContext(<Spring配置文件的名>)==
将普通路径解释为包含包路径的类路径资源名称(例如“mypackage/myresource.txt”)
FileSystemXmlApplicationContext 通过 文件系统路径/URL 加载指定配置文件
==new FileSystemXmlApplicationContext(<文件系统路径/URL>);==
**注意:**普通路径将始终被解释为相对于当前 VM 工作目录,即使它们以斜杠开头 使用显式的“file:”前缀来强制使用绝对文件路径
对象获取实例
//指定决定绝对路径的文件
new FileSystemXmlApplicationContext(new FileSystemResource("D:\applicationContext.xml"));
//指定相对根路径的文件
new ClassPathXmlApplicationContext("applicationContext.xml");
# Bean
Spring Bean标签 可以想象成 我们使用的 实例对象 ,Spring是通过在配置文件中进行调取出来的实例对象(反射原理) . 在以往的操作 是通过 硬编码 new 出来的新对象 ,因此 我们需要提前 编写好配置文件
Bean标签,因此我们需要自己手动配置Bean对象,以下有两种Spring配置文件支持的格式
- Properties配置文件 (只能存在键值形式存在
- XML配置文件
XML配置文件
该配置文件的根元素是 <beans>
,该元素包含了多个 <bean>
子元素,每一个 <bean>
子元素定义了一个对象
<!-- 使用id属性定义person1,其对应的实现类为com.mengma.person1 (类路径及包路径)-->
<bean id="person1" class="com.mengma.person1 " />
<!--使用name属性定义person2,其对应的实现类为com.mengma.domain.Person2 (类路径及包路径) -->
<bean name="Person2" class="com.mengma.domain.Person2"/>
bean标签属性说明
属性 | 值 | 说明 |
---|---|---|
id | String | Bean 的唯一标识符,Spring IoC 容器对 Bean的 配置和管理都通过该属性完成 |
name | String | Bean 的名称,可通过 name属性 为同一个 Bean同时指定多个名称,每个名之间用 逗号/分号 隔开。Spring容器 可通过 name属性 配置和管理容器中的 Bean |
class | String | Bean 的具体实现类 (全限定名 |
scope | String | 定义 的 [Bean作用域](#Bean 作用域) (点击跳转 |
autowire | byName | byType | Bean自动装配,根据定义的 name/type 进行自动装配 |
lazy-init | boolean | 延迟加载 (默认false),在getBean()方法调用时才实例 |
init-method | String | 初始化对象方法 |
destroy-method | String | 销毁对象方法 |
# 应用示例
实体类 People.calss
类型 | 成员变量 |
---|---|
String | name |
String | age |
配置文件 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">
<!-- Bean实例方式
- 构造器实例
- 静态工厂实例
- 实例工厂实例
-->
<!-- 无参构造器 -->
<bean id="person1" class="com.sans.Person"/>
<!-- 有参构造器 -->
<!-- Bean属性 初始化/销毁 -->
<bean id="person2" class="com.sans.Person" init-method="init" destroy-method="destroy">
<constructor-arg index="0" value="张三"/>
<constructor-arg index="1" value="Spring挺简单的嘛"/>
</bean>
<!-- 静态工厂实例 -->
<bean id="personFactory1" class="com.sans.MyFactory" factory-method="instanceLisi" />
<!-- 实例工厂实例 -->
<bean id="factory" class="com.sans.MyFactory"/>
<bean id="personFactory2" factory-bean="factory" factory-method="instanceZhangsan"/>
</beans>
测试
@Test
public void beanExampleTest() {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
Person person1 = (Person) applicationContext.getBean("person1");
Person person2 = (Person) applicationContext.getBean("person2");
Person factory1 = (Person) applicationContext.getBean("personFactory1");
Person factory2 = (Person) applicationContext.getBean("personFactory2");
System.out.println("person1 = " + person1);
System.out.println("person2 = " + person2);
System.out.println("factory1 = " + factory1);
System.out.println("factory2 = " + factory2);
/* 结果:
Person对象初始化: Person{name='张三', msg='Spring挺简单的嘛'}
Person对象初始化: Person{name='null', msg='null'}
person1 = Person{name='null', msg='null'}
person2 = Person{name='张三', msg='Spring挺简单的嘛'}
factory1 = Person{name='李四', msg='我也这么觉得!'}
factory2 = Person{name='张三', msg='Spring挺简单的嘛'}
*/
}
# Bean 作用域
Spring Bean作用域,可方便的管理 Bean应用的时期 以及场景
只需在 <bean>.scope
属性 配置范围值即可。scope范围值有以下6种:
属性值 | 说明 |
---|---|
singleton | 单例模式,每次在容器获取都是同一 Bean实例 (默认值) |
prototype | 原型模式,每次在容器获取都是新的 Bean实例 |
request | 每次request请求,容器都会创建一个 Bean实例。该域只在当前 request 内有效 |
session | 每次session会话,不同的会话使用的 Bean实例不同。该域仅在当前 Session 内有效 |
application | Web应用 共享一个 Bean实例。该域在当前 ServletContext 内有效(和单例模式差不多 |
websocket | websocket 的作用域是 WebSocket ,即在整个 WebSocket 中有效 |
# 依赖注入 DI
DI—Dependency Injection,即 依赖注入。需要通过 简单的配置,而无需任何代码就可指定目标需要的资源。而注入的方式有:
- setter
- 构造器
- 接口
- 注解
Ioc 和 DI 是同一个概念的不同角度描述。IoC是一种思想;DI是实现它的手段 Spring框架使用依 赖注入实现IoC
# 属性注入
Bean 属性注入,是将属性数据注入到 Bean 中的过程
实现属性注入方式 :
- 构造函数注入
- setter 注入
bean标签 代表的是一个实例对象,实例对象中 包含多个 属性/子标签 等。以下说明当中的标签以及作用
constructor-arg标签 构造实例。应用的构造器指定的参数进行实例对象。该标签有以下属性
属性 | 值 | 说明 |
---|---|---|
index | String | 传参的序号(从0开始 |
value | String | 指定传递的 常量值 |
name | String | 构造方法对应的 形参名称 |
ref | String | 引用配置 Bean实例 |
type | String | 传参的属性类型 |
**ref:**引用可通过 id/name 属性的值 进行引入
property标签 set注入。该标签是通过 属性的set方法进行填充数据,因此属性一定要有set方法。该标签有以下属性
属性 | 值 | 说明 |
---|---|---|
name | String | 属性名称 |
ref | String | 引用配置 Bean实例 |
value | String | 配置指定 常量值 |
注意:
name属性 在配置的时候,如果失去高亮,那么很有可能实体类没有配置该属性的 set方法 (前提:需要配置Spring上下文
set注入P命名空间,使用前提父标签需要引入以下配置
==xmlns:p="http://www.springframework.org/schema/p"==
集合属性配置 在配置当中难免会存在一些特殊情况,例如集合属性的配置。以下是 集合标签的说明
标签 | 说明 |
---|---|
<array> | 封装数组(可重复 |
<list> | 封装 List (可重复 |
<set> | 封装 Set(不可重复 |
<map> | 封装 Map (k和v均可任意类型 |
<props> | 封装 Map (k和v都是字符串类型 |
<array>
封装数组(可重复<ref>.bean
:引用对象元素<value>
:常量值
<list>
封装List(可重复<ref>.bean
:引用对象元素<value>
:常量值
<set>
封装 Set(不可重复<ref>.bean
:引用对象元素<value>
:常量值
<map>
封装 Map (k和v均可任意类型<entry>.key
:Map键 K<entry>.value
:Map值 V键和值可以 都可以 引用实例对象。引用方式 属性名后缀添加
-ref
<props>
封装 Map (k和v都是字符串类型<prop>.key
:Map键 K<prop>
:Map值 V(标签包裹
应用示例
步骤:
- 编写好实体类的属性以及get/set方法
- 在 xml/Properties 配置上编辑 bean实例
- 通过 ==new ClassPathXmlApplicationContext(<配置文件路径>).getBean()==方法获取实例对象
实体类 Person.class
public class Person {
private String name;
private String msg;
// 数组类型
private Person[] array;
// 集合 特殊应用
private List<Person> list;
private Set<String> set;
private Map<String, Object> map;
private Properties properties;
// 构造方法注入
public Person() {
}
public Person(String name, String msg) {
this.name = name;
this.msg = msg;
}
// 省略 自动生成的 get/set 方法
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", msg='" + msg + '\'' +
'}';
}
}
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"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 无参构造 -->
<bean id="person1" class="com.sans.bean.Person"/>
<!-- 全参构造 -->
<bean id="person2" class="com.sans.bean.Person">
<constructor-arg index="0" value="张三"/>
<constructor-arg index="1" value="Spring挺简单的嘛"/>
</bean>
<!-- set注入 -->
<bean id="person3" class="com.sans.bean.Person">
<property name="name" value="李四"/>
<property name="msg" value="我也觉得"/>
</bean>
<!-- P命名空间 set注入-->
<bean id="person4" class="com.sans.bean.Person" p:name="王五" p:msg="还行吧!挺好的"/>
<!-- set方法 集合注入 -->
<bean id="person5" class="com.sans.bean.Person">
<!-- 数组类型 -->
<property name="array">
<array>
<ref bean="person1"/>
<ref bean="person2"/>
<ref bean="person3"/>
<ref bean="person4"/>
</array>
</property>
<!-- List类型 -->
<property name="list">
<list>
<ref bean="person1"/>
<ref bean="person2"/>
<ref bean="person3"/>
<ref bean="person4"/>
</list>
</property>
<!-- Set类型 -->
<property name="set">
<set>
<value>张三</value>
<value>李四</value>
<value>王五</value>
</set>
</property>
<!-- Map类型 -->
<property name="map">
<map>
<entry key="No1" value="张三" />
<entry key="No2" value="李四"/>
<entry key="No3" value="王五"/>
</map>
</property>
<property name="properties">
<props>
<prop key="No1">张三</prop>
<prop key="No2">李四</prop>
<prop key="No3">王五</prop>
<prop key="No4">12</prop>
</props>
</property>
</bean>
</beans>
测试结果
public class Demo {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
/* 通过构造器 实例 */
@Test
public void constructor() {
Person person1 = (Person) applicationContext.getBean("person1");
Person person2 = (Person) applicationContext.getBean("person2");
System.out.println("person1 = " + person1);
System.out.println("person2 = " + person2);
/* 结果 toString方法直接打印
person1 = Person{name='null', msg='null'}
person2 = Person{name='张三', msg='Spring挺简单的嘛'}
*/
}
/* set注入 实例 */
@Test
public void setInjection() {
Person person3 = (Person) applicationContext.getBean("person3");
Person person4 = (Person) applicationContext.getBean("person4");
Person person5 = (Person) applicationContext.getBean("person5");
System.out.println("person3 = " + person3);
System.out.println("person4 = " + person4);
// 由于 person4对象 针对了 数组/集合 类型的属性测试
System.out.println("person5.getArray() = " + Arrays.toString(person5.getArray()));
System.out.println("person5.getList() = ");
person5.getList().forEach(person -> System.out.println("\t"+person));
System.out.println("person5.getSet() = " + person5.getSet());
System.out.println("person5.getMsg() = ");
person5.getMap().forEach((key, value) -> System.out.println("\t"+key+" : "+value));
System.out.println("person5.getProperties() = " + person5.getProperties());
/* 结果 (PS: 由于有一个是无参的引用因此为null
person3 = Person{name='李四', msg='我也觉得'}
person4 = Person{name='王五', msg='还行吧!挺好的'}
person5.getArray() = [Person{name='null', msg='null'}, Person{name='张三', msg='Spring挺简单的嘛'}, Person{name='李四', msg='我也觉得'}, Person{name='王五', msg='还行吧!挺好的'}]
person5.getList() =
Person{name='null', msg='null'}
Person{name='张三', msg='Spring挺简单的嘛'}
Person{name='李四', msg='我也觉得'}
Person{name='王五', msg='还行吧!挺好的'}
person5.getSet() = [张三, 李四, 王五]
person5.getMsg() =
No1 : 张三
No2 : 李四
No3 : 王五
person5.getProperties() = {No2=李四, No1=张三, No4=12, No3=王五}
*/
}
}
# 自动注入
Spring 的自动注入功能可以让 Spring容器 依据指定规则,为指定的 Bean 从应用的上下文中查找它所依赖的 Bean 并自动建立 Bean 之间的依赖关系。而这一过程是在完全不使用任何 <constructor-arg>.ref
/ <property>.ref
形式配置Bean
自动注入 主要作用是 简化 Spring 在 XML配置应用,因此在大工程中降低很多工作量
Spring容器 默认不支持自动装配的,需要在配置文件中的 <bean>.autowire
属性应用
<bean>.autowire
属性说明
属性值 | 说明 |
---|---|
byName | 根据 名称 自动注入 (id/name均可引用 |
byType | 根据 类型 自动注入 |
constructor | 根据 构造器参数 的数据类型,进行 byType模式 的自动装配 |
default | 默认采用上一级元素 <beans> 设置的自动装配规则(default-autowire)进行装配 |
no | 默认值,不使用自动注入 |
# byName装配
ByName表示 按属性名称自动装配,配置文件中 <bean>.id/name
属性值 必须与 类中属性名称 相同
示例
Person实体对象 (省略
public class MyFactory {
private Person person1;
private Person person2;
// 省略 自动生成的 set/get 方法
}
配置文件
<!-- 使用 id/name 属性 标明 -->
<bean id="person1" class="com.sans.bean.Person" p:name="张三" p:msg="Spring挺简单的嘛!"/>
<bean name="person2" class="com.sans.bean.Person" p:name="李四" p:msg="我也觉得"/>
<!-- byName -->
<bean id="Myfactory" class="com.sans.MyFactory" autowire="byName"/>
测试
@Test
public void auotInjection() {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
MyFactory factory = (MyFactory) applicationContext.getBean("Myfactory");
System.out.println(factory.getPerson1());
System.out.println(factory.getPerson2());
/* 结果:
Person{name='张三', msg='Spring挺简单的嘛!'}
Person{name='李四', msg='我也觉得'}
*/
}
# byType
byType表示 按属性类型自动装配,配置文件中 被注入类中的属性类型 与 容器内的Bean类型 相同,则自动注入
注意:
- 如果类中有一个以上的相同属性类型,那么该类型全部自动注入
- 在Spring Ioc容器上下文应用中不能存在相同类型的 Bean实例,否则无法引用
示例
Person实体对象 (省略
实体工厂对象 (省略
配置文件
<bean id="person1" class="com.sans.bean.Person" p:name="张三" p:msg="Spring挺简单的嘛!"/>
<!-- 不能存在相同类型
<bean name="person2" class="com.sans.bean.Person" p:name="李四" p:msg="我也觉得"/>-->
<!-- byClass -->
<bean id="Myfactory" class="com.sans.MyFactory" autowire="byType"/>
测试
@Test
public void auotInjection() {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
MyFactory factory = (MyFactory) applicationContext.getBean("Myfactory");
System.out.println(factory.getPerson1());
System.out.println(factory.getPerson2());
/* 结果:
Person{name='张三', msg='Spring挺简单的嘛!'}
Person{name='张三', msg='Spring挺简单的嘛!'}
*/
}
# 注解装配Bean
在 Spring 中,尽管使用 XML配置文件可实现 Bean 的装配工作,如果应用中 Bean 的数量较多,会导致 XML配置文件过于臃肿,从而给维护和升级带来一定的困难,因此 Spring 提供了注解应用,需要在原有的运行环境基础上做些变化,由此减少过多的Bean
@Component
在类上添加上 @Component
注解 表示该类实例的对象的权限交给 Spring容器 。注解的value属性用于指定bean的 id值 或 name值 ,但一般情况可省略 value 属性!(该注解指定id是类名的首字母小写)
以下注解与 @Component
注解 用法和功能 相同,表达的含义不同!
@Repository
dao层实现类的注解(持久层)
@Service
service层实现类的注解(业务层)
@Controller
controller层实现类的注解(控制层)
PS : 应用前提需要扫描 , 点击跳转
@Value
该 注解 是为指定属性 注入值。该注解用在 类的属性 或 指定set方法上 。 其注解注入 原理 和 set方法 写入是一样,因此 set方法 也可
package com;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component
public class MyUser {
//方式1
@Value("001")
private int id;
@Value("张三")
private String name;
@Value("23")
private int age;
@Value("洛杉矶")
private String locateion;
···
//方式2
@Value("001")
public void setId(int id) {
this.id = id;
}
@Value("张三")
public void setName(String name) {
this.name = name;
}
@Value("23")
public void setAge(int age) {
this.age = age;
}
@Value("洛杉矶")
public void setLocateion(String locateion) {
this.locateion = locateion;
}
···
}
# 包扫描
需要在 xml
配置文件中配置组件扫描器,用于在指定包中扫描的注解。如果未添加扫描,则对象添加的注解 将无法实例化
xml
配置文件 添加扫描
beans标签 配置属性
xmlns:context
属性: ==xmlns:context="http://www.springframework.org/schema/context"==xsi:schemaLocation
属性(添加值): http://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsd
··· <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> ··· </beans>
context:component-scan标签 指定包扫描 分隔符可以使用逗号(,)分号(;)还可以使用空格,不建议使用空格。
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!-- 包扫描 方法1: (包路径 Annotation.dao、Annotation.service、Annotation.controller)--> <context:component-scan base-package="Annotation.dao"/> <context:component-scan base-package="Annotation.service"/> <context:component-scan base-package="Annotation.controller"/> <!-- 包扫描 方法2: (包路径 Annotation.dao、Annotation.service、Annotation.controller)--> <context:component-scan base-package="Annotation.dao;Annotation.service;Annotation.dao"/> </beans>
添加对应注解即可
# 自动注入
自动注入指定对象的前提,需要为该对象添加注解,且 上级包 或 指定类 有扫描到!
@Autowired
用于对 Bean 的 属性变量、属性Set方法 及构造方法进行标注(构造方法应用的前提,参数必须指定的是对应的实例对象),配合对应的注解处理器完成 Bean 的自动配置工作。默认按照 Bean类型 进行装配
@Autowired 还有一个属性 required
,默认值为 true,表示匹配失败后终止程序运行;若值为 false,则匹配失败后 其属性值为 null
@Autowired 不同范围的作用
范围 | 说明 |
---|---|
构造器 | 构造器会被Spring调用以完成Bean的初始化 |
参数 | 参数会与Spring容器中的Bean匹配并传入 |
setter方法 | setter方法会被Spring调用将相关Bean注入 |
成员变量 | Spring会直接将相关Bean注入给字段 |
@Qualifier
与 @Autowired 注解配合使用,会将默认的按 Bean类型 装配修改为按 Bean实例队形名称 装配,Bean 的实例名称由 @Qualifier 注解的 value
参数 指定
@Resource
该注解 在jdk1.6版本以上 支持使用,Bean属性可指定按照 类型(type) 或 名称(name) 进行自动注入,可在 属性变量、属性Set方法 进行使用
@Autowired 与 @Resource 区别
注解 | 说明 |
---|---|
@Autowired | 根据 对象类型 进行获取 Bean对象 |
@Resource | 根据 方法名称 进行获取 Bean对象 |
提示
SpringBoot3.0.2以下版本 , @Resource
注解 需要参数 name
指定方法名称
← Spring Spring AOP→