深入学习控制反转IoC和依赖注入(第七天)——SSM框架的学习与应用(Spring + Spring MVC + MyBatis)-Java EE企业级应用开发学习记录

SSM框架的学习与应用(Spring + Spring MVC + MyBatis)-Java EE企业级应用开发学习记录(第七天)深入学习控制反转IoC和依赖注入DI

​ 昨天我们初步认识了Spring框架,了解其基本概念,还学习了IoC控制反转,它是Spring的基础,支撑着Spring对JavaBean的管理功能,JavaBean这就是简单工厂模式设计模式的经典实现。由Spring通过ApplicationContext帮我们创建对象,并且放在IoC容器中,等我们使用getBean方法从IoC容器中获取这个对象。以及Java中的反射机制,还有初步学习了依赖注入DI,那么今天就来深入学习一下控制反转IoC和依赖注入DI


今天我们要掌握的是

  • IoC控制反转设计原则和传统面向对象设计原则的区别
  • 什么是依赖注入?
  • 依赖注入的类型有哪些?

一、控制反转IoC的概念

​ 控制反转(Inversion of Control,缩写为IoC)是面向对象编程中的一个设计原则,用来降低程序代码之间的耦合度


IoC控制反转设计原则和传统面向对象设计原则的区别

(1)在传统面向对象编程中:获取对象的方式是用new关键字主动创建一个对象,也就是说应用程序掌握着对象的控制权。

传统面向对象程序设计原则如图。

![外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传]在这里插入图片描述

(2)IoC控制反转机制:指的是对象由Ioc容器统一管理,当程序需要使用对象时,可以直接从IoC容器中获取。这样对象的控制权就从应用程序转移到了IoC容器

IoC设计原则如图,借助于IoC容器实现具有依赖关系对象之间的解耦,各个对象类封装之后,通过IoC容器来关联这些对象类

在这里插入图片描述


二、什么是依赖注入?

基本概念:

依赖注入(Dependency Inject,缩写DI)就是由IoC容器在运行期间动态地将某种依赖资源注入对象之中。例如,将对象B注入(赋值)给对象A的成员变量。依赖注入的基本思想是明确地定义组件接口,独立开发各个组件,然后根据组件的依赖关系组装运行


依赖注入和控制反转的比较

​ 依赖注入(DI)和控制反转(IoC)是从不同角度来描述了同一件事情。依赖注入是从应用程序的角度描述,即应用程序依赖IoC容器创建并注入它所需要的外部资源而控制反转是从IoC容器的角度描述,即IoC容器控制应用程序,由IoC容器反向地向应用程序注入应用程序所需要的外部资源。这里所说的外部资源可以是外部实例对象,也可以是外部文件对象等。


依赖注入的类型

依赖注入的作用就是在使用Spring框架创建对象时,动态的将其所依赖的对象注入到Bean组件中

依赖注入通常有两种实现方式:
  • 构造方法注入: 构造方法注入是指Spring容器调用构造方法注入被依赖的实例,构造方法可以是有参的或者是无参的。Spring在读取配置信息后,会通过反射方式调用实例的构造方法,如果是有参构造方法,可以在构造方法中传入所需的参数值,最后创建类对象。
  • 属性setter方法注入: 属性setter方法注入是Spring最主流的注入方法,这种注入方法简单、直观,它是在被注入的类中声明一个setter方法,通过setter方法的参数注入对应的值。

构造方法注入

首先要了解一下:<constructor-arg>元素

​ 一个<constructor-arg>元素表示构造方法的一个参数,且定义时不区分顺序,只需要通过<constructor-arg>元素的name属性指定参数即可。<constructor-arg>元素还提供了type属性类指定参数的类型,避免字符串和基本数据类型的混淆

①新建pojo包,包内编写用户类:User.java

package pojo;

public class User {
    private int uid;//学生id
    private String name;// 姓名
    private int age;//年龄
    private String info;//学生信息
    
    //不写的话会默认生成无参构造器,若是写了有参构造器就不会自动生成无参的构造器,
    // 那就得自己创建一个无参构造器,方便方法使用

