当前位置: 首页 > news >正文

JAVA-SSM学习1 Spring-IOCDIBean-上

宿しはじめたときめきは

IOC、DI和bean的基本概念

IOC(Inversion of Control,控制反转)和 DI(Dependency Injection,依赖注入)是 Spring 框架的核心概念,它们共同构成了 Spring 框架的基础。

IOC(控制反转)

IOC 是一种设计原则,它将对象的创建和管理权从程序代码中转移到外部容器。在传统的程序设计中,对象的创建和依赖关系是由程序代码直接管理的。而使用 IOC 后,这些职责被反转给了一个容器(如 Spring 容器),容器负责创建对象、管理对象的生命周期以及处理对象之间的依赖关系。IOC容器中创建管理的对象称之为Bean对象

IOC的核心思想

‌控制权反转‌:原本由程序代码控制的对象创建和依赖关系管理,转由容器来管理。
‌解耦‌:通过 IOC,对象之间的依赖关系被解耦,降低了组件间的耦合度。
‌灵活性‌:通过配置文件或注解,可以轻松地改变对象的依赖关系,而无需修改代码。

DI(依赖注入)

DI 是实现 IOC 的一种具体方式。它指的是将一个对象所依赖的其他对象通过某种方式传递给该对象,而不是让对象自己去创建或查找依赖的对象。DI 通常通过构造函数、Setter 方法或字段注入等方式实现。

DI 的实现方式

‌构造函数注入‌:通过构造函数参数传递依赖对象。
‌Setter 方法注入‌:通过 Setter 方法设置依赖对象。
‌字段注入‌:直接通过字段注入依赖对象(通常使用注解实现)。

实现分层解耦的思路就是:将项目中的类交给IOC容器管理,然后应用程序运行时需要依赖的对象使用DI完成

IOC与DI的入门案例

这是一个简单的分层架构案例:

// src/main/java/org/example/Dao/Dao.java
public interface Dao {public void DaoFunc();
}// src/main/java/org/example/Dao/impl/DaoImpl.java
public class DaoImpl implements Dao {public void DaoFunc() {System.out.println("Now is Dao!");}
}// src/main/java/org/example/Service/Service.java
public interface Service {public void ServiceFunc();
}// src/main/java/org/example/Service/impl/ServiceImpl.java
public class ServiceImpl implements Service {private Dao dao  = new DaoImpl();public void ServiceFunc() {System.out.println("Now is Service!");dao.DaoFunc();}
}// src/main/java/org/example/App.java
public class App {public static void main(String[] args) {Service service = new ServiceImpl();service.ServiceFunc();}
}

现在使用IOC&DI进行解耦,关键在于通过设置和修改配置文件来进行IOC&DI管理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用于配置管理的bean对象,id表示bean对象的唯一标识,class用于识别bean对象对应的类--><bean id="Dao" class="org.example.Dao.impl.DaoImpl" /><bean id="Service" class="org.example.Service.impl.ServiceImpl" ><!-- property用于设置bean对象中的依赖,其中name属性表示依赖名称,ref属性后接bean对象的id--><property name="dao" ref="Dao" /></bean>
</beans>

涉及修改的代码如下:

