Spring框架-入门(IOC,DI)

2024-01-04

Spring框架

spring官网;https://spring.io/

简介

Spring框架是一个开源的Java应用程序框架,它提供了一种全面的编程和配置模型,可用于构建现代化的基于Java的企业级应用程序。

Spring框架提供的核心功能包括控制反转(IoC)、依赖注入(DI)、面向切面编程(AOP)、数据访问、事务管理、Web应用程序开发等方面。它可以与其他技术和框架无缝集成,如Hibernate、MyBatis、Struts、Servlet API,SpringMVC等。

使用Spring框架可以简化Java应用程序的开发过程,减少代码量,提高代码的可重用性和可维护性。它也提供了各种扩展和插件,以满足特定场景下的需求。

Spring框架的设计思想是基于松耦合、可扩展和可重用的原则,使得Java开发者可以更专注于业务逻辑的实现,而不必关注底层技术的细节。它已成为Java企业级应用程序开发中最流行和广泛使用的框架之一。

创建Spring项目

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

pom.xml

<?xml version="1.0" encoding="UTF-8"?> <!-- xml声明,指定版本和编码 -->
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
         http://maven.apache.org/xsd/maven-4.0.0.xsd"><!-- pom文件的根元素 -->

    <!-- 指定pom模型的版本 -->
    <modelVersion>4.0.0</modelVersion>

    <!-- 项目所属组织或公司的唯一标识符 -->
    <groupId>com.sin</groupId>
    <!-- 项目的唯一标识符 -->
    <artifactId>spring_demo</artifactId>
    <!-- 项目的版本号 -->
    <version>1.0-SNAPSHOT</version>

    <!-- 固定项目依赖版本 -->
    <properties>
        <!-- 指定源代码的字符编码 -->
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <!-- 指定使用的Spring框架版本 -->
        <spring.version>5.3.13</spring.version>
    </properties>

    <dependencies>
        <!-- spring-core库的依赖 -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>${spring.version}</version>
        </dependency>

        <!-- spring-context库的依赖 -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>${spring.version}</version>
        </dependency>

        <!-- spring-beans库的依赖 -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-beans</artifactId>
            <version>${spring.version}</version>
        </dependency>
    </dependencies>
</project>

理解IOC和DI:

在传统的程序

User.java

package com.sin.pojo;

/**
 * @createTime 2023/12/29 16:22
 * @createAuthor SIN
 * @use
 */
public class User {
    private int id;
    private String name;


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

    public int getId() {
        return id;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }
}

UserDao.java

package com.sin.Dao;

import com.sin.pojo.User;

/**
 * @createTime 2023/12/29 16:22
 * @createAuthor SIN
 * @use
 */
public class UserDao {
    public User getUser(int userId) {
        // 模拟从数据库中获取用户信息的逻辑
        // 这里假设直接返回一个User对象
        User user = new User();
        user.setId(userId);
        user.setName("张三");
        return user;
    }
}

UserService.java

package com.sin.service;

import com.sin.Dao.UserDao;
import com.sin.pojo.User;

/**
 * @createTime 2023/12/29 16:21
 * @createAuthor SIN
 * @use
 */
public class UserService {

    private UserDao userDao;

    public UserService() {
        userDao = new UserDao();
    }

    /**
     * 该方法允许外部设置UserDao对象,这种方式称之为依赖注入
     *
     * @param userDao
     */
    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }

    public void getUserInfo(int userId) {
        // 通过UserDao获取用户信息
        User user = userDao.getUser(userId);
        // 处理用户信息
        System.out.println("用户ID: " + user.getId());
        System.out.println("用户姓名: " + user.getName());
    }
}

在这里插入图片描述

在编写过程中发现:

  1. 需要获取数据时,需要创建 UserSercie 对象,每次创建 UserService 对象都会创建出一新的 UserDao 对象,这样做会导致内存的浪费,因为我们可能只需要一个 UserDao 对象,而不是每次都创建一个新的对象。
  2. 这种实现方式还存在对象生命周期的问题。如果 UserService 对象被销毁了,那么与之关联的 UserDao 对象也将被销毁,这可能会导致数据丢失或其他意想不到的问题。
  3. 高耦合性: UserService UserDao 之间的关系非常紧密, UserService 需要知道如何创建 UserDao 对象,并且需要在方法中直接调用 UserDao 的方法。这样做会使得代码高度耦合,难以维护。
  4. 代码重复:如果有多个类需要使用 UserDao 对象进行数据访问,那么就需要在每个类中都写出类似的代码,这将导致大量重复的代码,增加了代码的冗余。
  5. 可测试性差:在单元测试时,我们希望能够对 UserService 进行测试,而不是测试 UserDao 的实现。但是,由于 UserService 依赖于 UserDao ,因此需要在测试中实例化 UserDao 对象并设置到 UserService 中,这就使得测试变得困难。
  6. 不易扩展:如果我们需要更改 UserDao 的实现方式,比如从数据库中获取用户信息改为从Web服务中获取,那么就需要修改 UserService 的代码,这样做会使得代码更加脆弱,不易扩展。

