spring面试

1.Spring简介

Spring是一个轻量级的控制反转(IOC)和面向切面编程(AOP)的开发框架

优点

  • Spring是一个开源的免费框架
  • Spring是一个轻量级的,非侵入式的框架
  • 控制反转(IOC),面向切面编程(AOP)
  • 支持事务处理,对框架整合的支持

2.IOC、DI、AOP

1.IOC

IOC:就是控制反转,通俗来说就是我的不用自己创建实例对象,这些都交给Spring的bean工厂帮我们创建管理,通过面向接口编程的方式来实现对业务组件的动态依赖。

1.优点

降低了代码的耦合度,将对象之间的相互依赖关系交给IOC容器来管理,并由IOC容器完成对象的注入,这样可以很大程度上简化应用的开发,把应用从复杂的依赖关系中解放出来

2.DI

DI:就是依赖注入,组件之间的依赖关系由容器在运行期决定,形象的说即容器动态的将某个依赖关系注入到组件之中。 我们只需要简单的配置,而无需任何代码就可指定目标需要的资源,完成自身的业务逻辑,而不需要关心具体的资源来自何处

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

1.有哪些不同类型的依赖注入实现方式?

依赖注入是时下最流行的IOC实现方式,依赖注入分为接口注入、Setter方法注入、构造器注入三种方式。

3.AOP

AOP:面向切面编程,利用AOP可以对业务逻辑的各个部分进行隔离,从而使业务逻辑各部分之间的耦合度降低,提高程序的可重用行,同时提高了开发的效率。核心思想:不修改源代码的前提下,对程序进行增强。 可用于权限认证、日志、事务处理。

AOP实现的关键在于代理模式,AOP代理主要分为静态代理和动态代理,静态代理的代表为AspectJ,动态代理则以Spring AOP为代表

  • 静态: 由程序员创建代理类或特定工具自动生成源代码再对其编译,在程序运行前代理类的.class文件就已经存在了。
  • 动态:在程序运行时运用反射机制动态创建而成。(InvocationHandler的应用)

1.SpringAOP中的动态代理主要有两种方式:

  • JDK动态代理:通过反射来接受被代理的类,并且要求被代理的类必须实现一个接口。JDK动态dialing的核心是InvocationHandler接口和Proxy类。
  • CGLIB动态代理CGLIB是一个代码生成的类库,可以在运行时动态的生成某个类的子类,注意,CGLIB是通过继承的方式做的动态代理,因此如果某个类表标记为final,那么它是无法使用CGLIB做动态代理的。

二、什么是Spring bean

spring中的bean:我们一般获取对象的方法有两种,一种是手动直接new,另一种是直接交给spring来管理,交给spring来管理的就是bean容器先会实例化bean,然后自动注入,实例化的过程就需要以来BeanDefinition

Spring beans是那些形成Spring应用的主干的java对象,他们被Spring IOC容器初始化、装配、和管理,这些beans通过容器中配置的元数据创建

三、BeanFatory和ApplicationContext有什么区别?

两者都是通过xml来配置文件加载bean

1.BeanFactory

BeanFactroy采用的是延迟加载形式来注入Bean的,即只有在使用到某个Bean时(调用getBean()),才对该Bean进行加载实例化。

2.ApplicationContext

ApplicationContext它是在容器启动时,一次性创建了所有的Bean。这样在容器启动时,我们就可以发现Spring中存在的配置错误

四、Spring基于xml注入bean的集中方式

  • Set方法注入
  • 构造器注入
    • 通过index设置参数的位置,通过type设置参数类型
  • 静态工厂注入
    • 就是调用静态工厂的方法来获取自己需要的对象,为了让spring管理所有对象,我们不能直接通过“工程类.静态方法()”来获取对象,而是依然通过spring注入的形式获取。
  • 实例工厂
    • 实例工厂的意思是获取对象实例的方法不是静态的,所以你需要首先new工厂类,在调用普通的实例方法。

五、spring中bean的生命周期

① 实例化Bean

  • 对于BeanFactory容器,当客户向容器请求一个尚未初始化的bean时,或初始化bean的时候需要注入另一个尚未初始化的依赖时,容器就会调用creatBean进行实例化
  • 对于ApplicationContext容器,当容器启动结束后,通过获取BeanDefinition对象中的信息,实例化所有的bean

