spring之IoC知识梳理

本文详细介绍了Spring框架中的IoC容器概念及其多种实现方式,包括无参构造、有参构造、依赖注入等,同时讲解了如何通过工厂方法创建对象、自动装载以及基于注解的配置。

什么是 IoC

IoC 也叫控制反转

在传统的程序开发中,需要获取对象时,通常由开发者来手动创建实例化对象,但是在 Spring 框架中创建对象的工作不再由开发者完成,而是交给 IoC 容器来创建,我们直接获取即可,整个流程完成反转,因此是控制反转。

IOC的实现

创建实体类:

public class Student {
    private int id;
    private String name;
    }

在 pom.xml 中添加 Spring 依赖

创建配置文件,可以自定义文件名 spring.xml

无参构造方式

在 spring.xml 中配置 bean 标签,IoC 容器通过加载 bean 标签来创建对象。

<bean id="stu" class="com.sensen.springdemo.entity.Student"></bean>

调用 API 获取 IoC 创建的对象

        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
        Student stu = (Student) applicationContext.getBean("stu");
        System.out.println(stu);
        
        Student bean = applicationContext.getBean(Student.class);
        System.out.println(bean);

两种获取方式,上面的是根据id获取,后面的根据类名,返回结果都是属性值全为初始化值或null的对象.

如果想给对象赋值怎么办呢?
需要给实体类的每个属性加上set和get方法,spring通过对象的set方法和get方法来初始化对象。
之后修改bean配置即可:

<bean id="stu" class="com.southwind.entity.Student">
    <property name="id" value="1"></property>
    <property name="name" value="张三"></property>
</bean>

再次运行即可得到有值的对象

有参构造方式

先给对象加上有参数的构造方法.

修改bean配置:

    <bean id="stu" class="com.sensen.springdemo.entity.Student">
        <constructor-arg name="id" value="3"></constructor-arg>
        <constructor-arg name="name" value="小明"></constructor-arg>
    </bean>

再次运行,也是得到有值的对象.

依赖注入(DI)

如果 IoC 容器管理多个对象,并且对象之间有级联关系,如何实现?

创建 Classes 类

public class Classes {
    private int id;
    private String name;
}

在 Student 类中添加 Classes 属性

    private int id;
    private String name;
    private Classes classes;

spring.xml 中配置 classes 对象,然后将该对象赋值给 stu 对象

<?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="stu" class="com.sensen.springdemo.entity.Student">
        <constructor-arg name="id" value="3"></constructor-arg>
        <constructor-arg name="name" value="小明"></constructor-arg>
        <constructor-arg name="classes" ref="classes"></constructor-arg>
    </bean>

    <bean id="classes" class="com.sensen.springdemo.entity.Classes">
        <property name="id" value="1"></property>
        <property name="name" value="Java班"></property>
    </bean>

</beans>

再次获取 Student 对象,结果如下图所示。
在这里插入图片描述
在 spring.xml 中,通过 ref 属性将其他 bean 赋给当前 bean 对象,这种方式叫做依赖注入(DI),是 Spring 非常重要的机制,DI 是将不同对象进行关联的一种方式,是 IoC 的具体实现方式,通常 DI 和 IoC 是紧密结合在一起的,因此一般说的 IoC 包括 DI。

Spring 中的 bean

bean 是根据 scope 来生成的,scope 常用的两种类型:

singleton,单例,表示通过 Spring 容器获取的该对象是唯一的;
prototype,原型,表示通过 Spring 容器获取的对象都是不同的;

默认是singleton的,如果需要改变配置,只需修改为prototype即可:

<bean id="stu" class="com.sensen.springdemo.entity.Student" scope="prototype">

Spring 的依赖

依赖是 bean 和 bean 之间的一种关联方式,配置依赖关系后,被依赖的 bean 一定先创建,再创建依赖的 bean.

例如如下配置:

<bean id="user" class="com.southwind.entity.User" depends-on="car">
   <property name="id" value="1"></property>
   <property name="name" value="张三"></property>
   <property name="age" value="23"></property>
</bean>
<bean id="car" class="com.southwind.entity.Car">
   <property name="id" value="1"></property>
   <property name="brand" value="宝马"></property>
</bean>

正常情况下,spring创建bean的顺序是由上到下,但是如果创建了依赖关系,则会先创建被依赖的对象.

例如上面这种配置后,就会先创建 car,再创建 user.

Spring 读取外部资源

创建实体类TestConfig,,并加上setget方法:

public class TestConfig {
    private String url;
    private String user;
    private String root;