    //无参构造器
    public User() {
    }
    
    //有参构造器
    public User(int uid, String name, int age, String info) {
        this.uid = uid;
        this.name = name;
        this.age = age;
        this.info = info;
    }

    //getter和setter方法,这里不写,因为我们是使用构造器注入的方式
   
    //重写toString()方法,便于遍历输入信息让我们观察结果
    public String toString(){
        return "学生id:"+this.uid+", 姓名:"+this.name+",年龄:"+this.age+", 学生信息:"+this.info;
    }
}

这里千万记得要编写构造器

②在spring-config.xml中去编写bean,添加User类的配置信息

使用<constructor-arg>元素,name指定属性,value指定属性的值

<!--构造方法注入-->
<bean id="user" class="pojo.User">
    <!--使用constructor-arg 配置好各属性的值-->
    <constructor-arg name="uid" value="1"></constructor-arg>
    <constructor-arg name="age" value="13"></constructor-arg>
    <constructor-arg name="name" value="Stevedash"></constructor-arg>
    <constructor-arg name="info" value="世间无我这般人"></constructor-arg>
</bean>

在这里插入图片描述

这里的话,要记得写完所有的信息,因为我们构造器依赖注入根据的是有参构造器,若是构造器里面的参数列表,没有全部在bean中指定的话,或者指定参数错误,那么都会出现报错,如下图:

在这里插入图片描述

③编写测试类进行测试
package Test;

import javafx.application.Application;
import org.apache.log4j.Logger;
import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import pojo.User;

import static org.junit.jupiter.api.Assertions.*;

class UserTest {
    //定义日志对象,用来日志输出
    private Logger logger= Logger.getLogger(UserTest.class);
    @Test
    void UserTestByConstructor() {
        ApplicationContext context=new ClassPathXmlApplicationContext("spring-config.xml");
        User user = context.getBean("user",User.class);
        logger.info(user.toString());
    }
}

输出结果:

在这里插入图片描述
)

这种依赖注入方式就是构造方法注入


属性Setter方法注入

其实我们昨天就写过,属性setter方法注入了,现在再来一遍,就当复习加深一下

①编写Password.java,记得添加getter和setter方法,这里可以只添加setter方法但是一般来说都是要添加的。
package pojo;

public class Password {
    private String accountNumber;//账号名称
    private String password;//密码
    private String id;//消息记录的id

    //getter和setter方法

    public String getAccountNumber() {
        return accountNumber;
    }

