了解Spring

news/2024/11/19 17:22:01/

目录

什么是Spring?

DI

Spring 存与取

spring 的存操作

spring的取操作

更快速的进行 Spring  存 与 读

三大注入方式

@Autowired

set 注入

构造方法注入

Spring 容器中有多个相同的类时

Bean 作用域

设置作用域

Spring 执行流程

 Bean 生命周期


什么是Spring?

Spring : 它是一个开源框架 , 是包含了众多工具方法的 IoC 容器 , (说的就是对象的创建和销毁的权利都交给了 Spring 来管理了,它本身又具备了存储对象和获取对象的能力,通俗点来讲Spring就是容器, 帮我们管理对象的生命周期) 。

Spring 的核心功能就是 :如何将对象存入到 Spring 中,再从 Spring 中获取对象的过程。

IoC :(Inversion of Control) 控制反转 ,它是一种思想。

从文字上去了解肯定有点难懂,让我们拨开层层迷雾,一一道来。

先来解释一下什么是 控制反转  ,举个栗子 : 

 

设计这么一个关系链,最开始的思想:汽车调用车身,车身调用底盘,底盘调用轮胎,需要的参数都通过构造方法去传递。

代码如下 : 

// 汽车
public class Car {Framework framework = new Framework(12);public void init() {framework.init();}
}
// 车身
public class Framework {private Bottom bottom;public Framework(int size) {bottom = new Bottom(size);}public void init() {bottom.init();}
}
// 底盘
public class Bottom {// 大小private Tire tire;public Bottom(int size) {tire = new Tire(size);}public void init() {tire.init();}
}
// 轮胎
public class Tire {// 大小private int size;public Tire(int size) {this.size = size;}public void init() {System.out.println("轮胎大小:" + size);}
}

这种思想弊病在于:假设轮胎这一层需要加一个参数,这时底盘调用轮胎就得变,因为参数都是由汽车这个类传进来,因此车身调用底盘的参数就要变,汽车调用车身的参数也要变,牵一发而动全身

 

将思想再改变一下,我们将需要的参数进行封装,每次传递的时候,传递一个对象,这么即使需要修改参数,直接修改对象就好了。

代码如下 :

// 测试类
public class Main {public static void main(String[] args) {Tire tire = new Tire(12);Bottom bottom = new Bottom(tire);Framework framework = new Framework(bottom);Car car = new Car(framework);car.init();}
}
// 汽车
public class Car {private Framework framework;public Car(Framework framework) {this.framework = framework;}public void init() {framework.init();}
}
// 车身
public class Framework {private Bottom bottom;public Framework(Bottom bottom) {this.bottom = bottom;}public void init() {bottom.init();}
}
// 底盘
public class Bottom {// 大小private Tire tire;public Bottom(Tire tire) {this.tire = tire;}public void init() {tire.init();}
}
// 轮胎
public class Tire {// 大小private int size;public Tire(int size) {this.size = size;}public void init() {System.out.println("轮胎大小:" + size);}
}

这时我们的创建类的顺序就变成了 : Tire -> Bottom -> Framework -> Car.

最开始的创建类的顺序 : Car -> Framework -> Bottom -> Tire

第一次实现 是将 Car 创建 Framework , Framework 创建了 Bottom,依次往下,这样做,都是由上级对象创建并控制下级对象,而第二次实现则是将下级对象注入到当前对象中,此时下级对象的生命周期不在当前对象中,因此下级对象的控制权就不再由上级对象控制,这样即使下级类发生变化,当前类都是不受影响的。这就是IoC思想 , 这就是控制反转. 

对比上面两种思想,IoC 更为灵活,所有对象的生命创建和销毁的权利都交给了Main,Spring扮演的就是Main的角色。

 

DI

DI :(Dependency Injection)“ 依赖注入 ”。

DI :在程序运行期间,动态的将某个对象引入到当前类的机制。实现了对象之间的解耦。

DI 与 IoC 的关系 :

IoC 它就是一种思想,而 DI 就是实现了IoC这种思想的技术。

就像是 乐观锁 是一种思想,而 CAS 就是一种具体实现的技术。

Spring 存与取

创建一个 maven 项目 , 引入Spring的依赖 .这样得到一个Spring的项目.

spring 的存操作

第一步 , 创建一个Bean对象 . (Bean对象就是java代码中的类)

public class T {public String First() {return "First";}
}

