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. 高耦合性:UserServiceUserDao之间的关系非常紧密,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的压力。

文章来源:https://blog.csdn.net/qq_44715376/article/details/135380158
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。