    public void setAccountNumber(String accountNumber) {
        this.accountNumber = accountNumber;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getId() {
        return id;
    }

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

    //重写toString()方法,便于遍历输入信息让我们观察结果
    public String toString(){
        return "记录id:"+this.id+", 账号名称:"+this.accountNumber+", 密码:"+this.password;
    }
}
②spring-config.xml中去编写一下bean
  <!--setter方法注入-->
    <bean id="password" class="pojo.Password">
        <property name="id" value="1"></property>
        <property name="accountNumber" value="Stevedash@csdn.com"></property>
        <property name="password" value="11111111"></property>
    </bean>

在这里插入图片描述

③编写测试类
package Test;

import org.apache.log4j.Logger;
import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import pojo.Password;

import static org.junit.jupiter.api.Assertions.*;

class PasswordTest {
    //定义日志对象,用来日志输出
    private Logger logger= Logger.getLogger(PasswordTest.class);
    @Test
    void getId() {
        ApplicationContext context=new ClassPathXmlApplicationContext("spring-config.xml");
        //属性setter方法注入
        Password password=context.getBean("password",Password.class);
        logger.info(password.toString());
    }
}

输出结果如下:

在这里插入图片描述


接下来就尝试使用综合项目的方式进行编写吧,看看依赖注入的应用

下面的这种分层式是典型的软件开发中的分层和模块化设计

①新建dao层,dao包下创建接口UserDao.java,接口内添加login()
package dao;

public interface UserDao {
    public boolean login(String name,String password);
}

这是在创建数据访问层(DAO层),用于处理与数据库的交互。在这一层,我们定义了一些接口,这些接口描述了对数据库的增、删、改、查等操作,比如这里的login()方法是用来验证用户登录的。这样的设计分离了数据库访问逻辑与业务逻辑,方便修改和维护。


②创建UserDao的实现类,UserDaoImpl.java,俩面重写完善login()
package dao;

public class UserDaoImpl implements UserDao {
    @Override
    public boolean login(String name, String password) {
        if (name.equals("wgd")&&password.equals("stevedash")){
            return true;
        }
        return false;
    }
}

在这一步,我们为DAO接口创建了具体的实现类,这个实现类会实际地与数据库进行交互,执行具体的SQL查询和操作。这里的login()方法的实现涉及从数据库中查询用户信息来验证登录。我们这里只做了简单的校验


③新建service包,包下创建接口UserService.java,接口内添加login()
package service;

public interface UserService {
    public boolean login(String name,String password);
}

在这一步,我们在创建业务逻辑层(Service层)Service层是用来封装业务逻辑的,将一些复杂的业务操作封装成更高层次的方法,这样其他地方调用时可以更简洁地处理业务逻辑


④service包下新建impl包,包内创建UserService的实现类UserServiceIml,完善登录代码,代码如下:
package service.impl;

import dao.UserDao;
import service.UserService;

public class UserServiceImpl implements UserService {
    UserDao userDao;
    public void setUserDao(UserDao userDao)
    { this.userDao=userDao; }
    @Override
    public boolean login(String name, String password) {
        return userDao.login(name,password);
    }
}

我们为Service接口创建了具体的实现类这个实现类会调用DAO层的方法,处理业务逻辑,并且对数据进行处理和转换。这里的login()方法会调用DAO层的login()方法来进行用户登录验证


⑤spring-config.xml中编写bean信息
<bean id="userDao" class="dao.UserDaoImpl"></bean>
<!--setter方法注入-->
<bean id="userService" class="service.impl.UserServiceImpl">
    <property name="userDao" ref="userDao"></property>
</bean>

​ 这一步是将上述创建的类交由Spring容器管理。Spring容器通过配置文件中的bean信息,知道了哪些类需要由它来创建和管理,然后可以通过依赖注入等方式将不同层的对象关联起来。

spring-config.xml中,我们配置了Service的实现类为UserService,这使得我们可以在其他地方通过Spring容器获取这个Service对象,然后调用其中的方法,实现业务逻辑的处理


⑥编写测试类
package Test;

import org.apache.log4j.Logger;
import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import service.UserService;

import static org.junit.jupiter.api.Assertions.*;

class UserDaoImplTest {
    //定义日志对象,用来日志输出
    private Logger logger= Logger.getLogger(UserDaoImplTest.class);
    @Test
    void login() {
        // 加载applicationContext.xml配置
        ApplicationContext applicationContext = new
                ClassPathXmlApplicationContext("spring-config.xml");
        UserService userService = (UserService)applicationContext.getBean("userService");// 获取配置中的UserService实例
        boolean flag = userService.login("wgd", "stevedash");
        if (flag) {
          logger.info("登录成功");
        } else {
           logger.info("登录失败");
        }
    }
}

在这里插入图片描述


总结

​ 今天我们详细的讲解了控制反转IoC与依赖注入DI,包括控制反转的概念、IoC控制反转设计原则和传统面向对象设计原则的区别、什么是依赖注入、依赖注入的类型和依赖注入的应用。还粗略的讲解了一下,典型的软件开发中的分层和模块化设计,希望通过今天的学习,各位读者可以对Spring框架的基础有个大致的了解,为框架开发打下坚实基础。

​ 想要跟着学习的可以去我的资源里面找对应的文件下载,我的md文件也会发上去,项目文件会上传可以自己跟着学习一下。

作者:Stevedash

发表于:2023年8月31日 16点56分

注:本文内容基于个人学习理解,如有错误或疏漏,欢迎指正。感谢阅读!如果觉得有帮助,请点赞和分享。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Stevedash

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值