(第六天)初识Spring框架-SSM框架的学习与应用(Spring + Spring MVC + MyBatis)-Java EE企业级应用开发学习记录

SSM框架的学习与应用(Spring + Spring MVC + MyBatis)-Java EE企业级应用开发学习记录(第六天)初识Spring框架


​ 昨天我们已经把Mybatis框架的基本知识全部学完,内容有Mybatis是一个半自动化的持久层ORM框架,深入学习编写动态SQL,Mybatis的关联映射,一对一、一对多、多对多、Mybatis的缓存机制,一二级缓存的开启和设置,缓存命中率、如何使用idea链接数据库自动生成pojo类等。我们学习完了Mybatis,今天是第六天,那么我们要开始学习SSM框架的另外一个——Spring框架。


今天我们要掌握的是:

  • 了解Spring框架及其优点
  • 了解Spring框架的体系结构与Spring 5的新特性*
  • 掌握Spring框架入门程序的编写
  • 理解控制反转的概念
  • 掌握依赖注入的概念、应用

一、Spring框架的基本概念

​ Spring致力于解决Java EE应用中的各种问题,它是一个分层的Java SE/EE一站式(full-stack)开源的轻量级 Java 框架,被广泛应用于企业级 Java 应用程序的开发中。它提供了一系列的模块,用于解决常见的企业级应用开发问题,包括依赖注入、AOP(面向切面编程)、声明式事务管理、Web 应用等等

​ 对于一个Java开发者来说,Spring框架的熟练使用是必备的技能之一。Spring具有良好的设计和分层结构,它克服了传统重量型框架臃肿、低效的劣势,大大简化了项目开发中的技术复杂性。下面让我们来对Spring框架的基础知识进行详细的了解。


Spring框架的核心技术

它最为核心的理念是IoC(控制反转)和AOP(面向切面编程),其中,IoC是Spring的基础,它支撑着Spring对JavaBean的管理功能;AOP是Spring 的重要特性,AOP是通过预编译方式和运行期间动态代理实现程序功能,也就是说可以在不修改源代码的情况下,给程序统一添加功能。本文将带您探索 Spring 框架的强大功能与用途。


Spring 是一个综合性的框架,可以在各个层次的应用程序中发挥作用:

  1. 表现层(Presentation Layer)

    • 在表现层:Spring 提供了 Spring MVC 框架,用于构建 Web 应用程序的控制器、视图解析、请求处理等。它支持处理用户请求、展示页面、接收表单数据等。
    • Spring MVC 提供了通过注解或配置文件来定义控制器、请求映射、视图解析等的方式,使得开发人员可以更方便地构建和维护 Web 页面
  2. 业务逻辑层(Business Logic Layer)

    • 在业务逻辑层:Spring 提供了依赖注入(DI)和面向切面编程(AOP)等功能。这些功能可以帮助你更好地管理组件之间的关系,实现松耦合的设计,提高代码可测试性和可维护性。
    • Spring 的 DI 让你可以通过配置或注解将组件的依赖关系交由 Spring 容器管理,从而实现了对象的解耦和可替换性
    • AOP 允许我们在不改变原有代码的情况下,通过切面将横切关注点(如日志记录、事务管理)与业务逻辑分离
  3. 持久层(Persistence Layer)

    • 在持久层,Spring 提供了对多种持久化技术的支持,包括 JDBC、JPA、Hibernate 等。
    • Spring 的 JDBC 框架简化了数据库访问的操作,提供了模板类(JdbcTemplate)和声明式事务管理等功能,使数据库操作更方便和可靠
    • Spring 还可以集成其他 ORM 框架,如 Mybatis、Hibernate 和 JPA,使得数据持久化更加灵活和高效

简单来说的话就是,Spring 提供了一套综合性的工具和框架,可以在不同层次的应用程序中分别发挥作用,从而帮助开发人员构建更易于维护、可扩展和高效的应用。


Spring框架的优点

a.非侵入式设计

Spring是一种非侵入式(non-invasive)框架,所谓非侵入式是指Spring框架的API不会在业务逻辑上出现,也就是说业务逻辑应该是纯净的,不能出现与业务逻辑无关的代码。由于业务逻辑中没有Spring的API,所以业务逻辑代码也可以从Spring框架快速地移植到其他框架

b.降低耦合性

​ Spring就是一个大工厂,可以将所有对象的创建和依赖关系的维护工作都交给Spring容器管理,大大降低了组件之间的耦合性

c.支持AOP编程

​ Spring提供了对AOP的支持,AOP可以将一些通用的任务进行集中处理,如安全、事务和日志等,以减少通过传统OOP方法带来的代码冗余繁杂

d.支持声明式事务

​ 在Spring中,可以直接通过Spring配置文件管理数据库事务,省去了手动编程的繁琐,提高了开发效率。

e.方便程序的测试

​ Spring提供了对Junit的支持,开发人员可以通过Junit进行单元测试。

f.方便集成框架

​ Spring提供了一个广阔的基础平台,其内部提供了对各种框架的直接支持,如Struts、Hibernate、MyBatis、Quartz等,这些优秀框架可以与Spring无缝集成。

