▮全文概述
Spring是一个大的框架,东西很多,但此篇我们只讲解最为重要的概念:
- Spring是一个loC容器。
- Spring项目怎么创建
- Spring怎么使用
---------------分----------割-----------线----------------------------------------------------------------------
目录
---------------分----------割-----------线----------------------------------------------------------------------
▮一、loC(控制反转)
loC是控制反转。通常情况下,对象的创建和销毁都是由开发者来控制,由开发者来决定对象new的时机;所谓的控制反转,就是将“对象的创建和销毁”的控制权从开发者手中转回程序本身,转到程序新增的框架里,由程序来决定对象new的时机,将控制权交给程序。
为什么要这么做呢,我来举个例子就行。
假设公司生产各种产品,都需要一个原材料S,我们在java中定义这么一个类
//原材料S
public class S {
//重量
private int weight;
//初始化方法
public void init(int weight){
this.weight = weight;
System.out.println("获取重量为"+weight+"的原材料S");
}
//getter和setter,省略
}
有了原材料S后,公司要生产产品A,B,C,D等上百个产品,代码如下:
public class A {
//产品名称
private String name;
//所需原材料
private S material_S;
//构造方法
public A(int S_weight) {
this.name = "产品A";
this.material_S = new S();
//原材料初始化
material_S.init(S_weight);
System.out.println("生成了一个产品A");
}
}
- 产品B,C,D等上百个产品的代码与A一致 ,总共有上百个类里有这段代码
依靠这些产品,公司构建出了一个庞大而又复杂的项目用于实现业务。但是吧,随着时间的发展,业务对项目提出了新的要求,要求项目要对原材料进行精密的控制,要求每个产品给原材料标注上优劣,由此,我们的原材料S的代码变成
//原材料S
public class S {
//重量
private int weight;
//优劣
private String superior;
//构造方法
public void init(int weight,String superior) {
this.weight = weight;
this.superior = superior;
System.out.println("获取重量为"+weight+"的"+superior+"原材料S");
}
//getter和setter,省略
}
此时,你会发现, 你这小小的一个改动,将导致项目上百个产品类爆出错误。A,B,C,D等上百个产品全部爆红。
当然,你可以选择一个个的去修改代码,改成
但修改上百个产品的代码可不是一件轻松的事情,也是得亏于这个例子简单,勉强还能手动修改。但在真正的公司项目中,一个产品需要的原料可不是S一个,用S生产的产品可能不止上百 ,每次项目对S的修改可能也不止一处。多重叠加下来,一处小小的修改,可能就会导致全盘接崩。
为什么会发生上面这种情况呢?因为耦合度太高了,原材料S的变动会直接导致产品A的报错,这种高耦合的代码,及其不利于项目的维护或升级。对此,就有大佬提出了一种新的模式:
//构造方法
public A(S material_S) {
this.name = "产品A";
this.material_S = material_S;
System.out.println("生成了一个产品A");
}
一处非常简单的修改:产品A不在内部new新的原材料S的对象,而是直接靠外界传参。这样一来,无论你S怎么修改,对A几乎没有什么影响,A不会再报错,BCDEF等产品也都不会报错。原材料和产品之间的耦合降低,实现了低耦合。
当要生成产品A时,进行如下代码:
至此,还不算是完整的loC,因为还有关键问题没有解决:原材料S在哪里new呢?new出来后怎么传进A的构造方法里呢?对此,我们有很多方法能够做到,但都比较麻烦,不过值得高兴的是,这些事情不用我们来做。框架Spring,替我们做了这些事情。
框架≠实现loC。实现loC只是框架里的一种类别,所有能简化程序开发且有一套被人共识的标准的工具都可以看作是一个框架。
Spring会给原材料S打上一个标记,在项目启动的时候自动new一个S对象保存在内部容器,当new产品A需要S对象时,Spring会自动将这个对象注入到产品A构造方法的参数里。这个过程就是依赖注入思想的实现。
依赖注入(Dependency Injection,DI)是一种实现控制反转(IoC)的方式,它通过将对象的依赖关系从代码中移出,由容器负责在运行时动态地注入所需的依赖对象。
在依赖注入中,对象不再负责自己的依赖对象的创建和获取,而是通过外部的机制将依赖对象注入到目标对象中。这样做的好处是,目标对象不需要关心依赖对象的创建和获取细节,从而实现了解耦和灵活性。
至此,loC也完整实现了。对象的创建和销毁的控制权也从开发中反转到Spring框架中,最终回归到程序里。控制反转至此实现,Spring也因此被称为loC容器。接下来,我们将要进一步的讲解Spring的具体实现。
IoC(Inversion of Control,控制反转)是一种软件设计原则,它将对象的创建和依赖关系的管理从应用程序代码中转移到容器或框架中。在传统的编程模式中,对象之间的依赖关系通常由开发者手动创建和管理,但在IoC容器中,对象的创建和依赖关系的注入由容器负责。
IoC的核心思想是通过将对象的控制权反转给容器,实现了解耦和灵活性。在IoC容器中,对象的创建和生命周期由容器管理,而不是由开发者直接控制。开发者只需要定义对象之间的依赖关系,并通过容器来获取所需的对象实例。
IoC容器通常通过依赖注入(Dependency Injection,DI)来实现对象之间的依赖关系。依赖注入可以通过构造函数注入、属性注入或方法注入等方式实现。容器在创建对象时,会自动解析对象的依赖关系,并将所需的依赖对象注入到目标对象中。
IoC的好处包括:
松耦合:通过将对象的创建和依赖关系的管理交给容器,实现了对象之间的松耦合。对象只需要关注自身的功能,而不需要关心如何创建和获取依赖对象。
可测试性:由于对象的依赖关系由容器管理,可以更方便地进行单元测试。在测试时,可以使用模拟对象或者替代实现来替代真实的依赖对象,从而更容易进行测试。
可扩展性:通过IoC容器,可以更容易地添加、替换或修改对象的依赖关系,从而提高系统的可扩展性。
集中管理:IoC容器可以集中管理对象的创建和依赖关系,使得代码更加清晰、可维护和可读。
在Java开发中,Spring框架是一个流行的IoC容器,它提供了强大的依赖注入功能,可以帮助开发者实现IoC和依赖注入。通过Spring框架,开发者可以将对象的创建和依赖关系的管理交给Spring容器,从而实现松耦合和可测试的代码。
▮二、Spring项目的创建
博主使用的是21年社区版的IDEA,如有不同请自行调整。不要用最新版,后续的Spring Boot项目里有个插件在新版需要收费,用21年的就够了。
Spring项目的创建很简单,总共就两步:
- 创建Maven项目
- 添加Spring依赖
第一步,创建Maven项目。
项目名称和位置自行决定
第二步,添加Spring依赖。在pom.xml中添加下列标签
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>5.2.3.RELEASE</version>
</dependency>
</dependencies>
最终如下:
<?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>spring</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>5.2.3.RELEASE</version>
</dependency>
</dependencies>
</project>
点下maven的变更,至此,Spring项目以及创建完成。
项目的目录如下:
▮三、Spring的使用
- 定义基础类S,A和APP
- 将Bean注册到Spring中
- 获取Spring的上下文对象context
- 获取Bean并使用
第一步,定义基础类S,A和APP。
我们将之前的原材料S和产品A添加到目录java中
//原材料S
public class S {
//重量
private int weight;
//优劣
private String superior;
//构造方法
public void init(int weight,String superior) {
this.weight = weight;
this.superior = superior;
System.out.println("获取重量为"+weight+"的"+superior+"原材料S");
}
}
public class A {
//产品名称
private String name;
//所需原材料
private S material_S;
//构造方法
public A(S material_S) {
this.name = "产品A";
this.material_S = material_S;
System.out.println("生成了一个产品A");
}
}
再创建一个项目的启动类——APP,里面加上main方法
public class APP {
public static void main(String[] args) {
//......
}
}
第二步,将Bean注册到Spring中。
Bean的英文翻译是豆子,而java的图标是一个咖啡;咖啡是由咖啡豆煮出来的,而java代码是由对象组成的;所以说,Bean就是java里的实例对象。不过要注意,不是java的实例对象称为Bean,而是保存进Spring框架里的实例对象称为Bean,Bean是Spring框架对保存进的实例对象的称呼。
关于Bean的知识点还有很多,我们在后续分支中详解。
- 在Spring框架中,"Bean"是一个非常重要的概念。它是指由Spring容器管理的对象实例。在Spring中,对象实例被称为Bean,而不是简单的Java对象。
- Spring框架通过IoC容器来管理Bean的创建、配置和生命周期。IoC容器负责创建Bean实例,解决Bean之间的依赖关系,并在需要时将Bean注入到其他对象中。
- 在Spring中,Bean可以通过XML配置文件、注解或Java代码进行定义和配置。通过配置文件或注解,开发者可以指定Bean的类、依赖关系、作用域等信息。
- Spring框架提供了多种作用域的Bean,包括单例(Singleton)、原型(Prototype)、会话(Session)、请求(Request)等。单例作用域是最常见的,默认情况下Spring容器会创建Bean的单个实例,并在整个应用程序中共享该实例。而原型作用域则会在每次请求Bean时创建一个新的实例
什么叫把Bean注册进Spring中?就是说,把Bean的类名和路径填到一张表上,项目启动时,Spring会根据这张表,自动的创建对应的实例对象并保存进容器里,以待使用。
这张表就是注册表,我们以“XML”文件来构建这张表,在目录“resource”下创建文件“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.xsd">
</beans>
- 这段代码没必要理解或背诵,要用的时候直接复制过来就行,记得保存进自己的笔记里
- 标签<beans>:注册表
注册类S
<?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.xsd">
<bean id="S" class="S"></bean>
</beans>
- <bean>:注册表中的每一项,代表一个bean
- id:bean的标识符,通常设为类名
- class:bean的类路径,以目录“java”为当前目录,那类S的路径就是“S”
如果将A和S放进目录“model”中
那<bean>就要变为
<bean id="S" class="model.S"></bean>
第三步,获取Spring的上下文对象context。
context就相当于是Bean容器的对象,当new这个对象时,spring才会加载前面的配置文件‘spring-config’并将Bean添加进Bean容器。所以说,所有的Bean都要通过context来获取。
获取context对象的方式有两种,如下:
ApplicationContext context =
new ClassPathXmlApplicationContext("spring-config.xml");
- new 了一个ClassPathXmlApplicationContext,构造的参数是“spring-config.xml”的路径
BeanFactory context =
new XmlBeanFactory(new ClassPathResource("spring-config.xml"));
- new了一个XmlBeanFactory,构造的参数是ClassPathResource的对象
- ClassPathResource构造的参数是“spring-config.xml”的路径
这两种方式创作出的context没有本质上的区别,都可以正常使用。它们最主要的区别是:前者提前加载,在容器初始化时加载所有的Bean;后者延迟加载,在需要使用Bean的时候才会加载Bean。以目前来说,肯定优先使用前者,因为现在的内存很足,使用提前加载能提高项目的执行效率。
ApplicationContext VS BeanFactory:
• 延迟加载 vs 提前加载:
BeanFactory 使用延迟加载策略,即只有在需要使用 Bean 时才会实例化它。
ApplicationContext 在容器初始化时就会提前加载所有的单例 Bean 实例。• 功能扩展:
ApplicationContext 是 BeanFactory 的子接口,提供了更多的功能扩展,如国际化支持、事件发布、AOP 等。
ApplicationContext 支持更多的上下文实现,如基于 XML 配置文件的 ClassPathXmlApplicationContext、FileSystemXmlApplicationContext,以及基于注解的 AnnotationConfigApplicationContext。• 自动装配:
ApplicationContext 支持自动装配,可以根据依赖关系自动将 Bean 注入到需要它们的地方。
BeanFactory 需要显式配置依赖注入。• 容器加载方式:
BeanFactory 是一种基本的容器,可以通过编程方式创建和配置。
ApplicationContext 可以通过多种方式加载配置,如 XML 配置文件、注解或者 Java 代码。
第四步,获取Bean并使用。
在类APP的main方法里创建context并最终获取产品A
- getBean():从context中获取Bean,参数是“spring-config”里<bean>的id
至此,spring的使用就已经完成。我知道,此时大伙绝对有个不解,这样搞麻烦死的要死,我可以直接自己new一个S对象传参啊,干嘛要用这个context?
事实上,spring也不这么使用,这里的使用只是给大伙介绍一下spring框架背后的原理。开发中,真正使用的是注解,使用Spirng里的注解,让spring自动为我们进行以上操作。这个我们在分支中详解。
---------------分----------割-----------线----------------------------------------------------------------------
▮后续分支
▪【Spring注解】更简单的存取Bean
链接:
▪【Spring】Bean的生命周期
链接:
▪上一节点
▪根节点