这些问题可以通过使用IOC容器和DI框架来解决。在Spring框架中,我们可以通过配置文件或注解来告诉Spring容器如何创建对象并管理对象之间的依赖关系。Spring容器会在需要时自动创建对象,并确保每个对象只被创建一次,并且可以在整个应用程序中共享。这样做不仅可以提高应用程序的性能,还可以减少内存消耗和对象生命周期的问题。

在这里插入图片描述

IOC控制反转

控制反转(Inversion of Control,简称IoC)是一种软件设计原则,也是面向对象编程中的一种设计模式。它的核心思想是将对象的创建和依赖关系的管理交给容器来完成,而不是由对象自身来创建和管理其依赖的对象。

传统的程序设计中,对象之间的依赖关系常常通过对象自身来创建和管理。例如,在一个类中直接使用 new 关键字来创建其他类的实例,这样就导致了类与类之间的紧耦合,使得代码难以维护、扩展和测试。

而采用IoC的方式,对象之间的依赖关系被反转了。具体来说,IoC通过引入一个容 在这里插入图片描述
器(如Spring框架的ApplicationContext),容器负责创建对象并管理对象之间的依赖关系。在IoC容器中,我们可以配置对象的创建方式、依赖关系以及其他属性,然后由容器来实例化对象,并将所需的依赖注入到对象中。

IoC的优点包括:

  1. 解耦:IoC可以降低对象之间的耦合度,使得代码更加灵活、可维护和可扩展。

  2. 便于测试:由于依赖关系由容器管理,我们可以很容易地对对象进行单元测试,而不需要真正地创建依赖对象。

  3. 可以更好地支持面向接口编程:通过IoC容器可以轻松地切换不同的实现类,从而支持面向接口编程的设计原则。

  4. 提高了代码的可读性和可维护性:通过配置文件或注解来管理对象的创建和依赖关系,使得代码的逻辑更加清晰、易于理解和维护。

示例
pom.xml
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.3.13</version>
</dependency>

spring-context是Spring的核心模块之一,提供了Spring应用程序上下文的基础设施支持,也是Spring框架中最重要的部分之一。它负责管理Spring应用程序中所有bean的声明周期,并提供了依赖注入和面向切面编程等重要的特性。

主要提供的功能:

  1. ApplicationContext接口:这是Spring应用程序上下文的核心接口,定义了获取bean、注册bean、发布事件等操作的API。
  2. BeanFactory接口:定义了Spring IoC容器的基本功能,这是Spring应用程序上下文的基础。
  3. AOP支持:Spring提供了强大的AOP支持,可以在应用程序中轻松实现面向切面编程。
  4. 事件处理:Spring提供了事件机制,允许应用程序中的各个组件之间进行通信和协作。
  5. SpEL表达式语言:Spring提供了一种表达式语言,允许开发者使用类似于EL的语法来处理复杂的表达式和逻辑。
  6. 数据绑定:Spring提供了一种灵活的数据绑定机制,可以将请求参数、属性文件、XML文档等各种数据源与Java对象进行绑定。
Person.java
package com.sin.pojo;

/**
 * @createTime 2024/1/2 8:44
 * @createAuthor SIN
 * @use
 */
public class Person {