// src/main/java/org/example/Service/impl/ServiceImpl.java
@Setter //通过setter注入来自动获取bean对象
public class ServiceImpl implements Service {private Dao dao;public void ServiceFunc() {System.out.println("Now is Service!");dao.DaoFunc();}
}// src/main/java/org/example/App2.java
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;public class App2 {public static void main(String[] args) {ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml"); //根据配置文件生成新的IOC容器Service service = (Service) ctx.getBean("Service"); //获取bean对象service.ServiceFunc();}
}

Bean对象

bean对象的配置属性如下:

基础配置

id:一个bean对象存在唯一的id,通过id获取bean对象
class:bean的类型,即配置的全路径类名

name:定义bean的别名,使用别名也可以获取bean对象;可以定义多个,使用空格、,;进行分割
scope:定义bean的作用范围

scope="singleton" 表示单例(默认),适合生成一次就行可以多次复用的bean对象
scope="property" 表示非单例,适合需要记录不同状态的bean对象

Bean对象实例化方法

Bean对象实例化存在三种方法:①构造方法 ②静态工厂 ③实例工厂

构造方法(常用)

构造方法要素:①相关类提供可访问的构造方法且是无参方法 ②配置文件设置

<bean id="Dao" class="org.example.Dao.impl.DaoImpl" />

关于工厂的相关概念

工厂方法模式是一种创建型设计模式,它提供了一种创建对象的最佳方式,而无需向客户端暴露创建逻辑。该模式通过定义一个用于创建对象的接口,让子类决定实例化哪一个类,从而将对象的创建过程延迟到子类中进行。

工厂方法模式的核心思想是将对象的创建与使用分离,使得系统在不修改现有代码的情况下,能够轻松地引入新的产品类型。这种模式遵循五大设计原则之一的“开闭原则”,即对扩展开放、对修改关闭,提高了系统的灵活性和可维护性。


静态工厂(了解即可)

对于静态工厂,我们需要设置一个返回Dao类的静态工厂类

// src/main/java/org/example/Factory/DaoObjectFactory.java
public class DaoStaticFactory {public static Dao getDaoStaticFactory() {return new DaoImpl();}
}

然后在配置文件中设置静态工厂的bean对象属性

// src/main/resources/applicationContext.xml
<bean id="DaoStaticFactory" factory-method="getDaoStaticFactory" class="org.example.Factory.DaoStaticFactory" />
<bean id="Service" class="org.example.Service.impl.ServiceImpl" ><property name="dao" ref="DaoStaticFactory" />
</bean>

其余部分不变

实例化工厂(了解即可)

相较于静态工厂,实例化工厂需要先实例化对象然后再调用其中的工厂方法

// src/main/java/org/example/Factory/DaoObjectFactory.java
public class DaoObjectFactory {public Dao getDao() {return new DaoImpl();}
}// src/main/resources/applicationContext.xml
<bean id="DaoObjectFactory" class="org.example.Factory.DaoObjectFactory" />
<bean id="DaoObjectFactoryMethod" factory-method="getDao" factory-bean="DaoObjectFactory" />
<bean id="Service" class="org.example.Service.impl.ServiceImpl" ><property name="dao" ref="DaoObjectFactoryMethod" />
</bean>

我们发现,创建对象的方法名不固定,所以使用实现接口FactoryBean<T>的方法进行优化。这个实现方法常用

// src/main/java/org/example/Factory/DaoObjectFactoryBean.java
public class DaoObjectFactoryBean implements FactoryBean<Dao> {@Overridepublic @Nullable Dao getObject() throws Exception { //必须要实现的方法,用于返回Bean对象类的创建方法return new DaoImpl();}@Overridepublic @Nullable Class<?> getObjectType() { //必须要实现的方法,用于返回Bean对象类的类型return Dao.class;}@Overridepublic boolean isSingleton() { //非必要实现,实现的Bean对象是否为单例,true为单例false为非单例,默认为truereturn true;}
}// src/main/resources/applicationContext.xml
<bean id="DaoObjectFactoryBean" class="org.example.Factory.DaoObjectFactoryBean" />
<bean id="Service" class="org.example.Service.impl.ServiceImpl" ><property name="dao" ref="DaoObjectFactoryBean" />
</bean>

这三种方法,构造方法和实例化工厂的优化常用,其余了解即可。

Bean生命周期

bean的生命周期:bean对象从创建到销毁的过程

bean的生命周期的控制:bean对象创建前和销毁后所做的事情

基本流程如下:

初始化容器:

①创建对象(内存分配)
②执行构造方法
③执行属性注入(set操作)
④执行bean初始化方法

使用bean

执行业务操作

关闭/销毁bean容器

执行bean销毁方法

Bean对象生命周期控制方法

实现方式有两种:①Bean对象类中实现生命周期控制方法,然后在xml配置文件中配置生命周期控制方法 ②实现接口的生命周期控制方法

方法一

在bean对象相关类中实现生命周期控制方法,然后在配置文件中,在<beans>中设置<init-method><destroy-method>属性设置创建和销毁对应的方法

// src/main/java/org/example/Dao/impl/DaoImpl.java
public class DaoImpl implements Dao {@Overridepublic void DaoFunc() {System.out.println("Now is Dao!");}@Overridepublic void InitBean() {System.out.println("Now Init Bean!");}@Overridepublic void DestroyBean() {System.out.println("Now Destroy Bean!");}
}// src/main/resources/applicationContext.xml
<bean id="Dao" class="org.example.Dao.impl.DaoImpl" init-method="InitBean" destroy-method="DestroyBean"/>
<bean id="Service" class="org.example.Service.impl.ServiceImpl" ><property name="dao" ref="Dao" />
</bean>

效果如下:
image

方法二(了解即可)

通过实现InitializingBeanDisposableBean接口来实现创建和销毁控制方法

@Setter
@AllArgsConstructor
@NoArgsConstructor
public class ServiceImpl implements Service, InitializingBean, DisposableBean {private Dao dao;public void ServiceFunc() {System.out.println("Now is Service!");dao.DaoFunc();}@Overridepublic void afterPropertiesSet() throws Exception {System.out.println("Now Init Bean in Service!");}@Overridepublic void destroy() throws Exception {System.out.println("Now Destroy Bean in Service!");}}

效果如下:
image

其中,关于初始化控制方法的方法名为afterPropertiesSet,表示其为执行初始化属性(set)之后执行的方法

设置如下代码进行测试:

public class ServiceImpl implements Service, InitializingBean, DisposableBean {private Dao dao;public void setDao(Dao dao){System.out.println("Now Init Set!");this.dao = dao;}public void ServiceFunc() {System.out.println("Now is Service!");dao.DaoFunc();}@Overridepublic void afterPropertiesSet() throws Exception {System.out.println("Now Init Bean in Service!");}@Overridepublic void destroy() throws Exception {System.out.println("Now Destroy Bean in Service!");}}

测试结果如下:
image

Bean对象的销毁

一般情况下,我们运行JAVA程序是在JAVA虚拟机中,虚拟机启动,程序运行,初始化IOC容器,而程序执行完成后虚拟机直接关闭,没有销毁IOC容器的操作(或者说关闭之后销毁我们无法校验对应的生命周期销毁方法),因此为了使得销毁方法执行,需要手动设置销毁时机

方法一

使用ConfigurableAppliaction接口的close操作,进行手动关闭容器

ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
ctx.close();

方法二

使用ConfigurableAppliaction接口的close操作,注册关闭钩子,退出前先销毁IOC容器再退出虚拟机

ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
ctx.registerShutdownHook();

依赖注入

setter注入

setter注入分为普通注入引用注入,其关键在于<property>标签

引用注入

引用注入就是之前的设置类对象的引用,关键在于在<property>标签中设置ref属性

// src/main/resources/applicationContext.xml
<bean id="Dao" class="org.example.Dao.impl.DaoImpl" />
<bean id="Service" class="org.example.Service.impl.ServiceImpl" ><property name="dao" ref="Dao" />
</bean>// src/main/java/org/example/Service/impl/ServiceImpl.java
public class ServiceImpl implements Service{private Dao dao;
}

普通注入

普通注入就是设置类对象中的普通属性,关键在于在<property>标签中设置value属性

// src/main/resources/applicationContext.xml
<bean id="Dao" class="org.example.Dao.impl.DaoImpl" init-method="InitBean" destroy-method="DestroyBean"><property name="number" value="100" /><property name="status" value="OK" />
</bean>// src/main/java/org/example/Dao/impl/DaoImpl.java
@Setter
public class DaoImpl implements Dao {private int Number;private String Status;
}

构造器注入

构造器注入分为普通注入引用注入,其相较于setter注入,关键在于<constructor-arg>标签,并使用构造函数

<bean id="Dao" class="org.example.Dao.impl.DaoImpl" init-method="InitBean" destroy-method="DestroyBean"><constructor-arg name="Number" value="999" /><constructor-arg name="Status" value="Okay" />
</bean>
<bean id="Service" class="org.example.Service.impl.ServiceImpl" ><constructor-arg name="dao" ref="Dao" />
</bean>

为防止属性名过于耦合,还有设置type形参类型注入和设置index形参位置注入

// src/main/java/org/example/Dao/impl/DaoImpl.java
@Setter
public class DaoImpl implements Dao {private int Number;private String Status;
}// src/main/resources/applicationContext.xml// 形参类型注入
<bean id="Dao" class="org.example.Dao.impl.DaoImpl" init-method="InitBean" destroy-method="DestroyBean"><constructor-arg type="int" value="999" /><constructor-arg type="java.lang.String" value="Okay" />
</bean>// 形参位置注入
<bean id="Dao" class="org.example.Dao.impl.DaoImpl" init-method="InitBean" destroy-method="DestroyBean"><constructor-arg index="0" value="999" /><constructor-arg index="1" value="Okay" />
</bean>

强制依赖使用构造器注入,保证严谨;可选依赖使用setter注入,更加灵活。

Spring框架倡导使用构造器注入。

自己开发的模块推荐使用setter注入。


依赖自动装配

依赖自动装配关键在于autowire属性,主要方式有按类型装配byType和按名称装配byName

// src/main/resources/applicationContext.xml
<bean id="Dao" class="org.example.Dao.impl.DaoImpl" />
<bean id="Service" class="org.example.Service.impl.ServiceImpl" autowire="byType" /><bean id="Dao" class="org.example.Dao.impl.DaoImpl" />
<bean id="Service" class="org.example.Service.impl.ServiceImpl" autowire="byName" />

自动装配只用于引用类型依赖注入,不支持普通类型依赖注入

使用按类型装配byType必须保证容器中有唯一类型的bean,使用按名称装配byName必须保证容器中有指定名称的bean

相较于byTypebyName因变量名和配置耦合,因此不推荐使用

自动装配优先级低于setter注入和构造器注入,同时出现时自动装配失效


http://www.jsqmd.com/news/619791/

相关文章:

  • CSL编辑器完全指南:5分钟打造你的专属文献引用样式 ✨
  • Knowledge-Graph项目揭秘:知识图谱与深度学习的完美结合
  • 高效实战:5个AKShare核心技巧实现金融数据分析自动化(2024专业版)
  • 2024年Node.js最佳实践终极指南:102个技巧提升你的后端开发水平
  • ESP32实战-LVGL音乐播放界面移植与优化指南
  • 告别FileZilla!用MobaXterm+Samba在泰山派RK3566上搭建Windows文件共享(保姆级教程)
  • Berlekamp–Massey 算法
  • 从API解析到本地化:LinkSwift如何重新定义网盘直链下载体验
  • Termius vs WindTerm:哪个更适合你的远程开发需求?(Ubuntu平台实测对比)
  • SCM-02-配置库管理报告
  • YOLOv8 ROS 2完整部署教程:让机器人拥有火眼金睛的终极指南
  • 离线环境安装elk及设置密码认证
  • M2LOrder WebUI实战:Gradio Blocks高级定制+多Tab情感分析工作台
  • 多动症早期识别是什么?运动干预在儿童注意力缺陷中的作用是什么?
  • SCM-01-配置管理计划
  • 决胜408:从暴力枚举到最优解法的实战演进
  • StructBERT模型助力CSDN技术博客质量提升:相似文章检测与原创保护
  • Multisim仿真实战:六十进制计数器的设计与实现
  • 收藏!AI大模型这么火,普通程序员/小白能参与其中么?该怎么入门?
  • 为什么头部银行/制造/政务客户集体跳过Pilot直签SITS2026?揭秘其“可验证AI逻辑引擎”背后的4层可信架构设计
  • 在深度学习中,batch、epoch 和 iteration 的关系
  • QTableWidget 表格组件窗
  • P12264 『STA - R9』咏叹调调律
  • 手把手教你用ZYNQ+AD9361搭建SDR开发环境:从SPI配置到LVDS接口的避坑全记录
  • 三分钟掌握Bifrost:免费下载三星官方固件的终极解决方案
  • C#与C++进程高效对话:手把手教你用共享内存+互斥锁构建跨语言通信桥梁
  • 动态标签分配策略:OTA, SimOTA, Task-Aligned Assigner
  • OpenClaw安全实践:Qwen3-14B私有镜像+本地化执行边界管控
  • 附录S-1 客户服务计划
  • 破解付费墙限制:6款高效内容解锁工具完全指南