g.降低Java EE API的使用难度

​ Spring对Java EE开发中的一些API(如JDBC、JavaMail等)都进行了封装,大大降低了这些API的使用难度。


Spring的体系结构

在这里插入图片描述


Spring 5框架组成模块

​ Spring 框架主要有8大模块,每个大模块由多个或1个小模块组成,如Spring的核心容器模块(Core Container)是由Beans模块、Core模块、Context模块SpEL模块组成。下面结合String 的体系结构图对Spring体系结构中的主要模块进行简单介绍。

a.核心容器模块(Core Container)

​ 核心容器模块在Spring的功能体系中起着支撑性作用,是其他模块的基石。核心容器层主要由Beans模块、Core模块、Contex模块和SpEL模块组成。

核心容器模块各模块组成

(1)Beans模块。它提供了BeanFactory类,是工厂模式的经典实现,Beans模块的主要作用是创建和管理Bean对象。

(2)Core模块。它提供了Spring框架的基本组成部分,包括IoC和DI功能。

(3)Context模块。它构建于Beans模块和Core模块的基础之上,它可以通过ApplicationContext接口提供上下文信息。

(4)SpEL模块。它是Spring 3.0后新增的模块,提供了对SpEL表达式语言(Spring Expression Language)的支持,SpEL表达式语言是一个在程序运行时支持操作对象图的表达式语言。


b.数据访问及集成模块(Data Access/Integration)

​ 数据访问及集成模块用于访问和操作数据库中的数据,它主要包含JDBC模块、ORM模块、OXM模块、JMS模块和Transactions模块。

(1) JDBC模块。它提供了一个JDBC的抽象层,消除了冗长的JDBC编码并能够解析数据库供应商特有的错误代码。

(2)ORM模块。它为主流的对象关系映射API提供了集成层,用于集成主流的对象关系映射框架。 (3)OXM模块。它提供了对XML映射的抽象层的支持,如JAXB、Castor等。

(4)JMS模块。它主要用于传递消息,包含消息的生产和消费。自4.1版本后,JMS模块支持与Spring-message模块的集成。

(5)Transactions模块。它的主要功能是事务管理。


c.Web模块

​ Web模块的实现基于APPlicationContext基础之上,它提供了Web应用的各种工具类,包括了Web模块、Servlet模块、WebSocket模块和Portlet模块。

(1) Web模块。它提供了针对Web开发的集成特性,如大部分文件上传功能等。此外,Web模块还包含一个HTTP客户端和Spring远程处理支持的Web相关部分。

(2)Servlet模块。它提供了Spring的模型、视图、控制器以及Web应用程序的REST Web服务实现。

(3)WebSocket模块。它是Spring 4.0以后新增的模块,它提供了WebSocket 和SockJS的实现,以及对STOMP的支持。

(4)Portlet模块。它类似Servlet模块的功能,提供了Portlet环境下的MVC实现。


d.其他模块

​ Spring框架的其他模块还有AOP模块、Aspects模块、Instrumentation模块以及Test模

(1) AOP模块。它提供了对面向切面编程的支持,程序可以定义方法拦截器和切入点,将代码按照功能进行分离,以降低程序的耦合性。

(2)Aspects模块。它提供了与AspectJ集成的支持。

(3)Instrumentation模块。它提供了对类工具的支持,并且实现了类加载器,该模块可以在特定的应用服务器中使用。

(4)Messaging模块。它是Spring 4.0以后新增的模块,它提供了对消息传递体系结构和协议的支持。

(5)Test模块。它提供了对程序单元测试和集成测试的支持。

​ Spring 5是Spring当前最新的版本,与历史版本对比,Spring 5对Spring核心框架进行了修订和更新,增加了很多新特性,如支持响应式编程等。


Spring 5新特性:

a.更新JDK基线

​ 因为Spring 5代码库运行于JDK 8之上,所以Spring 5对JDK的最低要求是JDK 8,这可以促进Spring的使用者积极运用Java 8新特性。

b.修订核心框架

(1)基于JDK 8的反射增强,通过Spring 5提供的方法可以更加高效的对类或类的参数进行访问。

(2)核心的Spring接口提供了基于JDK 8的默认方法构建的选择性声明。

(3)用@Nullable和@NotNull注解来表明可为空的参数以及返回值,可以在编译时处理空值而不是在运行时抛出NullPointerExceptions异常。

c.更新核心容器

​ Spring 5支持候选组件索引作为类路径扫描的替代方案。从索引读取实体类,会使加载组件索引开销更低,因此,Spring程序的启动时间将会缩减。

d.支持响应式编程

​ 响应式编程是另外一种编程风格,它专注于构建对事件做出响应的应用程序。Spring 5包含响应流和Reactor(ReactiveStream的Java实现),响应流和Reactor支撑了Spring自身的功能及相关API。

e.支持函数式Web框架

​ Spring 5提供了一个函数式Web框架。该框架使用函数式编程风格来定义端点,它引入了两个基本组件: HandlerFunction和RouterFunction。HandlerFunction 表示处理接收到的请求并生成响应函数;RouterFunction替代了@RequestMapping注解,用于将接收到的请求转发到处理函数。

