Spring framework之配置文件实现IoC控制反转

Spring framework的第一大部分就是core container 核心容器,用于管理对象,其中有个IoC容器。

我们先来了解一下什么是IoC: 

    代码现状是:在业务层实现类中,我们需要new出数据层的一个具体的实现类对象才能实现数据层对应的方法。但当我们需要更换成实现了同一个数据层接口的另一个实现类时,因为new出来实现类不一样了,所以后面的代码肯定也要改。就是下面这种情况。

解决办法:在使用对象的时候,在程序中不要主动用new创造对象,转换为由外部提供对象。

这就是IoC(Inversion of Control)控制反转:就是把对象的创建控制权由程序转移到了外部。目的是为了解耦。

Spring提供了一个容器,IoC容器来充当IoC思想中的“外部” 。IoC容器负责对象的创建,初始化等一系列工作,被创建或被管理的对象在IoC容器中统称为bean。

这时还有一个问题,上面我们仅仅是完成了在外部创建对象的工作,当我们运行service时,如果IoC容器只提供给我们service的对象是不能到达效果的,因为service层的运行是需要依靠dao层的,所以IoC容器应该把这俩个对象都传给我们,并建立依赖。

其实这个建立依赖的工作,IoC容器是可以帮我们做的。如果我们需要的多个对象,IoC容器中都有,IoC容器就是帮我们建立依赖并传给我们,这个就叫做依赖注入DI。

DI(Dependency Injection)依赖注入:在容器中建立起多个bean之间的依赖关系的过程。

下面让我们开始使用配置文件来运行Spring:

  第一步,先导入spring对应的依赖包

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.2.10.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13.2</version>
            <scope>test</scope>
        </dependency>

第二步,创建需要的配置文件,这个文件在导完上面的依赖包才会有。

 给该配置文件起名叫applicationContext.xml  

下面我们就可以创造我们需要的bean了:用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="bookDao" name="dao bookDao2" class="org.example.dao.impl.bookDaoImpl" scope="prototype"/>


    <bean id="bookService" class="org.example.service.impl.bookServiceImpl"/>
</beans>

id属性给bean起名字     class属性给bean定义类型    

 一个bean的id属性只能有一个且不能重复,但name属性可以给该bean起多个别名,不能重复
name属性与id属性具有相同功能,都可以被用来获取该bean 。name中的多个别名之间可以用逗号,分号或者空格分隔。name属性不需要时可以省略。

到这我们完成了dao层和service层对象的创建,我们还需要对这俩个对象建立依赖:因为是service层依赖dao层,所以创建依赖的过程应写在service的bean中。

dao层代码:

public class bookDaoImpl implements bookDao {

    public bookDaoImpl() {
        System.out.println("bookDaoImpl被new");
    }


    @Override
    public void save() {
        System.out.println("bookDao....");
    }
}

我们需要先在service层代码中声明对应的set方法。

public class bookServiceImpl implements bookService{

    //5.删除业务层中使用new方式创建的对象
    //bookDao bookDao=new bookDaoImpl();
    bookDao bookDao;
    

    @Override
    public void save() {
        bookDao.save();
        System.out.println("bookService....");
    }

    public void setBookDao(bookDao bookDao) {
        this.bookDao = bookDao;
    }
}

然后在配置文件中添加下面的标签

<bean id="bookService" class="org.example.service.impl.bookServiceImpl">
        <property name="bookDao" ref="bookDao"/>
    </bean>

   property标签是配置当前bean的属性;   name属性为bookService类中的bookDao属性,ref为为bookDaoImpl配置的bean的id值。
最后我们就可以在main方法中获取我们的bean并执行其中的save方法了

public class beanConstruction {
    public static void main(String[] args) {
        //3.获取IoC容器
        ApplicationContext act=new ClassPathXmlApplicationContext("applicationContext.xml");

        //4.获取bean
        bookDao bookDao = (bookDao) act.getBean("bookDao");
        bookService bookService = (bookService) act.getBean("bookService");

        bookService.save();
}

获取IoC容器方法中的参数为我们前面写的Spring配置文件的名。

getBean()方法的参数为配置文件中bean标签的id属性值或者name属性值。另外因为该方法的返回值为Object类型,所以我们需要强转一下类型。

下面让我们来了解一下Spring是怎么创建对象的:Spring创建对象有3种方式

  第一种(默认):调用该对象无参的构造方法。通过上面的例子,就可以反映出Spring是通过无参的构造方法创建对象的。

  第二种:使用静态工厂实例化bean:

先创建一个静态工厂,通过一个静态方法返回我们需要的对象。

package org.example.factory;

import org.example.dao.bookDao;
import org.example.dao.impl.bookDaoImpl;

public class bookDaoFactory {
    public static bookDao getBookDao(){
        return new bookDaoImpl();
    }
}

 配置文件中的bean标签就应该这样写:

<bean id="bookMapper" class="org.example.factory.bookDaoFactory" factory-method="getBookDao"/>

如果不写factory-method属性 该bean创建的是factory工厂的对象,但我们需要的是工厂中的方法所返回的对象,所以我们需要用factory-method指明运用工厂中的哪个方法来返回对象

  第三种:使用实例化工厂:注意该类中返回所需对象的方法不是静态方法。

package org.example.factory;

import org.example.dao.bookDao;
import org.example.dao.impl.bookDaoImpl;

public class bookDaoFactory2 {
    public bookDao getBookDao(){
        return new bookDaoImpl();
    }
}

因为该工厂不是静态工厂,所以要先创建该工厂的bean

    <bean id="bookFactory" class="org.example.factory.bookDaoFactory2"/>
    <bean id="bookDao3" factory-method="getBookDao" factory-bean="bookFactory"/>

factory-bean 属性是工厂实例bean 然后调用该工厂的factory-method方法。

因为这种方式我们需要创建工厂bean,且工厂bean仅仅是配合其他bean使用的,没别的作用了,所以后续对该方式进行了改良:

package org.example.factory;

import org.example.dao.bookDao;
import org.example.dao.impl.bookDaoImpl;
import org.springframework.beans.factory.FactoryBean;

public class bookDaoFactoryBean implements FactoryBean<bookDao> {
    //代替原实例工厂中创建对象的方法
    @Override
    public bookDao getObject() throws Exception {
        return new bookDaoImpl();
    }

    @Override
    public Class<?> getObjectType() {
        return bookDao.class;
    }

    //该方法用于改变由该实例工厂创建出来的对象是否为单例
    //返回值为true时为单例,为false时不是单例
    //该方法一般不用重写
    @Override
    public boolean isSingleton() {
        return true;
    }
}

其中的方法都是实现了FactoryBean接口后重写的方法。

配置文件中的写法:

<bean id="bookDao4" class="org.example.factory.bookDaoFactoryBean"/>