spring framework Day05:依赖注入的几种方式

前言

依赖注入(Dependency Injection,简称DI)是一种设计模式,用于松耦合组件之间的依赖关系。在传统的编程模式中,一个对象通常会在自己内部创建和管理其他所需要的对象,而这些对象之间存在着紧密的依赖关系。这样做在一定程度上会导致代码难以维护、难以测试和难以重用。

依赖注入通过将一个对象所需要的依赖对象从该对象本身独立出来,由外部容器来负责创建和管理依赖对象,并在运行时将依赖注入到该对象中,从而实现了对象之间的松耦合,提高了代码的可维护性、可测试性和可重用性。

在Spring框架中,依赖注入是通过IoC(控制反转)实现的。IoC是一种设计模式,通过将对象之间的依赖关系交由容器来管理,将对象的创建、销毁等过程交给容器来负责,从而实现了对象之间的松耦合。Spring框架提供了一个IoC容器,也就是我们常说的Spring容器,负责管理Bean的生命周期、创建和管理Bean之间的依赖关系并完成依赖注入。

依赖注入是Spring框架中的核心特性之一,也是实现Spring框架轻量级、高效、松耦合的关键。通过依赖注入,我们可以更加方便地编写和维护高质量的代码。

控制反转(Inversion of Control,IoC)是一种软件设计的概念或模式。它通过反转对象的依赖关系来实现松耦合、可扩展和可维护的代码结构。

在传统的程序设计中,对象通常自己创建和管理它们所依赖的对象。而在控制反转中,对象不再自己创建和管理依赖的对象,而是将这一责任交给外部的容器或者框架。通过使用依赖注入(Dependency Injection)等技术,容器会负责创建对象及其依赖,并将依赖注入到需要它们的地方。

控制反转的好处在于它能够减少对象之间的直接耦合,提高代码的可测试性、可维护性和可扩展性。它也有助于降低代码的复杂度,促进更好的模块化和组件化。

一、常用的三种依赖注入方法

1、构造函数注入(Constructor Injection):通过构造函数将依赖参数传递给目标对象。在目标类的构造函数中声明依赖参数,并通过构造函数来实例化依赖对象。

示例:

/**
     * 声明接口
     */
    private UserService userService;

    /**
     * 注入的方式也可以通过构造方法来注入
     * @param userService
     */
    public UserController(UserService userService) {
        this.userService = userService;
    }

2、Setter方法注入(Setter Injection):通过Setter方法设置依赖属性。在目标类中定义一个Setter方法,用于接收依赖对象,并在容器初始化时通过Setter方法来注入依赖对象。

 /**
     * 声明接口
     */
    private UserService userService;

/**
     * 这个 set 方法是专门用于提供给容器进行注入使用的
     *
     * @param userService
     */
    public void setUserService(UserService userService) {
        this.userService = userService;
    }

3、字段注入(Field Injection):通过将依赖对象直接注入到目标类的字段上。在目标类中声明一个依赖对象的字段,并通过依赖注入容器自动将依赖对象注入到该字段。

 /**
     * 声明接口
     */
    @Autowired
    private UserService userService;

以上示例中的@Autowired注解是Spring框架提供的,用于标记需要注入的依赖对象。不推荐使用这种方法。

1、可读性差:使用@Autowired注解,依赖关系是通过注解实现的,而不是通过代码显式地表达出来。这可能导致代码的可读性降低,特别是在复杂的应用中。

2、隐式依赖:使用@Autowired注解时,容器会自动将依赖对象注入到目标对象中,这种隐式的依赖关系可能会导致代码更加难以理解和测试。显式声明依赖关系可以使代码更易于理解和维护。

3、容易引入错误:当一个类中存在多个相同类型的Bean时,使用@Autowired注解可能会引发歧义性问题。容器无法确定要注入哪个Bean,可能会导致运行时错误。

不过呢在一些简单的场景下还是可以使用这个注解的,大家根据自己的情景选择。

使用顺序:

构造方法注入 -> setter 方法注入 -> 字段注入

最优先使用构造方法注入

二、构造函数注入的使用

1、新建一个项目,结构如下

2、导入 spring 依赖  
 
    <!-- spring 的核心依赖 -->
    <dependencies>
        <!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.3.23</version>
        </dependency>
 
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>1.4.5</version>
        </dependency>
 
 
    </dependencies>
3、新建一个 UserDao 接口
public interface UserDao {

    void save();

}
4、新建一个 UserDaoImpl 实现类
@Slf4j
public class UserDaoImpl implements UserDao {
    @Override
    public void save() {
        log.info("insert into user_info....");
    }
}
 5、新建一个 UserService 接口
public interface UserService {

    void addUser();

}
6、新建一个 UserServiceImpl 实现类

@Slf4j
public class UserServiceImpl implements UserService {


    private UserDao userDao;

    /**
     * 提供一个具体的实现类
     *
     * @param userDao
     */
    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }

    @Override
    public void addUser() {
        log.info("处理业务逻辑.....");
        // 执行保存
        userDao.save();
    }
}
7、新建一个 UserController 类
public class UserController {


    /**
     * 声明接口
     */
    private UserService userService;

    /**
     * 注入的方式也可以通过构造方法来注入
     * @param userService
     */
    public UserController(UserService userService) {
        this.userService = userService;
    }

