Spring学习笔记-IOC(一)

目   录

所有案例代码!!!下载地址

内聚与耦合

传统的程序代码存在的问题

什么是耦合、内聚?

Spring-IOC的由来

传统的三层架构

普通工厂模式 

配置文件的方式

什么是IOC?

IOC入门小案例

环境说明

制作步骤

详细代码实现

IOC的配置(XML格式)

bean

bean的scope属性(重点)

单例bean的优势

单例bean的劣势

面试题:Spring 为啥把bean默认设计成单例?

bean的生命周期

bean的实例化方式(3种)

IOC配置(XML)的后续


所有案例代码!!!下载地址

Spring示例代码: 根据黑马程序员的视频课,自己课后练习代码汇总,与本人CSDN文章相对应。https://gitee.com/li_chen_xin_TL/spring-sample-code.git

内聚与耦合

传统的程序代码存在的问题

例如下述案例,是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

posted @   金鳞踏雨  阅读(14)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· .NET10 - 预览版1新功能体验(一)
点击右上角即可分享
微信分享提示