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分