Spring相关知识

Spring中,有两个id相同的bean,会报错吗,如果会报错,在哪个阶段报错?

  • 在同一个xml配置文件里,不能存在id相同的两个bean,否则spring容器启动的时候会报错。

  • 因为id这个属性表示一个Bean的唯一标志符号,所以Spring在启动的时候会去校验id的唯一性,一旦发现重复就会报错。

  • 这个错误发生在Spring对xml文件进行解析转化为BeanDefinition的阶段。

  • 但是在两个不同的Spring配置文件里面,可以存在id相同的两个bean。IOC容器在加载Bean的时候,默认会多个相同id的bean进行覆盖。

  • 在Spring3版本后,里面提供了@Configuration注解去声明一个配置类,然后使用@Bean注解实现Bean的声明。在这种情况下,如果我们在同一个配置类里面声明多个相同名字的Bean,在Spring IOC容器中只会注册第一个声明的Bean的实例,后续重复名字的Bean就不会再注册了。

  • 如果使用@Autowired注解根据类型实现依赖注入,启动时会提示找不到重复未注册的实例。如果使用Resource注解根据名词实现依赖注入,就会提示类型不匹配错误。这个错误是在Spring IOC容器里面的Bean初始化之后的依赖注入阶段发生的。

如何理解Spring Boot中的Starter?

  • Starter是启动依赖,它以功能为纬度,来维护对应的jar包的版本依赖,使得开发者可以不需要去关心这些版本冲突这种容易出错的细节。

  • Starter组件会把对应功能的所有jar包依赖全部导入进来,避免了开发者自己去引入依赖带来的麻烦。

  • Starter内部集成了自动装配的机制,也就是说在程序中依赖对应的starter组件以后,这个组件自动会集成到Spring生态下,并且对于相关Bean的管理,也是基于自动装配机制来完成。

  • 依赖Starter组件以后,这个组件对应的功能所需要的维护的外部化配置,会自动集成到Spring Boot里面,我们只需要在application.properties文件里面进行维护就行了。

  • Starter组件几乎完美的体现了Spring Boot里面约定优于配置的理念。

为什么要使用Spring框架

  • Spring是一个轻量级应用框架,它提供了IOC和AOP两个核心功能。

  • 它的核心目的是为了简化企业级应用程序的开发,使得开发者只需要关心业务需求,不需要关心Bean的管理,以及通过切面增强功能减少代码的侵入性。

  • Spring特性

    • 轻量:Spring是轻量的,基本的版本大约2MB。
    • IOC/DI:Spring通过IOC容器实现了Bean的生命周期的管理,以及通过DI实现依赖注入,从而实现了对象依赖的松耦合管理。
    • AOP:Spring支持面向切面的编程,从而把应用业务逻辑和系统服务分开。
    • MVC框架:Spring MVC提供了功能更加强大且更加灵活的Web框架支持。
    • 事务管理:Spring通过AOP实现了事务的统一管理,对应用开发中的事务处理提供了非常灵活的支持。

为什么越来越多人选择Spring Boot?

  • Spring的核心功能
    1. 可以独立运行Spring项目
    2. 内嵌的Servlet容器
    3. 提供starter简化Maven依赖
    4. 自动配置Spring
    5. 无代码生成,无XML配置

Spring如何解决循环依赖问题

  • 循环依赖是指一个或多个Bean实例之间存在直接或间接的依赖关系,构成循环调用。通常表现为三种形态:

    • 互相依赖:A依赖B,B依赖A
    • 间接依赖:两个以上的Bean存在间接依赖关系造成循环调用
    • 自我依赖:自己依赖自己造成了循环依赖
  • 三级缓存解决部循环依赖的问题

    • 第一级缓存存放完全初始化好的Bean,这个Bean可以直接使用了

    • 第二级缓存存放原始的Bean对象,也就是说Bean里面的属性还没有进行赋值

    • 第三级缓存存放Bean工厂对象,用来生成原始Bean对象并放入到二级缓存中

  • 三级缓存工作原理

    • 初始化BeanA,先在一级缓存中查找,没查到则先把BeanA实例化,然后把BeanA包装成ObjectFactory对象保存到三级缓存中。

    • BeanA开始对属性BeanB进行依赖注入,先在一级缓存中查找,没查到则开始初始化BeanB。然后创建BeanB实例,加入到三级缓存中。

    • BeanB开始对BeanA进行依赖注入,在三级缓存中找到了BeanA的工厂对象,再把工厂对象放入二级缓存,把BeanA从三级缓存中移除,再将二级缓存中不完整的BeanA注入到BeanB中。BeanB初始化成功以后保存到一级缓存。

    • BeanA从一级缓存中拿到BeanB实例,BeanA完成属性填充,执行完初始化并放入一级缓存。

