- 🎥 个人主页:Dikz12
- 🔥个人专栏:Spring学习之路
- 📕格言:吾愚多不敏,而愿加学
- 欢迎大家👍点赞✍评论⭐收藏
目录
Bean的作用域
代码实现
观察Bean的作用域
Bean的生命周期
Spring Boot自动配置
原理分析
1. 元注解.
2. @SpringBootConfiguration.
3. @EnableAutoConfiguration (开启⾃动配置).
4.@ComponentScan (包扫描)
总结
Bean的作用域
- 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
代码实现
定义几个不同作用域Bean.
@Configuration
public class BeanConfig {
@Scope("singleton")
@Bean
public User singleUser() {
return new User();
}
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
@Bean
public User prototypeUser() {
return new User();
}
//请求作用域
@RequestScope
@Bean
public User requestUser() {
return new User();
}
//会话作用域
@SessionScope
@Bean
public User sessionUser() {
return new User();
}
//全局作用域
@ApplicationScope
@Bean
public User applicationUser() {
return new User();
}
}
@RestController
@RequestMapping("/scope")
public class BeanScopeController {
@Autowired
private ApplicationContext context;
@Resource(name = "singleUser")
private User singleUser;
@Resource(name = "prototypeUser")
private User prototypeUser;
@Resource(name = "requestUser")
private User requestUser;
//单例作用域
@RequestMapping("/single")
public String single(){
/**
* 1. 从context获取对象
* 2. 属性注入获取对象
*/
User user = (User) context.getBean("singleUser");
return "context获取的对象:"+user+",属性注入获取的对象:"+singleUser;
}
//原型作用域(多例)
@RequestMapping("/prototype")
public String prototypeUser(){
/**
* 1. 从context获取对象
* 2. 属性注入获取对象
*/
User user = (User) context.getBean("prototypeUser");
// 栈上的引用地址
// return "context获取的对象:"+user+",属性注入获取的对象:"+prototypeUser;
//打印内存地址
return "context获取的对象:"+System.identityHashCode(user)+",属性注入获取的对象:"+System.identityHashCode(prototypeUser);
}
//请求作用域
@RequestMapping("/request")
public String requestUser() {
User user = (User) context.getBean("requestUser");
// return "context获取的对象:"+System.identityHashCode(user)+",属性注入获取的对象:"+System.identityHashCode(requestUser);
System.out.println(user.toString());
System.out.println(user.getClass().getName() + "@" + Integer.toHexString(user.hashCode()));
return "context获取的对象:"+user+",属性注入获取的对象:"+requestUser;
}
}
观察Bean的作用域
单例作用域 :
多次访问, 得到的都是同⼀个对象, 并且 @Autowired 和 applicationContext.getBean() 也是同⼀个对象.
多例作用域 :
请求作用域:
Bean的生命周期
⽣命周期指的是⼀个对象从诞⽣到销毁的整个⽣命过程,我们把这个过程就叫做⼀个对象的⽣命周期.
1. 实例化(为Bean分配内存空间)
2. 属性赋值(Bean注⼊和装配,⽐如 @AutoWired )
3. 初始化
a. 执⾏各种通知,如BeanNameAware ,BeanFactoryAware ,ApplicationContextAware 的接⼝⽅法.
b. 执⾏初始化⽅法
▪ xml定义 init-method
▪ 使⽤注解的⽅式 @PostConstruct
▪ 执⾏初始化后置⽅法( BeanPostProcessor )
4. 使⽤Bean.
5.销毁Bean.
实例化和属性赋值对应构造⽅法和setter⽅法的注⼊. 初始化和销毁是⽤⼾能⾃定义扩展的两个阶段,可以在实例化之后,类加载完成之前进⾏⾃定义"事件"处理.
⽐如我们现在需要买⼀栋房⼦,那么我们的流程是这样的:
- 先买房(实例化,从⽆到有)
- 装修(设置属性)
- 买家电,如洗⾐机,冰箱,电视,空调等([各种]初始化,可以⼊住);
- ⼊住(使⽤ Bean)
- 卖房(Bean 销毁)
执行流程如下:
Spring Boot自动配置
SpringBoot的⾃动配置就是当Spring容器启动后,⼀些配置类,bean对象等就⾃动存⼊到了IoC容器中,不需要我们⼿动去声明,从⽽简化了开发,省去了繁琐的配置操作.
SpringBoot⾃动配置,就是指SpringBoot是如何将依赖jar包中的配置类以及Bean加载到Spring IoC容器中的.
原理分析
SpringBoot是如何帮助我们做的呢?⼀切的来⾃起源SpringBoot的启动类开始.
@SpringBootApplication 标注的类就是SpringBoot项⽬的启动类.
@SpringBootApplication
public class SpringIocApplication {
public static void main(String[] args) {
//获取Spring上下⽂对象
ApplicationContext context = SpringApplication.run(SpringIocApplication.class, args);
//从Spring上下⽂中获取对象
BeanLifeComponent beanLifeComponent = context.getBean(BeanLifeComponent.class);
beanLifeComponent.use();
}
}
这个类和普通类唯⼀的区别就是 @SpringBootApplication 注解,这个注解也是SpringBoot实现
⾃动配置的核⼼.
@SpringBootApplication 是⼀个组合注解,注解中包含了:
1. 元注解.
JDK中提供了4个标准的⽤来对注解类型进⾏注解的注解类,我们称之为meta-annotation(元注
解),他们分别是:
• @Target描述注解的使⽤范围(即被修饰的注解可以⽤在什么地⽅)
• @Retention描述注解保留的时间范围
• @Documented描述在使⽤javadoc⼯具为类⽣成帮助⽂档时是否要保留其注解信息
• @Inherited使被它修饰的注解具有继承性(如果某个类使⽤了被@Inherited修饰的注解,则
其⼦类将⾃动具有该注解)
2. @SpringBootConfiguration.
⾥⾯就是@Configuration,标注当前类为配置类,其实只是做了⼀层封装改了个名字⽽已.
(@Indexed注解,是⽤来加速应⽤启动的,不⽤关⼼)
3. @EnableAutoConfiguration (开启⾃动配置).
看下@EnableAutoConfiguration 注解的实现:
这个注解包含两部分:
1. @Import({AutoConfigurationImportSelector.class}) 。
使⽤@Import注解,导⼊了实现ImportSelector接⼝的实现类 .
- selectImports() ⽅法底层调⽤ getAutoConfigurationEntry() ⽅法,获取可⾃动配置的
- 配置类信息集合.
- getAutoConfigurationEntry() ⽅法通过调⽤ getCandidateConfigurations(annotationMetadata, attributes) ⽅法获取在配置⽂件中配置的所有⾃动配置类的集合.
2. @AutoConfigurationPackage.
这个注解主要是导⼊⼀个配置⽂件 AutoConfigurationPackages.Registrar.class.
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
Registrar() {
}
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
AutoConfigurationPackages.register(registry, (String[])(new PackageImports(metadata)).getPackageNames().toArray(new String[0]));
}
public Set<Object> determineImports(AnnotationMetadata metadata) {
return Collections.singleton(new PackageImports(metadata));
}
}
(String[])(new PackageImports(metadata)).getPackageNames().toArray(new String[0])) : 当前启动类的包名.
所以,Registrar实现了 ImportBeanDefinitionRegistrar 类,就可以被注解@Import导⼊到spring容器⾥.
4.@ComponentScan (包扫描)
可以通过 basePackageClasses 或 basePackages 来定义要扫描的特定包,如果没有定义
特定的包,将从声明该注解的类的包开始扫描,这也是为什么SpringBoot项⽬声明的注解类必须要在启动类的⽬录下.
excludeFilters⾃定义过滤器,通常⽤于排除⼀些类,注解等.
总结
SpringBoot⾃动配置原理的⼤概流程如下: