目录
一.Bean的作用域
1.1 介绍
Bean的作用域是指Bean在Spring框架中的某种行为模式
,比如单例作用域表示Bean在整个Spring中只有一份,全局共享,当其他人修改了Bean的值,那么另一个人就会读取到修改后的值,Bean的这种行为模式就称之为Bean的作用域
1.2 作用域分类
在Spring中支持六种作用域,后四种在Spring MVC环境中才生效
作用域 | 说明 |
---|---|
单例作用域 singleton | Bean的默认作用域 ,每个Spring IoC容器内同名称的bean只有一个实例(单例) |
原型/多例作用域 prototype | 每次使用该bean时会创建新的实例 (非单例) |
请求作用域 request | 每个HTTP请求生命周期内,创建新的实例(web环境中) |
会话作用域 session | 每个HTTP Session生命周期内,创建新的实例(web环境中) |
全局作用域 application | 每个ServletContext生命周期内,创建新的实例(web环境中) |
HTTP WebSocket作用域 websocket | 每个WebSocket生命周期内,创建新的实例(web环境中) |
//创建不同作用域的Bean
import org.example.springprinciple.model.Dog;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.*;
import org.springframework.web.context.annotation.*;
@Configuration
public class DogConfig {
@Bean
@Scope(scopeName = ConfigurableBeanFactory.SCOPE_SINGLETON)
public Dog singletonDog(){
return new Dog();
}
@Bean
@Scope(scopeName = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public Dog prototypeDog(){
return new Dog();
}
@Bean
@RequestScope
public Dog requestDog(){
return new Dog();
}
@Bean
@SessionScope
public Dog sessionDog(){
return new Dog();
}
@Bean
@ApplicationScope
public Dog applicationDog(){
return new Dog();
}
}
//DogController类(接口,提供web环境)
import org.example.springprinciple.model.Dog;
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("/dog")
public class DogController {
@Autowired
private ApplicationContext context;
@Autowired
private Dog singletonDog;
@Autowired
private Dog prototypeDog;
@Autowired
private Dog requestDog;
@Autowired
private Dog sessionDog;
@Autowired
private Dog applicationDog;
@RequestMapping("/singleton")
public String singleton(){
Dog contextDog = context.getBean("singletonDog",Dog.class);
return "contextDog:" + contextDog + ",autowired注入:" + singletonDog;
}
@RequestMapping("/prototype")
public String prototype(){
Dog contextDog = context.getBean("prototypeDog",Dog.class);
return "contextDog:" + contextDog + ",autowired注入:" + prototypeDog;
}
@RequestMapping("/request")
public String request(){
Dog contextDog = context.getBean("requestDog",Dog.class);
return "contextDog:" + contextDog + ",autowired注入:" + requestDog;
}
@RequestMapping("/session")
public String sessionDog(){
Dog contextDog = context.getBean("sessionDog",Dog.class);
return "contextDog:" + contextDog + ",autowired注入:" + sessionDog;
}
@RequestMapping("/application")
public String applicationDog(){
Dog contextDog = context.getBean("applicationDog",Dog.class);
return "contextDog:" + contextDog + ",autowired注入:" + applicationDog;
}
}
二.Bean的生命周期
生命周期指的是一个对象从诞生到销毁的整个生命过程,Bean的生命周期分为以下五个部分:
- 实例化:为Bean分配内存空间
- 属性赋值:Bean注入和装配,比如
@Autowired
- 初始化:(1) 执行各种通知(如
BeanNameAware
,BeanFactoryAware
,ApplicationContextAware
的接口方法)(2) 执行初始化方法(xml定义init-method
,使用注解的方式@PostConstruct
,执行初始化后置方法BeanPostProcessor
) - 使用Bean
- 销毁Bean:使用销毁容器的各种方法,如
@PreDestroy
,DisposableBean
接口方法,destroy-method
三.SpringBoot自动配置
3.1 介绍
SpringBoot的自动配置就是当Spring容器启动后,一些配置类和Bean对象都能够就自动存入
到IoC容器中,不需要我们手动去声明,从而简化了开发,省去繁琐的配置操作
3.2 Spring加载Bean
Spring通过五大注解和@Bean注解可以把Bean加载到Spring IoC容器中,但前提是这些注解类需要和SpringBoot启动类(@SpringBootApplication
注释的类)在同一个目录下
,才能够生效
package org.example.test;//启动类位于org.example.springprinciple,与该类不在同一目录
import org.springframework.stereotype.Component;
@Component
public class TestConfig {
public void config(){
System.out.println("config");
}
}
//测试类
import org.example.test.TestConfig;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.ApplicationContext;
@SpringBootTest
class TestConfigTest {
@Autowired
private ApplicationContext applicationContext;
@Test
void config() {
TestConfig testConfig = applicationContext.getBean("testConfig",TestConfig.class);
System.out.println("获取到的Bean:" + testConfig);
}
}
可以看到当注解类和SpringBoot启动类不在同一目录下
时,SpringBoot没有扫描到TestConfig这个类,所以不会把Bean加载到Spring IoC容器中,那么如何解决这些问题呢?同时,当我们引入第三方Jar包
时,第三方Jar包中的代码目录肯定也不在启动器目录下,怎么让Spring帮我们管理这些Bean呢?带着这两个疑问,我们来看下面的解决方案
3.2.1 @ComponentScan
通过@ComponentScan
注解,可以指定Spring的扫描路径
,从而让Spring能够扫描到需要加载Bean的类。虽然能够解决注解类和SpringBoot启动类不在同一目录的问题,但是对于第三方Jar包,如果每个包都要用到@ComponentScan
,就会变得很繁琐,因而Spring没有采取这种方式
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;
@SpringBootApplication
//添加扫描路径
//可以添加多个路径,如@ComponentScan({包1,包2})
@ComponentScan("org.example")
public class SpringPrincipleApplication {
public static void main(String[] args) {
SpringApplication.run(SpringPrincipleApplication.class, args);
}
}
3.2.2 @Import 导入类
通过@Import
注解,可以导入具体的类,让Spring去加载这些类的Bean。这种方法同样能解决不在同一目录下的问题,但对于第三方Jar包也是过于繁琐,Spring没有采取这种方式
import org.example.test.TestConfig;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Import;
//可以导入多个类,如@Import({类1,类2})
@Import(TestConfig.class)
@SpringBootApplication
public class SpringPrincipleApplication {
public static void main(String[] args) {
SpringApplication.run(SpringPrincipleApplication.class, args);
}
}
3.2.3 @Import 导入ImportSelector接口实现类
同样使用了@Import
注解,先实现了ImportSelect接口,再使用@Import
导入到自定义注解中进行封装,添加到启动类上,这种方式有效地减少了导入的繁琐程度,Spring采取了与之类似的思想
//MyImportSelector类实现ImportSelector接口
import org.springframework.context.annotation.ImportSelector;
import org.springframework.core.type.AnnotationMetadata;
public class MyImportSelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
//String数组中添加要导入的类名
return new String[]{"org.example.test.TestConfig"};
}
}
//自定义注解导入MyImportSelector,进行封装
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 EnableMyConfig {
}
//启动类添加自定义注解
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@EnableMyConfig
@SpringBootApplication
public class SpringPrincipleApplication {
public static void main(String[] args) {
SpringApplication.run(SpringPrincipleApplication.class, args);
}
}
3.3 @SpringBootApplication 源码简单分析
3.3.1 整体分析
SpringBoot到底是怎么管理第三方Jar包的Bean,怎么自动配置,一切的起源都来自于SpringBoot启动类中的@SpringBootApplication
注解。@SpringBootApplication
是一个组合注解,注解中包含了:
- 元注解:
@Target
描述注解的范围,@Retention
描述注解保留的时间范围,@Documented
描述在使用javadoc工具为类生成帮助文档时是否要保留其注解信息,@Inherited
使被修饰的注解具有继承性(如果一个类使用了被@Inherited
修饰的注解,那么其子类自动具有该注解) @SpringBootConfiguration
:标注当前类为配置类@EnableAutoConfiguration
:开启自动配置,下面会详细讲@ComponentScan
:默认扫描启动类的目录路径
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(
excludeFilters = {@Filter(
type = FilterType.CUSTOM,
classes = {TypeExcludeFilter.class}
), @Filter(
type = FilterType.CUSTOM,
classes = {AutoConfigurationExcludeFilter.class}
)}
)
public @interface SpringBootApplication {
//源码方法体省略
}
3.3.2 @EnableAutoConfiguration
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
//源码方法体省略
}
@EnableAutoConfiguration
主要包含两个部分:
@Import({AutoConfigurationImportSelector.class})
,使用@Import
注解,导入了实现ImportSelector接口的实现类,获取自动配置的配置类信息
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
//源码其他方法省略
//ImportSelector接口实现类
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!this.isEnabled(annotationMetadata)) {
return NO_IMPORTS;
} else {
//获取自动配置的配置类信息
AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
}
//获取自动配置的配置类信息getAutoConfigurationEntry方法
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
if (!this.isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
} else {
AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
//获取配置文件中配置的所有自动配置类的集合
List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
//对集合进行筛选(去除无需导入的类)
configurations = this.removeDuplicates(configurations);
Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
this.checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = this.getConfigurationClassFilter().filter(configurations);
this.fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
}
//获取配置文件中配置的所有自动配置类的集合getCandidateConfigurations方法
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
ImportCandidates importCandidates = ImportCandidates.load(this.autoConfigurationAnnotation, this.getBeanClassLoader());
//获取所有基于META-INF/spring文件中配置类的集合
List<String> configurations = importCandidates.getCandidates();
Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring/" + this.autoConfigurationAnnotation.getName() + ".imports. If you are using a custom packaging, make sure that file is correct.");
return configurations;
}
}
@AutoConfigurationPackage
:将启动类所在包下的所有组件扫描加载到Spring容器中
//@AutoConfigurationPackage注解源码
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import({AutoConfigurationPackages.Registrar.class})
public @interface AutoConfigurationPackage {
String[] basePackages() default {};
Class<?>[] basePackageClasses() default {};
}
//AutoConfigurationPackages.Registrar.class
public abstract class AutoConfigurationPackages {
//源码其他方法省略
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
Registrar() {
}
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
//(String[])(new PackageImports(metadata)).getPackageNames().toArray(new String[0])
//表示当前启动类所在包名
AutoConfigurationPackages.register(registry, (String[])(new PackageImports(metadata)).getPackageNames().toArray(new String[0]));
}
public Set<Object> determineImports(AnnotationMetadata metadata) {
return Collections.singleton(new PackageImports(metadata));
}
}
}