三级缓存保存的是一个函数式接口,可以将lambda表达式作为参数放到方法的实参中,在方法执行的时候,并不会实际的调用当前lambda表达式,只有在调用getObject方法的时候才会去调用lambda表达式。

  • Spring本身只能解决单实例存在的循环依赖问题,依赖情况需要人为干预:
    • 多实例的Setter注入导致的循环依赖,需要把Bean改为单例

    • 构造器注入导致的循环依赖。可以通过@Lazy注解

    • DependsOn导致的循环依赖,找到注解循环依赖的地方,迫使它不循环依赖

    • 单例的代理对象Setter注入导致的循环依赖,可以使用@Lazy注解,或者使用@DependsOn注解指定加载先后关系

Spring中BeanFactory和FactoryBean的区别

BeanFactory

  • Spring中最核心的就是IOC容器,它保存了所有需要对外提供的Bean的实例。Spring对外暴露的ApplicationContext作为IOC容器最重要的接口,它也实现了BeanFactory。

  • BeanFactory相当于是IOC容器的顶级接口,是IOC容器最基础的实现,也是提供访问Spring容器的根接口。主要负责Bean的创建和访问,同时,在BeanFactory中还会完成对Bean的依赖注入。

FactoryBean

  • 它是一个特殊的Bean,可以返回创建Bean的工厂。FactoryBean接口可以根据不同的配置类型返回不同类型的Bean,它有一个核心的方法叫做getObject()。

  • 配置一个FactoryBean类型的bean,在获取bean的时候得到的并不是class属性中配置的这个类的对象,而是getObject()方法的返回值。通过这种机制,Spring可以帮我们把复杂组件创建的详细过程和繁琐细节都屏蔽起来,只把最简洁的使用界面展示给我们。

Spring中Bean的作用域有哪些?

  • singleton:单例,意味着整个Spring容器中只会存在一个Bean实例。

  • prototype:原型,每次从IOC容器中去获取指定Bean的时候,都会返回一个新的实例对象。

  • 基于Spring框架下的Web应用里面,增加了一个会话纬度来控制Bean的生命周期

    • request:针对每一次http请求,都会创建一个新的Bean。
    • session:以session会话为纬度,同一个session共享同一个Bean实例,不同的session产生不同的Bean实例。
    • globalSession:针对全局session纬度,共享一个Bean实例。

Spring中事务的传播行为有哪些?

  • 所谓事务传播行为,声明了多个事务的方法互相调用时,这个事务应该如何传播。比如说,mehtodA()调用methodB(),两个方法都显示开启了事务,那么methodB()是开启一个新事务,还是继续在methodA()这个事务中执行?这就取决于事务的传播行为。

  • 在Spring中,定义了7种事务传播行为

    • REQUIRED:默认的Spring事务传播级别,如果当前存在事务,则加入这个事务,如果不存在事务,就新建一个事务。
    • REQUIRE_NEW:不管是否存在事务,都会新开一个事务,新老事务互相独立。外部事务抛出异常回滚不会影响内部事务的正常提交。
    • NESTED:如果当前存在事务,则嵌套在当前事务中执行。如果没有当前事务,则新建一个事务,类似于REQUIRE_NEW。
    • SUPPORTS:表示支持当前事务,如果不存在事务,以非事务的方式执行。
    • NOT_SUPPORTED:表示以非事务的方式来运行,当前如果存在事务,则把当前事务挂起。
    • MANDATORY:强制事务执行,若当前不存在事务,则抛出异常。
    • NEVER:以非事务的方式运行,如果当前存在事务,则抛出异常。