f.支持Kotlin

​ Spring 5提供了对Kotlin语言的支持。Kotlin是一种支持函数式编程风格的面向对象语言,它运行在JVM之上,可以让代码更具有表现力、简洁性和可读性。有了对Kotlin的支持,开发人员可以进行深度的函数式Spring编程,这拓宽了Spring的应用领域。

g.提升测试功能

​ Spring 5完全支持Junit 5 Jupiter,因此可以使用Junit 5编写测试代码。除此之外,Spring 5还提供了在Spring TestContext Framework中进行并行测试的扩展。针对响应式编程模型,Spring 5引入了支持Spring webFlux的WebTestClient集成测试。


接触到Spring了基本就要开始综合项目的开发了,那么我们首先要了解一下设计模式

什么是设计模式?

​ 设计模式:软件模式是将模式的一般概念应用于软件开发领域,即软件开发的总体指导思路或参照样板。软件模式并非仅限于设计模式,还包括架构模式、分析模式和过程模式等,实际上,在软件生存期的每一个阶段都存在着一些被认同的模式。

目前主流的设计模式,有三种分类:

(一)按创建型模式分类:

  1. 简单工厂模式( Simple Factory Pattern )

  2. 工厂方法模式(Factory Method Pattern)

  3. 抽象工厂模式(Abstract Factory)

  4. 建造者模式

  5. 单例模式

(二)按结构型模式分类

  1. 适配器模式

  2. 桥接模式

  3. 装饰模式

  4. 外观模式

  5. 享元模式

  6. 代理模式

(三)按行为型模式分类

  1. 命令模式

  2. 中介者模式

  3. 观察者模式

  4. 状态模式

  5. 策略模式

这些后续,会一篇一篇发出来,会一直更新的,这里就优先讲一下简单工厂模式吧。


一、用一个例子来说明接口、简单工厂模式、控制反转IOC

步骤1、接口的创建,实现,和实例化对象练习:

​ Java接口是一系列方法的声明,是一些方法特征的集合,一个接口只有方法的特征没有方法的实现,因此这些方法可以在不同的地方被不同的类实现,而这些实现可以具有不同的行为(功能)。

​ 接口可以理解为一种特殊的类,里面全部是由全局常量公共的抽象方法所组成。接口是解决Java无法使用多继承的一种手段。

​ Java接口和抽象类的区别:简单来说, 接口是公开的,里面不能有私有的方法或变量(jdk8后可以有私有方法和静态常量,jdk9以后可以有私有的变量,这里的环境是jdk8,所以其他不讲),是用于让别人使用的,而抽象类是可以有私有方法或私有变量的,另外,实现接口的一定要实现接口里定义的所有方法,而实现抽象类可以有选择地重写需要用到的方法。 一般的应用里,最顶级的是接口,然后是抽象类实现接口,最后才到具体类实现。 还有,接口可以实现多重继承,而一个类只能继承一个超类,但可以通过继承多个接口实现多重继承,接口还有标识(里面没有任何方法,如Remote接口)和数据共享(里面的变量全是常量)的作用.

①创建任意项目后,创建simpleFactory包后,包内创建名字为Fruit的接口

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

②用同样的方法创建俩个实现了Fruit的类Apple和Banana

Apple.class

public class Apple implements Fruit {public void show() {System.out.println("采集苹果");		}
}

Banana.class

