目录
[1.1 概念](#1.1 概念)
[1.2 Bean的作用域](#1.2 Bean的作用域)
[1.3 @Resource 和 @Autowired](#1.3 @Resource 和 @Autowired)
[1.3.1 @Autowired](#1.3.1 @Autowired)
[1.3.2 @Resource](#1.3.2 @Resource)
[1.4 观察作用域](#1.4 观察作用域)
[2.1 代码演示](#2.1 代码演示)
[3.1 Spring加载Bean](#3.1 Spring加载Bean)
[3.2 解决方案](#3.2 解决方案)
[3.2.1 @ComponentScan](#3.2.1 @ComponentScan)
[3.2.2 @Import](#3.2.2 @Import)
[3.2.3 第三方依赖提供注解](#3.2.3 第三方依赖提供注解)
1.Bean的作用域
1.1 概念
在SpringloC&DI阶段,我们学习了Spring是如何帮助我们管理对象的.
-
通过
@controller
,@Service
,@Repository
,@Component
,@Configuration
,@Bean
来声明Bean对象。 -
通过
ApplicationContext
或者BeanFactory
来获取对象 -
通过
@Autowired
,Setter
方法或者构造方法等来为应用程序注入所依赖的Bean对象
1.2 Bean的作用域
在Spring中支持6种作用域,后4种在SpringMVC环境才生效
-
singleton:单例作用域
-
prototype:原型作用域(多例作用域)
-
request:请求作用域
-
session:会话作用域
-
Application:全局作用域
-
websocket:HTTP WebSocket 作用域
|-------------|--------------------------------------------|
| 作用域 | 说明 |
| singleton | 每个 Spring IoC 容器内同名称的 bean 只有一个实例(单例)(默认) |
| prototype | 每次使用该 bean 时会创建新的实例(非单例) |
| request | 每个 HTTP 请求生命周期内,创建新的实例(web 环境中,了解) |
| session | 每个 HTTP Session 生命周期内,创建新的实例(web 环境中,了解) |
| application | 每个 ServletContext 生命周期内,创建新的实例(web 环境中,了解) |
| websocket | 每个 WebSocket 生命周期内,创建新的实例(web 环境中,了解) |
参考文档:https://docs.spring.io/spring-framework/reference/core/beans/factory-scopes.html
java
package com.example.principle.mapper;
import lombok.Getter;
import lombok.Setter;
@Getter @Setter
public class Dog {
private String name;
}
java
package com.example.principle.config;
import com.example.principle.mapper.Dog;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
import org.springframework.web.context.annotation.ApplicationScope;
import org.springframework.web.context.annotation.RequestScope;
import org.springframework.web.context.annotation.SessionScope;
@Configuration
public class DogConfig {
@Bean
public Dog dog() {
Dog dog = new Dog();
dog.setName("汪汪");
return dog;
}
@Bean
@Scope(ConfigurableBeanFactory.SCOPE_SINGLETON)
public Dog singleDog() {
Dog dog = new Dog();
return dog;
}
@Bean
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public Dog protoDog() {
Dog dog = new Dog();
return dog;
}
@Bean
@RequestScope
public Dog requestDog() {
Dog dog = new Dog();
return dog;
}
@Bean
@SessionScope
public Dog sessionDog() {
Dog dog = new Dog();
return dog;
}
@Bean
@ApplicationScope
public Dog applicationDog() {
Dog dog = new Dog();
return dog;
}
}
@RequestScope
等同于@Scope(value=WebApplicationContext.SCOPE_REQUEST,proxyMode=ScopedProxyMode.TARGET_CLASS)
@SessionScope
等同于@Scope(value=WebApplicationContext.SCOPE_SESSION,proxyMode=ScopedProxyMode.TARGET_CLASS)
@ApplicationScope
等同于@Scope(value=WebApplicationContext.SCOPE_APPLICATION,proxyMode=ScopedProxyMode.TARGET_CLASS)
proxyMode用来为springbean设置代理.proxyMode=ScopedProxyMode.TARGET_CLASS
表示这个Bean基于CGLIB实现动态代理.++Request,session和application作用域的Bean需要设置proxyMode++
测试不同作用域的Bean取到的对象是否一样
java
package com.example.principle.controller;
import com.example.principle.mapper.Dog;
import jakarta.annotation.Resource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/scope")
public class DogController {
@Resource(name = "singleDog")
private Dog singleDog;
@Resource(name = "protoDog")
private Dog protoDog;
@Resource(name = "requestDog")
private Dog requestDog;
@Resource(name = "sessionDog")
private Dog sessionDog;
@Resource(name = "applicationDog")
private Dog applicationDog;
@Autowired
ApplicationContext context;
@RequestMapping("/getSingleDog")
public String getSingleDog() {
//从context中获取的对象
Dog dog = (Dog) context.getBean("singleDog");
return "注入对象:"+ singleDog.toString() + ",context Dog:"+ dog.toString();
}
@RequestMapping("/protoDog")
public String protoDog() {
Dog dog = (Dog) context.getBean("protoDog");
return "注入对象:"+ protoDog.toString() + ",context Dog:"+ dog.toString();
}
@RequestMapping("/request")
public String request(){
//从context中获取对象
Dog dog = (Dog)context.getBean("requestDog");
return "注入对象:"+ requestDog.toString() + ",context Dog:"+ dog.toString();
}
@RequestMapping("/session")
public String session(){
//从context中获取对象
Dog dog = (Dog)context.getBean("sessionDog");
return "注入对象:"+ sessionDog.toString() + ",context Dog:"+ dog.toString();
}
@RequestMapping("/application")
public String application(){
//从context中获取对象
Dog dog = (Dog)context.getBean("applicationDog");
return "注入对象:"+ applicationDog.toString() + ",context Dog:"+ dog.toString();
}
}
每个请求都获取两次Bean
@Autowired和applicationContext.getBean("singleDog")都是从Spring容器中获取对象.
1.3 @Resource
和 @Autowired
核心区别:@Autowired
是"ByType"起手,不行再"ByName";而 @Resource
是"ByName"起手,不行再"ByType"。
|-------------|---------------------------------|-------------------------------------------|
| 特性 | @Autowired (Spring 专属) | @Resource (JSR-250 标准) |
| 来源 | Spring 框架定义 | Java EE 的 JSR-250 标准提供,Spring 支持 |
| 默认注入方式 | 按类型(ByType) | 按名称(ByName) |
| 名称指定 | 需要与 @Qualifier("beanName") 配合使用 | 自带 name 属性(如 @Resource(name="singleDog")) |
| 是否支持按类型 | 是(核心方式) | 是(当未指定 name 且按名称找不到唯一bean时) |
| 是否支持按名称 | 是(需配合 @Qualifier) | 是(主要方式) |
| required 属性 | 有(@Autowired(required=false)) | 无(如果找不到且未指定name,会报错) |
1.3.1 @Autowired
-
机制:它首先根据类型在Spring IoC容器中查找匹配的Bean。
-
如果找到一个,直接注入。
-
如果找到多个相同类型的Bean(例如有多个
UserService
的实现类),Spring会报错NoUniqueBeanDefinitionException
。 -
解决多个Bean的冲突:必须使用
@Qualifier("specificBeanName")
注解来额外指定Bean的名称,从而切换到按名称注入。
-
java
// 1. 按类型注入
@Autowired
private SomeService someService;
// 2. 遇到多个同类型Bean,配合@Qualifier按名称注入
@Autowired
@Qualifier("mainDatabaseService") // 指定注入名为'mainDatabaseService'的Bean
private DatabaseService databaseService;
1.3.2 @Resource
-
机制:它首先根据名称(由
name
属性指定)在容器中查找匹配的Bean。-
如果指定了
name
(如@Resource(name="singleDog")
),它就只按这个名称找。 -
如果没有指定
name
,它会将字段名或属性名作为要查找的Bean名称(例如,字段名为userDao
,它就找名为userDao
的Bean)。 -
如果按名称找不到,它才会回退到按类型进行查找。
-
java
// 1. 明确指定名称(你例子中的方式)
@Resource(name = "singleDog")
private Person specialPerson;
// 2. 不指定name,默认使用字段名(singleDog)作为Bean名称查找
@Resource
private Person singleDog; // 会查找名为'singleDog'的Bean
// 3. 如果没找到名为'singleDog'的Bean,则会回退到按Person类型查找
// 如果此时容器中只有一个Person类型的Bean,注入也会成功
1.4 观察作用域
- 单例作用域
多次访问,得到的都是同一个对象,并且@Autowired
和applicationContext.getBean()
也是同一个对象.

- 多例作用域
观察ContextDog,每次获取的对象都不一样(注入的对象在Spring容器启动时,就已经注入了,所以多请求也不会发生变化)

- 请求作用域
在一次请求中,@Autowired
和applicationContext.getBean()
也是同一个对象.但是每次请求都会重新创建对象

- 会话作用域
在一个session中,多次请求,获取到的对象都是同一个.
换一个浏览器访问,发现会重新创建对象.(另一个Session)

- Application作用域
在一个应用中,多次访问都是同一个对象

2.Bean的生命周期
生命周期指的是一个对象从诞生到销毁的整个生命过程,我们把这个过程就叫做一个对象的生命周期
Bean的生命周期分为以下5个部分:
-
实例化(为Bean分配内存空间)
-
属性赋值(Bean注入和装配,比如@AutoWired)
-
初始化
-
执行各种通知,如
BeanNameAware
,BeanFactoryAware
,ApplicationContextAware
的接口方法. -
执行初始化方法
-
xml定义
init-method
-
使用注解的方式
@PostConstruct
-
执行初始化后置方法(
BeanPostProcessor
)
-
-
-
使用Bean
-
销毀Bean
- 销毁容器的各种方法,如
@PreDestroy
,DisposableBean
接口方法,destroy-method
.
- 销毁容器的各种方法,如
实例化和属性赋值对应构造方法和setter方法的注入,初始化和销毁是用户能自定义扩展的两个阶段,
可以在实例化之后,类加载完成之前进行自定义"事件"处理。
比如我们现在需要买一栋房子,那么我们的流程是这样的:
1.先买房(实例化,从无到有)
2.装修(设置属性)
3.买家电,如洗衣机,冰箱,电视,空调等([各种]初始化,可以入住);
4.入住(使用 Bean)
5.卖房(Bean销毁)

2.1 代码演示
java
package com.example.principle.component;
import com.example.principle.mapper.Dog;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.PreDestroy;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class BeanLifeComponent implements BeanNameAware {
private Dog dog;
//1.创建实例执行构造方法
public BeanLifeComponent() {
System.out.println("执行构造函数...");
}
//2.设置属性(执行Autowired赋值)
@Autowired
public void setDog(Dog dog) {
this.dog = dog;
System.out.println("使用setter方法, 完成属性装配...");
}
//4.初始化方法
@PostConstruct
public void init(){
System.out.println("执行init方法...");
}
//3.Aware 接口回调,该方法提供了该 Bean 在 Spring 容器中的 ID/名称
@Override
public void setBeanName(String name) {
System.out.println("执行setBeanName, name:"+name);
}
//5.使用Bean
public void use(){
System.out.println("执行use方法....");
}
//销毁Bean
@PreDestroy
public void destroy(){
System.out.println("执行destroy方法...");
}
}
singleton
作用域的范围是单个 Spring ApplicationContext
(IoC 容器),而 application
作用域的范围是整个 Web 应用程序的 ServletContext
。
3.SpringBoot自动配置
3.1 Spring加载Bean
java
package com.example.autoconfig;
import org.springframework.stereotype.Component;
@Component
public class BiteConfig {
public void use(){
System.out.println("bite config....");
}
}
java
@SpringBootTest
class SpringPrincipleApplicationTests {
@Autowired
private ApplicationContext applicationContext;
@Test
void contextLoads() {
//从容器中查找 "名称为 biteConfig 且类型为 BiteConfig" 的 Bean
BiteConfig biteConfig = context.getBean(BiteConfig.class, "biteConfig");
System.out.println(biteConfig);
}
}

观察日志: No qualifying bean of type 'com.bite.autoconfig.BiteConfig' available
没有com.bite.autoconfig.BiteConfig这个类型的Bean
3.2 解决方案
3.2.1 @ComponentScan
java
@SpringBootApplication
@ComponentScan("com.example.autoconfig")
public class SpringPrincipleApplication {
public static void main(String[] args) {
SpringApplication.run(SpringPrincipleApplication.class, args);
}
}
可以指定扫描多个包
@ComponentScan({"com.example.autoconfig","com.example.demo"})

3.2.2 @Import
@Import
导入主要有以下几种形式:
-
导入类
java@SpringBootApplication @Import({BiteConfig.class, BiteConfig2.class}) public class SpringPrincipleApplication { public static void main(String[] args) { SpringApplication.run(SpringPrincipleApplication.class, args); } }
-
导入ImportSelector接口实现类
javapackage com.example.autoconfig; import org.springframework.context.annotation.ImportSelector; import org.springframework.core.type.AnnotationMetadata; public class MyImportSelector implements ImportSelector { @Override public String[] selectImports(AnnotationMetadata importingClassMetadata) { //需要导入的全限定类名 return new String[] {"com.example.autoconfig.BiteConfig","com.example.autoconfig.BiteConfig2"}; } }
启动类
java@SpringBootApplication @Import(MyImportSelector.class) public class SpringPrincipleApplication { public static void main(String[] args) { SpringApplication.run(SpringPrincipleApplication.class, args); } }
3.2.3 第三方依赖提供注解
依赖中有哪些Bean,使用时需要配置哪些bean,第三方依赖最清楚,那能否由第三方依赖来做这件事呢?
-
比较常见的方案就是第三方依赖给我们提供一个注解,这个注解一般都以@EnableXxxx开头的注解,注解中封装的就是@Import注解
javapackage com.example.autoconfig; import org.springframework.context.annotation.Import; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Import(MyImportSelector.class) public @interface EnableBiteConfig { }
java
@SpringBootApplication
@EnableBiteConfig
public class SpringPrincipleApplication {
public static void main(String[] args) {
SpringApplication.run(SpringPrincipleApplication.class, args);
}
}
4.总结
SpringBoot自动配置原理的大概流程如下:
当SpringBoot程序启动时,会加载配置文件当中所定义的配置类,通过@Import注解将这些配置类全部加载到Spring的IOC容器中,交给IOC容器管理。