    @Override
    public String toString() {
        return "TestConfig{" +
                "url='" + url + '\'' +
                ", user='" + user + '\'' +
                ", root='" + root + '\'' +
                '}';
    }

    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }

    public String getUser() {
        return user;
    }

    public void setUser(String user) {
        this.user = user;
    }

    public String getRoot() {
        return root;
    }

    public void setRoot(String root) {
        this.root = root;
    }
}

在spring.xml的相同目录下,创建jdbc.properties

url = com.testConfig.com
user = root
pwd = root

之后在spring.xml中如下配置:

 <!-- 导入外部的资源文件 -->
    <context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder>
    <!-- 创建对象 -->
    <bean id="testConfig" class="com.sensen.springdemo.entity.TestConfig">
        <property name="url" value="${user}"></property>
        <property name="user" value="${pwd}"></property>
        <property name="root" value="${url}"></property>
    </bean>

之后运行

        ApplicationContext applicationContext1 = new ClassPathXmlApplicationContext("spring.xml");
        TestConfig testConfig = (TestConfig) applicationContext.getBean("testConfig");
        System.out.println(testConfig);

即可看到配置已经生效了.

IoC 通过工厂方法创建对象

IoC 是典型的工厂模式,把IOC看成一个大工厂,这个大工厂里面要生成的对象都是在XML文件中定义的,然后利用java的反射编程,根据xml给出的类名动态的生成相应的对象。

下面我们就来学习如何使用工厂模式来创建 bean。

静态工厂

先创建个Car类:

public class Car {
    private int num;
    private String brand;
    public Car(int num, String brand) {
        super();
        this.num = num;
        this.brand = brand;
    }

    @Override
    public String toString() {
        return "Car{" +
                "num=" + num +
                ", brand='" + brand + '\'' +
                '}';
    }
}

再来个静态工厂类:

public class StaticCarFactory {
    private static Map<Integer, Car> cars;
    static{
        cars = new HashMap<Integer,Car>();
        cars.put(1, new Car(1,"奥迪"));
        cars.put(2, new Car(2,"奥拓"));
    }
    public static Car getCar(int num){
        return cars.get(num);
    }
}

之后是xml的配置:

    <!--    在 spring.xml 中配置静态工厂-->
    <!--    factory-method 指向静态方法;-->
    <!--    constructor-arg 的 value 属性为调用静态方法所传的参数。-->
    <bean id="car1" class="com.sensen.springdemo.factory.StaticCarFactory" factory-method="getCar">
        <constructor-arg value="1"></constructor-arg>
    </bean>

最后在test中演示:

 		 //静态工厂
        Car car = (Car) applicationContext.getBean("car1");
        System.out.println(car);

即可看到奥迪车了。

实例工厂

还是上面的Car类,来个实例工厂:

public class InstanceCarFactory {
    private Map<Integer, Car> cars;
    public InstanceCarFactory() {
        cars = new HashMap<Integer,Car>();
        cars.put(1, new Car(1,"奥迪"));
        cars.put(2, new Car(2,"奥拓"));
    }
    public Car getCar(int num){
        return cars.get(num);
    }
}

xml的配置:

    <!-- 配置实例工厂对象 -->
    <bean id="carFactory" class="com.sensen.springdemo.factory.InstanceCarFactory"></bean>
    <!-- 通过实例工厂对象创建 car 对象 -->
    <bean id="car2" factory-bean="carFactory" factory-method="getCar">
        <constructor-arg value="2"></constructor-arg>
    </bean>

演示:

        //实例工厂
        Car car2 = (Car) applicationContext.getBean("car2");
        System.out.println(car2);

IoC 自动装载(autowire)

Spring 还提供了另外一种更加简便的方式:自动装载,不需要手动配置 property,IoC 容器会自动选择 bean 完成依赖注入。

自动装载有两种方式:

byName,通过属性名自动装载。

创建实体类,类中成员变量有Car对象:

public class Person {
    private int id;
    private String name;
    private Car car;

    @Override
    public String toString() {
        return "Person{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", car=" + car +
                '}';
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Car getCar() {
        return car;
    }

    public void setCar(Car car) {
        this.car = car;
    }
}

在xml中配置:

 <!--    自动装载-->
    <bean id="person" class="com.sensen.springdemo.entity.Person" autowire="byName">
        <property name="id" value="1"></property>
        <property name="name" value="张三"></property>
    </bean>
    <bean id="car" factory-bean="carFactory" factory-method="getCar">
        <constructor-arg value="2"></constructor-arg>
    </bean>

test类:

        Person person = (Person) applicationContext.getBean("person");
        System.out.println(person);

可以看到结果:
Person{id=1, name=‘张三’, car=Car{num=2, brand=‘奥拓’}}

byType,通过属性对应的数据类型自动装载。

其他的不变,把xml修改为:

    <!--    自动装载-->
    <bean id="person" class="com.sensen.springdemo.entity.Person" autowire="byType">
        <property name="id" value="1"></property>
        <property name="name" value="张三"></property>
    </bean>
    <bean id="car" factory-bean="carFactory" factory-method="getCar">
        <constructor-arg value="2"></constructor-arg>
    </bean>

注意的是需要把其他的Car类配置删除,不然spring无法识别你需要装载哪个Car。

Spring IoC 基于注解

新建一个项目,创建及格包:
controller,service,dao,entity。

在entity中新建user类:

package com.sensen.springboot.entity;

/**
 * @ClassName User
 * @Author linsen
 * @Date 2020/11/26 11:12
 **/
public class User {
    private int id;
    private String name;
    public User(int id, String name) {
        super();
        this.id = id;
        this.name = name;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }
}

在dao中新建接口和实现类,模拟数据:

public interface  UserDAO {
    public User getUserById(int id);
}
public class UserDAOImpl implements UserDAO{

    private static Map<Integer,User> users;

    static{
        users = new HashMap<Integer,User>();
        users.put(1, new User(1, "张三"));
        users.put(2, new User(2, "李四"));
        users.put(3, new User(3, "王五"));
    }

    @Override
    public User getUserById(int id) {
        // TODO Auto-generated method stub
        return users.get(id);
    }

}

在service中新建接口和实现类:

public interface UserService {
    public User getUserById(int id);
}

public class UserServiceImpl implements UserService{
    
    private UserDAO userDAO;

    @Override
    public User getUserById(int id) {
        // TODO Auto-generated method stub
        return userDAO.getUserById(id);
    }

    public UserDAO getUserDAO() {
        return userDAO;
    }

    public void setUserDAO(UserDAO userDAO) {
        this.userDAO = userDAO;
    }
}

最后在controller中新建类:

public class UserController {

    private UserService userService;

    public UserService getUserService() {
        return userService;
    }

    public void setUserService(UserService userService) {
        this.userService = userService;
    }

    public User getUserById(int id){
        return userService.getUserById(id);
    }
}

之后是spring.xml的配置:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">

    <!--    <context:component-scan base-package="com.sensen.springboot"></context:component-scan>-->
    <!-- 配置UserController -->
    <bean id="userController" class="com.sensen.springboot.controller.UserController">
        <property name="userService" ref="userService"></property>
    </bean>
    <!-- 配置UserService -->
    <bean id="userService" class="com.sensen.springboot.service.UserServiceImpl">
        <property name="userDAO" ref="userDAO"></property>
    </bean>
    <!-- 配置UserDAO -->
    <bean id="userDAO" class="com.sensen.springboot.dao.UserDAOImpl"></bean>
</beans>

新建main类:

public class ApplicationTest {
    public static void main(String[] args) {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
        UserController userController =(UserController)applicationContext.getBean("userController");
        System.out.println(userController.getUserById(1));
    }
}

即可输出结果:
User{id=1, name=‘张三’}

下面把项目改造成用注解实现,首先删除xml中所有配置,加上一句:
<context:component-scan base-package=“com.sensen.springboot”></context:component-scan>
这句话代表项目改用注解配置。

之后在各个类上加注解

@Controller
public class UserController {

    @Autowired
    private UserService userService;
..........................................
@Service
public class UserServiceImpl implements UserService{
	@Autowired
    private UserDAO userDAO;
...........................................
@Repository
public class UserDAOImpl implements UserDAO{

即可完成和上面一样的效果。类中属性的自动装载默认是通过 byType 的方式实现的。

当然也可以用byName的方式去完成:

将service的注解改成
@Service(“myUserService”)

之后再controller中自动装载service类时改成
@Autowired()
@Qualifier(“myUserService”)
private UserService userService;

效果相同,只是换成了byName的方式去装载。
@Qualifier() 中的值必须与 @Service() 中的值一致,才能完成自动装载。

源代码

两种方式配置ioc的代码

https://gitee.com/linsen97/spring-demo
https://gitee.com/linsen97/spring-demo2

简单的实现spring的ioc功能
https://gitee.com/linsen97/spring-ioc-impl

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值