2023整理--spring

1、Spring简介

        Spring是一个轻量级的Java开发框架,目的是为了解决企业级应用开发的业务逻辑层和其他各层的耦合问题;Spring最根本的使命是解决企业级应用开发的复杂性,即简化Java开发;Spring的两个核心特性是控制反转(IOC)和面向切面编程(AOP)

         为了降低Java开发的复杂性,Spring采取了四种关建策略:

  • 基于POJO的轻量级和最小侵入性编程
  • 通过依赖注入和面向接口实现松耦合
  • 基于切面和惯例进行声明式编程
  • 通过切面和模板减少样板式代码
2、 Spring框架的设计目标、设计理念和核心是什么

        Spring设计目标:Spring为开发者提供一个一站式轻量级应用开发平台

        Spring设计理念:在Java EE开发中,支持POJO和JavaBean开发方式,使应用面向接口开发,充分支持面向对象设计方法;Spring通过Ioc(控制反转)容器实现对象耦合管线的管理,并实现依赖反转,将对象之间的依赖关系交给Ioc容器,实现解耦

        Spring核心:IOC(控制反转)和AOP(面向切面编程),通过IOC容器管理POJO对象及它们之间的耦合关系;通过AOP以动态非侵入方式增强服务

3、Spring的优缺点

        优点:方便解耦,简洁开发:Spring是一个大工厂,可以将所有对象的创建和依赖关系维护交给Spring管理

                   AOP编程支持:Spring提供面向切面编程,可以方便实现对程序进行权限拦截、运行监控等功能

                   声明式事务支持:只需通过配置就可以完成对事务的管理,而无序手动编程

                   方便程序的测试:Spring对Junit4支持,可以通过注解方便测试Spring程序

                   方便集成各种优秀框架:Spring内部提供了对各种优秀框架的直接支持(如:Mybatis等)

                    降低JavaEE Api的使用难度

        缺点:Spring依赖反射,反射影响性能;使用门槛高

4、Spring的核心模块
  • IOC:控制反转是指创建对象的控制权转移,以前创建对象的主动权和时机由自己把控,而现在这种权力转移到Spring容器中,并由容器根据配置文件去创建实例和管理各个实例之间的依赖关系,对象与对象之间松耦合,利于功能的复用;Spring的配置文件中配置了类的字节码位置及信息,容器生成的时候加载配置文件识别字节码,通过反射创建类的对象;Spring的IOC有三种注入方式:构造器注入、setter方法注入,根据注解注入
  • AOP:面向切面编程,用于将那些与业务无关的但是对多个对象产生影响的公共行为和逻辑,抽取并封装成一个可重复使用的模块,这个模块被命名为切面(aspect),SpringAop使用的动态代理,所谓的动态代理就是说aop框架不会去修改字节码,而是每次运行时在内存中临时为方法生成一个AOP对象,这个对象包含了目标对象的全部方法,并且在特定的切点做了增强处理,并回调原对象
5、什么是Spring内部Bean?什么是外部Bean?

        内部Bean:当一个bean只能被某一个类使用时,称该bean为内部bean

        外部Bean:当一个bean可以被多个类使用时

6、Spring AOP的底层原理

        AOP底层是采用动态代理机制实现的:接口+实现类

  • 如果要代理的对象,实现了某一个接口,那么AOP会使用JDK Proxy去创建代理对象
  • 如果是没有实现接口的对象,无法使用JDK Proxy进行代理,AOP会使用Cglib Proxy生成一个被代理对象的子类作为代理
  • 也可以强制使用Cglib Proxy

底层就是由代理创建一个和impl实现类平级的一个对象,但是这个对象不是一个真正的对象,只是一个代理对象,可以实现和impl相同的功能,这个就是aop的横向机制原理,这样就不需要修改源代码

7、Spring框架中都用到了哪些设计模式

        工厂模式:BeanFactory就是简单工厂模式的体现,用来创建对象的实例

        单例模式:Bean默认为单例模式

        代理模式:Spring的AOP使用到了JDK动态代理和CGLIB字节码生成技术

        模板方法:用来解决代码重复的问题

        观察者模式:定义对象建一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都会得到通知被制动更新,eg:Spring中listener的实现ApplicationListener

