文章目录
1. Spring 介绍
1.1 Spring 是什么?
Spring(Spring Framework) 是一个开源的轻量级框架,是包含了众多工具方法的 IoC 容器。
那么什么 是 IoC 容器呢?
容器 本身的含义是用来容纳某种物品的装置,而在之前的学习中,我们就应该接触过一些容器了,比如存储数据的容器 List、Map、Set 等等,Web 容器 Tomcat 等等。
那么 IoC 是什么呢?
1.2 IoC 是什么?
IoC(Inversion of Control),译为控制反转,它不是什么技术,而是一种思想。在 Java 开发中,IoC 意味着你将设计好的依赖对象 B 交给容器控制,而不是按照传统的方式,在你需要使用依赖对象 B 的对象 A 内部直接控制。
IoC 既然译为控制反转,那么我们就要理解谁控制谁,控制什么?要理解为何要反转,反转了什么?
谁控制谁,控制什么?
按照传统的设计思想,如果我们想要在 A 对象内部使用 B 对象,那么我们就会在 A 中直接 new 一个 B,这是我们程序员主动去创建的依赖对象;而 IoC 容器能够存储你需要的对象 B,当你要使用这个依赖对象 B 时,IoC 容器就会来控制依赖对象的创建,而程序只需要引入这个已经创建好的对象 B 就行。IoC 容器主要是控制了外部资源的获取(不只是包含要依赖的对象,还包括文件等等)。
为何要反转,反转了什么?
按照传统的设计思想,如果我们想要在 A 对象内部使用 B 对象,那么我们就会在 A 中直接 new 一个 B,这是我们程序员主动去创建的依赖对象;而反转则是使容器来帮忙创建并注入依赖对象,它使得对象 A 不需要再主动去创建对象 B 了,而是被动的接受由容器已经创建好的依赖对象,即对象创建、管理的控制权被反转了,依赖对象的获取被反转了。
IoC 的作用:
IoC 不是一种技术,只是一种思想,一个重要的面向对象编程的法则,它能指导我们如何设计出松耦合、更优良的程序。传统应用程序都是由我们在类内部主动创建依赖对象,从而导致类与类之间高耦合,难于测试;有了 IoC 容器后,把创建和查找依赖对象的控制权交给了容器,由容器进行注入组合对象,所以对象与对象之间是松散耦合,这样也方便测试。除此之外,将对象存储在 IoC 容器相当于将以后可能⽤到的所有⼯具制作好后都放到仓库中,需要的时候直接取出就⾏,⽤完再把它放回到仓库,利于复用。⽽ new 对象的⽅式相当于,每次需要⼯具时,才现做,⽤完就扔掉了也不会保存,下次再⽤的时候还得重新做。
注意:
并不是只有 Spring 的容器才叫 IoC 容器,基于 IoC 容器的框架还有很多,并非是 Spring 特有的
到这里 IoC 已经差不多介绍完了,从上面的介绍可知 IoC 它只是一种思想,那么 IoC 思想具体的实现是怎么样的呢?我们就不得不提到 DI。
1.3 DI 是什么?
DI(Dependency Injection),译为依赖注入,它是指 IoC 容器在运行期间,动态地将某种依赖关系注入到对象之中。依赖注入的目的并非为软件系统带来更多功能,而是为了提升组件重用的频率,并为系统搭建一个灵活、可扩展的平台。通过依赖注入机制,我们只需要通过简单的配置,而无需任何代码就可指定目标需要的资源,完成自身的业务逻辑,而不需要关心具体的资源来自何处,由谁实现。
DI 既然译为依赖注入,那么我们就要理解谁依赖谁,为什么需要依赖,谁注入谁,注入了什么?
- 谁依赖谁:应用程序依赖 IoC 容器。
- 为什么需要依赖:应用程序需要 IoC 容器来提供对象所需的外部资源。
- 谁注入谁:IoC 容器注入应用程序某个对象。
- 注入了什么:注入了某个对象所需的外部资源(包括对象、资源、常量数据等等)。
IoC 和 DI 的关系:
IoC 和 DI 其实是同一个概念的不同角度的描述,IoC 是一种思想,而 DI 是具体的实现,通过引入 IoC 容器,利用依赖关系注入的方式,就能实现对象之间的解耦。
1.4 Spring 的核心功能
回归主题,Spring 是包含了众多工具方法的 IoC 容器,我们已经介绍了 IoC 是什么,那么如何理解“Spring 就是一个 IoC 容器”呢?
上面介绍了很多关于 IoC 的知识,但是 Spring 的本质上是一个容器,容器才是 Spring 框架实现功能的核心,既然 Spring 是一个容器,它就会具备容器的两个最基础的功能:
- 将对象存入到容器
- 从容器中取出对象
也就是说 Spring 最核心的功能 就是将对象存入到 Spring 中,再从 Spring 中获取到对象。又因为 Spring 是⼀个 IoC 容器,那么除了它本身具备的存储对象和获取对象的能⼒,还具有管理对象的创建和销毁的权利。
1.5 Spring 的应用上下文
我们已经了解了 Spring 是一个 IoC 容器,但是容器只是一个提供给需要被管理的对象的空间,还需要通过 Spring 的应用上下文,来向容器中添加我们需要的被管理的对象。
Spring 的应用上下文 可以简单的理解成将你需要 Spring 帮你管理的对象放入到容器的容器对象,它是 Spring 容器的一种抽象化表述。
常用的 Spring 上下文对象为
ApplicationContext
,它本质上就是一个维护 Bean 定义以及对象之间协作关系的高级接口,它是继承了上下文对象
BeanFactory
后派生而来的应用上下文的抽象接口。在功能上,相比于
BeanFactory
只能提供基本的 DI 功能,它能提供更多的企业级的服务,比如对国际化支持、资源访问、以及事件传播等等。在性能上,
ApplicationContext
是⼀次性加载并初始化所有的 Bean 对象(在获取上下文时,就会创建初始化所有的 Bean对象),所以调用快,⽽
BeanFactory
是需要哪个 Bean 才去加载哪个 Bean(在 getBean 时才会创建初始化指定的 Bean 对象),因此更加轻量,但调用慢。
对于上述两种上下文抽象接口,Spring 提供了多种类型的容器实现,用于不同场景的使用
ClassPathXmlApplicationContext
:从类路径下的一个或多个 xml 配置文件中加载上下文,适用于 xml 配置的方式。AnnotationConfigApplicationContext
:从基于一个或多个 Java 的配置类中加载上下文,适用于 Java 注解的方式。FileSystemXmlApplicationContext
:从文件系统下的一个或多个 xml 配置文件中加载上下文,从系统盘符中加载 xml 配置文件。AnnotationConfigWebApplicationContext
:专门为 web 应用准备的,适用于注解方式。XmlWebApplicationContext
:从 web 应用下的一个或多个 xml 配置文件加载上下文,适用于 xml 配置方式。
2. Spring 项目的创建和使用
在 Java 语言中对象也叫做 Bean,因此以下遇到的对象将以 Bean 著称。
2.1 创建 Maven 项目
- 创建一个 Maven 项目(在 IDEA 中创建好 Maven 项目)
- 添加 Spring 框架支持(在项目的
pom.xml
文件中添加 Spring 框架的支持,配置如下)> Spring 项目需要添加的依赖有 >> spring-context>
> (Spring 上下文)和 >> spring-beans>
> (管理对象的模块)。可以去 Maven 中央仓库搜索,也可以直接使用下面的依赖。<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>
- 添加启动类(在
/src/main/java
目录下创建一个启动类,类名自定义,包含 main 方法即可)
2.2 存储 Bean 对象
- 创建好需要的 Bean(可以创建一个包,用于存放要创建的 Bean)> 存储 Bean 分为两个步骤,先创建好需要的 Bean,再将创建好的 Bean 注册到 Spring 容器中
- 将 Bean 注册到 Spring 容器中> 先在 >
> resources>
> 目录下创建一个 >> xml>
> 文件(文件名没有要求),用于存放 Spring 的配置文件,配置文件是固定的内容,内容如下。<?xml version="1.0" encoding="UTF-8"?><beansxmlns="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.xsd"></beans>
> 将创建好的 Bean 添加到 Spring 配置文件中。在原有的 beans 标签内再创建一个 beans 标签,里面用于存放要注册的 Bean,然后在创建好的 beans 标签内创建一个 bean 标签,表示要注册的某个 Bean,该 bean 标签内添加一个 id 属性表示存储对象在 Spring 中的身份标识,即 Bean 对象的名字,再添加一个 class 属性表示要存储的对象(包含包名和类名)补充: Spring 存储 Bean 时的命名规则> > - 当 bean 标签中主动设置 id 属性来命名 Bean 对象的名字时,Spring 中存储该 Bean 的名字即为 id 的值。(源码如下)> > - 当 bean 标签没有设置 id 属性来命名 Bean 对象的名字时,Spring 中存储该 Bean 时生成的命名规则为:当类名的长度大于1,且第一个字符和第二个字符都为大写时,则直接命名为类名;否则将类名的第一个字符转成小写后,再返回新生成的字符。(源码如下)
2.3 获取并使用 Bean 对象
- 创建 Spring 上下文(这里使用
ApplicationContext
作为上下文对象,也可以使用BeanFactory
)// 得到 Spring 的上下⽂对象,创建的时候需要配置 Spring 配置信息ApplicationContext context =newClassPathXmlApplicationContext("spring-config.xml");
- 获取指定的 Bean 对象(这里使用上文正已经注册到 Spring 容器中的 Bean 对象 User)>
> getBean>
> 方法用于加载上下文中的 Bean 对象,它有多种重载的方法> > > -Object getBean(Bean 对象的名字)
> -T getBean(Bean 对象的名字, 要加载的类对象)
> -T getBean(要加载的类对象)
> > 注意: 如果当前的一个类型被重复注册到 Spring 的配置文件中,那么不能使用第三种方式去获取 Bean 对象,需要根据名称来获取。> >// 方式一User user =(User) context.getBean("user");// 方式二User user =(User) context.getBean("user",User.class);// 方式三User user =(User) context.getBean(User.class);
- 使用 Bean
user.say();
注意:Spring 中的 Bean 默认是单例模式。可以再创建一个 User 实例,打印两个实例得到结论
3. Spring 更简单的读取和存储对象方式
上面实现了基本的 Spring 存储和读取对象的操作,但是在操作过程中并不是那么的简单,接下来将介绍更简单的存储和读取 Bean 对象的方法,即使用注解。
3.1 存储 Bean 对象
之前将 Bean 存储到 Spring 中的方法,是在 Spring 的配置文件中直接添加 bean 注册内容,示例如下:
而通过使用注解,就无需在存储一个 Bean 对象时,在 Spring 的配置文件中再添加一个注册内容,只要在 Spring 的配置文件中配置一个扫描路径即可实现 Bean 的批量注册。
3.1.1 配置扫描路径
Spring 引入了组件自动扫描机制,它可以在类路径底下,扫描配置的
base-package
包下所有标注了
@Component
、
@Service
、
@Controller
、
@Repository
、
@Configuration
注解的类,并把这些类纳入进Spring 容器中管理。
在 Spring 配置文件中添加如下配置:
<?xml version="1.0" encoding="UTF-8"?><beansxmlns="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-scanbase-package="com.t4.beans"></content:component-scan></beans>
3.1.2 注册 Bean 相关的注解介绍
想要将对象存储在 Spring 中,有两种类型的注解可以实现:
- 类注解: 注解在类上,用于标识组件的类型注解描述
@Controller
组合了@Component
注解,应用在 mvc 层(控制层),DispatcherServlet
会自动扫描注解了此注解的类,然后将 web 请求映射到注解了@RequestMapping
的方法上。@Service
组合了@Component
注解,应用在 service 层(业务逻辑层)。@Repository
组合了@Component
注解,应用在 dao 层(数据访问层)。@Component
表示该类是一个“组件”,成为 Spring 管理的 Bean。当使用基于注解的配置和类路径扫描时,这些类被视为自动检测的候选对象。同时@Component
还是一个元注解。@Configuration
声明当前类为配置类,其中内部组合了@Component
注解,表明这个类是一个 Bean。 - 方法注解: 注解在方法上注解描述
@Bean
声明当前方法的返回值是一个 Bean,返回的 Bean 对应的类中可以定义init()
方法和destroy()
方法。
为何说
@Controller
、
@Service
、
@Repository
、
@Configuration
都组合了
@Component
注解呢?
通过源码发现,这些注解本身都带了一个注解
@Component
,即它们本身就属于
@Component
的“子类”
3.1.3 添加 @Controller 注解存储 Bean 对象
示例代码:
packagecom.t4.beans;importorg.springframework.stereotype.Controller;@ControllerpublicclassUserController{publicvoidsay(){System.out.println("你好 Controller!");}}
3.1.4 添加 @Service 注解存储 Bean 对象
示例代码:
packagecom.t4.beans;importorg.springframework.stereotype.Service;@ServicepublicclassUserService{publicvoidsay(){System.out.println("你好 Service!");}}
3.1.5 添加 @Repository 注解存储 Bean 对象
示例代码:
packagecom.t4.beans;importorg.springframework.stereotype.Repository;@RepositorypublicclassUserRepository{publicvoidsay(){System.out.println("你好 Repository!");}}
3.1.6 添加 @Component 注解存储 Bean 对象
示例代码:
packagecom.t4.beans;importorg.springframework.stereotype.Component;@ComponentpublicclassUserComponent{publicvoidsay(){System.out.println("你好 Component!");}}
3.1.7 添加 @Configuration 注解存储 Bean 对象
示例代码:
packagecom.t4.beans;importorg.springframework.context.annotation.Configuration;@ConfigurationpublicclassUserConfiguration{publicvoidsay(){System.out.println("你好 Configuration!");}}
3.1.8 添加 @Bean 注解存储 Bean 对象
注解
@Bean
需要与类注解搭配使用,例如常与注解
@Configuration
结合使用。带有
@Bean
注解的方法返回的对象会存到 Spring 容器中,Bean 对象的名字为方法名。
虽说使用
@Bean
注解要搭配类注解使用,看似要多出一笔,但是加上了类注解后能大大加快扫描的性能。
示例代码:
packagecom.t4.beans;importcom.t4.model.UserInfo;importorg.springframework.context.annotation.Bean;importorg.springframework.context.annotation.Configuration;@ConfigurationpublicclassUserBeans{@BeanpublicUserInfogetUserInfo(){UserInfo userInfo =newUserInfo();
userInfo.setId(1);
userInfo.setName("张三");
userInfo.setPassword("1234");return userInfo;}}
重命名 Bean:
由于方法名不能很直观的显示 Bean 名,为此可以重命名 Bean。重命名方式为:在
@Bean
注解后面增加一个 name 属性,该属性可以含多个值,其中
name = {}
都可以省略,只带新的 Bean 名。
注意1: 重命名后,如果起了多个名字,则使用任意一个都可以。但是不能再使用原方法名去获取 Bean
注意2: 重命名后,如果起了多个名字,获取的对象都是同一个
3.2 获取 Bean 对象
获取 Bean 对象也叫做对象装配或者对象注入,是把对象从容器中取出来放到某个类中。
之前我们介绍了先通过获取 Spring 应用上下文,再从中获取 Bean 对象的方法去得到 Bean,但是整体显得比较麻烦。接下来将介绍三种更简单的对象注入的方法:
- 方法一:属性注入
- 方法二:构造方法注入
- 方法三:Setter 注入
3.2.1 注入 Bean 相关注解介绍
这里介绍三种和注入 Bean 相关的注解:
注解描述包含的属性
@Autowired
可以标注在属性、构造方法和 Setter 方法上,用于完成自动装配。默认通过 Bean 类型进行查询,如果查询不到则会再通过 Bean 对象名进行查询,为 Spring 提供的注解。
required
@Resource
可以标注在属性和 Setter 方法上,用于完成自动装配。默认通过 Bean 名进行查询,为 JDK 提供的注解。
name
、
type
等
@Qualifier
用于指定需要注入的 Bean 对象的名字
value
- 使用
@Autowired
自动注入的时候,可以在属性或参数加上@Qualifier("xxx")
指定注入到对象; - 使用
@Autowired
注解时,当 Spring 无法找到匹配的 Bean 装配,它会抛出异常。要解决这个问题,可以通过@Autowired
的required
属性设置为 false 来禁用此检查功能。 - Spring 将
@Resource
注解的 name 属性的值解析为为 Bean 的名字,type 属性的值解析为为 Bean 的类型。 - 使用
@Resource
注解默认通过 Bean 名进行查询装配。如果使用 name 属性,则使用 Bean 名 自动注入策略;如果使用 type 属性,则使用 Bean 类型自动注入策略。 @Autowired
注解用于构造方法注入时,如果只有一个构造方法,可以省略使用@Autowired
,如果有多个注解则不能省略。@Autowired
可以用于数组和使用泛型的集合类型,然后 Spring 会将容器中所有类型符合的 Bean 注入进来。@Autowired
标注作用于 Map 类型时,如果 Map 的 key 为 String 类型,则 Spring 会将容器中所有类型符合 Map 的 value 对应的类型的 Bean 增加进来,用 Bean 的 id 或 name 作为 Map 的 key。@Autowired
标注作用于普通方法时,会产生一个副作用,就是在容器初始化该 Bean 实例的时候就会调用该方法。当然,前提是执行了自动装配,对于不满足装配条件的情况,该方法也不会被执行。
3.2.2 属性注入 Bean 对象
在需要的类中先创建一个要被注入的对象(该对象可以是一个集合,那么得到的结果就是所有匹配的 Bean 的集合),并在该对象上加上
@Autowired
或
@Resource
注解。
代码情景:
创建了一个 UserService 类,里面包含了 name 和 password 两个字段,将这个类存储到 Spring 中。在上述代码中的 UserController 类中通过 @Autowired 注解来将 UserService 这个对象注入到该类中,并创建了一个 getUserService 方法打印获取到的 Bean 对象。而 UserController 类本身又在启动类中被获取后调用 getUserService 方法。
示例代码1: 使用
@Autowired
注解
@ControllerpublicclassUserController{@AutowiredprivateUserService userService;publicvoidgetUserService(){System.out.println(userService);}}
示例代码2: 使用
@Resource
注解
@ControllerpublicclassUserController{@ResourceprivateUserService userService;publicvoidgetUserService(){System.out.println(userService);}}
3.2.3 构造方法注入 Bean 对象
在需要的类中先创建一个要被注入的对象,然后创建该类的构造方法,将要被注入的对象进行赋值,并在构造方法上加上
@Autowired
注解。
代码情景:
创建了一个 UserService 类,里面包含了 name 和 password 两个字段,将这个类存储到 Spring 中。在上述代码中的 UserController 类中通过 @Autowired 注解来将 UserService 这个对象注入到该类中,并创建了一个 getUserService 方法打印获取到的 Bean 对象。而 UserController 类本身又在启动类中被获取后调用 getUserService 方法。
示例代码:
使用 @Autowired 注解
@ControllerpublicclassUserController{privateUserService userService;@AutowiredpublicUserController(UserService userService){this.userService = userService;}publicvoidgetUSerService(){System.out.println(userService);}}
3.2.4 Setter 注入 Bean 对象
在需要的类中先创建一个要被注入的对象,然后创建该类的 Setter 方法,将要被注入的对象进行赋值,并在 Setter方法上加上
@Autowired
或
@Resource
注解。
代码情景:
创建了一个 UserService 类,里面包含了 name 和 password 两个字段,将这个类存储到 Spring 中。在上述代码中的 UserController 类中通过 @Autowired 注解来将 UserService 这个对象注入到该类中,并创建了一个 getUserService 方法打印获取到的 Bean 对象。而 UserController 类本身又在启动类中被获取后调用 getUserService 方法。
示例代码1: 使用
@Autowired
注解
@ControllerpublicclassUserController{privateUserService userService;@AutowiredpublicvoidsetUserService(UserService userService){this.userService = userService;}publicvoidgetUSerService(){System.out.println(userService);}}
示例代码2: 使用
@Resource
注解
@ControllerpublicclassUserController{privateUserService userService;@ResourcepublicvoidsetUserService(UserService userService){this.userService = userService;}publicvoidgetUSerService(){System.out.println(userService);}}
3.2.5 三种注入方式的优缺点
注入方式优点缺点属性注入写法最简单,可读性高。只适应 IoC 容器,移植性差。构造方法注入可以适应非 IoC 容器,通用性好,代码移植性高。如果有多个注入,代码会显得比较臃肿。Setter 注入只有对象是需要被注入时,才会注入依赖,而不是在这个类初始化的时候就注入。可以适应非 IoC 容器,通用性好,代码移植性高。不能将对象设为 final。
3.2.6 同一类型多个 @Bean 报错问题
当出现多个 Bean 返回的对象类型都相同时,如果注入的注解不设置具体注入的哪个 Bean,则会报错。对于这个问题有两种解决方式:
- 对于 @Autowired 注解,可以搭配一个 @Qualifuer 来指定要注入的 Bean
- 对于 @Resource 注解,可以设置一个 name 属性来指定要注入的 Bean
代码情景:
创建了一个 User 类,里面包含了 name 和 password 两个字段。再创建一个 UserService 类这个类通过两个 @Bean 注解的方法注册两个类型相同,名字不同的 Bean 存储到 Spring 中。在上述代码中的 UserController 类中通过 @Autowired 注解来将 User 这个对象注入到该类中,并创建了一个 printUser 方法打印获取到的 Bean 对象。而 UserController 类本身又在启动类中被获取后调用 printUser 方法。
示例代码1: 对于
@Autowired
的解决方式
@ControllerpublicclassUserController{@Autowired@Qualifier(value ="user1")privateUser user;publicvoidprintUser(){System.out.println(user);}}
示例代码2: 对于
@Resource
的解决方式
@ControllerpublicclassUserController{@Resource(name ="user2")privateUser user;publicvoidprintUser(){System.out.println(user);}}
4. Bean 的作用域
当在 Spring 中定义一个 bean 时,你可以声明该 bean 的作用域。例如,为了强制 Spring 在每次需要时都产生一个新的 bean 实例,你应该声明 bean 的作用域的属性为 prototype,即多例作用域。同理,如果你想让 Spring 在每次需要时都返回同一个 bean 实例,你应该声明 bean 的作用域的属性为 singleton,即单例作用域。
在 Spring 中 Bean 默认情况下是单例状态,即所有人拿到的 Bean 都是同一个对象。设置为单例减少了新生成实例的消耗、减少了 JVM 垃圾回收、能够快速获取到 Bean,也就是说能够很大程度的提高性能。
4.1 Bean 的六种作用域
Spring 容器在初始化一个 Bean 实例时,同时会指定该实例的作用域。Spring 有六种作用域,前两种是基于 整个 Spring 生态生效的,后四种是基于 Spring MVC 生效的。
4.1.1 单例作用域 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 默认选择该作⽤域
4.1.2 原型作用域/多例作用域 prototype
- 官⽅说明: Scopes a single bean definition to any number of object instances.
- 描述: 每次对该作⽤域下的 Bean 的请求都会创建新的实例:获取 Bean(即通过 applicationContext.getBean 等⽅法获取)及装配Bean(即通过 @Autowired 注⼊)都是新的对象实例。
- 场景: 通常有状态的 Bean 使⽤该作⽤域
4.1.3 请求作用域 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
- 备注: 限定 Spring MVC 中使⽤
4.1.4 会话作用域 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,⽐如:记录⼀个⽤户的登陆信息
- 备注: 限定 Spring MVC 中使⽤
4.1.5 全局作用域 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 中使⽤
4.1.6 HTTP WebSocket 作用域 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 中使⽤
4.2 单例作用域和全局作用域的区别
- singleton 是 Spring Core 的作用域;application 是 Spring Web 的作用域
- singleton 作用于 IoC 容器;application 作用于 Servlet 容器
4.3 设置作用域的方法
设置 Bean 的作用域有两种方式
- 方式一:在 Spring 配置文件中,对于注册的每一个 Bean 可以增加一个 scope 属性来设置 Bean 的作用域
- 方式二:通过
@Scope
注解就可以声明 Bean 的作用域,它既可以修饰方法也可以修饰类
示例代码1: 方式一
代码示例2: 方式二
5. Bean 的原理分析
5.1 Spring 的执行流程
Spring 的执行流程可以分为以下几步:
- 启动 Spring 容器(在 main 方法中启动)
- 实例化 Bean(加载 Spring 配置文件,实例化并申请内存)
- 将 Bean 注册到 Spring 中(将添加了和 Spring 注册 Bean 相关的注解的对象存储到容器中)
- 将 Bean 装配到需要的类中
5.2 Bean 的生命周期
Bean 的生命周期可以分为以下几个部分:
- 实例化 Bean(为 Bean 分配内存空间)
- 设置 Bean 属性(依赖注入和装配,必须在初始化 Bean 之前,因为初始化的时候可能会用到 Bean 对象)
- 初始化 Bean(初始化 Bean 时会按顺序执行以下几小步)- 实现各种 Aware 通知的方法,如
BeanNameAware
、BeanFactoryAware
、ApplicationContextAware
的接口方法- 执行BeanPostProcessor
初始化前置方法- 执行@PostConstruct
初始化方法,依赖注入操作之后被执行- 判断是否继承了InitializingBean
接口(调用afterPropertiesSet
方法)- 执行自己的init-method
初始化方法- 执行BeanPostProcessor
初始化后置方法 - 使用 Bean
- 销毁 Bean- 执行
@PreDestory
销毁前的方法- 判断是否继承了DisposableBean
接口(调用- 执行自己的destory-method
销毁前的方法
接下来可以通过一段代码来验证 Bean 的生命周期的过程,示例代码如下:
@ControllerpublicclassBeanLifeimplementsBeanNameAware,InitializingBean,DisposableBean{@OverridepublicvoidsetBeanName(String s){System.out.println("执行 BeanNameAware 接口的方法:"+ s);}@PostConstructpublicvoidpostConstruct(){System.out.println("执行 @PostConstruct");}@OverridepublicvoidafterPropertiesSet()throwsException{System.out.println("继承了 InitializingBean");}publicvoidmyInit(){System.out.println("执行 init-method");}@PreDestroypublicvoidpreDestroy(){System.out.println("执行 @PreDestroy");}publicvoiddestroy(){System.out.println("继承了 DisposableBean");}publicvoidmyDestroy(){System.out.println("执行 destroy-method");}}
版权归原作者 吞吞吐吐大魔王 所有, 如有侵权,请联系我们删除。