public class Banana implements Fruit {public void show() {System.out.println("采集香蕉");		}}
③实例化对象:a.若使用传统方法,不采用工厂模式,我们实例化苹果和香蕉的方法如下:
package simpleFactory;public class MainClass {public static void main(String[] args) {//不用工厂模式的实例化类的方法:Apple a1=new Apple(); //实例化一个苹果对象Banana b1=new Banana(); //实例化一个香蕉对象a1.show();//输出苹果对象的show方法b1.show ();//输出香蕉对象的show方法}
}
④实例化对象:b.使用简单工厂模式,实例化Apple和Banana,我们需要先创建一个工厂类名为:FruitFactory.java,代码如下:
package simpleFactory;public class FruitFactory {public static Fruit getFruit(String type) {Fruit fruitFactory = null; //先定义一个水果对象(没确定是苹果或香蕉)if(type.equals("Apple")){fruitFactory = new Apple();//创建苹果对象}else if(type.equals("Banana")){fruitFactory = new Banana();//创建香蕉对象}return fruitFactory;//返回对应的水果}}

这段代码的话,其实跟我们前面几天的封装自己的工具类是差不多的,就是封装通过统一的方法,根据接受的参数不同,然后来生成对应类型的对象

public class MainClass {public static void main(String[] args) {//工厂模式的方法;Fruit a1 = FruitFactory.getFruit("Apple");Fruit b1 = FruitFactory.getFruit("Banana");a1.show();//输出苹果对象的show方法        b1.show();//输出香蕉对象的show方法}
}

输出结果:在这里插入图片描述

⑤c.实例化拓展:使用反射创建实例,这样的话我们得对FruitFactory类的代码进行修改:
什么是反射?

反射是一种在运行时检查、检测和操作类、接口、字段、方法等程序结构的能力。它允许程序在运行时获取关于类的信息并操作类或对象的属性、方法和构造函数,而无需在编译时硬编码这些信息

Java中的反射机制允许我们:
  1. 获取类的信息:可以在运行时获取类的名称、父类、实现的接口、字段、方法等信息。
  2. 创建对象:通过反射可以实例化对象,就像使用 new 关键字一样。
  3. 调用方法:通过反射可以调用对象的方法,包括私有方法。
  4. 访问和修改字段:可以访问和修改对象的字段,包括私有字段。
  5. 操作构造函数:可以通过反射调用类的构造函数来实例化对象。

反射机制在某些情况下非常有用,例如在框架、库、插件和动态加载类等场景中。但需要注意,由于反射涉及到在运行时进行检查和操作,可能会影响性能,并且由于绕过了编译时的类型检查,可能导致运行时的类型错误。


修改代码如下:

	public class FruitFactory {public static Fruit getFruit(String type) {/*这里通过反射的方式获取到水果子类的字节码,即类对象,通过类对象的newInstance()方法创建水果子类*/Class fruit = Class.forName(FruitFactory.class.getPackage().getName()+"."+type);return (Fruit) fruit.newInstance();}}

(这里FruitFactory.class.getPackage().getName()是用来找到FruitFactory所在的包,你也可以直接写上你水果类所在的包的名称,如"simpleFactory."+type)

然后鼠标移到所有有波浪线的代码上,点add.exception.to…

如下图:

在这里插入图片描述

再回到主类MainClass的主函数Main中,鼠标移到波浪线上右击,添加add.throw.declaration

(注意:这里将工厂类和所创建的子类放在同一个包simFactory中,方便调用。)

上面通过反射的方式获取到水果子类的字节码,即类对象,通过类对象的newInstance()方法创建水果子类。

​ 保存运行后,结果虽然不变,但是这里采用了反射的方法,不需要去判断有多少种水果,分别写上代码,但是这里要加上throw的预出错处理,防止用户写错类名,无法创建出对应的类

在这里插入图片描述

上面的简单工厂模型实现了控制反转的概念,即创建类的对象由Apple,Banana转移到了FruitFactory这个工厂类上


二、下面我们学习使用Spring的搭建和并使用它实现控制反转,创建对象

Spring的作用简单介绍一下:

1、Spring 的主要作用就是为代码“解耦”,降低代码间的耦合度。在一个软件系统中,根据功能的不同,代码可以分成两大类:主业务逻辑和系统级业务逻辑。它们各自具有鲜明的特点:

  • 主业务代码间逻辑联系紧密,有具体的专业业务应用场景,复用性相对较低;
  • 系统级业务相对功能独立,没有具体的专业业务应用场景,主要是为主业务提供系统级服务,如用户、权限管理,日志记录、安全管理、事务管理等,复用性强

2、Spring 根据代码的功能特点,将降低耦合度的方式分为了两类:IOC 与AOP

  • IoC 使得主业务在相互调用过程中,不用再自己维护关系了,即不用再自己创建要使用的对象了。而是由 Spring 容器统一管理,自动“注入”。
  • 而 AOP 使得系统级服务得到了最大复用,且不用再由程序员手工将系统级服务“混杂”到主业务逻辑中了,而是由 Spring 容器统一完成“注入”。

搭建Spring环境

①往刚刚新建的项目里面修改pom

写上需要的依赖文件,代码如下:

<?xml version="1.0" encoding="UTF-8"?>
<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"><modelVersion>4.0.0</modelVersion><groupId>org.example</groupId><artifactId>springIocTest</artifactId><version>1.0-SNAPSHOT</version><dependencies><dependency><groupId>org.mybatis</groupId><artifactId>mybatis</artifactId><version>3.5.2</version></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.11</version></dependency><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.12</version><scope>compile</scope></dependency><dependency><groupId>log4j</groupId><artifactId>log4j</artifactId><version>1.2.17</version></dependency><!--Spring的基础包Spring-core--><dependency><groupId>org.springframework</groupId><artifactId>spring-core</artifactId><version>5.2.8.RELEASE</version></dependency><!--Spring的基础包Spring-beans--><dependency><groupId>org.springframework</groupId><artifactId>spring-beans</artifactId><version>5.2.8.RELEASE</version></dependency><!--Spring的基础包Spring-context--><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.2.8.RELEASE</version></dependency><!--Spring的基础包Spring-expressinon--><dependency><groupId>org.springframework</groupId><artifactId>spring-expression</artifactId><version>5.2.8.RELEASE</version></dependency><!--Spring的依赖包commons-logging--><dependency><groupId>commons-logging</groupId><artifactId>commons-logging</artifactId><version>1.2</version></dependency></dependencies><build><resources><resource><directory>src/main/java</directory><includes><include>**/*.properties</include><include>**/*.xml</include></includes><filtering>true</filtering></resource></resources></build>
</project>

然后点开右边的maven窗口,点刷新,等依赖文件下载完成:

在这里插入图片描述

若是下载失败可以自行导入本地离线包,我的项目资料文件资源文件里面有。

②创建entiy包,定义一个Person类
package entity;public class Person {private String name;private int age;public void show() {System.out.println("name:"+name+",age:"+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;}
}
③创建spring-config.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/beanshttp://www.springframework.org/schema/beans/spring-beans-3.2.xsd"><bean id="person" class="entity.Person"><property name="name" value="张三"></property><property name="age" value="24"></property></bean>
</beans>
④创建Test包,进行测试类编写
package Test;import entity.Person;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;public class PersonTest{@Testpublic void test1() {ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");Person person = context.getBean("person", Person.class);person.show();}
}

输出结果如下:

在这里插入图片描述

你会发现这里的值,跟我们在spring-config.xml中配置的bean一样。

在这里插入图片描述

在这里插入图片描述

这里我们并没有手动创建Person的实例(对象),是Spring通过ApplicationContext帮我们创建,并放在IoC容器里。ApplicationContext是一个IoC容器接口,它所创建的对象都称作是bean,也就是xml文件里的<bean id=" " class=" "></bean>这行配置信息。getBean方法就是从IoC容器里取得这个对象(根据标识id 和类名class),然后我们就可以调用该类的方法,如下图:

在这里插入图片描述


接下来尝试使用Spring框架来实现我们一开始的Fruit类

①在entity包下创建接口Fruit,并且接口中定义一个show()
package entity;public interface Fruit {public void show();
}

和前面的文章一样在Fruit处使用Alt+enter,创建出接口的实现类Apple和Banana

Apple.java代码如下:并且定义了一个int类型的weight变量

package entity;public class Apple implements Fruit {private int weight;@Overridepublic void show() {System.out.println("采集苹果:"+weight+"斤");}public int getWeight() {return weight;}public void setWeight(int weight) {this.weight = weight;}
}

Banana.java代码如下:并且也定义了一个int类型的weight变量

package entity;public class Banana implements Fruit {private int weight;public int getWeight() {return weight;}public void setWeight(int weight) {this.weight = weight;}@Overridepublic void show() {System.out.println("采集香蕉:"+weight+"斤");}
}
②往spring-config.xml中添加上Apple和Banana类

在这里插入图片描述

③编写测试类FruitTest

package Test;import entity.Apple;
import entity.Banana;
import entity.Fruit;
import javafx.application.Application;
import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;import static org.junit.jupiter.api.Assertions.*;class FruitTest {@Testvoid getFruitWeight() {ApplicationContext context =new ClassPathXmlApplicationContext("spring-config.xml");Fruit apple=context.getBean("apple", Apple.class);Fruit Banana=context.getBean("banana", entity.Banana.class);apple.show();Banana.show();}
}

在这里插入图片描述

可以看出简单工厂模式和我们使用Spring框架的效果其实是一样的,简单地说,Spring模型的核心思想,就是把创建对象的工作,交给Spring去做,在spring-config.xml中配置好要创建的对象,然后在实际代码用用context.getBean()函数创建出对象 (相对应于简单工厂模式用工厂类去创建对象)。

实现了控制的反转(IOC),即将创建对象的控制权转发生了转变


Spring的依赖注入

对象的组装,依赖注入练习,并了解“面向接口编程”的含义。

下面我们用一个例子来说明“依赖注入”这个概念。

说明:“依赖”指的是,在一个类中如果包含另一个类,如:

Class A;
Class B{A  aa;…
}
Class C{B b;…
}

上面的B类中包含着A类,(可以看成A类是B类的一个字段),这就表示B类“依赖”A类。在实例化B类之前,需要先实例化A类。

这种就是类和类之间的依赖关系。

传统的面向对象编程,类和类之间的依赖过于复杂。B依赖A,C又依赖B,D又依赖C,在创建对象D的时候,需要依次对前面的类进行实例化,相当麻烦,使用了Ioc之后,类之间的依赖关系就变得简单明了,请看下面我们用一个打印机的例子来说明使用Ioc之后依赖关系的解决方法,这种方法就称为“依赖注入”:


需求如下:

开发一个打印机模拟程序,使其符合以下条件。
  • 可以灵活地配置使用彩色墨盒或灰色墨盒。
  • 可以灵活地配置打印页面的大小。
  • 程序中包括打印机(Printer)、墨盒(Ink)和纸张(Paper)三类组件,如下所示

在这里插入图片描述

打印机依赖墨盒和纸张。

采取如下的步骤开发这个程序。

(1)定义Ink和Paper接口。

(2)使用Ink接口和Paper接口开发Printer程序。在开发Printer程序

时并不依赖Ink和Paper的具体实现类。

(3)开发Ink接口和Paper接口的实现类:Color Ink、Grey Ink和

Text Paper。

(4)组装打印机,运行调试。


①创建printer包,在包内定义Ink和Paper接口

(记得命名要符合驼峰命名,这样方便代码让别人读懂,自己也写的比较规范,利于团队协作)

Ink接口

package printer;public interface Ink {public String getColor();//判断什么颜色型号的打印机?
}

Paper接口

package printer;public interface Paper {public String getPaperType();//判断打印的纸张是什么纸型的?
}
②创建Printer类,用来接收参数,并且打印的类(记得这个不用继承接口)
package printer;public class Printer {//面向接口编程,而不是具体的实现类private Ink ink=null;//面向接口编程,而不是具体的实现类private Paper paper=null;public void print(String str){//输出标记颜色System.out.println("使用"+ink.getColor()+"打印\n");//输出纸型System.out.println("使用"+paper.getPaperType()+"打印\n");//输出字符串System.out.println(str);}//生成getter和setter方法public Ink getInk() {return ink;}public void setInk(Ink ink) {this.ink = ink;}public Paper getPaper() {return paper;}public void setPaper(Paper paper) {this.paper = paper;}
}

说明:Printer类中只有一个print()方法,输入参数是一个即将被打印的字符串,打印机将这个字符串逐个字符输出到纸张,然后将纸张中的内容输出。

在开发Printer程序的时候,只需要了解Ink接口和Paper接口即可,完全不需要依赖这些接口的某个具体实现类,这是符合实际情况的。在设计真实的打印机时也是这样,设计师只是针对纸张和墨盒的接口规范进行设计

在使用时,只要符合相应的规范,打印机就可以根据需要更换不同的墨盒和纸张。

软件设计与此类似,由于明确地定义了接口,在编写代码的时候,完全不用考虑和某个具体实现类的依赖关系,从而可以构建更复杂的系统。组件间的依赖关系和接口的重要性在将各个组件组装在一起的时候得以体现。通过这种开发模式,还可以根据需要方便地更换接口的实现,就像为打印机更换不同的墨盒和纸张一样。Spring提倡面向接口编程也是基于这样的考虑。

注意:Ink和Paper只是接口,不是类,是不能创建实例的,print()方法运行的时候是从哪里获得Ink和Paper的实例呢?这时就需要提供“插槽”,以便组装的时候可以将Ink和Paper的实例“注入”进来,下面我们实义Ink和Paper对应的实现类,以便用它们生成实例。


③创建Ink的俩个实现类,BlackWhiteInk和ColorInk分别代表黑白打印机和彩色打印机

这里因为类比较少,我就直接在printer包下创建了,因为我是几个实例一起写的,创建太多包,容易搞混,当然了,如果是有经验的那么就还是按照各个分类包来编写实现类和接口。BlackWhiteInk.java

package printer;public class BlackWhiteInk implements Ink {@Overridepublic String getColor() {String color="黑白打印机";return color;}
}

ColorInk.java

package printer;public class ColorInk implements Ink {@Overridepublic String getColor() {String color="彩色打印机";return color;}
}
③创建Paper的实现类,PaperType,里面创建变量用来接收纸型的信息。

上面的俩个实例都是指定好的了,因为只有这俩种打印。纸型有很多种,A3、A4、A5等

package printer;public class PaperType implements Paper {private String paper;//纸型@Overridepublic String getPaperType() {return paper;}public String getPaper() {return paper;}public void setPaper(String paper) {this.paper = paper;}
}
//说明:在我们不仅可以注入某个类的实例,还可以注入基本数据类型、字符串等类型的数据。
④在 spring-config.xml 配置文件中注册实体类,并使用控制反转(IoC)容器来实例化和组装打印机对象
<?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/beanshttp://www.springframework.org/schema/beans/spring-beans-3.2.xsd"><bean id="person" class="entity.Person"><property name="name" value="张三"></property><property name="age" value="24"></property></bean><bean id="apple" class="entity.Apple"><property name="weight" value="3"></property></bean><bean id="banana" class="entity.Banana"><property name="weight" value="5"></property></bean><!-- 定义彩色墨盒bean,该bean的id是colorInk,class指定该bean实例的实现类 --><bean id="colorInk" class="printer.ColorInk"></bean><!-- 定义灰色墨盒bean(也就是黑白打印机),该bean的id是blackWhiteInk,class指定该bean实例的实现类 --><bean id="blackWhiteInk" class="printer.BlackWhiteInk"></bean><!-- 定义A4纸张bean,该bean的id是a4Paper,class指定该bean实例的实现类 --><bean id="a4Paper" class="printer.PaperType"><property name="paper" value="A4paper"></property></bean><!-- 定义5纸张bean,该bean的id是a5Paper,class指定该bean实例的实现类 --><bean id="a5Paper" class="printer.PaperType"><property name="paper" value="A5paper"></property></bean><!-- 组装打印机。定义打印机bean,该bean的id是printer, class指定该bean实例的实现类 --><bean id="printer" class="printer.Printer"><!-- 通过ref属性注入已经定义好的bean --><!-- 注入彩色墨盒 --><property name="ink" ref="colorInk"/><!-- 注入A4打印纸张 --><property name="paper" ref="a4Paper"/></bean>
</beans>

我们实例化了前面的各个类,并给里面的变量赋了值,最后“组装”了一台彩色的、使用a4打印纸的打印机。需要注意的是,这里没有使用<property>的value属性,而是使用了ref属性。

value属性用于注入基本数据类型以及字符串类型的值。ref属性用于注入已经定义好的Bean,如刚刚定义好的colorInk、blackWhiteInk、a4Paper和a5Paper。


⑤编写测试类,进行测试

在这里插入图片描述

若是想要更换打印机的配置,也就是组装新的打印机,可以回去spring-config.xml中修改bean中的配置,又或者可以新建一个新的bean组装新的打印机。

在这里插入图片描述

和Spring有关的只有组装和运行两部分代码。仅这两部分代码就让我们获得了像更换打印机的墨盒和打印纸一样更换程序组件的能力。这就是Spring依赖注入的魔力。

通过Spring的强大组装能力,我们在开发每个程序组件的时候,只要明确关联组件的接口定义,而不需要关心具体实现,这就是所谓的“面向接口编程”。

以上的操作,类和类之间的依赖关系,通过Ioc在xml之中可以很清晰地表示出来,很容易实现类和类之间的组装,这种做法我们称为“依赖注入”,英文简写为DI,希望通过这个例子能够帮助各位慢慢消化理解这个概念。


总结:

​ 今天是学习SSM框架的第六天,主题是:初识Spring框架,因为后面我们要开发综合项目了,所以今天先学习了简单的设计模式**-简单工厂模式,以及三种方式创建实例。然后了解了Spring框架是一个分层的Java SE/EE一站式(full-stack)开源的轻量级** Java 框架。基本熟悉了Spring框架的核心IoC和AoP的基本概念,还学了IoC控制反转DI依赖注入,以及这种方式创建的优点。Spring框架的熟练使用是必备的技能之一,十分重要,熟练的掌握它们能够极大的提高开发效率。

​ 想要跟着学习的可以去我的资源里面找对应的文件下载,我的md文件也会发上去,项目文件会上传可以自己跟着学习一下。

作者:Stevedash

发表于:2023年8月30日 17点34分

注:本文内容基于个人学习理解,如有错误或疏漏,欢迎指正。感谢阅读!如果觉得有帮助,请点赞和分享。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/117655.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Python:列表推导式

相关阅读 Python专栏https://blog.csdn.net/weixin_45791458/category_12403403.html?spm1001.2014.3001.5482 列表推导式使得创建特定列表的方式更简洁。常见的用法为&#xff0c;对序列或可迭代对象中的每个元素应用某种操作&#xff0c;用生成的结果创建新的列表&#xff…

用树形dp+状压维护树上操作的计数问题:0902T3

发现操作数 k ≤ 6 k\le6 k≤6&#xff0c;可以考虑对操作进行状压。 然后找找性质&#xff0c;发现要么删掉一棵子树&#xff0c;要么进去该子树。可以视为每种操作有两种情况。 然后分讨一下当前该如何转移。 树形dp的顺序&#xff1a; 合并子树考虑当前往上的边的方向 …

使用HTTPS模式建立高效爬虫IP服务器详细步骤

嘿&#xff0c;各位爬虫小伙伴们&#xff01;想要自己建立一个高效的爬虫IP服务器吗&#xff1f;今天我就来分享一个简单而强大的解决方案——使用HTTPS模式建立工具&#xff01;本文将为你提供详细的操作步骤和代码示例&#xff0c;让你快速上手&#xff0c;轻松建立自己的爬虫…

查看GPU占用率

如何监控NVIDIA GPU 的运行状态和使用情况_nvidia 85c_LiBiGo的博客-CSDN博客设备跟踪和管理正成为机器学习工程的中心焦点。这个任务的核心是在模型训练过程中跟踪和报告gpu的使用效率。有效的GPU监控可以帮助我们配置一些非常重要的超参数&#xff0c;例如批大小&#xff0c;…

【数据结构与算法篇】手撕八大排序算法之交换排序

​&#x1f47b;内容专栏&#xff1a; 《数据结构与算法篇》 &#x1f428;本文概括&#xff1a;常见交换排序包括冒泡排序与快速排序&#xff0c;本篇讲述冒泡排序与快速排序的思想及实现、复杂度分析。 &#x1f43c;本文作者&#xff1a; 花 蝶 &#x1f438;发布时间&#…

Mysql的page,索引,Explain Type等基本常识

Mysql的基本问题 Mysql 为什么建议使用自增id&#xff1f; 因为id&#xff08;主键&#xff09;是自增的话&#xff0c;那么在有序的保存用户数据到页中的时候&#xff0c;可以天然的保存&#xff0c;并且是在聚集索引&#xff08;id&#xff09;中的叶子节点可以很好的减少插…

Django报错:SystemCheckError: System check identified some issues解决办法

今天练习django自定义标签时&#xff0c;一开始在APPbook中写了自定义标签book_tags.py 测试成功&#xff0c;之后新建了一个APPblogs&#xff0c;测试在blogs中创建模板使用自定义标签&#xff0c;于是直接把book/templatetags包直接赋值到blogs目录里。在页面里加载自定义标…

84. 柱状图中最大的矩形

84. 柱状图中最大的矩形 C代码&#xff1a;暴力&#xff1a;遍历所有可能 int largestRectangleArea(int* heights, int heightsSize){// 直接遍历所有可能&#xff1a;超出时间限制int ans 0;for (int l 0; l < heightsSize; l) {int minHeight INT_MAX;for (int r l…

OpenCV(八):图像二值化

目录 1.固定值二值化 2.自适应阈值二值化 3.Android JNI完整代码 1.固定值二值化 固定阈值二值化是OpenCV中一种简单而常用的图像处理技术&#xff0c;用于将图像转换为二值图像。在固定阈值二值化中&#xff0c;像素值根据一个预定义的阈值进行分类&#xff0c;大于阈值的…

Redis—常用数据结构

Redis—常用数据结构 &#x1f50e;数据结构与内部编码 Redis 中常用的数据结构包括 Strings—字符串Hashes—哈希表Lists—列表Sets—集合Sorted sets—有序集合 Redis 底层在实现上述数据结构时, 会在源码层面针对上述实现进行特定优化, 以达到节省时间 / 节省空间的效果 …

Vue基础2:传值方法

Description 传值就是为了联动&#xff0c;能够及时准确传值获取值才是王道。 Valuation Methods props和$emit props是父传子&#xff0c;$emit是子传父。 props的使用 父组件传出值 <tableList ref"table" :options"options" :header-data"C…

Android Glide preload RecyclerView切入后台不可见再切换可见只加载当前视野可见区域item图片,Kotlin

Android Glide preload RecyclerView切入后台不可见再切换可见只加载当前视野可见区域item图片&#xff0c;Kotlin <uses-permission android:name"android.permission.READ_EXTERNAL_STORAGE" /><uses-permission android:name"android.permission.RE…

合宙Air724UG LuatOS-Air LVGL API控件--图表 (Chart)

图表 (Chart) 一幅图胜过一千个字&#xff0c;通过图表展示出的数据内容能让用户更快速有效的了解数据特征。 代码示例 – 创建图表 chart lvgl.chart_create(lvgl.scr_act(), nil) lvgl.obj_set_size(chart, 200, 150) lvgl.obj_align(chart, nil, lvgl.ALIGN_CENTER, 0, …

docker 笔记1

目录 1.为什么有docker ? 2.Docker 的核心概念 3.容器与虚拟机比较 3.1传统的虚拟化技术 3.2容器技术 3.3Docker容器的有什么作用&#xff1f; 3.4应用案例 4. docker 安装下载 4.1CentOS Docker 安装 4.2 Docker的基本组成 &#xff1f;&#xff08;面试&#xff09…

OpenShift 4 - 用 Prometheus 和 Grafana 监视用户应用定制的观测指标(视频)

《OpenShift / RHEL / DevSecOps 汇总目录》 说明&#xff1a;本文已经在 OpenShift 4.13 的环境中验证 文章目录 OpenShift 的监控功能构成部署被监控应用用 OpenShift 内置功能监控应用用 Grafana 监控应用安装 Grafana 运行环境配置 Grafana 数据源定制监控 Dashboard 演示视…

剑指 Offer 57 - II. 和为s的连续正数序列(简单)

题目&#xff1a; class Solution { public:vector<vector<int>> findContinuousSequence(int target) { //本题使用滑动窗口&#xff08;双指针&#xff09;int i1, j1; //定义左右边界&#xff0c;一般是左闭右开int sum0; //窗口内的和vector&…

弹性盒子的使用

一、定义 弹性盒子是一种用于按照布局元素的一维布局方法&#xff0c;它可以简便、完整、响应式地实现各种页面布局。 容器中存在两条轴&#xff0c;主轴和交叉轴(相当于我们坐标轴的x轴和y轴)。我们可以通过flex-direction来决定主轴的方向。 主轴&#xff08;main axis&am…

设计模式行为模式-命令模式

文章目录 前言定义结构工作原理优点适用场景消息队列模式Demo实现分写业务总结 前言 定义 命令模式&#xff08;Command Pattern&#xff09;是一种行为型设计模式&#xff0c;用于将请求封装为对象&#xff0c;从而使你可以使用不同的请求、队列或者日志请求来参数化其他对象…

MySQL高阶语句(三)

一、NULL值 在 SQL 语句使用过程中&#xff0c;经常会碰到 NULL 这几个字符。通常使用 NULL 来表示缺失 的值&#xff0c;也就是在表中该字段是没有值的。如果在创建表时&#xff0c;限制某些字段不为空&#xff0c;则可以使用 NOT NULL 关键字&#xff0c;不使用则默认可以为空…

zookeeper 集群

zookeeper 集群 1、zookeeper 集群说明 initLimit 是Zookeeper用它来限定集群中的Zookeeper服务器连接到Leader的时限 syncLimit 限制了follower服务器与leader服务器之间请求和应答之间的时限 服务器名称与地址&#xff1a;集群信息&#xff08;服务器编号&#xff0c;服务器…