8、Spring框架中有哪些不同类型的事件
  • 上下文更新事件(ContextRefreshedEvent):在调用ConfigurbaleApplicationContext接口中refresh()方法时被触发
  • 上下文开始事件(ContextStartedEvent):当容器调用ConfigurableApplicationContext的
    Start()方法开始/重新开始容器时触发该事件
  • 上下文停止事件(ContextStoppedEvent):当容器调用ConfigurableApplicationContext
    的Stop()方法停止容器时触发该事件
  • 上下文关闭事件(ContextClosedEvent):当ApplicationContext被关闭时触发该事件。容
    器被关闭时,其管理的所有单例Bean都被销毁
  • 请求处理事件(RequestHandledEvent):在Web应用中,当一个http请求(request)结
    束触发该事件。如果一个bean实现了ApplicationListener接口,当一个ApplicationEvent 被
    发布以后,bean会自动被通知
9、Spring控制反转--IOC

      IOC 的全称是Inversion Of Control, 也就是控制反转,它的核心思想是把对象的管理
权限交给容器, IOC负责创建对象、管理对象(通过依赖注入DI),装配对象、配置对象,并且管理这些对象的整个声明周期

        1)、IOC控制反转有什么作用

                管理对象的创建和依赖关系的维护

        2)、IOC的实现机制

                IOC的实现原理就是工厂模式加反射机制

        3)、IOC工作流程

        ①、IOC容器初始化:主要是根据程序中定义的XML 或者注解等Bean 的声明方式,通过解析和加载后生成BeanDefinition,然后把BeanDefinition 注册到IOC容器;通过注解或者xml 声明的bean 都会解析得到一个BeanDefinition 实体,实体中包含这个bean 中定义的基本属性。最后把这个BeanDefinition 保存到一个Map(beanDefinitionMap) 集合里面,以 beanName 作为 Key,以 BeanDefinition 对象为value,从而完成了IOC 的初始化;IoC 容器的作用就是对这些注册的Bean 的定义信息进行处理和维护,它IoC 容器控制反转的核心

        ②、完成Bean初始化及依赖注入:通过反射针对没有设置lazy-init 属性的单例bean 进行初始化;完成Bean 的依赖注入

        ③、Bean的使用:通常我们会通过@Autowired 或者BeanFactory.getBean()从IOC 容器中获
取指定的bean 实例。,针对设置layy-init 属性以及非单例bean 的实例化,是在每次获取bean 对象的时候,调用bean 的初始化方法来完成实例化的,并且Spring IOC 容器不会去管理这些Bean

10、BeanFactory和ApplicationContext的区别
  • BeanFactory和ApplicationContext是spring的两大核心接口,都可以当作spring的容器;ApplicationContext是BeanFactory的子接口
  • ApplicationContext接口作为BeanFactory的派生,除了提供BeanFactory所有的功能外还提供了更完整的框架功能:继承MessageSource,故支持国际化;统一的资源文件访问方式;提供在监听器中注册bean的事件;同时加载多个配置文件;载入多个(有继承关系)上下文,使得每一个上下文专注于一个特定的层次
  • BeanFactory采用延迟加载形式注入bean,即只有在使用到某个bean时,才对bean进行加载实例化,如果bean的某一个属性没有注入,BeanFactory加载后,直至第一次使用调用getBean方法才会抛出异常;ApplicationContext在容器启动时,一次性创建了所有的bean,故在容器启动时可以发现Spring中存在的配置错误,有利于检查依赖项是否注入(ApplicationContext占用内存空间,当应用程序配置bean较多时,程序启动较慢)
  • BeanFactory通常以编程的方式创建;ApplicationContext还能以声明的形式创建,如使用ContextKiader
  • BeanFactory和ApplicationContext都支持BeanPostProcessor、BenaFactoryPostProcessor的使用,两者区别:BeanFactory需要手动注册;ApplicationContext则是自动注册
11、什么是依赖注入DI

        ioc控制反转是一个很大的概念,可以用不同的方式实现,主要实现方式有两种:依赖注入和依赖查找

        1)、依赖注入的基本原则

                应用组件不应该负责查找资源或者其它依赖的协作对象,配置对象的工作
