Spring常见面试题总结

版权声明:本文为博主原创文章,欢迎转载,转载请注明作者、原文超链接 https://blog.csdn.net/weixin_43863007/article/details/89042688
1. 什么是Spring?
  • Spring是一个轻量级的IOC和AOP容器框架,是为Java应用程序提供基础性服务的一套框架,目的是用于简化企业应用程序的开发,它使得开发者只需要关心业务需求
  • 主要由以下几个模块组成:
  1. Spring Core:核心类库,提供IOC服务;
  2. Spring Context:提供框架式的Bean访问方式,以及企业级功能(JNDI,定时任务等)
  3. Spring AOP:AOP服务;
  4. Spring DAO:对JDBC的抽象,简化了数据访问异常的处理
  5. Spring ORM:对现有orm框架的支持
  6. Spring WEB:提供了基本的面向web的综合特性,例如多方文件上传;
  7. Spring MVC:提供面向web应用的model-view-controller实现
2. Spring的优点?
  1. Spring的控制反转和依赖注入特性将对象之间的依赖关系交由框架处理,减低组件的耦合性
  2. Spring提供了AOP技术,支持将一些通用任务,如安全,事务,日志,权限进行集中式管理,从而提供更好的复用
  3. Spring对于主流的应用框架提供了继承支持
3. Spring的AOP理解:
  • AOP,一般称为面向切面,作为面向对象的一种补充,用于将那些与业务无关,但却对多个对象产生影响的公共行为和逻辑,抽取并封装成一个可重用的模块,这个模块被命名为切面(Aspect),减少系统中的重复代码,降低了模块建的耦合度,同时提高了系统的可维护性,可以用于权限认证,日志,事务处理
  • Aop实现的关键在于代理模式 Aop代理主要分为静态代理和动态代理,静态代理的代表为AspectJ;动态代理则以SpringAop为代表
  1. AspectJ是静态代理的增强,所谓静态代理,就是Aop框架会在编译阶段生成Aop代理类,因此也成为编译时增强,他会在I编译阶段将AspectJ(切面)织入到Java字节码中,运行的时候就是增强之后的AOp对象
  2. SpringAOP使用的是动态代理,所谓的动态代理就是说AOP框架不会去修改字节码,而是每次运行时在内存中临时为方法生成一个AOP对象,这个AOP对象包含了目标对象的全部方法,并且在特定的切点做了增强处理,并回调原有对象的方法
    SpringAOP中的动态代理主要有两种方式,JDK动态代理和CGLIB动态代理:
    ①JDK动态代理只是提供接口的代理,不支持类的代理,核心InvocationHandler接口和Proxy类,InvocationHandler通过invoke() 方法反射来调用目标类中的代码,动态的将横切逻辑和业务编织在一起,接着,Proxy利用InvocationHandler动态的创建一个符合某一接口的实例,生成目标类的代理对象
    ②如果代理类没有实现InvocationHandler接口,那么SpringAOP会选择使用CGLIB来动态代理目标类CGLIB是一个代理生成的类库,可以在运行时动态的生成指定类的一个子类对象,并覆盖其中特定方法并添加增强代码,从而实现AOP,CGLIB是通过继承的方式做的动态代理,因此如果某个类被标记为final,那么它是无法使用CGLIB做动态代理的
  3. 静态代理与动态代理的区别是在与生成AOP代理对象的时机不同,相对来说AspectJ的静态代理方式具有更好的性能,但是AspectJ需要特定的编译器进行处理,而SpringAOP则无需特定的编译器处理
4. Spring的IOC理解:
  1. IOC就是控制反转,是指创建对象的控制权的转移,以前创建对象的主动权和时机都是自己把控的,而现在这种权利转移到Spring容器中,并由容器更具配置文件去创建实例和管理各个实例之间的依赖挂系,对象与对象之间松散耦合,也利于功能的复用,DI依赖注入,和控制反转是同一个概念的不同角度的描述,应用程序在运行时依赖IOC容器来动态的注入对象需要的外部资源
  2. 最直观的表达就是,IOC让对象的创建不用去new了,可以由Spring自动生产,使用Java的反射机制,根据配置文件在运行时动态的去创建对象以及管理对象,并调用对象的方法的
  3. Spring的IOC有三中注入方式:构造器注入,setter方法注入,根据注解注入