第二步 ,  将 Bean 对象存储到 Spring 当中.

 其中 , 第一小步, 在resources 目录下创建一个spring的配置文件.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:content="http://www.springframework.org/schema/context"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<!--    <content:component-scan base-package="com.demo"></content:component-scan>--></beans>

将Bean对象放在这个配置文件中 , 这样在Spring在启动项目时 , 这些Bean对象会被初始化出来.

第二小步 , 将 Bean 对象配置到 Spring 配置文件中 .

spring的取操作

取操作 : 首先得到 Spring (上下文) 对象 , 然后从Spring中取出对象 .

public class Main {public static void main(String[] args) {// 1. 先得到 Spring (上下文) 对象ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");// 2. 从 Spring 中取出 Bean对象T t = (T)context.getBean("first"); // 根据 id 来得到 Bean 对象,如果参数为null那么就会强转错误// T t = context.getBean(T.class); // 根据 Bean对象的类型来获取 Bean对象 , 但如果Spring中存在相同的对象时,这时使用类型来获取Bean方式就会报错.// T t = context.getBean("first",T.class) 根据 id 和 Bean 类型获取 Bean 对象}
}

 

 除了ApplicationContext 可以获得Spring对象之外 , BeanFactoy 也可以获取 Spring 对象。

 BeanFactory context = new XmlBeanFactory(new ClassPathResource("spring-config.xml"));

关于 ApplicationContextBeanFactory区别

相同点 : 都是可以得到 Spring 上下文对象。都是来自 Spring 的顶级接口。

不同点 : 在继承关系和功能上 : ApplicationContext  属于 BeanFactory 的子类。BeanFactory 只有最基础的访问 Bean 的能力,而 Application 除了拥有 父类 BeanFactory 功能外,还包含了更多的功能,例如:国际化支持、资源访问、时间传播 等等

                从性能上来看 : ApplicationContext 加载方式是将 Bean 对象一次性加载,所以在后面访问 Bean 对象时会比 BeanFactory 快,(牺牲了空间换取了时间)。BeanFactory 需要某个Bean对象 时,才去加载Bean 对象,所在它在执行 Bean 获取时,速度会比较慢。

更快速的进行 Spring  存

最开始的存就是往 xml 的配置文件中放 Bean对象,但如果后期的项目足够大,对象越来越多,那么这个放操作就会很繁琐并且难以维护。因此使用注解可以更快速的进行存与取。

要做的工作首先,配置要扫描的路径,(也就是那些包下的类需要被加载到 Spring 中),效率会更高。