应该由IOC容器负责,“查找资源”的逻辑应该从应用组件的代码中抽取出来,交给IOC容器负责。容器全权负责组件的装配,它会把符合依赖关系的对象通过属性(JavaBean中的setter)或者是构造器传递给需要的对象。

        2)依赖注入有什么优势

  • 查找定位操作与应用代码完全无关
  • 不依赖于容器的API,可以很容易的在任何容器以外使用应用对象
  • 不需要特殊的接口,绝大数对象可以做到完全不依赖对象

        3)、有哪些不同类型的依赖注入实现方式

                接口注入、setter方法注入、构造器注入

  • 接口注入:灵活性和易用性比较差,Spring4已经开始弃用
  • 构造器注入:通过容器触发一个类的构造器来实现的,该类有一系列参数,每个参数代表一个对其它类的依赖
  • setter方法注入:容器通过调用无参构造器或无参static工厂方法实例化bean之后,调用该bean的setter方法,即实现了基于setter的依赖注入

12、什么是SpringBeans

        SpringBeans是那些形成Spring应用主干的java对象,他们被SpringIoc容器初始化、装配和管理,这些bean通过容器中配置的元数据创建,例如以xml文件中的形式

13、一个SpringBean定义包含什么

        一个SpringBean的定义包含容器必知的所有配置的元数据,包括如何创建一个bean,它的声明周期以及它的依赖

14、Spring有几种方法给Spring容器提供配置元数据
  • XML配置文件
  • 基于注解的配置
  • 基于java的配置
15、Spring的配置文件都包含了什么信息

        Spring的配置文件是一个xml文件,这个文件包含了类信息,描述里如何配置他们以及如何相互调用

16、Spring基于xml注入bean的几种方式
  • Set方法注入
  • 构造器注入
  • 通过index设置参数的位置
  • 通过type设置参数类型
  • 静态工厂注入
  • 实例工厂
17、怎么定义类的作用域

        当定义一个类在Spring里,还可以给这个bean声明一个作用域,可以通过bean定义中的scope属性来定义,例如当spring要在需要的时候每次生产一个新的bean实例,bean的scope属性被指认为prototype;一个bean每次使用的时候必须返回同一个实例,这个bean的scope属性必须设置为singleton

18、Spring支持的几种bean的作用域

    常规的有一下两个:

  • singleton:bean在每一个Spring Ioc容器中只有一个实例
  • prototype:一个bean的定义可以有多个实例,即每次请求获取bean时都会重新创建实例,故每次获取到的实例对象都是不相同的,作用域范围仅仅是调用getBean方法直至获取对象

        基于spring框架下的web应用里,增加了一个会话维度来控制Bean的生命周期,如下:

  • request:每次http请求都会创建一个bean,该作用域仅在基于web的spring ApplicationContext情形有效,它的作用域范围是每次发起 HTTP 请求直至拿到响应结果
  • session:在一个http session中,一个bean定义对应一个实例,该作用域仅在基于web的spring ApplicationContext情形有效,它的作用域范围是浏览器首次访问至浏览器关闭
  • global-session:在一个全局的http session中,一个bean对应一个实例,它的作用域范围是整个 WebApplicationContext 容器
19、Spring框架中的单例bean是线程安全的么

        不是,Spring框架中单例bean不是线程安全的

        Spring中bean默认是单例模式的,spring没有对单例bean进行多线程的封装处理

        实际上大部分时候 spring bean 无状态的(比如 dao 类),所有某种程度上来说 bean 也是安全的,但如果 bean 有状态的话(比如 view model 对象),那就要开发者自己去保证线程安全了,最简单的就是改变 bean 的作用域,把“singleton”变更为“prototype”,这样请求 bean 相当于new Bean()了,所以就可以保证线程安全了(有状态就是有数据存储功能,无状态就不会保存数据)

20、Spring如何处理线程并发问题
  • 在一般情况下,只有无状态的Bean才可以在多线程环境下共享,在Spring中,绝大部分Bean都可以声明为singleton作用域,因为Spring对一些Bean中非线程安全状态采用ThreadLocal进行处理,解决线程安全问题
  • ThreadLocal和线程同步机制都是为了解决多线程中相同变量的访问冲突问题。同步机制采用了“时间换空间”的方式,仅提供一份变量,不同的线程在访问前需要获取锁,没获得锁的线程则需要排队。而ThreadLocal采用了“空间换时间”的方式
  • ThreadLocal会为每一个线程提供一个独立的变量副本,从而隔离了多个线程对数据的访问冲突。因为每一个线程都拥有自己的变量副本,从而也就没有必要对该变量进行同步了。ThreadLocal提
    供了线程安全的共享对象,在编写多线程代码时,可以把不安全的变量封装进ThreadLocal