Spring里面的事务和分布式事务的使用如何区分,以及两个事务之间有什么关联?

  • 在Spring里面没有提供事务,只是提供了对数据库事务管理的封装。通过声明式的事务配置,使得开发人员可以从一些复杂的事务处理中得到解脱,我们不再需要关心连接的获取、连接的关闭、事务提交、事务回滚这些操作。更加聚焦在业务层面的开发。所以,Spring里面的事务,本质上是数据库层面的事务,这种事务的管理,主要是针对单个数据库里面多个数据表操作的,去满足事务的ACID特性。

  • 分布式事务,是解决多个数据库的事务操作的数据一致性问题,传统的关系型数据库不支持跨库事务的操作,所以需要引入分布式事务的解决方案。

  • Spring并没有提供分布式事务场景的支持,所以Spring事务和分布式事务在使用上并没有直接的关联性。

SpringBean生命周期的执行流程

  • Spring生命周期全过程大致分为五个阶段

    • 创建前准备阶段

      • 这个阶段主要是在Bean加载之前,从Spring上下文和相关配置中解析并查找Bean有关的配置内容。比如init-method - 容器在初始化bean时调用的方法、destory-method - 容器在销毁Bean时调用的方法,以及Bean加载过程中的前置和后置处理。
    • 创建实例阶段

      • 这个阶段主要是通过反射来创建Bean对象的实例对象,并且扫描和解析Bean声明的一些属性。
    • 依赖注入阶段

      • 在这个阶段,会检测被实例化的Bean是否存在其他依赖,如果存在其他依赖,就需要对这些被依赖Bean进行注入。通过@Autowired@Setter等依赖注入的配置。
      • 在这个阶段还有触发一些扩展的调用,比如常见的扩展类:BeanPostProcessors用来实现Bean初始化前后的回调、InitializingBean类中的afterPropertiesSet()方法,给属性赋值、BeanFactoryAwawre等等。
    • 容器缓存阶段

      • 主要是把Bean保存到IOC容器中缓存起来,到了这个阶段,Bean就可以被开发者使用了。
      • 后置处理器方法也是在这个阶段触发的。
    • 销毁实例阶段

      • 这个阶段,是完成Spring应用上下文关闭时,将销毁Spring上下文中所有的Bean。
      • 如果Bean实现了DisposableBean接口,或者配置了destory-method属性,将会在这个阶段被调用。

Spring中有哪些方式可以把Bean注入到IOC容器?

  • 使用XML的方式来声明Bean的定义

  • 使用@CompontScan注解来扫描声明了@Controller@Service@Pepository@Component注解的类。

  • 使用@Configuratiuon注解声明配置类,并使用@Bean注解实现Bean的定义,这种方式其实是xml配置方式的一种演变,是Spring迈入到无配置化时代的里程碑。

  • 使用@Implrt注解,导入配置类或者普通的Bean

  • 使用FactoryBean工厂bean,动态构建一个Bean实例,SpringCloudOpenFeign里面的动态代理实例就是使用FactoryBean来实现的。

  • 实现@ImportBeanDefinitionRegistar接口,可以动态注入Bean实例。这个在SpringBoot里面的启动注解有用到。

  • 实现ImportSelector接口,动态批量注入配置类或者Bean对象,这个在SpringBoot里面的自动装配机制里面有用到。

Spring Boot中自动装配机制的原理

  • 自动装配,简单来说就是自动把第三方组件的Bean装载到Spring IOC容器里面,不需要开发人员再去写Bean的装配配置。

  • 在Spring Boot应用里面,只需要在启动类加一个@SpringBootApplication注解就可以实现自动装配。

  • 核心流程

    • 导入starter,就会导入autoconfigure包。
    • autoconfigure包里面有一个文件META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports,里面指定的所有启动要加载的自动配置类。
    • @EnableAutoConfiguration会自动把上面文件里面写的所有自动配置类都导入进来。xxxAutoConfiguration是有条件注解进行按需加载。
    • xxxAutoConfiguration给容器中导入一堆组件,组件都是从xxxProperties中提取属性值。
    • xxxProperties是和配置文件进行绑定的。
  • 所以只要导入starter、修改配置文件,就能修改底层行为。

