参考自B站UP主视频《孙哥说Spring5》
文章目录
概述
什么是注入?
注入:通过 Spring ⼯⼚及配置⽂件,为所创建对象的成员变量赋值。
为什么要注入?
- 通过编码的⽅式,为成员变量进⾏赋值,存在耦合。
- 注入的好处:解耦合。
举个例子
public void test4() {
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("/applicationContext.xml");
Person person = (Person) ctx.getBean("person");
person.setId(1);
person.setName("zhenyu");
System.out.println(person);
}
通过代码为变量赋值,如果我们以后想修改变量的值,需要修改代码,重新编译,存在耦合
Spring 中如何注入
- 类的成员变量提供 set get ⽅法【底层还是调用了get set方法注入】
- 配置 spring 的配置⽂件
<bean id="person" name="p" class="com.yusael.basic.Person"> <property name="id"> <value>10</value> </property> <property name="name"> <value>yusael</value> </property> </bean>
Spring中的注入原理分析
Spring 底层通过调用对象属性对应的 set 方法,完成成员变量的赋值,这种⽅式也称为 Set注入。
Set注入的变量类型:
- JDK内置类型
8种基本类型 + String、数组类型、set集合、list集合、Map计划和、Properties集合。 - 用户自定义类型
针对于不同类型的成员变量,在<property标签中,需要嵌套其他标签:
<property>
xxxxx
</property>
对于不同类型的Set注入方式
JDK 内置类型
8种基本类型 + String
<property name="id">
<value>10</value>
</property>
<property name="name">
<value>yusael</value>
</property>
数组
<property name="emails">
<list>
<value>abc@qq.com</value>
<value>123@qq.com</value>
<value>hello@qq.com</value>
</list>
</property>
Set集合
<property name="tels">
<set>
<!-- set会自动去重 -->
<!-- set集合中如果没有约束泛型,那这里可以使用任何类型而不仅仅是value -->
<!-- 甚至还可以是set里面嵌套set -->
<value>138xxxxxxxxxx</value>
<value>139xxxxxxxxxx</value>
<value>138xxxxxxxxxx</value>
</set>
</property>
List集合
<property name="addresses">
<list>
<value>China</value>
<value>Earth</value>
<value>hell</value>
</list>
</property>
Map集合
<property name="qqs">
<map>
<entry>
<key><value>hello</value></key>
<value>12312312312</value>
</entry>
<entry>
<key><value>world</value></key>
<value>21314214214</value>
</entry>
</map>
</property>
Properites
<property name="p">
<props>
<prop key="key1">value1</prop>
<prop key="key2">value2</prop>
<prop key="key3">value3</prop>
</props>
</property>
对于复杂JDK类型,如Date等,就需要程序员⾃定义类型转换器,处理。
用户自定义类型
第一种方式
- 为成员变量提供 set get ⽅法
- 配置⽂件中进⾏注入(赋值)
<bean id="userService" class="com.yusael.service.UserServiceImpl">
<property name="userDAO">
<bean class="com.yusael.dao.UserDAOImpl"/>
</property>
</bean>
第⼀种赋值⽅式存在的问题:
- 配置⽂件代码冗余;
- 被注入的对象 (UserDAO)多次创建,浪费(JVM)内存资源。
第二种方式
- 为成员变量提供 set get ⽅法;
- 配置⽂件中进⾏配置;
<bean id="userDAO" class="com.yusael.dao.UserDAOImpl"></bean>
<bean id="userService" class="com.yusael.service.UserServiceImpl">
<property name="userDAO">
<ref bean="userDAO"/>
</property>
</bean>
Spring4.x 废除了 <ref local=""/>
基本等效于 <ref bean=""/>
- 区别在于 local 只能引用本配置文件的
- 而 bean 不仅可以引用本配置文件的,还可以引用父配置文件的
简化Set注入
基于属性的简化
JDK 类型注入:
原来我们注入一个属性的写法:
<property name="id">
<value>10</value>
</property>
简化后
<property name="id" value="10"/>
注意:value 属性只能简化 8种基本类型 + String
用户自定义类型注入:
简化前
<bean id="userDAO" class="com.yusael.dao.UserDAOImpl"></bean>
<bean id="userService" class="com.yusael.service.UserServiceImpl">
<property name="userDAO">
<ref bean="userDAO"/>
</property>
</bean>
简化后:
<bean id="userDAO" class="com.yusael.dao.UserDAOImpl"></bean>
<bean id="userService" class="com.yusael.service.UserServiceImpl">
<property name="userDAO" ref="userDAO"/>
</bean>
基于P命名空间的简化
p
即properties
的意思
先要在xml
文件中引入命名空间
头部 beans 标签中加入
<beans xmlnx:p="http://www.springframework.org/schema/p">
JDK 类型注入:
简化前
<bean id="person" name="p" class="com.yusael.basic.Person">
<property name="id">
<value>10</value>
</property>
<property name="name">
<value>yusael</value>
</property>
</bean>
JDK 类型注入 - 基于p命名空间的简化后:
<bean id="person" name="p" class="com.yusael.basic.Person" p:name="yusael" p:id="10"/>
用户自定义类型注入:
简化前
<bean id="userDAO" class="com.yusael.dao.UserDAOImpl"></bean>
<bean id="userService" class="com.yusael.service.UserServiceImpl">
<property name="userDAO">
<ref bean="userDAO"/>
</property>
</bean>
用户自定义类型注入 - 基于p命名空间的简化后:
<bean id="userDAO" class="com.yusael.dao.UserDAOImpl"></bean>
<bean id="userService" class="com.yusael.service.UserServiceImpl" p:userDAO-ref="userDAO"/>
构造注入
注入:通过 Spring 的配置⽂件,为成员变量赋值;
Set注入:Spring 调⽤ Set 方法 通过 配置⽂件 为成员变量赋值;
构造注入:Spring 调⽤ 构造方法 通过 配置⽂件 为成员变量赋值;
构造注入方式
- 提供有参构造方法
public class Customer {
private String name;
private int age;
public Customer(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Customer{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
- Spring 配置文件
<bean id="customer" class="com.yusael.constructor.Customer">
<constructor-arg>
<value>zhenyu</value>
</constructor-arg>
<constructor-arg>
<value>21</value>
</constructor-arg>
</bean>
构造方法是会重载的
参数不同
- 参数个数不同时,通过控制
<constructor-arg>
标签的数量进⾏区分;
如果只有一个参数的话,只需要一对 <constructor-arg> 标签:
<bean id="customer" class="com.yusael.constructor.Customer">
<constructor-arg>
<value>zhenyu</value>
</constructor-arg>
</bean>
如果有两个参数的话,用两对 <constructor-arg> 标签,以此类推
<bean id="customer" class="com.yusael.constructor.Customer">
<constructor-arg>
<value>zhenyu</value>
</constructor-arg>
<constructor-arg>
<value>22</value>
</constructor-arg>
</bean>
参数相同
- 构造参数个数相同时,通过在标签引入 type 属性 进⾏类型的区分
<constructor-arg type="">
<bean id="customer" class="com.yusael.constructor.Customer">
<constructor-arg type="int">
<value>20</value>
</constructor-arg>
</bean>
总结
既然有两种注入方法,那一般会用哪一种呢?
set 注入更多
- 构造注入麻烦(重载)
- Spring 框架底层⼤量应⽤了 set注入。
反转控制 Inverse Of Control
反转控制(IOC Inverse of Control),也称为 转移控制。
- 控制:对于成员变量赋值的控制权
- 反转控制:把对于成员变量赋值的控制权,从代码中**转移(反转)**到 Spring ⼯⼚和配置⽂件中完成
- 好处:解耦合
- 底层实现:工厂设计模式
依赖注入 Dependency Injection
- 注⼊:通过 Spring 的⼯⼚及配置⽂件,为对象(bean,组件)的成员变量赋值
- 依赖注⼊:当⼀个类需要另⼀个类时,就意味着依赖,⼀旦出现依赖,就可以把另⼀个类作为本类的成员变量,最终通过 Spring 配置⽂件进⾏注⼊(赋值)
- 好处:解耦合