21、Spring框架中bean的生命周期

        Spring中bean的生命周期大概分为五个阶段:创建前准备阶段、创建实例阶段、依赖注入阶段、容器缓存阶段、销毁实例阶段

  • 创建前准备阶段:主要作用是Bean在开始加载之前,需要上下文和相关配置解析并查找Bean的有关实现
  • 创建实例阶段:主要是通过反射创建Bean的实例对象,并且扫描和解析Bean声明的一些属性
  • 依赖注入阶段:如果被实例化的Bean 存在依赖其他Bean 对象的情况,则需要对这些依赖bean 进行对象注入;同时,这个阶段会触发一些扩展应用
  • 容器缓存阶段:主要是把Bean保存到容器以及Spring缓存中,到了这个阶段,Bean已经可以被使用了
  • 销毁实例阶段:当Spring应用上下文关闭时,该上下文中的所有Bean都会被销毁,如果存在Bean 实现了DisposableBean 接口,或者配置了`destory-method`属性,会
    在这个阶段被调用

  •  Spring对bean进行实例化
  • Spring将值和bean的引用注入到bean对应的属性中
  • 如果bean实现了BeanNameAware接口,Spring将bean的ID传递给setBean-Name()方法
  • 如果bean实现了BeanFactoryAware接口,Spring将调用setBeanFactory()方法,将BeanFactory容器实例传入
  • 如果bean实现了ApplicationContextAware接口,Spring将调用setApplicationContext()方法,将bean所在的应用上下文的引用传入进来
  • 如果bean实现了BeanPostProcessor接口,Spring将调用它们的postProcessBeforeInitialization()方法
  • 如果bean实现了InitializingBean接口,Spring将调用它们的after-PropertiesSet()方法;如果bean使用initmethod声明了初始化方法,该方法也会被调用
  • 如果bean实现了BeanPostProcessor接口,Spring将调用它们的post-ProcessAfterInitialization()方法
  • bean已经准备就绪,可以被应用程序使用了,它们将一直驻留在应用上下文中,直到该应
    用上下文被销毁
  • 如果bean实现了DisposableBean接口,Spring将调用它的destroy()接口方法。同样,如果bean使用destroy-method声明了销毁方法,该方法也会被调用
22、什么是bean装配?什么是bean的自动装配

        bean装配是指spring容器中把bean组装到一起,前提是容器需要知道bean的依赖关系,如何通过依赖注入来把它们装配在一起

        bean的自动装配:在spring框架中,在配置文件中设定bean的依赖关系是一个很好的机制,Spring容器能够自动装配相互合作的bean,即意味着容器不需要配置,能通过bean工厂自动处理bean之间的协作,这意味着 Spring可以通过向Bean Factory中注入的方式自动搞定bean之间的依赖关系。自动装配可以设置在每个bean上,也可以设定在特定的bean上

23、可以在Spring中注入一个null和一个空字符串么?

        可以

24、怎么开启注解装配

        注解装配在默认情况下是不开启的,为了使用注解装配,必须在Spring配置文件中配置context:annotation-config/元素

25、@Component、@Controller、@Service、@Repository的区别
  • @Component:这将 java 类标记为 bean。它是任何 Spring 管理组件的通用构造型。spring 的组件扫描机制现在可以将其拾取并将其拉入应用程序环境中
  • @Controller:这将一个类标记为 Spring Web MVC 控制器。标有它的 Bean 会自动导入到 IOC 容器中
  • @Service:此注解是组件注解的特化。它不会对 @Component 注解提供任何其他行为。您可以在服务层类中使用 @Service 而不是 @Component,因为它以更好的方式指定了意图
  • @Repository:这个注解是具有类似用途和功能的 @Component 注解的特化。它为 DAO 提供了额外的好处。它将 DAO 导入 IOC 容器,并使未经检查的异常有资格转换为 Spring
    DataAccessException
