2 学习 IoC

1 什么是控制反转

在传统的程序开发中,需要调用对象时,通常由调用者来创建被调用者的示例,即对象是由调用者主动new出来的。
但在Spring框架中创建对象的工作不再由调用者来完成,而是交给IoC容器来创建,再推送给调用者,整个流程完成反转,所以是控制反转。

在这里插入图片描述

2 配置文件

  • 通过配置 bean 标签来完成对象的管理
    • id:对象名
    • class:对象的模板类(所有交给IoC容器来管理的类必须有无参构造函数,因为Spring底层是通过反射机制来创建对象的,而反射机制调用的是无参构造)
  • 对象的成员变量通过 property 标签赋值
    • name:成员变量名
    • vlaue:成员变量值(基本数据类型,String可以直接赋值;如果是其他引用,不能通过 value 赋值)
    • ref:将 IoC 中的另一个bean付给当前的成员变量(DI 依赖注入)
<?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="student" class="com.southwind.entity.Student">
        <property name="age" value="22"></property>
        <property name="id" value="1"></property>
        <property name="name" value="张三"></property>
        <property name="addr" ref="address"></property>
    </bean>

    <bean id="address" class="com.southwind.entity.address">
        <property name="name" value="科技路"></property>
        <property name="id" value="1"></property>
    </bean>

</beans>

3 如何使用 IoC?

  1. 创建 Maven 工程,pom.xml 添加依赖
    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.3.22</version>
        </dependency>
    </dependencies>
  1. 创建实体类 Student
package com.southwind.entity;

import lombok.Data;

@Data
public class Student {
    private long id;
    private String name;
    private int age;
}
  1. 传统的开发方式:手动 new Student
package com.southwind.test;

import com.southwind.entity.Student;

public class Test {
    public static void main(String args[]){
        Student student = new Student();
        student.setId(1L);
        student.setName("张三");
        student.setAge(22);
        System.out.println(student);
    }
}

  1. 通过 IoC容器 创建对象,在配置文件中添加需要管理的对象,XML 格式的配置文件,文件名可以自定义
  • 定义一个 spring.xml 配置文件(创建一个 XML Configuration File 文件):
    在这里插入图片描述
<?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="student" class="com.southwind.entity.Student">
        <property name="age" value="22"></property>
        <property name="id" value="1"></property>
        <property name="name" value="张三"></property>
    </bean>

</beans>
  • IoC的基本使用:
package com.southwind.test;

import com.southwind.entity.Student;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test {
    public static void main(String args[]){
        // 加载配置文件
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
        Student student = (Student) applicationContext.getBean("student");
        System.out.println(student);
    }
}

4 IoC 底层原理

  • 读取配置文件,解析XML
  • 通过反射机制实例化配置文件中所配置所有的bean

4.1 IoC底层原理的简单演示

(1)根据加载配置文件的代码可知

// 加载配置文件
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
Student student = (Student) applicationContext.getBean("student");
System.out.println(student);

/*
	ApplicationContext是一个接口,有一个方法:getBean(String)
	ClassPathXmlApplicationContext是实现它的类

	调用反射机制,通过无参构造函数创建。
*/	

(2)手动编写 ApplicationContext 和 ClassPathXmlApplicationContext

在这里插入图片描述
① ApplicationContext

package com.southwind.ioc;

public interface ApplicationContext {
    public Object getBean(String id);
}

② ClassPathXmlApplicationContext

package com.southwind.ioc;

import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Locale;
import java.util.Map;

public class ClassPathXmlApplicationContext implements ApplicationContext {
    private Map<String, Object> ioc = new HashMap<>();