    private String name;
    private int age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
applicationContext.xml

注意:文件位置必须在src/main/resources目录下。这是默认的资源目录,Spring会自动将其包含在类路径中。

<?xml version="1.0" encoding="UTF-8"?><!-- XML声明,xml版本和编码格式 -->

<!-- spring配置文件起点  -->
<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
            id : bean名称
            class :SpringBean对应的Java对象
    -->
    <bean id="person" class="com.sin.pojo.Person">
        <!-- 对bean的属性进行赋值 -->
        <property name="name" value="张三"/>
        <property name="age" value="18"/>
    </bean>


</beans>

bean : 是指 Spring容器管理的Java对象 ,是Spring框架的核心概念之一。在Spring中Bean是应用程序的基本构建模块,代表着一个可重用的组件,它可以是任何普通的Java对象,也可以是由Spring容易创建和管理的特殊对象。这些对象可以通过Spring容器进行创建,配置和管理,从而实现依赖注入和面向切面编程等功能。

为什么使用Spring配置文件来赋值?

可以将应用程序中各个组件之间的依赖关系和属性值都统一管理,从而实现松耦合的设计和高度可配置性。

PersonTest.java
package com.sin.pojo;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * @createTime 2024/1/2 8:48
 * @createAuthor SIN
 * @use 通过Spring容器获取并使用一个被管理的Bean
 */
public class PersonTest {

    public static void main(String[] args) {
        /**
         * 创建ApplicationContext对象,
         * ApplicationContext : 是Spring的顶层接口,用于表示整个应用程序的上下文环境
         * ClassPathXmlApplicationContext :通过指定文件来获取spring容器
         */
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        /**
         * 从容器中获取指定bean
         * getBean :返回类型为Object,我们需要将其为具体类型,
         */
        Person person = (Person) applicationContext.getBean("person");
        System.out.println(person);
    }
}

流程图
请添加图片描述

在整个过程中发现不用创建Person对象了,不需要new Person();减少了内存空间的占用。整个过程将对象的创建交给Spring来管理。

DI依赖注入

依赖注入(Dependency Injection,简称DI)是控制反转(IoC)的一个具体实现方式。它是指通过外部将对象的依赖关系注入到对象中,而不是由对象自身来创建和管理依赖的对象。

在传统的程序设计中,对象通常需要自己创建和管理其所依赖的其他对象。这种方式导致了对象之间的紧耦合,使得代码难以测试、扩展和维护。而采用依赖注入,我们将对象的依赖关系交给容器来管理,对象只需声明自己需要哪些依赖,容器则负责将相应的依赖注入到对象中。

依赖注入可以通过以下几种方式实现:

  1. 构造函数注入(Constructor Injection):通过对象的构造函数来传递依赖。在对象创建时,容器会根据构造函数的参数类型来自动注入所需的依赖。

  2. Setter方法注入(Setter Injection):通过对象的setter方法来注入依赖。容器会调用对象的setter方法,并将相应的依赖作为参数传入。

  3. 接口注入(Interface Injection):通过对象实现一个特定的接口,该接口定义了注入依赖的方法。容器会在对象创建后,调用接口方法并注入相应的依赖。

依赖注入的优点包括:

  1. 解耦:通过将对象的依赖关系交给容器管理,实现了对象之间的解耦,使得代码更加灵活、可维护和可扩展。

  2. 可测试性:由于对象的依赖关系由容器注入,我们可以很容易地使用模拟对象来进行单元测试,而不需要真实的依赖对象。

  3. 灵活性:依赖注入使得切换和替换不同的依赖实现变得容易,从而支持面向接口编程和可插拔的架构设计。

传统的方式
GreetingService.java
package com.sin.service;

/**
 * @createTime 2024/1/2 10:52
 * @createAuthor SIN
 * @use
 */
public interface GreetingService {

    public void sayHello();
}
GreetingServiceImpl.java
package com.sin.service.impl;

import com.sin.service.GreetingService;

import java.awt.*;

/**
 * @createTime 2024/1/2 10:52
 * @createAuthor SIN
 * @use
 */
public class GreetingServiceImpl implements GreetingService {

    @Override
    public void sayHello() {
        System.out.println("hello,world!");
    }
}
GreetingTest.java
package com.sin.test;

import com.sin.service.impl.GreetingServiceImpl;
import org.junit.Test;

/**
 * @createTime 2024/1/2 10:53
 * @createAuthor SIN
 * @use
 */
public class GreetingTest {

    @Test
    public void testGreeting(){
        GreetingServiceImpl greetingService = new GreetingServiceImpl();

        greetingService.sayHello();
    }
}
使用DI依赖注入
GreetingService.java
package com.sin.service;

/**
 * @createTime 2024/1/2 10:52
 * @createAuthor SIN
 * @use
 */
public interface GreetingService {

