本篇内容:
- 1、属性注入
- 2、构造方法注入(注入方式:参数名称,参数类型,参数索引,反射注入)
- 3、循环注入
- 4、工厂方法注入(普通工厂类,静态工厂类)
- 5、注入方式选择
一、属性注入
- 属性注入是指通过setXxx()注入bean的属性值或依赖对象。属性注入方式比较灵活,属性注入为采用方式
1.1、属性注入实例
- 属性注入要求Bean提供一个默认构造函数,并给需要注入的属性提供sett方法
Spring先调用Bean的默认构造方法实例化Bean对象,在通过反射的方式调用setter方法注入属性
1.1.1、创建java实体类
public class SetterObject {
private String color;
private int size;
public void getData() {
System.out.println("SetterObject{" +
"color='" + color + '\'' +
", size=" + size +
'}');
}
public void setColor(String color) {
this.color = color;
}
public void setSize(int size) {
this.size = size;
}
}
1.1.2、配置文件
<?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 id="setter" class="com.demo.setter.SetterObject">
<property name="color" value="红色"/>
<property name="size" value="4188"/>
</bean>
</beans>
说明:
property name=”color” value=”红色”
- property:为属性注入标识
- name:为属性的名称
- value:为属性对应的值
注意:
- Spring只会检查Bean中是否对应的方法,不会检查是否有对应的属性采用变量。
1.1.3、测试类
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({"file:src/main/resources/setter.xml"})
public class SetterObjectTest {
@Autowired
private SetterObject setter;
@Test
public void getBean() {
setter.getData();
}
}
1.2、java中属性命名规范
- Spring配置文件中的元素所指定的属性名和Bean实现类的Setter方法满足Java命名规范:xxx属性对应的setXxx()
二、构造函数注入
- 保证了一些必要属性在Bean实例化的时候得到设置。
- 使用构造函数方式注入Bean必须提供带参的构造函数
定义java实体对象
public class ConstructorObject {
private String desc;
private String title;
private int size;
public ConstructorObject(String desc, int size) {
this.desc = desc;
this.size = size;
}
public ConstructorObject(String desc, String title, int size) {
this.desc = desc;
this.title = title;
this.size = size;
}
public void getData() {
System.out.println("ConstructorObject{" +
"desc='" + desc + '\'' +
", title='" + title + '\'' +
", size=" + size +
'}');
}
}
2.1、按照参数名称方式注入
- 使用constructor-arg元中的name属性,name对应为构造函数中的参数名称,value为参数名称对应的值
配置文件
<bean id="constructorObject_name" class="com.demo.constructor.ConstructorObject">
<constructor-arg name="desc" value="test-desc-data"/>
<constructor-arg name="title" value="test-title-data"/>
<constructor-arg name="size" value="4188"/>
</bean>
name="desc":对应的参数名称
value="test-title-data":对应的参数值
2.2、按照参数类型注入
- 使用constructor-arg元中的type属性,type对应为构造函数中的参数类型,value为参数名称对应的值
配置文件
<bean id="constructorObject_type" class="com.demo.constructor.ConstructorObject">
<constructor-arg type="java.lang.String" value="test-desc-type"/>
<constructor-arg type="java.lang.String" value="test-title-type"/>
<constructor-arg type="int" value="4188"/>
</bean>
type="int":对应的参数类型
value="4188":对应的参数值
2.3、按照参数索引注入
<bean id="constructorObject_index" class="com.demo.constructor.ConstructorObject">
<constructor-arg index="0" value="test-desc-index"/>
<constructor-arg index="1" value="test-title-index"/>
<constructor-arg index="2" value="4188"/>
</bean>
说明
- index=”0”:对应的索引值,代表为第0个参数
- value=”test-desc-index”:参数对应的值
2.4、按照反射方式注入
- 当构造函数中参数的类型是不相同的,可以通过java反射方式获取参数对应的类型
1、构造函数
public ConstructorObject(String desc, int size) {
this.desc = desc;
this.size = size;
}
2、配置
<bean id="constructorObject_reflection" class="com.demo.constructor.ConstructorObject">
<constructor-arg value="test-desc-reflection"/>
<constructor-arg value="4188"/>
</bean>
完整配置文件
<?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 id="constructorObject_name" class="com.demo.constructor.ConstructorObject">
<constructor-arg name="desc" value="test-desc-data"/>
<constructor-arg name="title" value="test-title-data"/>
<constructor-arg name="size" value="4188"/>
</bean>
<bean id="constructorObject_type" class="com.demo.constructor.ConstructorObject">
<constructor-arg type="java.lang.String" value="test-desc-type"/>
<constructor-arg type="java.lang.String" value="test-title-type"/>
<constructor-arg type="int" value="4188"/>
</bean>
<bean id="constructorObject_index" class="com.demo.constructor.ConstructorObject">
<constructor-arg index="0" value="test-desc-index"/>
<constructor-arg index="1" value="test-title-index"/>
<constructor-arg index="2" value="4188"/>
</bean>
<bean id="constructorObject_reflection" class="com.demo.constructor.ConstructorObject">
<constructor-arg value="test-desc-reflection"/>
<constructor-arg value="4188"/>
</bean>
</beans>
完整测试类
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({"file:src/main/resources/constructor.xml"})
public class ConstructorObjectTest {
@Autowired
public ConstructorObject constructorObject_name;
@Autowired
public ConstructorObject constructorObject_type;
@Autowired
public ConstructorObject constructorObject_index;
@Autowired
public ConstructorObject constructorObject_reflection;
@Test
public void getBeanByName() {
constructorObject_name.getData();
}
@Test
public void getBeanByType() {
constructorObject_type.getData();
}
@Test
public void getBeanByIndex() {
constructorObject_index.getData();
}
@Test
public void getBeanByReflection() {
constructorObject_reflection.getData();
}
}
三、循环注入
- Spring容器对构造方法配置Bean进行实例化有个前提,Bean构造函数中的参数必须准完成。由于这个限制如果两个Bean都采用构造函数的方式注入就会出现类似线程死锁的循环依赖问题
1、java实体对象
public class ForeObjectA {
private String desc;
private ForeObjectB foreObjectB;
public ForeObjectA(String desc, ForeObjectB foreObjectB) {
this.desc = desc;
this.foreObjectB = foreObjectB;
}
public void getData() {
System.out.println("ForeObjectA{" +
"desc='" + desc + '\'' +
", foreObjectB=" + foreObjectB +
'}');
}
}
2、java实体对象
public class ForeObjectB {
private String title;
private ForeObjectA foreObjectA;
public ForeObjectB(String title, ForeObjectA foreObjectA) {
this.title = title;
this.foreObjectA = foreObjectA;
}
public void getData() {
System.out.println( "ForeObjectB{" +
"title='" + title + '\'' +
", foreObjectA=" + foreObjectA +
'}');
}
}
3、配置文件
<?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 id="foreA" class="com.demo.fore.ForeObjectA">
<constructor-arg name="desc" value="test-desc"/>
<constructor-arg name="foreObjectB" ref="foreB"/>
</bean>
<bean id="foreB" class="com.demo.fore.ForeObjectB">
<constructor-arg name="title" value="test-title"/>
<constructor-arg name="foreObjectA" ref="foreA"/>
</bean>
</beans>
4、测试类
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({"file:src/main/resources/fore.xml"})
public class ForeObjectTest {
@Autowired
private ForeObjectB foreB;
@Autowired
private ForeObjectA foreA;
@Test
public void getBean() {
foreA.getData();
foreB.getData();
}
}
5、解决方案:
四、工厂方法注入
java实体对象
public class CarObject {
private String color;
private String title;
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
@Override
public String toString() {
return "CarObject{" +
"color='" + color + '\'' +
", title='" + title + '\'' +
'}';
}
}
4.1、非静态工厂方法注入
- 在Java中非静态工厂类必须要实例化工厂类才能调用工厂类方法
- 工厂类负责创建一个或多个目标类实例,工厂类方法一般以接口或抽象类变量的形式返回目标类
- 工厂类对外屏蔽了目标的实例化步骤,调用者不需知道具体的目标类
public CarObject createCar() {
CarObject carObject = new CarObject();
carObject.setColor("银灰色");
carObject.setTitle("雷克萨斯");
return carObject;
}
配置文件
<bean id="factory" class="com.demo.factory.CarFactory"/>
<bean id="car" factory-method="createCar" factory-bean="factory"/>
说明:
id="factory":定义工厂类
factory-method="createCar":指定创建实体类(id="car")对应的工厂方法
factory-bean="factory":指定创建实体类(id="car") 对应的工厂类
4.2、静态工厂
- 静态工厂类在使用时不需要创建工厂类对象就可以直接调用工厂类中的方法,在Spring中也不需要的定义工厂类Bean
public static CarObject newCar() {
CarObject carObject = new CarObject();
carObject.setColor("银灰色");
carObject.setTitle("雷克萨斯");
return carObject;
}
配置文件
<bean id="newCar" class="com.demo.factory.CarFactory" factory-method="newCar"/>
说明:
class="com.demo.factory.CarFactory":对应的静态工厂类
factory-method="newCar":工厂类对应的静态方法
五、注入方式选择
- Spring有3种方式选择,具体注入方式选择并没有统一标准
1、使用构造函数方式的优点
- 1、保证重要的属性在Bean实例化时被设置好。
- 2、不需要为每个属性提供setter方法,减少方法个数,避免外部错误调用
2、构造函数方式的缺点
- 1、属性过多,构造方法就变得很多,可读性差
- 2、不够灵活,可选属性也需要提供参数
- 3、多个构造方法存在歧义
- 4、构造方法不利于类的扩展与继承,子类需要引用父类的构造方法
- 5、构造方法存在循环注入问题
代码地址
https://github.com/brusion/brusion-code/tree/master/demo-java/03%20-%20demo%20-%20spring/spring-study-4x/01-ioc/01-ioc-type