Spring学习笔记-IOC(一)
目 录
所有案例代码!!!下载地址
内聚与耦合
传统的程序代码存在的问题
例如下述案例,是JDBC的示例代码,它可以实现数据库的查询product表的操作。
但是,如果我们想要修改数据库名 或者 修改SQL语句,就要修改下面的代码,这样子的代码“连接性”太强了!想要修改一个小东西,就要去动源代码,影响开发效率。这种代码的耦合性太高了
什么是耦合、内聚?
耦合:代码书写的过程中所用的技术的结合紧密度,用于衡量软件中各个模块之间的互联关系。
内聚:代码书写的过程中单个模块内部各组成部分间的联系,用于衡量软件中各个功能模块内部的功能联系。
显然,我们希望自己写出来的东西是具有——高内聚、低耦合的特点。
就是同一个模块内的各个元素之间要高度紧密,但是不同模块之间的相互依赖要尽可能的少。
Spring-IOC的由来
传统的三层架构
在三层架构中,是表现层、业务层、数据层,这里我们以业务层调用数据层为例。
在业务层中new了一个数据层UserDaoImp1的实现类,但是这个时候,我们又有了一个UserDaoImp2,我们想修改业务层,让业务层new一个UserDaoImp2的实现类。
这时候我们就要去修改业务层UserServiceImpl中的代码。
如此一下耦合度较高!
有没有一个降低耦合度的办法呢?
答:有!使用工厂模式!
普通工厂模式
为了解决上述问题,我们使用了工厂设计模式,将调用数据层的方法放在工厂类中。
显然,这时候我们如果要修改实现类只要更改工厂类的调用。就不用去修改源代码了!但是却要修改工厂的代码。
有没有一种方式可以不用改代码呢?
有!使用配置文件的方式。
配置文件的方式
我们创建一个实现类,将其写到配置文件中,然后工厂类通过反射将配置文件的信息读取,业务层用工厂进行调用。
如果此时再有一个实现类需要被调用,我们就不用去该工厂的代码了!修改配置文件即可!
这样子,就断开了工厂与实现类之间的耦合;配置文件和实现类之间耦合在了一起,但是这样子的耦合是被允许的!
这是因为,如果类之间有耦合,那么修改之后要重新编译、打包、发布;而配置文件的变更,就只要将该文件发送到服务器即可。
而工厂+配置的形式就是Spring的雏形!
直接一点说,就是Spring中最核心的思想——IOC。
什么是IOC?
IOC:控制反转,Spring反向控制应用程序所需要使用的外部资源。
Spring控制的资源全部放在Spring容器中,该容器称为IOC容器。
当你需要用资源的时候,Spring就会给你提供你所需要的资源;而你自己就不用去管对象的创建与释放。直白一点说就是:主动变被动
IoC 带来的最大改变不是代码层面的,而是从思想层面上发生了“主从换位”的改变。原本调用者是主动的一方,它想要使用什么资源就会主动出击,自己创建;但在 Spring 应用中,IoC 容器掌握着主动权,调用者则变成了被动的一方,被动的等待 IoC 容器创建它所需要的对象(Bean)。
IOC入门小案例
环境说明
模拟三层架构中表现层调用业务层功能
-
表现层:UserApp模拟UserServlet(使用main方法模拟)
-
业务层:UserService
制作步骤
1.导入spring坐标(5.1.9.release)
2.编写业务层接口与实现类
3.建立spring配置文件
4.配置所需资源(Service)为spring控制的资源
5.表现层(App)通过spring获取资源(Service实例)
详细代码实现
(1)导入spring坐标(5.1.9.release)
首先,显然学习到了Spring,那maven想必不会陌生了吧,我们现在maven的pom.xml中导入Spring的坐标
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.1.9.RELEASE</version>
</dependency>
</dependencies>
注意,如果等等程序运行后出错十有八九是下图的勾没有打上!——将IDE 构建/运行操作委托...
不要惊讶,IDEA官方现在自己有提出插件汉化。
(2)编写业务层接口与实现类
UserService接口
public interface UserService {
public void save();
}
UserServiceImpl实现类
public class UserServiceImpl implements UserService {
public void save() {
System.out.println("user save running ....");
}
}
(3)建立spring配置文件
spring的配置文件的名称——applicationContext.xml (公认的)
(4)配置所需资源(Service)为spring控制的资源
<?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
https://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 1.创建Spring控制的资源-->
<!-- id就是名字,随便命名 -->
<bean id="userService" class="com.itheima.service.impl.UserServiceImpl"></bean>
</beans>
(5)表现层(App)通过spring获取资源(Service实例)
public class UserApp {
public static void main(String[] args) {
// 2.加载配置文件
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
// 3.获取资源
UserService userService = (UserService) ctx.getBean("userService");
userService.save();
}
}
IOC的配置(XML格式)
IOC的配置可以有两种形式——XML、注解
这里我们先讲述XML格式的IOC配置
关于配置大体有如下几样东西需要学习:
- bean
- DI
- properties文件
- 团队开发
- ApplicationContext
- 第三方资源配置
bean
在Spring中配置文件中的bean,指的是bean标签(不是实体类对象的那个bean包哈!不要混了)
格式
<beans>
<bean />
</beans>
作用
定义spring中的资源,受此标签定义的资源将受到spring控制
基本属性
id:bean的名称,通过id值获取bean
class:bean的类型
name:bean的名称,可以通过name值获取bean,用于多人配合时给bean起别名
我们使用上面的案例代码来讲述
这里使用了name属性,简单的说就是给UserServiceImpl实现类对象取了3个名字!
<!-- bean可以定义多个名称,使用name属性2完成,中间使用","来分割-->
<bean id="userService" name="b1,b2" class="com.itheima.service.impl.UserServiceImpl">
</bean>
下图红色箭头的位置使用userService、b1、b2三个名称都是可以的!
bean的scope属性(重点)
scope属性默认是singleton原型的
作用
定义bean的作用范围 ,用于控制创建后的对象是否是单例的。
取值
-
singleton:设定创建出的对象保存在spring容器中,是一个单例的对象
-
prototype:设定创建出的对象保存在spring容器中,是一个非单例的对象(原型)
-
request、session、application、 websocket :设定创建出的对象放置在web容器对应的位置
后面四个取值对我们现在初学阶段来说意义不大,我们重点关注前两属性值singleton(单例)、prototype(原型)
区别
(1)作用不同
singleton单例:一个bean被声明为单例时,处理多次请求时spring容器里只实例化一个bean,后续的请求公用这个对象,这个对象存储在一个map中,当有请求时,先在缓存中(map)查找是否存在,存在则使用,不存在才实例化一个对象
prototype原型:每当有请求来就实例化一个新的bean,没有缓存
总的来说
1、单例的bean只有第一次创建新的bean 后面都会复用该bean,所以不会频繁创建对象。
2、原型的bean每次都会新创建
(2)bean创建时机不同
使用singleton属性,bean对象是在加载容器时创建;
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
使用prototype属性,bean对象是在获取对象(getBean)时创建
UserService userService1 = (UserService) ctx.getBean("userService");
UserService userService2 = (UserService) ctx.getBean("userService");
UserService userService3 = (UserService) ctx.getBean("userService")
单例bean的优势
由于不会每次都新创建新对象所以有一下几个性能上的优势:
-
减少了新生成实例的消耗
新生成实例消耗包括两方面,第一,spring会通过反射或者cglib来生成bean实例这都是耗性能的操作,其次给对象分配内存也会涉及复杂算法。 -
减少jvm垃圾回收
由于不会给每个请求都新生成bean实例,所以自然回收的对象少了。 -
可以快速获取到bean
因为单例的获取bean操作除了第一次生成之外其余的都是从缓存里获取的所以很快。
单例bean的劣势
单例的bean一个很大的劣势就是他不能做到线程安全
,由于所有请求都共享一个bean实例,所以这个bean要是有状态的一个bean的话可能在并发场景下出现问题,而原型的bean则不会有这样问题(但也有例外,比如他被单例bean依赖),因为给每个请求都新创建实例。
面试题:Spring 为啥把bean默认设计成单例?
- 为了提高性能
- 少创建实例*
- 垃圾回收
- 缓存快速获取
bean的生命周期
bean的创建与销毁,我们可以通过相应的属性,指定bean创建和销毁时所执行的工作。
而这个相应的属性就是——init-method,destroy-method,它们是bean标签下的两个属性。
格式
<bean id="名字" init-method="方法名" destroy-method="方法名"></bean>
关于bean生命周期我们可以直接给出结论
当scope=“singleton”时,spring容器中有且仅有一个对象,init方法在创建容器时仅执行一次
当scope=“prototype”时,spring容器要创建同一类型的多个对象,init方法在每个对象创建时均执行一次
当scope=“singleton”时,关闭容器会导致bean实例的销毁,调用destroy方法一次(单例模式时,bean归spring容器管理)
当scope=“prototype”时,对象的销毁由垃圾回收机制gc()控制,destroy方法将不会被执行(bean不归spring容器管理)
代码演示说明
案例的结构如下,注意UserService接口里面时空的!什么都没有,本案例只是为了研究bean的创建、销毁时是怎样执行工作的?
UserServiceImpl
public class UserServiceImpl implements UserService {
public UserServiceImpl() {
System.out.println("constructor is running...");
}
public void init() {
System.out.println("init...");
}
public void destroy() {
System.out.println("destroy...");
}
}
UserApp
public class UserApp {
public static void main(String[] args) {
// 2.加载配置文件
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
// 3.获取资源
UserService userService1 = (UserService) ctx.getBean("userService");
UserService userService2 = (UserService) ctx.getBean("userService");
UserService userService3 = (UserService) ctx.getBean("userService");
ctx.close();
}
}
(1)当我们定义scope属性为singleton(单例)模式时
applicationContext.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
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="userService" scope="singleton" init-method="init" destroy-method="destroy" class="com.itheima.service.impl.UserServiceImpl"/>
</beans>
虽然有多次调用getBean方法,但是由于时单例,所以创建一个对象,并且在单例模式中,该对象的销毁时由Spring容器管的!
(2)当我们定义scope属性为property(原型)模式时
<?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
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="userService" scope="prototype" init-method="init" destroy-method="destroy" class="com.itheima.service.impl.UserServiceImpl"/>
</beans>
显然时没有执行销毁的,此时bean的销毁不归Spring容器管!是由垃圾回收机制gc()控制。
bean的实例化方式(3种)
(1)构造器实例化(重点)
(2)静态工厂方式实例化(了解)
(3)实例工厂方式实例化(了解)
可以看看这里,比较详细
Spring实例化Bean的三种方法 (biancheng.net)http://c.biancheng.net/view/4256.html
静态工厂方式实例化
我们要使用到factory-bean
<bean id="userService4" class="com.itheima.service.UserServiceFactory1" factory-method="工厂对象中的静态方法名"/>
定义bean对象创建方式,使用静态工厂的形式创建bean,兼容早期遗留系统的升级工作
实例工厂方式实例化
我们要使用到factory-bean,factory-method
-
使用实例工厂创建bean首先需要将实例工厂配置bean,交由spring进行管理
-
factory-bean是实例工厂的beanId
<bean id="factoryBean" class="com.itheima.service.UserServiceFactory2" />
<bean id="userService5" factory-bean="factoryBean" factory-method="工厂对象中的方法名"/>
IOC配置(XML)的后续
- bean
- DI
- properties文件
- 团队开发
- ApplicationContext
- 第三方资源配置
我们这篇文章只讲了bean相关的内容,后续请看下文
Spring学习笔记-IOC(二)_Tensorflow-CSDN博客https://blog.csdn.net/weixin_43715214/article/details/123288486
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· .NET10 - 预览版1新功能体验(一)