5. BeanFactory和ApplicationContext有什么区别?
  • BeanFactory和ApplicationContext是Spring的两大核心接口,都可以当作Spring的容器,其中ApplicationCOntext是Beanfactory的子接口
  • BeanFactory:是Spring里面最底层的接口,包含了各种Bean的定义,读取Bean配置文档,管理Bean的加载,实例化,控制Bean的声明周期,维护Bean之间的依赖关系,ApplicationContext接口作为BeanFactory的派生,除了提供BeanFactory所具有的功能外,还提供了更完整的框架功能:
  1. 继承MessageSource,因此支持国际化
  2. 统一的资源文件访问方式
  3. 提供在监听器中注册Bean的事件
  4. 同时加载多个配置文件
  5. 载入多个上下文,使得每一个上下文都专注于一个特定的层次,比如应用的web层
  • BeanFactory采用的是延迟加载形式来注入Bean的,即只有在使用到某个Bean时,才对该Bean进行加载实例化,这样,我们就不能发现一个存在Spring的配置问题,如果Bean的某一个属性没有注入,Beanfactory加载后,直至第一次使用调用getBean方法才会抛出异常
  • ApplicationContext它是在容器启动时,一次性创建了所有的Bean,这样,在容器启动时,我们就可以发现Spring中存在的配置错误,这样有利于检查所有依赖属性是否注入,ApplicationContext启动后预载入所有的单实例Bean,通过预载入单实例Bean,确保当你需要的时候就不用等待,因为他们已经创建好了
  • 相对于基本的BeanFactory,ApplicationContext唯一的不足是占用内存空间,当应用程序配置Bean较多时,程序启动较慢
  • Beanfactory通常以编程的方式被创建,ApplicationContext还能一声明的方式被创建,如使用ContextLoader
请解释Spring Bean的声明周期?
  • 首先说一下servlet的声明周期:实例化,初始化init,接收请求service,销毁destroy;
  • Spring上下文中的Bean声明周期也是类似
  1. 实例化Bean
    对于BeanFactory容器,当客户向容器请求一个尚未初始化的Bean时,或初始化Bean的时候需要注入另一个尚未初始化的依赖时,容器就会调用createBean进行实例化,对于ApplicationContext容器,当容器启动结束后,通过获取BeanDefinition对象中的信息,实例化所有的Bean
  2. 设置对象属性(依赖注入)
    实例化后的对象被封装在Beanwrapper对象中,紧接着,Spring根据BeanDefinition中的信息,以及通过beanwrapper提供的设置属性的接口完成依赖注入
  3. ** 处理Aware接口** :
    接着,Spring会检测该对象是否实现了xxxAware接口,并将相关的xxxAware实例注入给Bean:
    ①如果这个Bean已经实现了BeanNameAware接口,会调用它实现的SetBeanName方法,此处传递的就是Spring配置文件中Bean的id值;
    ②如果这个Bean已经实现了beanFactoryAware接口,会调用它实现的SetBeanFactory方法,传递的是Spring工厂自身
    ③如果这个Bean实现了ApplicationContextAware接口,会调用setApplicationContext(ApplicationContext)方法,传入Spring上下文
  4. BeanpostProcessor:
    如果相对Bean进行一些自定义的处理,那么可以让Bean实现了Beanpostprocessor接口,那将会调用postProcessorbeforeInitialization(Object obj, String s)方法,由于这个方法是在Bean初始化结束时调用的,所以可以被应用于内存或缓存技术
  5. InitializingBean 与 init-method
    如果Bean在Spring配置文件中配置了init-method属性,则会自动调用器配置的初始化方法
  6. **如果这个Bean实现了BeanPostProcessor接口,将会调用postProcessAfterInitialization(Object obj, String s)方法
    以上几步完成后,Bean就已经被正确创建了,之后就可以使用这个Bean了
  7. DisposableBean:
    当Bean不再需要时,会经过清理阶段,如果Bean实现了DisposableBean这个接口,会调用其实现的destroy方法
  8. destroy-method
    最后,如果这个Bean的Spring配置中配置了destroy-method属性,会自动调用其配置的销毁方法
7. Spring框架中的单例Bean是线程安全的么?
  • Spring框架并没有对单例Bean进行任何多线程的封装处理,关于单例Bean的线程安全和并发问题需要开发者自行去搞定,但实际上,大部分的Spring Bean并没有可变的状态(比如service类和dao类),所以在某种程度上说Spring的单例Bean是线程安全的,如果你的Bean有多种状态的话,就需要自行保护线程安全,最浅显的解决办法就是将多态Bean的作用域有singleton变更为prototype
