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 是一个综合性的框架,可以在各个层次的应用程序中发挥作用:
-
表现层(Presentation Layer):
- 在表现层:Spring 提供了 Spring MVC 框架,用于构建 Web 应用程序的控制器、视图解析、请求处理等。它支持处理用户请求、展示页面、接收表单数据等。
- Spring MVC 提供了通过注解或配置文件来定义控制器、请求映射、视图解析等的方式,使得开发人员可以更方便地构建和维护 Web 页面。
-
业务逻辑层(Business Logic Layer):
- 在业务逻辑层:Spring 提供了依赖注入(DI)和面向切面编程(AOP)等功能。这些功能可以帮助你更好地管理组件之间的关系,实现松耦合的设计,提高代码可测试性和可维护性。
- Spring 的 DI 让你可以通过配置或注解将组件的依赖关系交由 Spring 容器管理,从而实现了对象的解耦和可替换性。
- AOP 允许我们在不改变原有代码的情况下,通过切面将横切关注点(如日志记录、事务管理)与业务逻辑分离。
-
持久层(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了基本就要开始综合项目的开发了,那么我们首先要了解一下设计模式
什么是设计模式?
设计模式:软件模式是将模式的一般概念应用于软件开发领域,即软件开发的总体指导思路或参照样板。软件模式并非仅限于设计模式,还包括架构模式、分析模式和过程模式等,实际上,在软件生存期的每一个阶段都存在着一些被认同的模式。
目前主流的设计模式,有三种分类:
(一)按创建型模式分类:
-
简单工厂模式( Simple Factory Pattern )
-
工厂方法模式(Factory Method Pattern)
-
抽象工厂模式(Abstract Factory)
-
建造者模式
-
单例模式
(二)按结构型模式分类
-
适配器模式
-
桥接模式
-
装饰模式
-
外观模式
-
享元模式
-
代理模式
(三)按行为型模式分类
-
命令模式
-
中介者模式
-
观察者模式
-
状态模式
-
策略模式
这些后续,会一篇一篇发出来,会一直更新的,这里就优先讲一下简单工厂模式吧。
一、用一个例子来说明接口、简单工厂模式、控制反转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中的反射机制允许我们:
- 获取类的信息:可以在运行时获取类的名称、父类、实现的接口、字段、方法等信息。
- 创建对象:通过反射可以实例化对象,就像使用
new
关键字一样。 - 调用方法:通过反射可以调用对象的方法,包括私有方法。
- 访问和修改字段:可以访问和修改对象的字段,包括私有字段。
- 操作构造函数:可以通过反射调用类的构造函数来实例化对象。
反射机制在某些情况下非常有用,例如在框架、库、插件和动态加载类等场景中。但需要注意,由于反射涉及到在运行时进行检查和操作,可能会影响性能,并且由于绕过了编译时的类型检查,可能导致运行时的类型错误。
修改代码如下:
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/beans
http://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{
@Test
public 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;
@Override
public 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;
}
@Override
public 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 {
@Test
void 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 {
@Override
public String getColor() {
String color="黑白打印机";
return color;
}
}
ColorInk.java
package printer;
public class ColorInk implements Ink {
@Override
public String getColor() {
String color="彩色打印机";
return color;
}
}
③创建Paper的实现类,PaperType,里面创建变量用来接收纸型的信息。
上面的俩个实例都是指定好的了,因为只有这俩种打印。纸型有很多种,A3、A4、A5等
package printer;
public class PaperType implements Paper {
private String paper;//纸型
@Override
public 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/beans
http://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分