Spring IoC-属性注入
# 一、属性注入方式
Spring中有三种属性注入的方式:
Filed注入
:给Bena的某个遍历赋值Setter注入
:调用setXXX()方法进行赋值构造器注入
:使用构造方法进行输入注入
通常而言,我们一般是使用set方法或者构造函数进行属性注入。
# 1、Setter注入
XML配置
<?xml version="1.0" encoding="UTF-8" ?>
<beans>
<bean id = "AService" class="me.xu.spring.test.demo02.AServiceImpl">
<property name="content" type="String" value="hello A"/>
</bean>
</beans>
除了之前我们所配置的id
和class
,在<bean>
标签下引入了<property>
标签,这个标签包含了三个属性:
name
:注入属性的名称type
:注入属性的类型value
:注入属性的具体值
Bean定义
public interface AService {
void A();
}
public class AServiceImpl implements AService {
private String content;
@Override
public void A() {
System.out.println(content);
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
}
# 2、构造器注入
XML配置
<?xml version="1.0" encoding="UTF-8" ?>
<beans>
<bean id = "BService" class="me.xu.spring.test.demo02.BServiceImpl">
<constructor-arg name="content" type="String" value="hello b"/>
<constructor-arg name="type" type="int" value="1"/>
</bean>
</beans>
除了之前我们所配置的id
和class
,在<bean>
标签下引入了<constructor-arg>
标签,这个标签同样包含了三个属性:
name
:注入属性的名称type
:注入属性的类型value
:注入属性的具体值
Bean定义
public interface BService {
void B();
}
public class BServiceImpl implements BService{
private String content;
private int type;
public BServiceImpl(String content, int type) {
this.content = content;
this.type = type;
}
@Override
public void B() {
System.out.println(type + ":" + content);
}
}
# 二、属性类实现
上述三种方式只是在XML中进行定义,我们还需要去定义这些相关属性的类配置
# 1、Setter配置类
单个配置
public class PropertyValue {
private final String name;
private final String type;
private final Object value;
public PropertyValue(String type, String name, Object value) {
this.type = type;
this.name = name;
this.value = value;
}
public String getType() {
return this.type;
}
public String getName() {
return this.name;
}
public Object getValue() {
return this.value;
}
}
实际开发中,我们可能会为一个类的多个属性进行配置,上述类只能表示其中一个属性的配置,因此我们还需要额外一个类来管理所有属性的配置。
所有配置
public class PropertyValues {
private final List<PropertyValue> propertyValueList;
public PropertyValues() {
propertyValueList = new ArrayList<PropertyValue>(10);
}
public List<PropertyValue> getPropertyValueList() {
return propertyValueList;
}
public int size() {
return propertyValueList.size();
}
public void addPropertyValue(PropertyValue pv) {
propertyValueList.add(pv);
}
public void addPropertyValue(String propertyType, String propertyName, Object propertyValue) {
addPropertyValue(new PropertyValue(propertyType, propertyName, propertyValue));
}
public void removePropertyValue(PropertyValue pv) {
propertyValueList.remove(pv);
}
public void removePropertyValue(String propertyName) {
propertyValueList.remove(getPropertyValue(propertyName));
}
public PropertyValue[] getPropertyValues() {
return this.propertyValueList.toArray(new PropertyValue[this.propertyValueList.size()]);
}
public PropertyValue getPropertyValue(String propertyName) {
for (PropertyValue pv : propertyValueList) {
if (pv.getName().equals(propertyName)) {
return pv;
}
}
return null;
}
public Object get(String propertyName) {
PropertyValue pv = getPropertyValue(propertyName);
return (pv != null ? pv.getValue() : null);
}
public boolean contains(String propertyName) {
return (getPropertyValue(propertyName) != null);
}
public boolean isEmpty() {
return this.propertyValueList.isEmpty();
}
}
该类封装了一些对PropertyValue
集合的一些常规操作。
# 2、构造函数配置类
单个配置
public class ArgumentValue {
private Object value;
private String type;
private String name;
public ArgumentValue(String type, Object value) {
this.value = value;
this.type = type;
}
public ArgumentValue(String type, String name, Object value) {
this.value = value;
this.type = type;
this.name = name;
}
public void setValue(Object value) {
this.value = value;
}
public Object getValue() {
return this.value;
}
public void setType(String type) {
this.type = type;
}
public String getType() {
return this.type;
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return this.name;
}
}
所有配置
public class ArgumentValues {
private final List<ArgumentValue> argumentValueList = new ArrayList<ArgumentValue>();
public ArgumentValues() {
}
public void addArgumentValue(ArgumentValue argumentValue) {
this.argumentValueList.add(argumentValue);
}
public ArgumentValue getIndexedArgumentValue(int index) {
ArgumentValue argumentValue = this.argumentValueList.get(index);
return argumentValue;
}
public int getArgumentCount() {
return (this.argumentValueList.size());
}
public boolean isEmpty() {
return (this.argumentValueList.isEmpty());
}
}
# 三、属性注入
# 1、BeanDefinition扩展
我们需要在BeanDefinition
中添加两个新的属性,来分别存放Setter配置类和构造函数配置类
package me.xu.spring.beans;
public class BeanDefinition {
/**
* 构造函数配置类
*/
private ArgumentValues constructorArgumentValues;
/**
* 属性配置类
*/
private PropertyValues propertyValues;
public void setConstructorArgumentValues(ArgumentValues constructorArgumentValues) {
this.constructorArgumentValues =
(constructorArgumentValues != null ? constructorArgumentValues : new ArgumentValues());
}
public ArgumentValues getConstructorArgumentValues() {
return this.constructorArgumentValues;
}
public boolean hasConstructorArgumentValues() {
return !this.constructorArgumentValues.isEmpty();
}
public void setPropertyValues(PropertyValues propertyValues) {
this.propertyValues = (propertyValues != null ? propertyValues : new PropertyValues());
}
public PropertyValues getPropertyValues() {
return this.propertyValues;
}
}
# 2、解析XML额外动作
之前解析XML,实例化BeanDefinition
我们只注入了id和class,现在需要额外解析构造函数配置类和属性配置类:
public class XmlBeanDefinitionReader {
SimpleBeanFactory simpleBeanFactory;
/**
* 构造函数
*/
public XmlBeanDefinitionReader(SimpleBeanFactory simpleBeanFactory) {
this.simpleBeanFactory = simpleBeanFactory;
}
/**
* 遍历资源,将资源中的bean对象进行注册
* @param resource 外部资源
*/
public void loadBeanDefinitions(Resource resource) {
while (resource.hasNext()) {
Element element = (Element) resource.next();
String beanID = element.attributeValue("id");
String beanClassName = element.attributeValue("class");
BeanDefinition beanDefinition = new BeanDefinition(beanID, beanClassName);
// 解析动作开始
// 处理属性
List<Element> propertyElements = element.elements("property");
PropertyValues propertyValues = new PropertyValues();
for (Element e : propertyElements) {
// 获取属性
String name = e.attributeValue("name");
String type = e.attributeValue("type");
String value = e.attributeValue("value");
// 添加到属性集合中
propertyValues.addPropertyValue(new PropertyValue(type, name, value));
}
beanDefinition.setPropertyValues(propertyValues);
// 处理构造器参数
List<Element> constrElements = element.elements("constructor-arg");
ArgumentValues argumentValues = new ArgumentValues();
for (Element e : constrElements) {
// 获取属性
String name = e.attributeValue("name");
String type = e.attributeValue("type");
String value = e.attributeValue("value");
// 添加到构造器参数集合中
argumentValues.addArgumentValue(new ArgumentValue(value, type, name));
}
beanDefinition.setConstructorArgumentValues(argumentValues);
simpleBeanFactory.registerBeanDefinition(beanID, beanDefinition);
}
}
}
程序在加载 Bean 的定义时要获取 <property>
和<constructor-arg>
,只要循环处理它们对应标签的属性:type、name、value 即可。随后,我们通过 addPropertyValue 和 addArgumentValue 两个方法就能将注入的配置读取进内存。
# 3、注入Bean
步骤2我们完成了,将构造函数配置类和属性配置类注入到内存中,现在我们需要将这个内存的属性注入给Bean。
private Object createBean(BeanDefinition beanDefinition) {
// 类名
Class<?> clz = null;
Object obj = null;
// 构造函数
Constructor<?> constructor = null;
try {
// 反射获取类名
clz = Class.forName(beanDefinition.getClassName());
} catch (Exception e) {
e.printStackTrace();
}
// 处理构造器参数
ArgumentValues argumentValues = beanDefinition.getConstructorArgumentValues();
if (!argumentValues.isEmpty()) {
// 参数类型
Class<?>[] paramTypes = new Class<?>[argumentValues.getArgumentCount()];
// 参数值
Object[] paramValues = new Object[argumentValues.getArgumentCount()];
// 遍历构造器参数集合
for (int i = 0; i < argumentValues.getArgumentCount(); i++) {
ArgumentValue argumentValue = argumentValues.getIndexedArgumentValue(i);
if ("String".equals(argumentValue.getType()) || "java.lang.String".equals(argumentValue.getType())) {
paramTypes[i] = String.class;
paramValues[i] = argumentValue.getValue();
} else if ("Integer".equals(argumentValue.getType()) || "java.lang.Integer".equals(argumentValue.getType())) {
paramTypes[i] = Integer.class;
paramValues[i] = Integer.valueOf((String) argumentValue.getValue());
} else if ("int".equals(argumentValue.getType())) {
paramTypes[i] = int.class;
paramValues[i] = Integer.valueOf((String) argumentValue.getValue()).intValue();
} else {
paramTypes[i] = String.class;
paramValues[i] = argumentValue.getValue();
}
}
try {
// 获取构造函数
constructor = clz.getConstructor(paramTypes);
// 利用构造函数反射实例化Bean
obj = constructor.newInstance(paramValues);
} catch (NoSuchMethodException | InvocationTargetException | IllegalArgumentException | SecurityException e) {
e.printStackTrace();
} catch (InstantiationException | IllegalAccessException e) {
throw new RuntimeException(e);
}
} else {
try {
obj = clz.newInstance();
} catch (InstantiationException | IllegalAccessException e) {
throw new RuntimeException(e);
}
}
// 处理属性
PropertyValues propertyValues = beanDefinition.getPropertyValues();
if (!propertyValues.isEmpty()) {
// 遍历属性集合
for (int i = 0; i < propertyValues.size(); i++) {
PropertyValue propertyValue = propertyValues.getPropertyValueList().get(i);
String pName = propertyValue.getName();
String pType = propertyValue.getType();
Object pValue = propertyValue.getValue();
Class<?>[] paramTypes = new Class<?>[1];
if ("String".equals(pType) || "java.lang.String".equals(pType)) {
paramTypes[0] = String.class;
} else if ("Integer".equals(pType) || "java.lang.Integer".equals(pType)) {
paramTypes[0] = Integer.class;
} else if ("int".equals(pType)) {
paramTypes[0] = int.class;
} else {
paramTypes[0] = String.class;
}
Object[] paramValues = new Object[1];
paramValues[0] = pValue;
// 方法名字
String methodName = "set" + pName.substring(0, 1).toUpperCase() + pName.substring(1);
Method method = null;
try {
// 获取到方法
method = clz.getMethod(methodName, paramTypes);
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
}
try {
// 反射调用方法
method.invoke(obj, paramValues);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
return obj;
}
该方法中存在两个步骤:
- 处理constructor参数:通过反射获取构造方法,再通过构造方法进行属性注入。
- 处理property:通过反射获取setXXX()方法,再通过set方法进行属性注入。
# 四、案例测试
# 1、XML配置
<?xml version="1.0" encoding="UTF-8" ?>
<beans>
<bean id = "DemoService" class = "me.xu.spring.test.demo01.DemoServiceImpl"></bean>
<bean id = "AService" class="me.xu.spring.test.demo02.AServiceImpl">
<property name="content" type="String" value="hello A"/>
</bean>
<bean id = "BService" class="me.xu.spring.test.demo02.BServiceImpl">
<constructor-arg name="content" type="String" value="hello b"/>
<constructor-arg name="type" type="int" value="1"/>
</bean>
</beans>
现在XML中配置了三个Bean,我们对后两个Bean进行测试。
# 2、Bean实体
public interface AService {
void A();
}
public class AServiceImpl implements AService {
private String content;
@Override
public void A() {
System.out.println(content);
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
}
public interface BService {
void B();
}
public class BServiceImpl implements BService{
private String content;
private int type;
public BServiceImpl(String content, int type) {
this.content = content;
this.type = type;
}
@Override
public void B() {
System.out.println(type + ":" + content);
}
}
# 3、测试
public static void main(String[] args) throws BeansException {
ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("demo.xml");
AService aService = (AService)classPathXmlApplicationContext.getBean("AService");
aService.A();
BService bService = (BService)classPathXmlApplicationContext.getBean("BService");
bService.B();
}
// output
// hello A
// 1:hello b
# 参考
上次更新: 2024/06/29, 15:13:44