SpringBean

SpringBean的生命周期

IOC流程:

  • 通过@ComponentScan注解,解析此路径下的所有类,将获取到的类存放在List<Class>集合中;(类似于SPI机制获取META/INF下的所有类)
  • 通过@Component注解,注册BeanDefinition,将其存放在Map<BeanName, BeanDefinition> map中;(根据@Component注解实现按需引入

SpringBean的生命周期:

  1. 属性填充
  2. 调用 * Aware接口 {
          实现BeanNameAware,设置setBeanName();
          实现BeanFactoryAware,设置setBeanFactory();
          实现ApplicationContextAware,设置setApplicationContext(),传入spring上下文的引用
    }
  3. 实现BeanPostProcessor接口的postProcessorBeforeInitialization(),对bean进行加工,例如生成动态代理
  4. 初始化Bean:实现InitializingBean接口的afterPropertiesSet()   或者  执行init-method;
  5. 实现BeanPostProcessor接口的postProcessorAfterInitialization();
  6. 销毁( 单个 )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:{
  1. @EnableAutoConfiguration:启动SpringBoot的自动配置机制;
  2. @Configuration:允许在上下文中注册额外的bean 或者 导入其他配置类;
  3. @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加载配置类的方式

  1. 使用注解 @ComponentScan
  2. 使用注解 @Import{
               导入普通类;
               导入选择器ImportSelector;
               导入注册器 ImportBeanDefinitionRegistrar
}

参考:



springBean的作用域

  • singleton:单例模式、springIOC容器中只会存在一个共享的Bean实例,无论有多少个Bean引用它,始终指向同一个对象;
  • prototype:原型模式、每次通过spring容器获取prototype定义的bean时,容器都将创建一个新的bean实例,每个bean实例都有自己的属性和状态;
有状态的bean使用prototype作用域、无状态的bean使用singleton作用域;
  • 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,因此存在资源竞争。
  1. 单例bean是无状态的bean,也就是线程中的操作不会对bean的成员执行查询以外的操作,那么这个单例bean是线程安全的;
  2. 单例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、数据结构、杂文、算法、计算机网络、操作系统、设计模式等相关内容

全部评论

相关推荐

点赞 评论 收藏
分享
Pandaileee:校友加油我现在也只有一个保底太难了
点赞 评论 收藏
分享
2 收藏 评论
分享
牛客网
牛客企业服务