26、@Autowired和@Resource之间的区别

        @Autowired和@Resource都是用户Bean的依赖注入

        @Autowired和@Resource的区别

  • @Autowired默认是按照类型实现Bean的依赖注入,默认情况下要求依赖对象必须存在(可以设置它的required属性为false);如果在Spring IOC容器里面存在多个相同类型的Bean实例,由于@Autowired 注解是根据类型来注入Bean 实例的;所以Spring 启动的时候,会提示一个错,只能注入一个实例Bean,但是存在多个导致注入失败,针对这个问题可以使用@Primary 或者@Qualifier 这两个注解来解决。@Primary 表示主要的bean,当存在多个相同类型的Bean 的时候,优先使用声明了@Primary 的Bean;)@Qualifier 的作用类似于条件筛选,它可以根据Bean 的名字找到需要装配的目标Bean。
  • @Resource默认是按照名称来装配注入的,只有当找不到与名称相对应的bean才会按照类型装配注入;如果都未匹配到就会报错
27、@Qualifer注解作用

        当创建多个类型相同的bean并希望仅使用属性装配其中一个bean时,可以使用@Qualifer注解和@Autowired通过指定应该装配哪个确切的bean来消除定义

28、Spring支持的事务管理类型

        Spring支持两种类型的事务管理

  • 编程式事务管理:可以通过编程的方式管理事务
  • 声明式事务管理:可以将业务代码和事务管理分离,只需注解和xml配置管理事务
29、Spring事务的实现方式和实现原理

        Spring事务的本质就是数据库对事务的支持,没有数据库的事务支持,spring是无法提供事务功能的;真正的数据库层的事务提交和回滚是通过binlog或redo log实现的

30、Spring的事务传播行为

        Spring事务的传播行为是指:当多个事务同时存在时。spring是如何处理这些事务的行为的

  • PROPAGATION_REQUIRED:如果当前没有事务,就创建一个新事务,如果当前存在事务,就 加入该事务,该设置是最常用的设置
  • PROPAGATION_SUPPORTS:支持当前事务,如果当前存在事务,就加入该事务,如果当前不 存在事务,就以非事务执行
  • PROPAGATION_MANDATORY:支持当前事务,如果当前存在事务,就加入该事务,如果当前 不存在事务,就抛出异常
  • PROPAGATION_REQUIRES_NEW:创建新事务,无论当前存不存在事务,都创建新事务
  • PROPAGATION_NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前 事务挂起。
  • PROPAGATION_NEVER:以非事务方式执行,如果当前存在事务,则抛出异常
  • PROPAGATION_NESTED:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则 按REQUIRED属性执行
31、Spring AOP的动态代理方式

        Spring AOP的动态代理方式主要有两种:JDK动态代理和CGLIB动态代理

  • JDK动态代理:只提供接口的代理,不提供类的代理。。核心InvocationHandler接口和Proxy类,InvocationHandler 通过invoke()方法反射来调用目标类中的代码,动态地将横切逻辑和
    业务编织在一起;接着,Proxy利用 InvocationHandler动态创建一个符合某一接口的的实
    例, 生成目标类的代理对象。
  • 如果代理类没有实现 InvocationHandler 接口,那么Spring AOP会选择使用CGLIB来动态代
    理目标类。CGLIB(Code Generation Library),是一个代码生成的类库,可以在运行时动
    态的生成指定类的一个子类对象,并覆盖其中特定方法并添加增强代码,从而实现AOP。
    CGLIB是通过继承的方式做的动态代理,因此如果某个类被标记为final,那么它是无法使用
    CGLIB做动态代理的
32、Spring AOP里面的几个名词
  • 切面(Aspect):切面是通知和切点的结合;通知和切点共同定义了切面的全部内容,在Spring AOP中,切面可以使用通用类(基于模式的风格)或者在普通类中以@AspectJ注解来实现
  • 连接点(Join point):指方法,在Spring AOP中,一个连接点代表一个方法的执行;应用可能有数以千计的时机应用通知。这些时机被称为连接点。连接点是在应用执行过程中能够插
    入切面的一个点。这个点可以是调用方法时、抛出异常时、甚至修改一个字段时。切面代码可以利用这些点插入到应用的正常流程之中,并添加新的行为
  • 通知(Advice):在Aop术语中,切面的工作被称为通知
  • 切入点(Poincut):切点的定义会匹配通知所要织入的一个或多个连接点。我们通常使用
    明确的类和方法名称,或是利用正则表达式定义所匹配的类和方法名称来指定这些切点
  • 引入(Introduction):引入允许我们向现有类添加新方法或属性
  • 目标对象(Target Object): 被一个或者多个切面(aspect)所通知(advise)的对象。
    它通常是一个代理对象。也有人把它叫做 被通知(adviced) 对象。 既然Spring AOP是通过运行时代理实现的,这个对象永远是一个 被代理(proxied) 对象
  • 织入(Weaving):织入是把切面应用到目标对象并创建新的代理对象的过程。在目标对象
    的生命周期里有多少个点可以进行织入