BeanDefinition:用于保存bean的相关信息,包括属性、构造方法参数、依赖的bean实例名称以及是否延迟加载、单例等。他是实例化bean的原材料,spring就是根据BeanDefintion中的信息实例化bean

②设置对象属性(依赖注入)

实例化后的对象被封装在BeanWrapper对象中,紧接着,Spring根据BeanDefinition中的信息以及通过BeanWrapper提供的设置属性的接口完成依赖注入

③处理Aware接口

xxxAware:xxxAware接口的出现就是为了让xxx感受到自身的一些属性。比如BeanFactoryAware接口就是为了能够获取到BeanFactory对象。

接着,Spring会检测该对象是否实现了xxxAware接口,并将相关的xxxAware实例注给Bean:

  • 如果这个Bean已经实现了BeanNameAware接口,会调用它实现的setBeanName(String beanId)方法,此处传递的就是Spring配置文件中Bean的id值
  • 如果这个Bean已经实现了BeanFactoryAware接口,会调用它实现的setBeanFactory()方法,传递的是Spring工厂自身。
  • 如果这个Bean已经实现了ApplicationContextAware接口,会调用setApplicationContext(ApplicationContext)方法,传入Spring上下文;

④BeanPostProcessor:

如果想对Bean进行一些自定义的处理,那么可以让Bean实现了BeanPostProcessor接口,那将会调用postProcessBeforeInitialization(Object obj, String s)方法。

⑤InitializingBean与init−method:

如果Bean在Spring配置文件中配置了 init-method 属性,则会自动调用其配置的初始化方法。

⑥如果这个Bean实现了BeanPostProcessor接口,将会调用postProcessAfterInitialization(Objectobj,Strings)方法;

由于这个方法是在Bean初始化结束时调用的,所以可以被应用于内存或缓存技术;

以上几个步骤完成之后,Bean就已经被正确创建了,之后就可以使用这个Bean了。

⑦DisposableBean
当Bean不再需要时,会经过清理阶段,如果Bean实现了DisposableBean这个接口,会调用其实现的**destroy()**方法;

⑧destroy−method:
最后,如果这个Bean的Spring配置中配置了destroy-method属性,会自动调用其配置的销毁方法

六、解释Spring支持的几种bean的作用域

  • singleton: 默认每个容器中只有一个bean的实例,单例的模式由BeanFactory自身来维护。
  • prototype: 为每个bean请求提供一个实例。该bean在每次被注入的时候,都要重现创建一个实例。
  • request:为每一个网络请求创建一个实例,在请求完成以后,bean会失效并被垃圾回收器回收。
  • session:与request范围类似,确保每个session中有一个bean的实例,在session过期后,bean会随之失效。
  • global−session:类似于标准的HTTP Session作用域,不过它仅仅在基于portlet的web应用中才有意义,Protlet规范定义了全局Session的概念,它被所有构成某个portlrt web应用的各种不同的portlet所共享。

Portlet:是基于java的web组件,有portlet容器管理,并由容器处理请求,生产动态内容

七、Spring框架中的单例Beans是线程安全的吗

Spring框架并没有对单例进行任何多线程的封装处理,关于bean的线程安全和并发问题需要开发者自行去搞定。但实际上,大部分的Spring bean并没有可变的状态,所以在某种程度上说Spring的单例bean是线程安全的。如果你的bean有多种状态的话(比如View Model对象),就需要自行保证线程安全。

解决的办法就是:将多态bean的作用域由“singleton”变更为“prototype”或者使用ThreadLocal进行处理。

如果Bean配置为singleton,当有两个用户访问都调用到了该Bean,会出现资源共享的问题。

八、spring中的bean的生成过程

  1. Bean的加载和解析—BeandefinationReader
    在Spring中,Bean有多种定义方式,比如XML形式,Annotaion注解形式以及SpringBoot中的配置类等形式,BeandefinationReader该接口的作用就是加载和解析Bean的配置信息,每种形式都有对应的一种BeandefinationReade来进行解析,比如XML文件是XMLBeandefinationReade,他们最终都会解析为BeanDefinition对象,在IOC内部维护着一个CourrentHashMap,每个bean都对应着一个BeanDefinition对象。
  2. Bean的注册—BeanDefinitionRegistry

