学习总结录 学习总结录
首页
归档
分类
标签
  • Java基础
  • Java集合
  • MySQL
  • Redis
  • JVM
  • 多线程
  • 计算机网络
  • 操作系统
  • Spring
  • Kafka
  • Elasticsearch
  • Python
  • 面试专题
  • 案例实践
  • 工具使用
  • 项目搭建
  • 服务治理
  • ORM框架
  • 分布式组件
  • MiniSpring
  • 设计模式
  • 算法思想
  • 编码规范
友链
关于
GitHub (opens new window)
首页
归档
分类
标签
  • Java基础
  • Java集合
  • MySQL
  • Redis
  • JVM
  • 多线程
  • 计算机网络
  • 操作系统
  • Spring
  • Kafka
  • Elasticsearch
  • Python
  • 面试专题
  • 案例实践
  • 工具使用
  • 项目搭建
  • 服务治理
  • ORM框架
  • 分布式组件
  • MiniSpring
  • 设计模式
  • 算法思想
  • 编码规范
友链
关于
GitHub (opens new window)
  • 案例实践

  • 工具使用

  • 项目搭建

  • 服务治理

  • ORM框架

  • MiniSpring

    • Spring IoC-原始Ioc
    • Spring IoC-扩展Bean
    • Spring IoC-属性注入
      • 一、属性注入方式
        • 1、Setter注入
        • 2、构造器注入
      • 二、属性类实现
        • 1、Setter配置类
        • 2、构造函数配置类
      • 三、属性注入
        • 1、BeanDefinition扩展
        • 2、解析XML额外动作
        • 3、注入Bean
      • 四、案例测试
        • 1、XML配置
        • 2、Bean实体
        • 3、测试
      • 参考
    • Spring Ioc-解决循环依赖
    • Spring Ioc-注解支持
    • Spinrg IoC-IoC完善
  • 案例归档
  • MiniSpring
旭日
2023-05-07
目录

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

# 参考

手把手带你写一个 MiniSpring (opens new window)

#Spring
上次更新: 2024/06/29, 15:13:44
Spring IoC-扩展Bean
Spring Ioc-解决循环依赖

← Spring IoC-扩展Bean Spring Ioc-解决循环依赖→

最近更新
01
基础概念
10-31
02
Pytorch
10-30
03
Numpy
10-30
更多文章>
Theme by Vdoing | Copyright © 2021-2024 旭日 | 蜀ICP备2021000788号-1
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式