    public ClassPathXmlApplicationContext(String path){
        try{
            SAXReader reader = new SAXReader();
            Document document = reader.read("src/main/resources/"+path);
            Element root = document.getRootElement();
            Iterator<Element> iterator = root.elementIterator();
            while (iterator.hasNext()){
                Element element = iterator.next();
                String id = element.attributeValue("id");
                String className = element.attributeValue("class");
                // 通过反射机制创建对象
                Class clazz = Class.forName(className);
                // 获取无参构造函数
                Constructor constructor = clazz.getConstructor();
                Object object = constructor.newInstance();
                // 给目标对象赋值
                Iterator<Element> beanIter = element.elementIterator();
                while(beanIter.hasNext()){
                    Element property = beanIter.next();
                    String name = property.attributeValue("name");
                    String valueStr = property.attributeValue("value");
                    String ref = property.attributeValue("ref");
                    if(ref == null){
                        String methodName = "set"+name.substring(0,1).toUpperCase()+name.substring(1,name.length());
                        Field field = clazz.getDeclaredField(name);
                        Method method = clazz.getDeclaredMethod(methodName,field.getType());
                        // 根据成员变量的数据类型将 value 进行转换
                        Object value = null;
                        if(field.getType().getName() == "long"){
                            value = Long.parseLong(valueStr);
                        }
                        if(field.getType().getName() == "java.lang.String"){
                            value = valueStr;
                        }
                        if(field.getType().getName() == "int"){
                            value = Integer.parseInt(valueStr);
                        }
                        method.invoke(object, value);
                    }
                }
                ioc.put(id,object);
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    @Override
    public Object getBean(String id) {
        return ioc.get(id);
    }
}

③ 运行结果

Student(id=1, name=张三, age=22, addr=null)

// addr的赋值也是类似,以后再补充

5 获取IoC容器创建的对象

5.1 通过 id 获取对象

Student student = (Student) applicationContext.getBean("student");

5.2 通过 运行时类 获取对象

Student student = (Student) applicationContext.getBean(Student.class);

这种方式存在一个问题,配置文件中一个数据类型的对象只能由一个实例,否则会抛出异常,因为没有唯一的bean。

6 创建对象的方式

6.1 无参构造

在实体类中创建对应的有参构造函数。
@NoArgsConstructor 无参构造

	<bean id="student" class="com.southwind.entity.Student">
        <property name="age" value="22"></property>
        <property name="id" value="1"></property>
        <property name="name" value="张三"></property>
        <property name="addr" ref="address"></property>
    </bean>
    
    <bean id="address" class="com.southwind.entity.address">
        <property name="name" value="科技路"></property>
        <property name="id" value="1"></property>
    </bean>

6.2 有参构造

在实体类中创建对应的有参构造函数。
@AllArgsConstructor 有参构造

 <!-- 写法一 -->
 	<bean id="student2" class="com.southwind.entity.Student">
        <constructor-arg name="id" value="3"></constructor-arg>
        <constructor-arg name="name" value="小米"></constructor-arg>
        <constructor-arg name="age" value="18"></constructor-arg>
        <constructor-arg name="addr" ref="address2"></constructor-arg>
    </bean>

    <bean id="address2" class="com.southwind.entity.address">
        <constructor-arg name="id" value="2"></constructor-arg>
        <constructor-arg name="name" value="可新路"></constructor-arg>
    </bean>

<!-- 写法二 -->
<!-- name 省略,但要按顺序 -->
 	<bean id="student2" class="com.southwind.entity.Student">
        <constructor-arg value="3"></constructor-arg>
        <constructor-arg value="小米"></constructor-arg>
        <constructor-arg value="18"></constructor-arg>
        <constructor-arg ref="address2"></constructor-arg>
    </bean>

    <bean id="address2" class="com.southwind.entity.address">
        <constructor-arg value="2"></constructor-arg>
        <constructor-arg value="可新路"></constructor-arg>
    </bean>

<!-- 写法三 -->
<!-- index代替name -->
 	<bean id="student2" class="com.southwind.entity.Student">
        <constructor-arg index="0" value="3"></constructor-arg>
        <constructor-arg index="1" value="小米"></constructor-arg>
        <constructor-arg index="2" value="18"></constructor-arg>
        <constructor-arg index="3" ref="address2"></constructor-arg>
    </bean>

    <bean id="address2" class="com.southwind.entity.address">
        <constructor-arg index="0" value="2"></constructor-arg>
        <constructor-arg index="1" value="可新路"></constructor-arg>
    </bean>

注解:
(1)@AllArgsConstructor 有参构造
(2)@NoArgsConstructor 无参构造

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值