BeanDefinitionRegistry是一个接口,实现了AliasRegistry接口,定义了一些对bean的常用操作,大概有如下功能:

  • 以Map<String,BeanDefinition>的形式注册bean
  • 根据beanName删除和获取beanDefiniation
  • 得到持有的beanDefiniation
  • 根据beanName判断是否包含beanDefiniation

一般注册bean的方法是registerBeanDefinition,DefaultListableBeanFactory 通过实现 BeanDefinitionRegistry 接口,重写该方法。

  1. 功能扩展和Bean的实例化

第一种情况使用BeanFactoryProcessors直接扩展

Spring是一个框架,需要考虑扩展性,这里就是显示扩展性中的一环。它可以做什么事呢?

  • 注册一些BeanDefinition到注册中心
  • 修改注册中心内已注册过的BeanDdfinition信息。

第二种注册自定义BeanPostProcessors的进行扩展或者使用Spring固有的BeanPostProcessors进行扩展。

  • 想让Bean做一些自定义的处理,就可以实现BeanPostProcessors这个接口来做扩展,注意创建实例前后都可以进行扩展。
  • 根据BeanDefinition获取合适的Constructor进行反射创建实例,根据BeanDefinition注入依赖,并调用init方法进行实例的初始化。
  • 操作完成的对象可以放在实例缓存池中,比如普通单实例、FactoryBean实例、Spring系统实例等。

九、FactoryBean和BeanFactory的区别

BeanFactory
BeanFactory是IOC最基本的容器,负责生产和管理bean,提供了IOC容器应遵守的最基本的接口。

FactoryBean
FactoryBean是一个接口,挡在IOC容器中的Bean实现了
一般情况下,Spring通过发射机制利用bean的class属性指定实现类实例化Bean,在某些情况下,实例化Bean过程比较复杂,如果按照传统方式,则需要在bena中提供大量的配置信息,并且配置方式的灵活性受限。这时采用编码的方式可能会得到一个简单的方案,所以Spring位次提供了一个FactoryBean的工厂类接口,用户可以是实现该接口定制是实例化Bean的逻辑。

总结BeanFactory和FactoryBean其实没有什么可比性,只是两者的名称比较接近。BeanFactory是提供了IOC容器最基本的心事,给具体的IOC容器的实现提供了规范;FactoryBean可以说为IOC容器中Bean的实现提供了更加灵活的方式,FactoryBean在IOC容器的基础上给Bean的实现加上了一个简单工厂模式和装饰模式,我们可以再getObject()方法灵活配置。

十、spring如何处理线程并发问题

  • 在一般情况下,只有无状态的Bean才可以在欧线程环境下共享
  • 绝大多数都可以生命为singleton作用域,因为Spring对一些Bean中非线程安全状态采用ThredLocal进行处理,解决线程安全问题。

ThreadLocal会为每个线程提供一个独立的变量副本,从而隔离了多个线程对数据的访问冲突。因为每一个线程都拥有自己的变量副本,从而也就没有必要对该变量进行同步了。

十一、Spring的自动装配

在Spring中,对象无需自己查找或创建与其关联的其他对象,由容器负责把需要相互协作的对象引用赋予各个对象,使用autowire来配置自动装载模式。

在Spring中xml配置中共有5中自动装配

  • no:默认的方式是不进行自动装配,通过手动设置ref属性来进行装配bean。
  • byName:通过bean的名称进行自动装配,如果一个bean的property与另一bean的name相同,就进行自动装配
  • byType:通过参数的数据类型 进行自动装配
  • constructor:利用构造函数 进行装配,并且构造函数的参数通过byType进行装配
  • autodetect:自动探测,如果有构造方法,to弄过construct的方法自动装配,否则使用byType的方式自动装配

Autowired可用于:构造函数、成员变量、Setter方法
Autowired和Resource的区别:

  • Autowired默认是按照类型装配注入的,默认情况下它要求依赖对象必须存在。
  • Resource默认是按照名称来装配注入的,只有当找不到名称匹配的bean才会按照类型来装配注入。

十二、Spring框架中用到了那些设计模式

  • 工厂模式:BeanFactory就是简单工厂模式的体现,用来创建对象的实例
  • 单例模式:Bean默认为单例模式
  • 代理模式:Spring的AOP功能用到了JDK的动态代理和CGLIB字节码生成技术
  • 模板方法:用来解决代码重复的问题,
  • 观察者模式:定义对象键一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都会得到通知被制动更新,如Spring中listener的实现