33、Spring只支持方法级别的连接点

        因为Spring基于动态代理,所以Spring支持方法连接点。Spring缺少对字段连接点的支持,而且不支持构造器连接点;方法之外的连接点拦截功能,可以利用Aspect补充

34、Spring通知类型
  • 前置通知(Before):在目标方法被调用之前调用的通知功能
  • 后置通知(After):在目标方法完成之后调用的通知,此时不会关心方法的输出是什么
  • 返回通知(After-returning):在目标方法成功执行之后调用的通知
  • 异常通知(After-throwing):在目标方法抛出异常后调用的通知
  • 环绕通知(Around):通知包裹了被通知的方法,在被通知的方法调用之前和调用之后执行自定义的行为
35、Spring MVC简述

        Spring MVC是一种基于Java语言开发,实现了Web MVC设计模式,请求驱动类型的轻量级Web框架;采用了MVC架构模式思想,通过把Model、View、Controler分离,将Web进行职责解耦,从而把负责的Web应用分成逻辑清晰的几个组件,Spring MVC有九大组件:

  • MultipartResolver 文件处理器:对应的初始化方法是initMultipartResolver(context),用于处理上传请求
  • LocaleResolver 当前环境处理器:其对应的初始化方法是initLocaleResolver(context),SpringMVC主要有两个地方用到了Locale:a、ViewResolver试图解析时;b、用到国际化资源或主题时
  • ThemeResolver 主题处理器:其对应的初始化方法是initThemeResolver(context),用于解析主题。也就是解析样式、图片及它们所形成的显示效果的集合
  • HandlerMapping 处理器映射器:对应的初始化方法是initHandlerMappings(context),在SpringMVC 中会有很多请求,每个请求都需要一个Handler 处理。HandlerMapping 的作用便是找到请求相应的处理器Handler 和Interceptor
  • HandlerAdapter 处理器适配器:对应的初始化方法是initHandlerAdapters(context),是一个适配器。HandlerAdapters 要做的事情就是如何让固定的Servlet 处理方法调用灵活的Handler 来进行处理
  • HandlerExceptionResolver 异常处理器:对应的初始化方法是initHandlerExceptionResolvers(context):它的主要作用是处理其他组件产生的异常情况
  • RequestToViewNameTranslator 视图名称翻译器:对应的初始化方法是initRequestToViewNameTranslator(context),作用是从请求中获取ViewName
  • ViewResolvers 页面渲染处理器:对应的初始化方法是initViewResolvers(context),主要作用是将String 类型的视图名和Locale 解析为View 类型的视图
  • FlashMapManager 参数传递管理器:对应的初始化方法是initFlashMapManager(context),在实际应用中,为了避免重复提交,我们可以在处理完post 请求后重定向到另外一个get 请求,这个get 请求可以用来返回页面渲染需要的信息。FlashMap 就是用于这种请求重定向场景中的参数传递。

        在Spring MVC九大组件中,涉及到请求处理响应的核心是:HandlerMapping、HandlerAdapter、ViewResolver

HandlerMapping 回到调用HandlerAdapter
;HandlerAdapter 会返回ModelAndView;ModelAndView 根据用户传入参数得到ViewResolvers
;ViewResolvers 会将用户传入的参数封装为View,交给引擎进行渲染

36、Spring是如何解决循环依赖问题的

        如果在代码中,将两个或多个Bean互相之间持有对方的应用就会发生循环依赖;循环依赖会导致注入死循环,是Sping产生循环依赖的原因;循环依赖有三种形态:

  • A依赖B,B又依赖A,他们之间形成了循环依赖
  • A依赖B,B依赖C,C依赖A,形成了循环依赖
  • 自我依赖,A依赖A形成了循环依赖

        Spring中设计了三级缓存解决循环依赖问题:

A、B两个类相互依赖,初始化A的时候,第一步实例化A完成(生成对象工厂实例放入三级缓存),注入依赖属性B,一级缓存查询B没有,二级缓存查询B没有,初始化B(生成对象工厂实例放入三级缓存),注入依赖属性A,一级缓存查询A没有,二级缓存查询A没有,三级缓存查询到A的对象工厂,需要AOP增强则生成A的代理对象,没有则直接创建A实例对象,并将A放入到二级缓存,注入A的代理对象完成,生成代理对象B,B移入一级缓存。继续A属性注入(B的代理对象),然后可能还会依赖注入C对象流程和B一致,所有依赖注入完成后A初始化,生成A的代理对象,发现A的代理对象已存在,则跳过,放入一级缓存。此时A的代理对象也是提前生成的,但是仅针对循环依赖提前生成。核心思想就是把 Bean 的实例化和 Bean 中属性的依 赖注入这两个过程分离出来

        Spring一级缓存存放所有的成熟Bean,可以被使用;二级缓存中存放所有的早期Bean,Bean里面的属性还没有进行赋值仅被实例化,依赖注入未实现;三级缓存存放Bean工厂对象,用来生成原始Bean对象并放入到二级缓存

        先取一级缓存,再取二级缓存;

        Spring本身只能解决单实例存在的循环引用问题,但是以下情况仍需人为干涉:

  • 多实例的 Setter 注入导致的循环依赖,需要把 Bean 改成单例
  • 构造器注入导致的循环依赖,可以通过@Lazy 注解
  • DependsOn 导致的循环依赖,找到注解循环依赖的地方,迫使它不循环依赖
  • 单例的代理对象 Setter 注入导致的循环依赖;可以使用@Lazy 注解或者使用@DependsOn 注解指定加载先后关系。
37、Spring三级缓存的作用是什么?

        三级缓存是用来存储代理Bean,当调用getBean()方法时,发现目标Bean需要通过代理工厂创建,此时会将创建好的实例保存到三级缓存,最终也会将赋值好的Bean同步到一级缓存中

38、Spring中哪些情况下,不能解决循环依赖问题

        1)、多例Bean通过setter注入的情况下不能解决循环依赖问题

        2)、构造器注入Bean的情况,不能解决循环依赖问题

        3)、单例的代理Bean通过setter注入的情况下,不能解决循环依赖问题

        4)、设置了@DependsOn的Bean,不能解决循环依赖问题

39、Spring MVC的执行流程

  • 用户发起请求,请求先被Servlet 拦截转发给Spring MVC 框架
  • Spring MVC 里面的DispatcherSerlvet 核心控制器,会接收到请求并转发给
    HandlerMapping
  • HandlerMapping 负责解析请求,根据请求信息和配置信息找到匹配的Controller
    类,不过这里如果有配置拦截器,就会按照顺序执行拦截器里面的preHandle 方
  • 找到匹配的Controller 以后,把请求参数传递给Controller 里面的方法
  • Controller 中的方法执行完以后,会返回一个ModeAndView,这里面会包括视
    图名称和需要传递给视图的模型数据
  • 视图解析器根据名称找到视图,然后把数据模型填充到视图里面再渲染成Html 内
    容返回给客户端
39、Spring 中有哪些方式可以把Bean 注入到IOC 容器

        Bean注入到IOC容器里面有七种方式:

  • 使用xml 的方式来声明Bean 的定义,Spring 容器在启动的时候会加载并解析这个xml,把bean 装载到IOC 容器中
  • 使用@CompontScan 注解来扫描声明了@Controller、@Service、@Repository、@Component 注解的类
  • 使用@Configuration 注解声明配置类,并使用@Bean 注解实现Bean 的定义,这种方式其实是xml 配置方式的一种演变,是Spring 迈入到无配置化时代的里程碑
  • 使用@Import 注解,导入配置类或者普通的Bean
  • 使用FactoryBean 工厂bean,动态构建一个Bean 实例,Spring CloudOpenFeign 里面的动态代理实例就是使用FactoryBean 来实现的
  • 实现ImportBeanDefinitionRegistrar 接口,可以动态注入Bean 实例。这个在Spring Boot 里面的启动注解有用到
  • 实现ImportSelector 接口,动态批量注入配置类或者Bean 对象,这个在Spring Boot 里面的自动装配机制里面有用到
