SpringBean
SpringBean的生命周期
IOC流程:
- 通过@ComponentScan注解,解析此路径下的所有类,将获取到的类存放在List<Class>集合中;(类似于SPI机制获取META/INF下的所有类)
- 通过@Component注解,注册BeanDefinition,将其存放在Map<BeanName, BeanDefinition> map中;(根据@Component注解实现按需引入)
SpringBean的生命周期:
- 属性填充
-
调用 * Aware接口 {
实现BeanNameAware,设置setBeanName();实现BeanFactoryAware,设置setBeanFactory();实现ApplicationContextAware,设置setApplicationContext(),传入spring上下文的引用}
- 实现BeanPostProcessor接口的postProcessorBeforeInitialization(),对bean进行加工,例如生成动态代理;
- 初始化Bean:实现InitializingBean接口的afterPropertiesSet() 或者 执行init-method;
- 实现BeanPostProcessor接口的postProcessorAfterInitialization();
- 销毁( 单个 )Bean:实现DisposableBean接口的distroy() 或者 执行 destroy-method;
参考:
具体步骤:
SpringBoot自动装配原理
总结:
- SpringBoot 通过 @EnableAutoConfiguration 开启自动装配,通过 SpringFactoriesLoader.loadFactories() 最终加载META-INF/ spring.factories 中以 EnableAutoConfiguration.class 为 key 的、符合条件的配置类,将其通过 @Conditional 过滤掉不必要的自动配置类,想要其生效必须引入 spring-boot-starter-xxx包实现起步依赖。
- SPI参考:https://github.com/HSshuo/SPI
SpringBoot定义了一套接口规范,这套规范规定:SpringBoot在启动时会扫描外部引用 jar 包中的 META-INF/spring.factories 文件,将文件中配置的类型信息加载到 Spring 容器,并执行类中定义的各种操作。对于外部 jar 来说,只需要按照 SpringBoot 定义的标准,就能将自己的功能装置进 SpringBoot。
源码详解
核心注解:@SpringBootApplication:{
- @EnableAutoConfiguration:启动SpringBoot的自动配置机制;
- @Configuration:允许在上下文中注册额外的bean 或者 导入其他配置类;
- @ComponentScan:加载 value 路径下的所有类也就是默认扫描启动类所在包下所有的类,将被 @Component 注解的 bean 加载到 IOC 容器中。
}
- @EnableAutoConfiguration,是指是通过 @import 引入 AutoConfigurationImportSelector 类实现
@Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @AutoConfigurationPackage @Import({AutoConfigurationImportSelector.class}) public @interface EnableAutoConfiguration { String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration"; Class<?>[] exclude() default {}; String[] excludeName() default {}; }
- AutoConfigurationImportSelector 实现 ImportSelector接口,实现了其中的selectImports方法,调用 SpringFactoriesLoader.loadFactories() ,从META-INF/spring.factories 中筛选出以 EnableAutoConfiguration.class 为 key的、符合条件的配置类,并通过@ConditionalOnXXX过滤不必要的自动配置类。
补充:SpringBoot加载配置类的方式
- 使用注解 @ComponentScan
- 使用注解 @Import{
导入选择器ImportSelector;
导入注册器 ImportBeanDefinitionRegistrar
}
}
参考:
springBean的作用域
- singleton:单例模式、springIOC容器中只会存在一个共享的Bean实例,无论有多少个Bean引用它,始终指向同一个对象;
- prototype:原型模式、每次通过spring容器获取prototype定义的bean时,容器都将创建一个新的bean实例,每个bean实例都有自己的属性和状态;
- request:在一次http请求中,容器后返回该bean的同一个实例;不同的http请求则会产生新的bean,而且bean仅在当前http request内有效;
- session:同request作用域;在一次http session请求中,容器后返回该bean的同一个实例;不同的session请求则会产生新的bean,而且bean仅在当前session内有效;
- global session:在一个全局的http session中,容器会返回该bean的同一个实例,仅在使用portlet context时有效。
单例bean是否为线程安全
需要引申出bean的引用域的概念;
单例bean,所有线程都共享一个单例实例bean,因此存在资源竞争。
- 单例bean是无状态的bean,也就是线程中的操作不会对bean的成员执行查询以外的操作,那么这个单例bean是线程安全的;
- 单例bean是有状态的bean,也就是有数据的存储功能,线程是不安全的;
解决:
- 添加注解@Scope声明为原型的bean,这样每次都会创建一个对象,所以线程之间不涉及到bean的共享,也就不会出现线程安全的问题;
- 一般使用通过ThreadLocal实现对于里面的具体变量的线程安全;
spring循环依赖
spring如何避免循环依赖
- spring是通过递归的方式获取目标bean及其所依赖的bean;
- spring实例化一个bean的时候是分两步进行的,首先实例化目标bean,然后为其注入属性;
spring在实例化一个bean的时候,会首先递归的实例化其依赖的所有bean,直到某个bean没有依赖其他bean,此时就会将该实例返回,然后反递归的将获取到的bean设置为各个上层bean的属性。
spring如何检查循环依赖
spring如何解决循环依赖
现象:构造器注入,A在实例化前,发现构造器参数B也是bean,先去实例化B,B则发现构造器参数A也是bean,先去实例化A;
思路:将已经实例化但是仍未初始化的bean暴露出来;
三级缓存结构
- 一级缓存(singletonObjects):单例池,结构为:Map<BeanName, BeanDefinition>。用于存储被完整创建的bean;
- 二级缓存(earlySingletonObjects):缓存正在创建中的,提前暴露的半实例化(未完成属性填充和执行初始化方法)的bean对象,同时也存储singletonFactories中生成的代理bean对象;(对应bean的声明周期的BeanPostProcessor阶段)
- 三级缓存(singletonFactories):缓存单例工厂,Map<BeanName, ObjectFactory> 引入对象工厂的目的是为了能够在真正创建对象的时候可以干事情,例如创建代理对象等。
作用
二级缓存的作用:
- 存储SingletonFactory生成的代理对象,防止每次都调用singletonFactory.getObject方法生成新的代理对象,直接从二级缓存中拿;
三级缓存的作用:
- 为了在没有缓存依赖的情况下,延迟代理对象的创建。因为在没有循环依赖的时候,就不需要提前创建代理对象,可以将其延迟到初始化完成之后再创建;
- 补充:为什么需要提前创建,因为不提前创建注入的就是原始bean,而不是代理bean就会出错。
三级缓存结构直接使用二级缓存可以做到嘛?
- 二级缓存直接在实例化之后,就创建代理对象,这样就不需要三级缓存了。但是Spring的设计原则是延迟创建,是在bean初始化之后才为其创建代理。
暴露时机
FactoryBean和BeanFactory的区别
beanFactory:
- 是负责生产和管理bean的工厂,是ioc容器最底层的接口,spring用来管理和装配普通bean的ioc容器;
- 需要在程序启动时根据传入参数产生各种类型的bean,也就是需要事先装配这个bean、事先定义好、由哪些组件组成;
- 用途:主要通过getbean()获取spring容器中的bean、containsBean()判断容器中是否存在该对象、isSingleton()判断beanName是否为单例对象
factoryBean:
- 在ioc容器的基础上给bean的实现加上了一个简单工厂模式和装饰模式,是一个可以生产对象和装饰对象的工厂bean;
- 在程序运行中产生指定类型的bean,也就是定义一种类型的bean,在创建的时候再去实现其具体的功能;
- 包含的方法:getObject():返回实例、getObjectType():返回该装饰对象的bean的类型,isSingleton():判断bean是否为单例;
- 如果使用Name为hshuo,从容器里面拿bean返回的是getObject()里面的内容,是真实的bean;如果加上&hshuo,返回的是hshuo自己的实例;
- 用途:AOP 实际上是 Spring 在运行是创建出来的代理对象,这个对象是在运行时才被创建的,而不是在启动时定义的,这与工厂方法模式是一致的。更生动地说,AOP 代理对象通过 java 反射机制在运行时创建代理对象,并根据业务需求将相应的方法编织到代理对象的目标方法中。Spring 中的 ProxyFactoryBean 就是干这事的。
补充:
hshuo的面试之路 文章被收录于专栏
作者目标是找到一份Java后端方向的工作 此专栏用来记录从Bilibili、书本、其他优质博客上面学习的内容 用于巩固、总结内容 主要包含Docker、Dubbo、Java基础、JUC、Maven、MySQL、Redis、SpringBoot、SpringCloud、数据结构、杂文、算法、计算机网络、操作系统、设计模式等相关内容