 重点注意:如果想要加载的对象不在扫描路径下,那么就不能被加载到Spring的IoC容器中

将对象存储到 Spring 中,有两种注解类型可以实现:

1 .  首先是类注解 : @Controller 、@Service 、@Repository 、@Component 、@Configuration

五大类注解 :在项目中扮演的角色是不一样的。

@Controller (控制器): 归属于业务逻辑层,用来控制用户的行为,它主要是用来检查用户参数的有效性。

@Service (服务) :  归属于服务层,调用持久层的类实现相应的功能。(它不会直接和数据库直接交互,像是控制中心一样的存在)

@Repository (仓库) :归属于持久层,是直接和数据库进行交互的。

@Configuration (配置) : 归属与配置层,是用来配置当前项目的一些信息。

@Component (组件) : 归属于公共工具类 , 提供公共方法。

对于上述的5大注解 ,举个栗子:登录博客,这时要输入账号和密码,回车键一摁,前端将请求发给后端,后端拿到信息就会先核实一下登录信息是否正确 这个操作就是 控制器来管理的,而如果你想要发布一个博客,Controller就会调用服务层,服务层会根据请求会涉及到那些功能去进行调用持久层,持久层就会去调用数据库中的表。

@Controller // 注解
public class UController {public String inp() {return "采用注解方式来注入";}
}
public static void main(String[] args) {ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");UController uController = context.getBean("UController",UController.class);System.out.println(uController.inp());}

 获取Bean对象 :

如果使用最开始的获取Bean对象的方式,默认情况下 id 就是将类名首字母小写。

特殊情况下:如果类名的首字母和第二个字母都是大写的情况下此时 id 就是原类名。

可以看Spring中的源码

2 .  采用方法注解 : @Bean

@Bean 注解,如果没有给@bean设置name属性的话,默认情况下 获取对象的 id 就是方法名,并且如果设置了name属性之后,那么就不能再使用 方法名来获取对象了。

注意:在采用方法注解时,是将方法返回的对象放入到 Spring 当中。

           使用@Bean注解时,要配合5大类注解来使用。否则会是无效注解。

@Controller
public class UController {@Beanpublic UController inp() {return new UController();}
}
    public static void main(String[] args) {ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");UController uController = context.getBean("inp",UController.class);}
@Controller
public class UController {@Bean(name = {"n1","n2"}) // 设置name属性public UController inp() {return new UController();}
}
public static void main(String[] args) {ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");UController uController = context.getBean("n1",UController.class);}

三大注入方式

@Autowired

接下来 , 我们使用注解的方式来获取对象。

public class User {// 使用属性注入的方式获取 Bean对象@Autowiredprivate UController uController;
}

加上 @Autowired ,Spring就会自动把对象赋值给变量。

@Autowired 

优点 : 实现简单

缺点 :1. 功能性问题 :不能注入不可变(final)对象。(final对象要么直接赋值,要么在构造方法中赋值)

 2 . 通用性问题 : 只适用于 IoC 容器。 

 3 . 设计原则问题 : 更容易违背单一设计原则。(因为属性注入的方式更简便 从而就会导致滥用,所以它更容易违背单一设计原则)。

set 注入

public class User {// 2. set 注入private UController uController;@Autowiredpublic void setuController(UController uController) {this.uController = uController;}public void hi() {System.out.println("hi");}
}

优点 : 更加符合单一设计原则。(相比起属性注入,set 注入更繁琐,因此更加符合单一设计原则)

缺点 : 1. 不能注入不可变对象。

             2. 注入对象可被修改。(set方法是普通 set 方法, 可以被重复调用,在被调用时就存在修改的风险)

构造方法注入

public class User {private UController uController;@Autowiredpublic User(UController uController) {this.uController = uController;}
}

优点 : 构造方法注入相比与前两种注入方法,它可以注入不可变对象,并且它只会执行一次,也不存在像Setter注入那样,被注入的对象随时被修改的情况。

1 . 可注入不可变对象。

2 . 注入对象不会被修改

3 . 注入对象会被完全初始化

4 . 通用性更好。

附加 :

@Resource VS @Autowried

相同点 : 都是用来实现依赖注入的。

不同点 :1. @ Autowired 来自于 Spring,而 @Resource 来自于 JDK 。

                2. 相比起 @Autowired  ,@Resource 支持更多的参数设置,例如 name 设置,根据名称获取 Bean。

                3. @Autowired 可用与 Setter 注入,构造方法注入和属性注入。而@Resource 只能用于Setter注入和属性注入,不能使用构造方法注入。

Spring 容器中有多个相同的类时

@Component
public class PeopleBeans {@Beanpublic Student student1() {return new Student();}@Beanpublic Student student2() {return new Student();}
}@Controller
public class PeoController {@Resourceprivate Student stu;public void fun() {System.out.println(stu.toString());}
}public class Student {private int age = 10;public Student() {System.out.println("实现了构造方法");}@Overridepublic String toString() {return "Student{" +"age=" + age +'}';}
}// 测试
public class Test {public static void main(String[] args) {ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");}
}

 报错的原因是因为容器中有两个相同的类 , 程序无法确定将那个依赖注入,因此弹出错误让程序猿处理.

解决方案 : 

如果使用 @Resource : 可采用 name 属性来指定依赖的名称 

@Resource(name = "student1")

如果使用 @Autowried : 可以配合 @Qualifier 来设置依赖的名称

@Autowired
@Qualifier("student1")

Bean 作用域

Bean 作用域 : Bean 在整个 Spring 框架(项目)中的行为模式 . 

六大作用域 :

1 . singleton : 单例作用域 (默认情况下的作用域)

2 . prototype : 原型作用域 (多例作用域)

 以下四种都是在SpringMVC 中存在 :

3 . request : 请求作用域

4 . session : 回话作用域

5 . application : 全局作用域

6 . websocket : HTTP WebSocket 作用域

其中 singleton 是默认选择的作用域 , 它在 IoC容器中只存在一份 , 无论是通过 ApplicationContext 或者 注解的方式获取到的都是同一个对象.

prototype : 每次从IoC容器中取出都会是一个新的对象 .

其中 request : 每次 http 请求会创建新的Bean实例 , 类似于 prototype . 

session : 在一个 http session 中, 定义一个 Bean 实例, 一般用来记录用户的登录信息.

singleton 与 application 的区别

singleton 是 Spring Core 的作用域 , application 是 Spring Web 的作用域.

singleton 作用 IoC 的容器, 而 application 作用于 Servlet 容器 .

设置作用域

使用 @Scope 注解 , 可以修饰类或者方法.

以下两种方式都可以 :  

@Scope("prototype")
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)

Spring 执行流程

首先启动Spring项目 , 结合 xml 文件对Bean对象进行初始化 , 根据配置好的目录扫描带有五大注解的类或者带有@Bean注解的方法 , 将这些Bean对象注册到容器中. 最后给带有@Autowried 和 @Resource 注解的变量进行依赖注入 .

 Bean 生命周期

生命周期指的是一个对象从诞生到销毁的整个生命过程 , 我们将这个过程叫做一个对象的生命周期 .

Bean的生命周期主要分为 5 个部分 :

1 . 实例化 Bean (为对象分配内存 , 将字节码转换成内存中的对象 , 实现了从无到有,这是Bean里面什么也没有)

2 . 设置属性 (Bean 注入和装配)

3 . Bean 初始化 (各种通知 , 进行初始化的前置工作,进行初始化工作 [使用注解 @PostConstruct 初始化, 使用 (xml) init - method 初始化],初始化的后置工作)

4 . 使用Bean 

5 . 销毁Bean

End........


http://www.ppmy.cn/news/762981.html

相关文章

【macOS 系列】如何调整启动台图标大小和行数

1、使用指令&#xff0c;这是隐藏的技巧&#xff0c;在控制台输入如下指令 defaults write com.apple.dock springboard-rows -int 6 defaults write com.apple.dock springboard-columns -int 8 defaults write com.apple.dock ResetLaunchPad -bool TRUE killall Dock以上表…

台式机DIY选择,主板

一直想组装一个电脑主机&#xff0c;暂时只对显卡内存等了解一二&#xff0c;总结这篇博文就是做一个参考&#xff0c;也希望喜欢的朋友给些建议 文章目录 一直想组装一个电脑主机&#xff0c;暂时只对显卡内存等了解一二&#xff0c;总结这篇博文就是做一个参考&#xff0c;也…

深入解析:关于新算法备案法规的影响与应对策略

在我们步入这个科技发展日新月异的时代&#xff0c;算法技术的迅速崛起无疑带给我们无限可能&#xff0c;但同时也给我们的社会带来了诸多挑战。近期&#xff0c;新的算法备案法规的出台&#xff0c;引发了各行各业的广泛关注。今天&#xff0c;让我们一同深入解析这个法规的影…

vue表格某一条数据上移、下移的2种思路

第一种思路:利用数组与对象的关系:数组索引就是对象的属性名key关系交换位置(原理与二相同都是操作索引) html<el-table :data="collectorData"> <el-table-column prop="name" label="设备名称"></el-table-column><…

C卡催收评分卡

转自公众号&#xff1a;天天学风控 1、定义&#xff1a;C卡&#xff08;Collection scorecard&#xff09;催收评分卡&#xff1a;在帐户管理期&#xff0c;对逾期帐户预测催收策略反应的概率&#xff0c;从而采取相应的催收措施。 2、业务阶段&#xff1a;C卡适用于贷后环节&…

PCIE 4路SFP+ 10G光纤通道适配器 / 光纤数据采集板 / 硬件加速卡(5片 XC7K325T\410T) VS KU115 万兆光纤数据加速卡

PCIE720是一款基于PCI Express总线架构的高性能计算&#xff08;HPC&#xff09;硬件加速卡&#xff0c;板卡采用Xilinx的高性能28nm 7系列FPGA作为运算节点&#xff0c;在资源、接口以及时钟的优化&#xff0c;为高性能计算提供卓越的硬件加速性能。板卡一共具有5个FPGA处理节…

非接触(IC卡、M1卡、CPU卡)ID卡的区别是什么

集成电路芯片可以是存储器或微处理器。带有存储器的IC卡又称为记忆卡或存储卡&#xff0c;带有微处理器的IC卡又称为智能卡或智慧卡。记忆卡可以存储大量信息&#xff1b;智能卡则不仅具有记忆能力&#xff0c;而且还具有处理信息的功能。IC卡可以十分方便地存汽车费、电话费、…

比较IC卡、ID卡、M1卡、CPU卡它们之间有什么区别?

IC卡的定义 : IC&#xff08;Integrated Circuit&#xff09;卡是1970年由法国人Roland Moreno发明的&#xff0c;他第一次将可编程设置的IC芯片放于卡片中&#xff0c;使卡片具有更多功能。“IC卡”和“磁卡”都是从技术角度起的名字&#xff0c;不能将其和“信用卡”、“电…