8. Spring如何处理线程并发问题?
  • 在一般情况下,只有无状态的Bean才可以在多线程环境下共享,在Spring中,绝大部分Bean都可以声明为singleton作用域,因为Spring对一些Bean中非线程安全状态采用ThreadLocal进行处理,解决线程安全问题
  • Threadlocal和线程同步机制都是为了解决多线程中相同变量的访问冲突问题,同步机制采用了时间换空间的方式,仅提供一份变量,不同的线程在访问前需要获取锁,没获得锁的线程则需要排队,而ThreadLocal采用了空间换时间的方式
  • ThreadLocal会为每一个线程提供一个独立的变量副本,从而隔离了多个线程对数据的访问冲突,因为每一个线程都拥有自己的变量副本,从而也就没有必要对该变量进行同步了,ThreadLocal提供了线程安全的共享对象,在编写多线程代码时,可以把不安全的变量封装进ThreadLocal
9. Spring的自动装配:
  • 在 Spring中,对象无需自己查找或创建与其关联的其他对象,由容器负责把需要相互协作的对象引用赋予各个对象,使用@Autowired来配置自动装载模式
  • 在Spring框架xml配置中共有5种自动装配:
  1. no:默认的方式是不进行自动装配的,通过手工设置ref属性类进行装配Bean
  2. byName:通过Bean的名称进行自动装配,如果一个Bean的property与另一Bean的name相同,就进行自动装配
  3. byType:通过参数的数据类型进行自动装配
  4. constructor:利用构造函数进行装配,并且构造函数的参数通过byType进行装配
  5. autodetect;自动探测,如果有构造方法,通过construct的方式自动装配,否则使用byType的方式自动装配
  • 基于注解的方式:
    使用@Autowired注解来自动装配指定的Bean,在使用@Autowired注解之前需要在Spring文件进行配置<context:annotation-config/>,,在启动SpringIOC时,容器自动装载了一个AutowiredAnnotationBeanPostProcessor后置处理器,当容器扫描到@Autowied,@resource时,就会在IOC容器自动查找需要的Bean,并装配给该对象的属性,在使用@Autowied时,首先在容器中查询对应类型的Bean:
  1. 如果查询结果刚好为一个,就将该Bean装配给@Autowied指定的数据
  2. 如果查询的结果不止一个,那么@Autowied会根据名称来查找
  3. 如果上述查找的结果为空,那么会抛出异常
  4. @Autowired和@Resource之间的区别
    @Autowired默认是按照类型装配注入的,默认情况下它要求依赖对象必须存在
    @Resource默认是按照名称来装配注入的,只有当找不到与名称匹配的Bean才会按照类型来装配注入
10. Spring框架中都用到了哪些设计模式?
  1. 工厂模式:Beanfactory就是简单工厂模式的体现,用来创建对象的实例
  2. 单例模式:Bean默认为单例模式
  3. 代理模式:SpringAOp功能就是用到了JDK的动态代理和CGLIB字节码生成技术
  4. 模板方法:用来解决代码重复问题,比如RestTemplate
  5. 观察者模式:定义对象键一种一对多的依赖关系,当一个对象的状态发生改变时候,所有依赖于它的对象都会得到通知被制动更新,如Spring中listenter的实现-ApplicationListener