    public void sayHello();
}
GreetingServiceImpl.java
package com.sin.service.impl;

import com.sin.service.GreetingService;

import java.awt.*;

/**
 * @createTime 2024/1/2 10:52
 * @createAuthor SIN
 * @use
 */
public class GreetingServiceImpl implements GreetingService {

    @Override
    public void sayHello() {
        System.out.println("hello,world!");
    }
}
AppConfig.java
package com.sin.config;

import com.sin.service.GreetingService;
import com.sin.service.impl.GreetingServiceImpl;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * @createTime 2024/1/2 10:56
 * @createAuthor SIN
 * @use
 */
@Configuration // 标记为一个配置类,该类将提供Bean的定义和配置,
public class AppConfig {

    /**
     * 定义一个Bean,方法名getGreetingService将作为Bean的名称,默认为方法名(也可以通过name属性指定其他名称)。
     * @return 创建GreetingServiceImpl对象, 该对象将被Spring容器管理,
     */
    @Bean
    public GreetingService getGreetingService() {
        return new GreetingServiceImpl();
    }
}

AppTest.java
package com.sin.test;

import com.sin.config.AppConfig;
import com.sin.service.GreetingService;
import com.sin.service.impl.GreetingServiceImpl;
import org.junit.Test;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

/**
 * @createTime 2024/1/2 10:57
 * @createAuthor SIN
 * @use
 */
public class AppTest {

    @Test
    public void test(){
        /**
         * 创建spring容器并加载配置类
         * new AnnotationConfigApplicationContext(AppConfig.class)创建对象时加载配置类
         */
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);

        // 获取依赖注入的Bean
        // 通过bean名称来获取
        GreetingService greetingService1 =(GreetingService) context.getBean("getGreetingService");
        // 通过bean类型来获取
        GreetingService greetingService2 =context.getBean(GreetingService.class);


        // 使用依赖注入的Bean
        greetingService1.sayHello();
        greetingService2.sayHello();

    }
}

AnnotationConfigApplicationContext是一个基于注解配置方式的Spring容器,它负责加载配置类、注册和管理Bean的定义、提供依赖注入功能以及管理Bean的生命周期。

在这里插入图片描述

两种方式都可以用来获取Spring容器中的Bean,如果知道Bean的名称就可以使用Bean的名称作为参数来获取。如果只知道Bean的类型,则使用类型作为参数来获取。

流程图

请添加图片描述

总结:如果不使用Spring框架的依赖注入,需要手动创建和管理对象,这可能会导致代码冗余和可维护性的问题。但对于简单的应用程序,手动创建对象可能是可行的。

使用Spring框架的依赖注入,发现朱需要创建一次GreetingServiceImpl对象,就可以多次使用。通过声明式配置,简化了对象的创建和组装过程,降低了代码复杂度。Spring容器又可以管理依赖对象的生命周期,避免了内存泄漏的问题,大大减轻了JVM的压力。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Spring框架-入门(IOC,DI) 的相关文章