十三、Component和Bean的区别是什么

  1. 作用对象不同
    Component注解作用于类,而Bean注解作用于方法
  2. Component注解通常是通过类路劲扫描来自动侦测以及自动装配到Spring容器中
    (可以使用ComponentScan注解定义要扫描的路劲)。Bean注解通常是在标有该注解的方法
  3. Bean注解比Component注解的自定义性更强,而且很多地方只能通过Bean来注册bean
    比如当引用第三方库的类需要装配到Spring容器的时候,就只能通过Bean注解实现。

十四、Spring中的事务

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

Spring支持编程式事务管理和声明式事务管理两种方式:

  • 编程式事务管理使用TransactionTemplate
  • 声明式事务管理建立在AOP上的,其本质是通过AOP功能,对方法前后进行拦截,将事务处理的功能编织到拦截的方法中,也就是在目标方法开始之前加入一个事务,在执行完目标方法之后根据执行情况提交或者回滚事务。

1.spring的事务传播行为

spring事务的传播行为说的是,当多个事务同时存在的时候,spring如何处理这些事务的行为。

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

2.spring中的隔离级别

  • ISOLATION_DEFAULT:这是个 PlatfromTransactionManager 默认的隔离级别,使用数据库默认的事务隔离级别。
  • ISOLATION_READ_UNCOMMITTED:读未提交,允许另外一个事务可以看到这个事务未提交的数据。
  • ISOLATION_READ_COMMITTED:读已提交,保证一个事务修改的数据提交后才能被另一事务读取,而且能看到该事务对已有记录的更新。
  • ISOLATION_REPEATABLE_READ:可重复读,保证一个事务修改的数据提交后才能被另一事务读取,但是不能看到该事务对已有记录的更新。
  • ISOLATION_SERIALIZABLE:一个事务在执行的过程中完全看不到其他事务对数据库所做的更新。

十五、Spring中的循环依赖问题

1.问题+解决

spring中的循环依赖:其实就是循环引用,也就是两个或两个以上的bean互相持有对方,最终形成闭环。比如A依赖于B,而B依赖于C,C却依赖于A。

  1. 原型模式(protoType)循坏依赖—无法解决
  2. 单例Bean循环依赖—构造参数产生依赖—无法解决

这两种spring都无法解决只能抛出异常

  1. 单例Bean循环依赖-setter产生依赖—可以解决
    使用三级缓存+提前曝光的方式来解决。

三级缓存:用来保存不同时期对象的三个集合。

  • 一级缓存:singletonObjects指单例对象的缓存
  • 二级缓存:singletonFactories:单例对象工厂的缓存。
  • 三级缓存:earlySingletonObjects:提前曝光的单例对象的缓存。

2.setter单例的循环以来的解决过程

以A,B两个对象的相互引用为例讲解setter单例的循环依赖的解决过程。

  • getBean(A)时,先获取A对应的BeanDafiniton信息, 先到一级缓存中查询,因为该对象从未初始化过,一定拿不到,然后记录当前BeanName到CreationSet中,通过Class拿到无参构造利用反射创建早期对象。
  • 执行MerageBeanDefinitionPostProcessor操作,处理Autowried等注解,将早期对象封装到ObjectFactory内,放入三级缓存。
  • 进行依赖注入,发现依赖了类型B,进行getBean(B)操作。
  • 通过反射创建B的早期对象,执行MerageBeanDefinitionPostProcessor操作,处理@Autowried等注解,将早期对象封装到ObjectFactory内,放入三级缓存。
  • 进行依赖注入发现类型A,进行getBean(A)操作。
  • 获取A的BeanDefinition信息,先从一级缓存中获取,发现没有,检查CurrentlyCreationSet发现A在创建中,从三级缓存中获取到A的早期对象,并且将A对象升级到二级缓存。
  • B完成依赖注入,将B对象存放至一级缓存,并且将二三级缓存中关于B的数据清理。
  • A完成依赖注入,将A存放至一级缓存,并且将二三级缓存中有关A的数据清理。

3.为什么这种机制无法解决原型模式下的setter依赖?

scope=“prototype”:意思是每次请求都会创建一个实例对象。而对于prototype作用域的Bean,Spring容器不进行缓存Bean对象,根本无法提前暴露一个创建中的Bean。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值