11. Spring事务的实现方式和实现原理:

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

  • Spring事务的种类:
    Spring支持编程式的事务管理和声明式事务管理两种方式:
    ①编程式事务管理使用TransactionTemplate
    ②声明式事务管理建立在AOP之上的,其本质是通过AOP功能,对方法前后进行拦截,将事务处理的功能编织到拦截的方法中,也就是在目标方法之前加入一个事务,在执行完目标方法之后根据执行情况提交或者回滚事务
  1. 声明式事务最大的优点就是不需要在业务逻辑代码中掺杂事务管理代码,只需要在配置文件中做相关的事务规则声明,或者通过@Transactional注解的方式,便可以将事务规则应用到业务逻辑中
  2. 声明式事务管理要优于编程式事务管理,这正是spring倡导的非侵入式的开发方式,是业务代码不受污染,只要加上注解就可以获得完全的事务支持,唯一不足地方是,最细粒度只能作用到方法级别,无法做到像编程式事务那样可以作用到代码块级别
  • Spring的事务传播行为
    Spring事务的传播行为说的是,当多个事务同时存在的时候,Spring如何处理这些事务的行为
  1. PROPAGATION_REQUIRED:如果当前没有事务,就创建一个新事物,如果当前存在事务,就加入该事物,该设置是最常用的设置
  2. PROPAGATION_SUPPORTS:支持当前事务,如果当前存在事务,就加入该事务,如果当前不存在失去,就以非事务执行
  3. PROPAGATION_MANDATORY:支持当前事务,如果当前存在事务,就加入该事务,如果当前不存在事务,就抛出异常
  4. PROPAGATION_REQUIRES_NEW:创建新事物,无论当前存不存在事务,都创建新事务
  5. PROPAGATION_NOT_SUPPORTED:以非实物当时执行操作,如果当前存在事务,就把当前事务挂起
  6. PROPAGATION_NEVER:以非实物方式执行,如果当前存在事务,则抛出异常
  7. PROPAGATION_NESTED:如果当前存在事务,则在嵌套事务内执行,如果当前没有事务,则按REQUIRED属性执行
  • Spring中的隔离级别
  1. 读未提交(Read uncommitted):读未提交,顾名思义,就是一个事务可以读取另一个未提交事务的数据。
  2. 读提交(Read committed):读提交,顾名思义,就是一个事务要等另一个事务提交后才能读取数据。
    分析:这就是读提交,若有事务对数据进行更新(UPDATE)操作时,读操作事务要等待这个更新操作事务提交后才能读取数据,可以解决脏读问题。但在这个事例中,出现了一个事务范围内两个相同的查询却返回了不同数据,这就是不可重复读。
  3. 重复读(Repeatable read);重复读,就是在开始读取数据(事务开启)时,不再允许修改操作
    分析:重复读可以解决不可重复读问题。写到这里,应该明白的一点就是,不可重复读对应的是修改,即UPDATE操作。但是可能还会有幻读问题。因为幻读问题对应的是插入INSERT操作,而不是UPDATE操作。
  4. 序列化(Serializable):Serializable 是最高的事务隔离级别,在该级别下,事务串行化顺序执行,可以避免脏读、不可重复读与幻读。但是这种事务隔离级别效率低下,比较耗数据库性能,一般不使用。
12. Spring框架中有哪些不同类型的时间?
  1. 上下文更新事件(ContextRefreshedEvent):在调用ConfigurableApplicationContext接口中的refresh方法时被触发
  2. 上下文开始事件(ContextStartedEvent):当容器调用ConfigurableApplicationContext的Start()方法开始/重新开始容器时触发该事件。
  3. 上下文停止事件(ContextStoppedEvent):当容器调用ConfigurableApplicationContext的Stop()方法停止容器时触发该事件。
  4. 上下文关闭事件(ContextClosedEvent):当ApplicationContext被关闭时触发该事件。容器被关闭时,其管理的所有单例Bean都被销毁。
  5. 请求处理事件(RequestHandledEvent):在Web应用中,当一个http请求(request)结束触发该事件。
    如果一个Bean实现了ApplicationListener接口,当一个ApplicationEvent被发布以后,Bean会自动被通知
13. 解释一下SpringAOP里面的几个名词:
  1. Target:目标类,需要被代理的类,如service
  2. Joinpoint:连接点,指哪些可能被拦截的方法,如目标类的所有法
  3. PointCut:切入点,已经被增强的连接点,如addStudent()
  4. Advice:增强/通知,增强代码,如before()/after()等
  5. Weaving:织入,把增强应用到目标类而创建出代理对象的这一个过程
  6. Proxy:代理,目标类被织入增强后就产生一个代理类
  7. Aspect:切面,切入点和增强的结合,被抽取的公共模块,可能会横切多个对象
14. Spring的通知类型有哪些?
  1. 前置通知(Before advice):在某连接点之前执行的通知,单这个通知不能阻止连接点的执行
  2. 返回后通知():在某连接点正常完成后执行的通知,例如一个方法没有排除异常,正常返回
  3. 抛出异常后通知():在方法抛出异常退出时执行的通知
  4. 后通知():当某连接点退出时候执行的通知
  5. 环绕通知():包围一个连接点的公职,如方法调用,这是最强大的一种通知类型,环绕通知可以在方法调用前后完成自定的行为,它也会选择是否继续执行连接点或直接返回他们自己的返回值或抛出异常来结束执行,环绕通知是最常用的一种通知类型,大部分基于拦截的AOP框架

环绕通知:必须手动执行目标方法
Try{
//在目标方法前面执行的是前置通知
执行目标方法,调过来
//在目标方法后面执行的是后置通知
}catch(){
//出现异常,异常通知
}

猜你喜欢

转载自blog.csdn.net/weixin_43863007/article/details/89042688