随机推荐

  • 5.1 中央寄存器的原理和组成

    思维导图 中央处理器 CPU 核心原理与组成 引言 中央处理器 CPU 作为计算机的 大脑 在现代计算机硬件中扮演着至关重要的角色 本文旨在深入探讨CPU的基本原理和组成部分 为读者提供一个全面的理解 CPU的基本原理 1 信息处理 CPU
  • <sa8650>sa8650 CDT-之-汽车CDT配置用户指南(上)

    sa8650 sa8650 CDT 之 汽车CDT配置用户指南 上 2 CDT概述 2 1 Platform ID值 2 2 CDT一般结构 2 3 CDT头 2 4 块元数据 2 5 CDBs 2 6 加载CDT的启动过程
  • 实现智能化运维的关键驱动力,你知道可观测性工具吗

    可观测性是指根据系统产生的数据评估内部系统状态的能力 对于IT运维团队来说 可观测性工具是非常重要的 通过这些工具 IT团队可以同时观察或深入了解IT基础架构中不同应用程序和资源的健康状况和状态 从而主动检测异常 分析问题并解决问题 可观测
  • linux centos使用rz、sz命令上传下载文件

    一般情况下 我们会使用终端软件 如 XShell SecureCRT 或 FinalShell 来连接远程服务器后 使用 rz 命令上传本地文件到远程服务器 再解压发版上线 一 安转使用 系统 Linux CentOS 7 安装 rz 和
  • Metasploit使用msfconsole命令启动时,报错‘/usr/share/metasploit-framework/......’

    当使用msfconsole命令启动时 msf无法启动 且报错 解决办法 1 更新apt程序库 apt get update 2 更新metasploit 框架 apt get install metasploit framework 3 重
  • 【C++项目】【报错】[错误] new: No such file or directory, compilation terminated【及解决方法】

    一 问题描述 C源代码文件在编译过程中报错 错误 new No such file or directory compilation terminated 代码如下 include
  • 第8章 多媒体嵌入

    学习目标 了解视频 音频嵌入技术 能够总结HTML5视频 音频嵌入技术的优点 了解常用的视频文件格式和音频文件格式 能够归纳HTML5支持的视频和音频格式 掌握HTML5中视频的嵌入方法 能够在HTML5页面中添加视频文件 掌握HTML5中
  • 服务器的丢包率高怎么办

    网络出现丢包状况了怎么办 具体情况可以从以下几点来判断 1 有可能是线路故障导致的 所以可以用光纤打光仪先判断是否是光纤的问题 2 对于设备方面来说 很多都是网络接口的光纤接触不良等 3 也可能是操作系统的问题 比如网卡问题和网络运营线路问
  • element ui弹窗在别的弹窗下方,优先级不高的问题

    在 弹窗 的标签中加入append to body即可解决该问题
  • Python基础(十六、数据容器汇总)

    文章目录 一 数据容器汇总 二 数据容器通用操作 1 遍历 2 通用统计 len 容器 max 容器 min 容器
  • 解决重定向导致的cookie丢失

    转发前加上 String s response getHeader Set Cookie s HttpOnly Secure SameSite None response setHeader Set Cookie s
  • Java编写CS架构学生管理系统

    一 环境准备 工具 eclipse navicat 环境 jdk8 数据库 mysql5 7 二 正式开始 第一步 分析需求 就是我们需要知道该干什么 登录功能 对学生信息增删改查操作 第二步 创建项目StudentManager 由于我们
  • catkin_make 编译报错 Unable to find either executable ‘empy‘ or Python module ‘em‘...

    文章目录 写在前面 一 问题描述 二 解决方法 参考链接 写在前面 自己的测试环境 Ubuntu20 04 一 问题描述 自己安装完 anaconda 后 再次执行 catkin make 遇到如下问题 CMake Error at opt
  • How to collect data

    How to collect data 爬虫 Java Python 反爬虫 自动化测试工具 Selenium QMetry Automation Studio Te
  • 【ESP32接入国产大模型之文心一言】

    1 怎样接入文心一言 随着人工智能技术的不断发展 自然语言处理领域也得到了广泛的关注和应用 在这个领域中 文心一言作为一款强大的自然语言处理工具 具有许多重要的应用价值 本文将重点介绍如何通过ESP32接入国产大模型之文心一言api 以及其
  • 公司PCB设计需要外包,需要准备哪些资料给PCB设计公司呢?

    现阶段许多公司仍然是让硬件工程师来进行PCB设计和方案开发 除开这些 硬件工程师还要做更多的专业工作 这样势必会使产品上市的时间大大延长 而且现在随着高速数字电子技术的发展 对高速PCB设计的要求也越高 信号完整性仿真分析 nbsp 时序分
  • 14.10-其他阻塞和非阻塞混合使用的原则

    其他阻塞和非阻塞混合使用的原则 1 同时使用阻塞和非阻塞赋值 2 对同一变量既阻塞赋值又非阻塞赋值 综合出错 原则5 不要在同一个always块中同时使用阻塞和非阻塞赋值 1 同时使用阻塞和非阻塞赋值 Verilog语法并没有禁止将阻塞和非
  • DriveMLM

    本人转载于大佬 大型语言模型为智能驾驶开辟了新的格局 赋予了他们类似人类的思维和认知能力 本文深入研究了大型语言模型 LLM 在自动驾驶 AD 中的潜力 进而提出了DriveMLM 这是一种基于LLM的AD框架 可以在仿真环境中实现闭环自动
  • 协议茶馆:TLV 格式及编码

    本篇是多年前的存篇 出处不详 旧酒换新瓶 温故知新 有了新的理解 一 什么是 TLV 格式 几乎所有的通信都有协议 而几乎所有的需要在卡片和终端之间传送的数据 结构 都是 TLV 格式的 TLV 是 tag length 和 value 的
  • Spring框架-入门(IOC,DI)

    文章目录 Spring框架 简介 创建Spring项目 理解IOC和DI IOC控制反转 示例