40、Spring 中BeanFactory 和FactoryBean 的区别

        Spring 里面的核心功能是IOC 容器,所谓IOC 容器呢,本质上就是一个Bean
的容器或者是一个Bean 的工厂。它能够根据xml 里面声明的Bean 配置进行bean 的加载和初始化,然后BeanFactory来生产我们需要的各种各样的Bean,BeanFactory 的理解有如下两个:

  • BeanFactory 是所有Spring Bean 容器的顶级接口,它为Spring 的容器定义了一套规范,并提供像getBean 这样的方法从容器中获取指定的Bean 实例
  • BeanFactory 在产生Bean 的同时,还提供了解决Bean 之间的依赖注入的能力,也就是所谓的DI

        FactoryBean 是一个工厂Bean,它是一个接口,主要的功能是动态生成某一个类型的Bean 的实例,也就是说,我们可以自定义一个Bean 并且加载到IOC 容器里面。它里面有一个重要的方法叫getObject(),这个方法里面就是用来实现动态构建Bean的过程;Spring Cloud 里面OpenFeign 组件,客户端的代理类,就是使用了FactoryBean来实现的

41、Spring中,有两个id相同的Bean会报错么?如果会报错,什么阶段会报错

        在同一个XML 配置文件里面,不能存在id 相同的两个bean,否则spring 容器启动的时候会报错;因为id 这个属性表示一个Bean 的唯一标志符号,所以Spring 在启动的时候会去验证id 的唯一性,一旦发现重复就会报错,这个错误发生Spring 对XML 文件进行解析转化为BeanDefinition 的阶段

        在两个不同的Spring 配置文件里面,可以存在id 相同的两个bean。IOC 容器在加载Bean 的时候,默认会多个相同id的bean 进行覆盖;Spring3.x 里面提供@Configuration 注解去声明一个配置类,然后使用@Bean 注解实现Bean 的声明,这种方式完全取代了XMl,这种情况下,如果我们在同一个配置类里面声明多个相同名字的bean,在Spring IOC 容器中只会注册第一个声明的Bean 的实例,后续重复名字的Bean 就不会再注册了。

如果使用@Autowired 注解根据类型实现依赖注入,因为IOC 容器只有UserService01 的实例,所以启动的时候会提示找不到UserService02 这个实例;如果使用@Resource 注解根据名词实现依赖注入,在IOC 容器里面得到的实例对象是
UserService01,于是Spring 把UserService01 这个实例赋值给UserService02,就会提示类型不匹配错误。  这个错误是在Spring IOC容器里面的bean初始化之后依赖注入阶段发生的

42、@Conditional注解的作用

        @Conditional 注解的作用是为 Bean 的装载提供了一个条件判断,只有满足条件的情况下,Spring 才会把当前 Bean 装载到 IOC 容器中,这个条件的实现逻辑,我们可以实现 Condition 接口并重写 matches 方法自己去实现,所以@Conditional 注解增加了 Bean 装载的灵活性,在 Spring Boot 里面,对@Conditional 注解做了更进一步的扩展,比如增加了@ConditionalOnClass、@ConditionalOnBean等注解

43、Spring Bean的定义包含哪写内容

        Spring Bean 的配置内容非常多,我主要列举九个关键的配置属性,比如:class、scope、lazy-init、depends-on、name、constructor-arg、properties、init-method、destroy-method 等,这些属性都是要在 Spring 配置文件中声明的内容。在 Spring 容器启动后,这些配置内容都会映射到一个叫做 BeanDefinition 的对象中。然后,所有的 BeanDefinition 对象都会保存到一个叫做 beanDefinitionMap 的容器中,这个容器是 Map 类型,以 Bean的唯一标识作为 key,以 BeanDefinition 对象实例作为值。这样 Spring 容器创建 Bean时,就不需要再次读取和解析配置文件,只需要根据 Bean 的唯一标识,去beanDefinitionMap 中取到对应的 BeanDefinition 对象即可

        

  44、Spring 为什么要定义作用域

        定义 Bean 的作用域,相当于用户可以通过配置的方式限制 Spring Bean 的使用范围,以起到保护 Bean 安全的作用

        

                

                  

              

        

              

        

        

        

                   

  • 19
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值