Spring Boot的约定优于配置,你的理解是什么?

  • 约定优于配置是一种软件设计的范式,它的核心思想是减少软件开发人员对于配置项的维护,从而让开发人员更加聚焦在业务逻辑上。

  • 基于传统的Spring框架开发Web应用,我们需要做很多和业务开发无关并且只需要做一次的配置。通过Spring Boot,我们可以快速开发基于Spring生态下的应用程序。

    • Spring Boot Starter启动依赖,它能帮我们管理所有jar包版本。
    • Spring Boot会自动内置一个Tomcat容器来运行Web应用,我们不需要再去单独做应用部署。
    • Spring Boot通过扫描约定路径下的Spring.factories文件来识别配置类,实现Bean的自动装配。
    • Spring Boot会默认加载的配置文件application.properties等等。

Spring Cloud的理解

  • Spring Cloud是Spring官方推出来的一套微服务解决方案。

  • 在这套标准里,Spring集成了Netflix公司的OSS开源套件,比如Zuul实现应用网关、Eureka实现服务注册与发现、Ribbon实现负载均衡、Hystrix实现服务熔断。但是随着Netflix OSS相关技术组件的闭源和停止维护,Spring官方也自研了一些组件,比如Getway实现网关、LoadBalancer实现负载均衡。

  • 另外,Alibaba里面的开源组件也实现了Spring Cloud的标准,成为了Spring Cloud里面的另外一套微服务解决方案。包括Dubbo做rpc通信、Nacos实现服务注册与发现以及动态配置中心、Sentinel实现服务限流和服务降级等等。

  • 在Spring Cloud出现之前,为了解决微服务架构里面的各种技术问题,需要去集成各种开源框架,因为标准和兼容性问题,所以在实践的时候很麻烦,而Spring Cloud统一了这样一个标准。降低了微服务架构的开发难度,只需要在Spring Boot的项目基础上通过starter启动依赖集成相关组件就能轻松解决各种问题。

Spring IOC的工作流程

  • IOC的全称是Inversion Of Control,也就是控制反转,它的核心思想是把对象的管理权限交给容器。

  • 应用程序如果需要使用到某个对象的实例,直接从IOC容器中去获取就行,这样设计的好处是降低了程序里面对象与对象之间的耦合性。使得程序的整个体系结构变得更加灵活。

  • Spring里面很多方式去定义Bean,比如XML里面的<Bean>标签、@Service@Component@Repository@Configuration配置类中的@Bean注解等等。Spring在启动的时候,会去解析这些Bean然后保存到容器里面。

  • 工作流程

    • IOC容器的初始化
      • 这个阶段主要是根据程序中定义的XML或者注解等Bean的声明方式,通过解析和加载后生成BeanDefinition,然后把BeanDefinition注册到IOC容器。
      • 通过注解或者xml声明的bean都会得到解析得到一个BeanDefinition实体,实体中包含这个bean中定义的基本属性。最后把这个BeanDefinition保存到一个Map集合里面,从而完成IOC的初始化。
    • 完成Bean初始化及依赖注入
      • 通过反射针对没有设置lazy-init属性的单例bean进行初始化。
      • 完成Bean的注入。
    • Bean的使用
      • 通常我们会使用@Autowwired或者BeanFactory.getBean()从IOC容器中获取指定的bean实例。
      • 针对设置lazy-init属性以及非单例bean的实例化,是在每次获取bean对象的时候,调用bean的初始化方法来完成实例化的,并且Spring IOC容器不会去管理这些Bean。
posted @ 2023-09-06 22:55  Pearl...T  阅读(27)  评论(0编辑  收藏  举报