文章目录

  • 1.Spring是什么?
    • 什么是容器?
    • 什么是IoC呢?
      • 控制反转程序开发
      • 理解Spring IoC
      • DI概念
    • Spring的创建和使用
      • 创建Spring项目
      • 添加Spring框架支持
      • 创建启动类和main
    • 存储Bean对象
      • 创建Bean对象
      • 将Bean注册到Spring容器中
      • 总结
    • 更简单的Spring读取和存储对象
      • 存储Bean对象
        • 配置扫描路径
        • 添加注解存储Bean对象
        • 注意Bean的命名
      • 使用Bean注解来存储对象
      • 重命名Bean
    • 获取Bean对象(对象装配)
      • 属性注入
      • 构造方法注入
      • Setter注入
      • 三种注入的优缺点分析
      • @Resource:另⼀种注⼊关键字
      • @AutoWired和Resource注解的区别
      • 同⼀类型多个 @Bean 报错
    • Bean的作用域和生命周期
      • 作用域定义
      • Bean的六种作用域
        • singleton
        • prototype(原型对象)
        • Request
        • session
        • application(了解)
        • websocket(了解)
      • 单例作⽤域(singleton)和全局作⽤域(application)区别
      • Bean的原理分析
        • Bean的执行流程![在这里插入图片描述](https://img-blog.csdnimg.cn/712852be395049b58ddc74d1a0cff59f.png)
      • Bean的生命周期
      • 生命周期演示

1.Spring是什么?

我们通常所说的 Spring 指的是 Spring Framework(Spring 框架),它是⼀个开源框架,有着活跃⽽庞 ⼤的社区,这就是它之所以能⻓久不衰的原因。Spring ⽀持⼴泛的应⽤场景,它可以让 Java 企业级的 应⽤程序开发起来更简单。

⽤⼀句话概括 Spring:Spring 是包含了众多⼯具⽅法的 IoC 容器

由此引发的问题是:什么是容器? 什么又是Ioc容器???

什么是容器?

容纳某种物品的装置
比如前面学数据结构中的List / Map (容纳数据)等等
包括 Tomcat (Web容器)

什么是IoC呢?

Ioc实际上是控制反转的意思 —> Inversion of control
如何理解控制反转?

传统程序开发
假设我们去构建一辆车,利用传统的程序思想进行开发…
构建⼀辆⻋(Car Class),然⽽⻋需要依赖⻋身(FrameWork Class),⽽⻋身需要依赖底盘(Bottom Class),⽽底盘需要依赖轮胎(Tire Class),最终程序的实现代码如下


```java
在这里插入代码片
```public class NewCarExample {public static void main(String[] args) {Car car = new Car();car.init();}/*** 汽车对象*/static class Car {public void init() {// 依赖车身Framework framework = new Framework();framework.init();}}/*** 车身类*/static class Framework {public void init() {// 依赖底盘Bottom bottom = new Bottom();bottom.init();}}/*** 底盘类*/static class Bottom {public void init() {// 依赖轮胎Tire tire = new Tire();tire.init();}}/*** 轮胎类*/static class Tire {// 尺寸private int size = 30;public void init() {System.out.println("轮胎尺寸:" + size);}}
}

通过代码我们发现了一些规律, Car类依赖于Framework,也就是new Car的时候,Framework也被创建了,并且调用init方法,接下来调用的init方法又自动去new Bottom,因为通过这些类的含义知道,他们之间是相互依赖的,如果你想去建造一辆车,就需要去保证这种依赖关系

但是这种设计思想也有缺陷,当你满足不同客户的需求的时候,轮胎的尺寸不同,你就要去修改参数,又或者你想加别的参数,可能解决方法是 在每个类中加上参数,通过参数传递就行了呀.实际上这种方式问题很大,因为这几个类的依赖性很强,我们不断的因为客户的需求而修改类的代码是很麻烦而且很容易出Bug的行为.

传统程序开发的缺陷:
以上程序中,轮胎的尺⼨的固定的,然⽽随着对的⻋的需求量越来越⼤,个性化需求也会越来越多,这 时候我们就需要加⼯多种尺⼨的轮胎,那这个时候就要对上⾯的程序进⾏修改了

public class NewCarExample2 {public static void main(String[] args) {Car car = new Car();car.init(50, "猛男粉");}/*** 汽车对象*/static class Car {public void init(int size, String color) {// 依赖车身Framework framework = new Framework();framework.init(size, color);}}/*** 车身类*/static class Framework {public void init(int size, String color) {// 依赖底盘Bottom bottom = new Bottom();bottom.init(size, color);}}/*** 底盘类*/static class Bottom {public void init(int size, String color) {// 依赖轮胎Tire tire = new Tire();tire.init(size, color);}}/*** 轮胎类*/static class Tire {// 尺寸
//        private int size = 30;public void init(int size, String color) {System.out.println("轮胎尺寸:" + size + " | 颜色:" + color);}}
}

这种就是不断的修改类中的代码,实际上并不能完全解决问题反而会出很大的问题.
从以上代码可以看出,以上程序的问题是:当最底层代码改动之后,整个调⽤链上的所有代码都需要修 改。

我们可以尝试不在每个类中⾃⼰创建下级类,如果⾃⼰创建下级类就会出现当下级类发⽣改变操作,⾃ ⼰也要跟着修改。 此时,我们只需要将原来由⾃⼰创建的下级类,改为传递的⽅式(也就是注⼊的⽅式),因为我们不需 要在当前类中创建下级类了,所以下级类即使发⽣变化(创建或减少参数),当前类本身也⽆需修改任 何代码,这样就完成了程序的解耦。 解决传统开发中的缺陷

PS:解耦指的是解决了代码的耦合性,耦合性也可以换⼀种叫法叫程序相关性。好的程序代码的耦合 性(代码之间的相关性)是很低的,也就是代码之间要实现解耦

实际上IoC就是解耦操作

这就好⽐我们打造⼀辆完整的汽⻋,如果所有的配件都是⾃⼰造,那么当客户需求发⽣改变的时候, ⽐如轮胎的尺⼨不再是原来的尺⼨了,那我们要⾃⼰动⼿来改了,但如果我们是把轮胎外包出去,那 么即使是轮胎的尺⼨发⽣变变了,我们只需要向代理⼯⼚下订单就⾏了,我们⾃身是不需要出⼒的

控制反转程序开发

基于上述思路,之前我们是通过在构造方法中创建依赖类的方法,现在换一种思路就是改为 依赖注入的方法

public class IocCarExample {public static void main(String[] args) {Tire tire = new Tire(50, "红色");Bottom bottom = new Bottom(tire);Framework framework = new Framework(bottom);Car car = new Car(framework);car.run();}static class Car {private Framework framework;public Car(Framework framework) {this.framework = framework;}public void run() {framework.init();}}static class Framework {private Bottom bottom;public Framework(Bottom bottom) {this.bottom = bottom;}public void init() {bottom.init();}}static class Bottom {private Tire tire;public Bottom(Tire tire) {this.tire = tire;}public void init() {tire.init();}}static class Tire {private int size;private String color;public Tire(int size, String color) {this.size = size;this.color = color;}public void init() {System.out.println("轮胎:" + size + " | 颜色:" + color);}}
}

仔细观察这种代码跟上面的有什么区别???
这是很重要的一点

当我们new Car的时候,已经创建好了framework并将framework注入到car的构造方法中,并且 car.run中去执行framework的init方法
每个类的init方法实际上都是去执行下一个类的init方法
反正这种代码是很精妙的,需要自己好好看看,博主在这也说不太懂,毕竟刚学…
但是最重要的一点是,你跟着代码的思路走发现他确确实实解决了上树的问题,无论我们怎么去改或者加多少个参数,都是很简单的事情,只需要在new Tire那里和 Tire类本身进行修改即可

这就是IoC思想,接下里的两张图可以进行对比一下

我们发现了⼀个规:,通⽤程序的实现代码,类的创建顺序是反的,传统代码是 Car 控制并创建了 Framework,Framework 创建并创建了 Bottom,依次往下,⽽改进之后的控制权发⽣的反转,不再是 上级对象创建并控制下级对象了,⽽是下级对象把注⼊将当前对象中,下级的控制权不再由上级类控制 了,这样即使下级类发⽣任何改变,当前类都是不受影响的,这就是典型的控制反转,也就是 IoC 的实 现思想。

在传统设计里面,我们new 的是Car,但是改的是Tire,由于改了Tire就要改Bottom等等
在IoC中,我们通过注入依赖的方式,改的还是Tire,但是我们已经将Tire注入到Bottom中去了,所以Bottom也随之改变了,之后也是如此,所以他主要是对之前的控制顺序进行了反转,所以IoC叫做控制反转

IoC的设计思想说完了,那么如何去理解Spring是一个Ioc容器呢???

理解Spring IoC

既然 Spring 是⼀个 IoC(控制反转)容器,重点还在“容器”⼆字上,那么它就具备两个最基础的功能: 将对象存⼊到容器; 从容器中取出对象。 也就是说学 Spring 最核⼼的功能,就是学如何将对象存⼊到 Spring 中,再从 Spring 中获取对象的过 程。

将对象存放到容器中的好处:将对象存储在 IoC 容器相当于将以后可能⽤的所有⼯具制作好都放到仓 库中,需要的时候直接取就⾏了,⽤完再把它放回到仓库。⽽ new 对象的⽅式相当于,每次需要⼯具 了,才现做,⽤完就扔掉了也不会保存,下次再⽤的时候还得重新做,这就是 IoC 容器和普通程序开 发的区别。

Spring 是⼀个 IoC 容器,说的是对象的创建和销毁的权利都交给 Spring 来管理了,它本身⼜具备了存 储对象和获取对象的能⼒。

DI概念

说到 IoC 不得不提的⼀个词就是“DI”,DI 是 Dependency Injection 的缩写,翻译成中⽂是“依赖注 ⼊”的意思。 所谓依赖注⼊,就是由 IoC 容器在运⾏期间,动态地将某种依赖关系注⼊到对象之中。所以,依 赖注⼊(DI)和控制反转(IoC)是从不同的⻆度的描述的同⼀件事情,就是指通过引⼊ IoC 容 器,利⽤依赖关系注⼊的⽅式,实现对象之间的解耦。 IoC 是“⽬标”也是⼀种思想,⽽⽬标和思想只是⼀种指导原则,最终还是要有可⾏的落地⽅案,⽽ DI 就 属于具体的实现。

DI就是我们讲的依赖注入的实现,通过将下级类注入进去的具体实现
所以建议大家多去看看这种通过注入方式来实现的代码,这对我们如何去设计代码是很重要的

Spring的创建和使用

Spring作为容器,最重要的两个功能就是:

  1. 将对象存储到容器中
  2. 将对象从容器中取出来
    实际上这里存放的内容就是对象特叫做Bean

创建Spring项目

接下来使⽤ Maven ⽅式来创建⼀个 Spring 项⽬,创建 Spring 项⽬和 Servlet 类似,总共分为以下 3 步:

  1. 创建⼀个普通 Maven 项⽬。
  2. 添加 Spring 框架⽀持(spring-context、spring-beans)。
  3. 添加启动类。
    虽然是Spring项目,但是还是基于maven的,这根Spring Boot不一样的

添加Spring框架支持

在项⽬的 pom.xml 中添加 Spring 框架的⽀持,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>

从上面的依赖可以看到,添加了两个框架:

  1. spring-context Spring上下文
  2. spring-beans 管理对象模块

这跟Servlet很像的,都是需要去添加依赖来下载jar包

这里需要去设置依赖源,博主这里设置的国内的阿里源,最重要的是需要勾选住,否则后期下载别的依赖就会很慢(下载国外的源 网络不太行)

创建启动类和main


到这里Spring的配置和创建就完成了

存储Bean对象

存储 Bean 分为以下 2 步:

  1. 存储 Bean 之前,先得有 Bean 才⾏,因此先要创建⼀个 Bean。
  2. 将创建的 Bean 注册到 Spring 容器中。

创建Bean对象

将Bean注册到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"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"></beans>

接下来,再将 User 对象注册到 Spring 中就可以,具体操作是在 中添加如下配置:

<beans><bean id="user" class="com.bit.User"></bean>
</beans>

在beans里面每一个bean都代表一个注册类,id就不是类名,而是beanName
class代表的是注册类存放的位置

写到这里之后,意味着bean注册成功

然后就可以利用spring-context上下文来将bean对象取出来用


这里通过Application来进行创建的,当然还有别的方法

ApplicationContext 和 BeanFactory 效果是⼀样的,ApplicationContext 属于 BeanFactory 的⼦类, 它们的区别如下。

ApplicationContext VS BeanFactory(常⻅⾯试题):

相同点:都从容器中获取bean,并且都有getBean方法
不同点1、ApplicationContext属于BeanFactory的子类。BeanFactory 只提供了基础访问Bean法,
而ApplicationContext除了拥有BeanFactory的所有功能之外,还提供了更的方法实现,比如对国际化的支持、资源访问的支持、以及事件和传播等方面的支持。
2、从性能方面来说二者是不同,BeanFactory 是按需加载Bean,ApplicationContext是饿汉方式,
在创建时会将所有的Bean都加载起来,以备以后使用。

PS:⽽ ClassPathXmlApplicationContext 属于 ApplicationContext 的⼦类,拥有 ApplicationContext 的所有功能,是通过 xml 的配置来获取所有的 Bean 容器的


第一种里面的"userinfo"要跟当时的bean id对应,并且返回的是一个object类型还需要强转才能拿到
第二种里面直接通过类型进行获取,但是假设同一个类型注册了多次就会出现问题,他要保证注册次数是唯一
这就是注册两次的情况,所以不建议使用

第三种就是双重保险

总结

  1. 先去创建maven项目并且去pom.xml中注入依赖spring-context 和spring-beans
  2. 添加配置文件 spring-config.xml
  3. 创建Bean
  4. 在spring-config.xml中注入Bean
  5. 利用spring-context得到Application对象,并且使用getBean()获取对象
  6. 使用对象

更简单的Spring读取和存储对象

经过前⾯的学习,我们已经可以实现基本的 Spring 读取和存储对象的操作了,但在操作的过程中我们 发现读取和存储对象并没有想象中的那么“简单”,所以接下来我们要学习更加简单的操作 Bean 对象的 ⽅法。
在 Spring 中想要更简单的存储和读取对象的核⼼是使⽤注解,也就是我们接下来要学习 Spring 中的相 关注解,来存储和读取 Bean 对象

存储Bean对象

之前我们在注册类的时候,每注册一个类都要写一行配置,现在可以通过注解的方式来更方便的完成
在开始存对象之前,需要准备工作

配置扫描路径

注意:想要将对象成功的存储到 Spring 中,我们需要配置⼀下存储对象的扫描包路径,只有被配置的 包下的所有类,添加了注解才能被正确的识别并保存到 Spring 中
在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"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="   ">
</content:component-scan>
</beans>

base-package就是设置的扫描路径,你可以把想要注册的类都放到这个路径下,他就会自动去扫描注册
也就是说,即使添加了注解,如果不是在配置的扫描包下的类对象,也是不能被存储到 Spring 中的

添加注解存储Bean对象

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

  1. 类注解:@Controller、@Service、@Repository、@Component、@Configuration。
  2. ⽅法注解:@Bean。

接下来我们分别来看。 使⽤ @Controller 存储 bean 的代码如下所示:

@Controller // 将对象存储到 Spring 中
public class UserController {public void sayHi(String name) {System.out.println("Hi," + name);}
}

更简单的注册结束了,还是利用原来的方式进行上下文的读取

public class Application {public static void main(String[] args) {// 1.得到 spring 上下⽂ApplicationContext context =new ClassPathXmlApplicationContext("spring-config.xml");// 2.得到 beanUserController userController = (UserController)
context.getBean("userController");// 3.调⽤ bean ⽅法userController.sayHi("Bit");}
}

类注解一共是五类,分别是

  • @Controller(控制器)
  • @Service(服务)
  • @Repository(仓库)
  • @Component(组件)
  • Configuration(配置)
    其实这些类注解的功能都是一样的,加了某一个类注解并且在扫描路径下,就可以完成注册类工作
    但是不同的业务逻辑用到的类注解是不一样的,他也是为了提供程序员的开发效率

既然功能是⼀样的,为什么需要这么多的类注解呢?
这和为什么每个省/市都有⾃⼰的⻋牌号是⼀样的?⽐如陕⻄的⻋牌号就是:陕X:XXXXXX,北京的⻋ 牌号:京X:XXXXXX,⼀样。甚⾄⼀个省不同的县区也是不同的,⽐如⻄安就是,陕A:XXXXX,咸 阳:陕B:XXXXXX,宝鸡,陕C:XXXXXX,⼀样。这样做的好处除了可以节约号码之外,更重要的作 ⽤是可以直观的标识⼀辆⻋的归属地。 那么为什么需要怎么多的类注解也是相同的原因,就是让程序员看到类注解之后,就能直接了解当前类 的⽤途,⽐如: @Controller:表示的是业务逻辑层; @Servie:服务层; @Repository:持久层; @Configuration:配置层。

一个完成的程序必然是有很逻辑化的步骤顺序来执行的,直到Resposity这一层才能和数据库进行交互

这些类注解之间的关系通过源码可以看到

注意Bean的命名

通过上⾯示例,我们可以看出,通常我们 bean 使⽤的都是标准的⼤驼峰命名,⽽读取的时候⾸字⺟⼩ 写就可以获取到 bean 了,如下图所示:

实际上小驼峰的类名就是BeanName,因为现在采取更简单的存储Bean方法,所以不需要去一行一行的注册,通过注解的方式来实现第一种取出Bean,但是没有了ID
User user = (User) context.getBean(“user”);

此时getBean里面的参数就是类名,但是这个类名有点古怪

先说结论:
当类似于User的时候,beanName就是 user
当类似于UCompontent的时候,beanName就不变了

我们可以找BeanName命名方式的源码,进行查找
实际上beanName的命名方式是属于jdk而不是spring的jar包的
连续按两次shift出现查找框

使用Bean注解来存储对象


然⽽,当我们写完以上代码,尝试获取 bean 对象中的 user1 时却发现,根本获取不到

因为Bean注解是方法注解,要配合着类注解一起使用才能生效

@Component
public class Users {@Beanpublic User user1() {User user = new User();user.setId(1);user.setName("Java");return user;}
}

这样就大功告成了…

重命名Bean

在上面代码的基础上加上这样一行:
@Bean(name = {“u1”})就表示重命名了

@Component
public class Users {@Bean(name = {"u1"})public User user1() {User user = new User();user.setId(1);user.setName("Java");return user;}
}

这样做法意味着程序员不一定就要拿引用名去作为getBean的参数,当你觉得这个引用名字不好,你就可以采取这种重命名来设置,一旦你设置了重命名之后,引用名就不行了
此时我们使⽤ u1 就可以获取到 User 对象了,当然了设置name是可以有多个,无论你设置多少个name都可以获取到对象,并且 name={} 可以省略

获取Bean对象(对象装配)

获取 bean 对象也叫做对象装配,是把对象取出来放到某个类中,有时候也叫对象注⼊
对象装配有三种实现方法:

  • 1.属性注入
  • 构造方法注入
  • Setter注入
    下⾯我们按照实际开发中的模式,将 Service(注解) 类注⼊到 Controller(注解) 类中

属性注入

属性注⼊是使⽤ @Autowired 实现的,将 Service 类注⼊到 Controller 类中
代码如下:

Service类

import org.springframework.stereotype.Service;
@Service
public class UserService {/*** 根据 ID 获取⽤户数据** @param id* @return*/public User getUser(Integer id) {// 伪代码,不连接数据库User user = new User();user.setId(id);user.setName("Java-" + id);return user;}
}

Controller类代码:

因为是在Controlller中注入Service,所以在Controlller中加入了
private UserService userService,@Autowired注解就表示将这个类注入到Controller中, 名字就是userService,通过这个名字去调用注入类的方法和属性

这种注入方式是属性注入, 注入Bean跟前面的注册类是两码事,之前的注册类意思是将Bean存入到Spring仓库中,随取随用.这里的注入类,是将一个类注入到另一个类中,也符合我们之前讲的IoC容器思想和DI

获取 Controller 中的 getUser ⽅法
结果如下;

属性注入的核心就是 在创造注入类的引用前面加上@AutoWired注解

构造方法注入

构造⽅法注⼊是在类的构造⽅法中实现注⼊,如下代码所示:

@Controller
public class UserController2 {// 注⼊⽅法2:构造⽅法注⼊private UserService userService;@Autowiredpublic UserController2(UserService userService) {this.userService = userService;//已经注入了,赋值即可}public User getUser(Integer id) {return userService.getUser(id);}
}

当前类如果只有一个构造方法,就不需要去加注解,有多个构造方法的时候,必须指定某一个方法嘉善注解,才能注入成功

跟上面的属性注入不同的是,构造方法注入的注解是加在构造方法的上面,并且将引用作为参数传了进来,然后就可以在别的方法中去使用注入类了,这也是官方最推荐的一种。

Setter注入

Setter 注⼊和属性的 Setter ⽅法实现类似,只不过在设置 set ⽅法的时候需要加上 @Autowired 注 解,如下代码所示:

@Controller
public class UserController3 {// 注⼊⽅法3:Setter注⼊private UserService userService;@Autowiredpublic void setUserService(UserService userService) {this.userService = userService;}public User getUser(Integer id) {return userService.getUser(id);}
}

当然了,Setter如果不加@AutoWired自然是不能注入成功的…

三种注入的优缺点分析

  • 属性注⼊的优点是简洁,使⽤⽅便;缺点是只能⽤于 IoC 容器,如果是⾮ IoC 容器不可⽤,并且只 有在使⽤的时候才会出现 NPE(空指针异常)。
  • 构造⽅法注⼊是 Spring 推荐的注⼊⽅式,它的缺点是如果有多个注⼊会显得⽐较臃肿,但出现这种 情况你应该考虑⼀下当前类是否符合程序的单⼀职责的设计模式了,它的优点是通⽤性,在使⽤之 前⼀定能把保证注⼊的类不为空。
  • Setter ⽅式是 Spring 前期版本推荐的注⼊⽅式,但通⽤性不如构造⽅法,所有 Spring 现版本已经 推荐使⽤构造⽅法注⼊的⽅式来进⾏类注⼊了。

属性注入收到了容器的限制,只能在Ioc容器中可以注入,但是它很简便
构造方法注入的通用性很强,并且构造方法注入是可以确保使用对象之前,注入对象已经初始化过了,然后开始构造,避免出现空指针,先去注入之后,在执行构造方法,并且构造方法的参数过多时,开发者就要检查自己的代码哦是否符合单一设计原则规范.
Setter注入很单纯,避免了构造方法的注入传多个参数,但是Setter的通用性没构造方法通用性强

虽然官方推荐构造方法注入,但是我们是实践中还是来使用属性注入

@Resource:另⼀种注⼊关键字

在进⾏类注⼊时,除了可以使⽤ @Autowired 关键字之外,我们还可以使⽤ @Resource 进⾏注⼊,如 下代码所示:

@Controller
public class UserController {// 注⼊@Resourceprivate UserService userService;public User getUser(Integer id) {return userService.getUser(id);}
}

@AutoWired和Resource注解的区别

  • 出身不同:@Autowired 来⾃于 Spring,⽽ @Resource 来⾃于 JDK 的注解;
  • 使⽤时设置的参数不同:相⽐于 @Autowired 来说,@Resource ⽀持更多的参数设置,例如 name 设置,根据名称获取 Bean。
  • @Autowired 支持三种注入方式,但是@Resource是不支持构造方法注入的

同⼀类型多个 @Bean 报错

当出现以下多个 Bean,返回同⼀对象类型时程序会报错

@Component
public class Users {@Beanpublic User user1() {User user = new User();user.setId(1);user.setName("Java");return user;}@Beanpublic User user2() {User user = new User();user.setId(2);user.setName("MySQL");return user;}
}

在另一类中去注入的时候

@Controller
public class UserController4 {// 注⼊@Resourceprivate User user;public User getUser() {return user;}
}


这就是出现了同一个类型多个Bean的报错,

Bean的作用域和生命周期

从前⾯的课程我们可以看出 Spring 是⽤来读取和存储 Bean,因此在 Spring 中 Bean 是最核⼼的操作 资源,所以接下来我们深⼊学习⼀下 Bean 对象

通过⼀个案例来看 Bean 作⽤域的问题

假设现在有⼀个公共的 Bean,提供给 A ⽤户和 B ⽤户使⽤,然⽽在使⽤的途中 A ⽤户却“悄悄”地修改 了公共 Bean 的数据,导致 B ⽤户在使⽤时发⽣了预期之外的逻辑错误。(说好⼀起到⽩头,你却悄悄 焗了油)。

被修改的Bean

公共Bean

@Component
public class Users {@Beanpublic User user1() {User user = new User();user.setId(1);user.setName("Java"); // 【重点:名称是 Java】return user;}
}

A用户在使用时,进行了修改操作

@Controller
public class BeanScopesController {@Autowiredprivate User user1;public User getUser1() {User user = user1;System.out.println("Bean 原 Name:" + user.getName());user.setName("悟空"); // 【重点:进⾏了修改操作】return user;}
}

B用户再去使用的时候

@Controller
public class BeanScopesController2 {@Autowiredprivate User user1;public User getUser1() {User user = user1;return user;}
}

打印A和B用户使用公共Bean的值

public class BeanScopesTest {public static void main(String[] args) {ApplicationContext context = new
ClassPathXmlApplicationContext("spring-config.xml");BeanScopesController beanScopesController =
context.getBean(BeanScopesController.class);System.out.println("A 对象修改之后 Name:" +
beanScopesController.getUser1().toString());BeanScopesController2 beanScopesController2 =
context.getBean(BeanScopesController2.class);System.out.println("B 对象读取到的 Name:" +
beanScopesController2.getUser1().toString());}
}


说明A在修改之后,B再去打印发现公共Bean也被修改了

原因分析:

操作以上问题的原因是因为 Bean 默认情况下是单例状态(singleton),也就是所有⼈的使⽤的都是同 ⼀个对象,之前我们学单例模式的时候都知道,使⽤单例可以很⼤程度上提⾼性能,所以在 Spring 中 Bean 的作⽤域默认也是 singleton 单例模式

作用域定义

限定程序中变量的可⽤范围叫做作⽤域,或者说在源代码中定义变量的某个区域就叫做作⽤域。

⽽ Bean 的作⽤域是指 Bean 在 Spring 整个框架中的某种⾏为模式,⽐如 singleton 单例作⽤域,就表 示 Bean 在整个 Spring 中只有⼀份,它是全局共享的,那么当其他⼈修改了这个值之后,那么另⼀个 ⼈读取到的就是被修改的值。

Bean的六种作用域

Spring 容器在初始化⼀个 Bean 的实例时,同时会指定该实例的作⽤域。Spring有 6 种作⽤域, 最后四种是基于 Spring MVC ⽣效的:

  • singleton:单例作⽤域
  • prototype:原型作⽤域(多例作⽤域)
  • request:请求作⽤域
  • session:回话作⽤域
  • application:全局作⽤域
  • websocket:HTTP WebSocket 作⽤域
    注意后 4 种状态是 Spring MVC 中的值,在普通的 Spring 项⽬中只有前两种

singleton

  • 官⽅说明:(Default) Scopes a single bean definition to a single object instance for each Spring IoC container.
  • 描述:该作⽤域下的Bean在IoC容器中只存在⼀个实例:获取Bean(即通过 applicationContext.getBean等⽅法获取)及装配Bean(即通过@Autowired注⼊)都是同⼀ 个对象。
  • 场景:通常⽆状态的Bean使⽤该作⽤域。⽆状态表示Bean对象的属性状态不需要更新 备注:Spring默认选择该作⽤域

无状态的意思是说明Bean的属性不需要更改,在这种情况下默认就是singleton模式

prototype(原型对象)

  • 官⽅说明:Scopes a single bean definition to any number of object instances.
  • 描述:每次对该作⽤域下的Bean的请求都会创建新的实例:获取Bean(即通过 applicationContext.getBean等⽅法获取)及装配Bean(即通过@Autowired注⼊)都是新的 对象实例。
  • 场景:通常有状态的Bean使⽤该作⽤域

有状态就是说 这个Bean很可能会被修改或者一定被修改,你就需要设置成prototype作用域,这样他每次都是去创建新的Bean

Request

  • 官⽅说明:Scopes a single bean definition to the lifecycle of a single HTTP request. That is, each HTTP request has its own instance of a bean created off the back of a single bean definition. Only valid in the context of a web-aware Spring ApplicationContext.
  • 描述:每次http请求会创建新的Bean实例,类似于prototype
  • 场景:⼀次http的请求和响应的共享Bean
  • 备注:限定SpringMVC中使⽤,他跟在Spring中的prototype是很像的,每次请求都会创建新的对象

session

  • 官⽅说明:Scopes a single bean definition to the lifecycle of an HTTP Session. Only valid in the context of a web-aware Spring ApplicationContext.
  • 描述:在⼀个http session中,定义⼀个Bean实例
  • 场景:⽤户回话的共享Bean, ⽐如:记录⼀个⽤户的登陆信息
  • 备注:限定SpringMVC中使⽤

application(了解)

  • 官⽅说明:Scopes a single bean definition to the lifecycle of a ServletContext. Only valid in the context of a web-aware Spring ApplicationContext.
  • 描述:在⼀个http servlet Context中,定义⼀个Bean实例
  • 场景:Web应⽤的上下⽂信息,⽐如:记录⼀个应⽤的共享信息
  • 备注:限定SpringMVC中使⽤

websocket(了解)

  • 官⽅说明:Scopes a single bean definition to the lifecycle of a WebSocket. Only valid in the context of a web-aware Spring ApplicationContext.
  • 描述:在⼀个HTTP WebSocket的⽣命周期中,定义⼀个Bean实例
  • 场景:WebSocket的每次会话中,保存了⼀个Map结构的头信息,将⽤来包裹客户端消息 头。第⼀次初始化后,直到WebSocket结束都是同⼀个Bean。
  • 备注:限定Spring WebSocket中使⽤

单例作⽤域(singleton)和全局作⽤域(application)区别

  1. singleton 是 Spring Core 的作⽤域;application 是 Spring Web 中的作⽤域
  2. singleton 作⽤于 IoC 的容器,⽽ application 作⽤于 Servlet 容器。

Bean的原理分析

Bean的执行流程

Bean 执⾏流程(Spring 执⾏流程):启动 Spring 容器 -> 实例化 Bean(分配内存空间,从⽆到有) -> Bean 注册到 Spring 中(存操作) -> 将 Bean 装配到需要的类中(取操作)。

Bean的生命周期

所谓的⽣命周期指的是⼀个对象从诞⽣到销毁的整个⽣命过程,我们把这个过程就叫做⼀个对象的⽣命 周期。
Bean 的⽣命周期分为以下 5 ⼤部分:

1 实例化Bean(分配内存空间)
2 设置属性(Bean的注入和装配)
3 Bean的初始化

  • 实现了各种 Aware 通知的⽅法,如 BeanNameAware、 BeanFactoryAware、 ApplicationContextAware 的接⼝⽅法;
  • 执⾏ BeanPostProcessor 初始化前置⽅法;
  • 执⾏ @PostConstruct 初始化⽅法,依赖注⼊操作之后被执⾏;
  • 执⾏⾃⼰指定的 init-method ⽅法(如果有指定的话);
  • 执⾏ BeanPostProcessor 初始化后置⽅法。

4 使用Bean
5 销毁Bean

生命周期演示

import org.springframework.beans.factory.BeanNameAware;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
@Component
public class BeanLifeComponent implements BeanNameAware {@PostConstructpublic void postConstruct() {System.out.println("执⾏ PostConstruct()");}public void init() {System.out.println("执⾏ init-method");}@PreDestroypublic void preDestroy() {System.out.println("执⾏:preDestroy()");}public void setBeanName(String s) {System.out.println("执⾏了 setBeanName ⽅法:" + s);}
}

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"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.bit.component">
</content:component-scan><beans><bean id="beanLifeComponent"
class="com.bit.component.BeanLifeComponent" init-method="init"></bean></beans>
</beans>

调用类

import com.bit.controller.BeanLife;
import
org.springframework.context.support.ClassPathXmlApplicationContext;
public class BeanLifeTest {public static void main(String[] args) {ClassPathXmlApplicationContext context =new ClassPathXmlApplicationContext("spring-config.xml");BeanLife life = context.getBean(BeanLife.class);System.out.println("执⾏ main ⽅法");// 执⾏销毁⽅法context.destroy();}
}

这个结果跟上面的不太一样,但是没啥太大区别,主要就是第一条和第四条是没写的,因为第一条是Aware没有重写,然后就是使用bean这是正常方法,也没写其余的顺序是一致的总结:本节课介绍了 Bean 的 6 种作⽤域:

  • singleton:单例作⽤域
  • prototype:原型作⽤域(多例作⽤域)
  • request:请求作⽤域
  • session:回话作⽤域
  • application:全局作⽤域
  • websocket:HTTP WebSocket 作⽤域

其中前两种是 spring 核⼼作⽤域,⽽后 4 种是 spring mvc 中的作⽤域,也介绍了 spring 的执⾏ 流程和 bean 的⽣命周期,其中 bean 的作⽤域是最重要的知识点也是常⻅的⾯试题,⽽ bean ⼤ 的执⾏流程也⼀定要牢记。

Spring核心和设计思想相关推荐

  1. JavaEE进阶 - Spring 核心 与 设计思想 - 细节狂魔

    文章目录 Spring 是什么? 什么是容器? 什么是 IoC?- 通常就是面试中关于框架的第一个问题. 传统程序的开发 总结 理解 Spring IoC DI 总结 Spring 是什么? 我们通常 ...

  2. Spring为什么这么火 之 Spring蕴含的设计思想

    目录 Spring是什么? 1.什么是IoC? 2.传统程序开发 3.控制反转式程序开发 4.理解Spring IoC 5.依赖注入[DI] 总结 前言 很多人说:"Java程序员都是Spr ...

  3. RocketMQ核心架构设计思想

    RocketMQ中NettyRemotingServer的Reactor多线程模型 图1 1.一个 Reactor 主线程(eventLoopGroupBoss)负责监听 TCP网络连接请求建立好连接 ...

  4. Spring 框架蕴含的设计思想

    在 Google Guava 源码讲解中,我们讲到开发通用功能模块的一些比较普适的开发思想,比如产品意识.服务意识.代码质量意识.不要重复早轮子等.今天,我们剖析一下 Spring 框架背后的一些经典 ...

  5. OSGI框架的功能和设计思想

    摘录自InfoQ电子书:<OSGi原理与最佳实践(精选版).pdf> 支持模块化的动态部署 基于 OSGi 而构建的系统可以以模块化的方式(例如 jar 文件等)动态地部署至框架中,从而增 ...

  6. 从面向对象设计思想出发理解Spring AOP编程

    都说AOP是OOP(面向对象)的继承与延续,我觉得理解AOP还是得从OOP出发,经历从暴露问题到解决问题的过程. 目录 一. 面向对象设计思想(OOP) (1)概述 1. POP编程 2. OOP的优 ...

  7. 《小马哥讲Spring核心编程思想》-第一章学习笔记(1)

    <小马哥讲Spring核心编程思想>-第一章学习笔记(1) 一.课程介绍 1.为什么要学习spring? 2.深入学习spring的难点有哪些? 3.课程的设计思路是怎样的? 二.内容综述 ...

  8. 小马哥spring编程核心思想_小马哥讲Spring核心编程思想

    小马哥讲Spring核心编程思想 ├─第01章:Spring Framework总览 (12讲) │      01丨课程介绍.mp4 │      02丨内容综述.mp4 │      03丨课前准 ...

  9. 透彻理解Spring事务设计思想之手写实现

    2019独角兽企业重金招聘Python工程师标准>>> 前言 事务,是描述一组操作的抽象,比如对数据库的一组操作,要么全部成功,要么全部失败.事务具有4个特性:Atomicity(原 ...

最新文章

  1. socket connec连接超时处理
  2. servlet核心API的UML图
  3. leetcode面试题 08.03. 魔术索引(二分)
  4. Pycharm不能用了
  5. python中sklearn中的Imputer模块改动
  6. 启动和停止数据库——停止例程
  7. Linux环境下分析和排查系统故障
  8. 前端接收到的Url参数有中文乱码
  9. jmeter 控制偏离_Jmeter 笔记(1)-安装 基本组件
  10. 《Web漏洞防护》读书笔记——第7章,访问控制防护
  11. 供应链业务架构设计概览
  12. netbsd apache php mysql,NetBSD配置aria2的web前端YAAW笔记
  13. 欧拉筛素数的应用-漂亮数
  14. vue获取facebook用户邮箱、头像并登录
  15. stripe海外支付php教程
  16. matlab 广义最小二乘,广义最小二乘辨识的matlab实现
  17. js 万年历农历转阳历 方法_JS实现带阴历的日历功能详解
  18. 世界上最强大的两个字母的单词
  19. Blender:Lowpoly手部建模流程(附blender源文件下载)
  20. OpenStack ironic裸金属部署(裸金属作为独立服务)

热门文章

  1. UE4如何添加开场Logo动画?
  2. BeanCopier性能对比
  3. ETL流程、数据流图及ETL过程解决方案
  4. beforeDestroy与destroyed的使用
  5. 「镁客·请讲」芝麻科技朱智:用线下大数据提高商家运营效率
  6. OpenGL视口变换函数:glViewport
  7. Android设备上部署Pytorch,实现性别识别,男女分类
  8. 【LeetCode每日一题:799.香槟塔~~~模拟】
  9. php 断言,PHP 断言(assert) 详解
  10. Linux命令行五大装B技术