    public String addUser() {

        userService.addUser();
        return "success";
    }

}
8、在 resources 下新建一个 spring 的 xml 文件 beans.xml,在配置文件中完成 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">

    <!-- 装配 userDao -->
    <bean id="userDao" class="edu.nf.ch05.dao.impl.UserDaoImpl"/>

    <!--  装配 UserService -->
    <bean id="userService" class="edu.nf.ch05.service.impl.UserServiceImpl">
        <!-- 同理,service 也是使用同样的方式注入 dao -->
        <property name="userDao" ref="userDao"/>
    </bean>

    <!-- 装配 userController -->
    <bean id="userController" class="edu.nf.ch05.controller.UserController">
    

        <!-- 也可以通过构造方法注入
             这里的 name 对应的是构造方法的参数名
         -->
        <constructor-arg name="userService" ref="userService"/>
    </bean>

</beans>
9、现在已经全部写完了,是不是觉得还不够清楚呢?不知道依赖了什么,不知道依赖注入什么?来给大家画个图。

 以前我们如果是要调用 dao 层是不是只能通过 new 一个对象出来去调用 dao 层的方法,现在我们通过依赖的关系去把它们都绑定在一起,一层依赖一层,我们需要用到 dao 层的方法现在只需要从容器中拿就可以了,我们直接拿 Controller 层就可以调用到 dao 层的的方法,这三者的关系是 Controller 依赖 service 依赖 dao。

所谓的控制反转,就是让容器自动将需要的 Bean 注入到相关的类中完成装配的过程
 不用自己主动去找(主动),容器会自己给到我们(被动)。

意思就是以前的做法是要通过我们手动去 new 一个对象然后才能调用这个对象的方法,现在不需要手动去 new 啦,直接把它们都交给 spring 容器去管理去创建,然后我们需要用到的时候直接去容器中拿就可以啦。

10、测试
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        UserController bean = context.getBean(UserController.class);
        bean.addUser();
    }

 测试结果

三、setter 方法注入

1、改 UserController
public class UserController {

    /**
     * 声明接口
     */
    private UserService userService;


    /**
     * 这个 set 方法是专门用于提供给容器进行注入使用的
     *
     * @param userService
     */
        public void setUserService(UserService userService) {
            this.userService = userService;
        }

    public String addUser() {

        userService.addUser();
        return "success";
    }

}
2、改 beans.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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!-- 装配 userDao -->
    <bean id="userDao" class="edu.nf.ch05.dao.impl.UserDaoImpl"/>

    <!--  装配 UserService -->
    <bean id="userService" class="edu.nf.ch05.service.impl.UserServiceImpl">
        <!-- 同理,service 也是使用同样的方式注入 dao -->
        <property name="userDao" ref="userDao"/>
    </bean>

    <!-- 装配 userController -->
    <bean id="userController" class="edu.nf.ch05.controller.UserController">

                <property name="userService" ref="userService"/>

    </bean>

</beans>

通过 set 方法注入 UserService,name 对应的是 set 方法去掉 set 然后将下一个首字母改为小写的名称
     ref 属性引用需要注入的 bean 的 id,这里就是上面装配的 userService
     这样容器就会自动将 UserServiceImpl 这个 bean
     通过 set 方法注入到 UserController 中

 3、测试
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        UserController bean = context.getBean(UserController.class);
        bean.addUser();
    }

测试结果

 四、字段注入

1、改 UserServiceImp 
@Slf4j
public class UserServiceImpl implements UserService {


    private UserDao userDao;

    /**
     * 提供一个具体的实现类
     *
     * @param userDao
     */
    @Autowired
    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }

    @Override
    public void addUser() {
        log.info("处理业务逻辑.....");

        // 执行保存
        userDao.save();
    }
}

通过在 setter 方法上使用 @Autowired 注解完成自动装配 

2、改 UserController 
public class UserController {

    /**
     * 声明接口
     */
    private UserService userService;

    /**
     * 这个 set 方法是专门用于提供给容器进行注入使用的
     *
     * @param userService
     */
    @Autowired
    public void setUserService(UserService userService) {
        this.userService = userService;
    }

    public String addUser() {

        userService.addUser();
        return "success";
    }

}
3、改 beans.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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!-- 装配 userDao -->
    <bean id="userDao" class="edu.nf.ch05.dao.impl.UserDaoImpl"/>

    <!--  装配 UserService -->
    <bean id="userService" class="edu.nf.ch05.service.impl.UserServiceImpl">
        <!-- 同理,service 也是使用同样的方式注入 dao -->
        <property name="userDao" ref="userDao"/>
    </bean>

    <!-- 装配 userController -->
    <bean id="userController" class="edu.nf.ch05.controller.UserController">
      
                <property name="userService" ref="userService"/>
    </bean>

</beans>

除了可以用 setter 方法自动装配,还可以使用构造方法自动装配 bean。方法都是一样的,写一个构造方法,在方法上用 @Autowired就可以了。

 五、总结

本章节主要讲了,依赖注入和控制反转。

1、依赖注入

在容器中建立bean与bean之间的依赖关系的整个过程,称为依赖注入
  业务层要用数据层的类对象,以前是自己new的
  现在自己不new了,靠别人[外部其实指的就是IOC容器]来给注入进来
  这种思想就是依赖注入

2、控制反转

使用对象时,由主动new产生对象转换为由外部提供对象,此过程中对象创建控制权由程序转移到
外部,此思想称为控制反转。
业务层要用数据层的类对象,以前是自己new的
现在自己不new了,交给别人[外部]来创建对象
别人[外部]就反转控制了数据层对象的创建权
这种思想就是控制反转

六、gitee 案例

案例完整地址:https://gitee.com/qiu-feng1/spring-framework.git

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值