Spring面试题

介绍

简单介绍一下你对Spring的理解。

Spring是一个一站式的轻量级的java开发框架;
两个特性:控制反转(IOC)面向切面编程(AOP);
针对于WEB层(springMVC)、业务层(IOC)、持久层(jdbcTemplate)等都提供了多种配置解决方案。

为什么要用Spring?

  1. 方便解耦。所有对象的创建和依赖关系的维护工作都交给Spring容器的管理。
  2. 低侵入式设计,代码污染极低,同时令代码对框架的依赖最小化。
  3. 支持AOP。减少系统重复代码,增加复用性。
  4. 声明式事务。只需要通过配置就可以完成对事物的管理。

为什么说Spring是一个容器?

用来形容它用来存储单例的bean对象这个特性。

AOP

⭐️⭐️什么是AOP?

AOP(Aspect-Oriented Programming,面向切面编程)能够将那些与业务无关,却为业务模块所共同调用的逻辑或责任(例如事务处理日志管理权限控制等)封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可扩展性和可维护性。

Spring AOP是基于动态代理的,如果要代理的对象实现了某个接口,那么Spring AOP就会使用JDK动态代理去创建代理对象;而对于没有实现接口的对象,就无法使用JDK动态代理,转而使用CGlib动态代理生成一个被代理对象的子类来作为代理。

当然也可以使用AspectJ(静态代理),Spring AOP中已经集成了AspectJ

JDK动态代理和cglib动态代理有什么区别?

JDK动态代理生成的代理类是继承了Proxy类,由于java不能多继承,只能由接口来完成对代理的实现,所以代理类必须传入被代理类实现的接口。(基于反射机制,只能针对接口编程)
cglib动态代理比JDK快,基于继承机制,继承被代理类,所以方法不要声明为final,然后重写父类方法达到增强了类的作用。需要额外引入cglib的依赖包。

Jdk动态代理要求被代理的类要实现接口,而cglib不需要,cglib能根据内存中为其创建子类(代理对象)
Spring Aop底层原理详解(利用spring后置处理器实现AOP)

Spring AOP和AspectJ AOP有什么区别?

Spring AOP是属于运行时增强,而AspectJ是编译时增强。Spring AOP基于代理(Proxying),而AspectJ基于字节码操作(Bytecode Manipulation)。

Spring AOP已经集成了AspectJ,AspectJ应该算得上是Java生态系统中最完整的AOP框架了。AspectJ相比于Spring AOP功能更加强大,但是Spring AOP相对来说更简单。

如果我们的切面比较少,那么两者性能差异不大。但是,当切面太多的话,最好选择AspectJ,它比SpringAOP快很多。

解释下Spring AOP里的几个名词

  • 连接点(Join Point):就是spring允许增强的地方,和方法有关的前前后后都是连接点。
  • 通知、增强处理(Advice):就是你想要的功能,也就是安全、事物、日志等。你给先定义好,然后再想用的地方用一下。包含切面的一段处理代码。
  • 切入点(Pointcut) :织入增强的连接点 就叫切入点。简单说就是不是所有的连接点都有机会成为切入点,只有那些受临幸的才可以。
  • 切面(Aspect): 切面是通知和切入点的结合。通知说明了干什么和什么时候干(什么时候通过规范的方法名中的before,after,around等就能知道);切入点说明了在哪干(指定到底是哪个方法)。这就是一个完整的切面定义。
  • 引入(introduction) :允许我们向现有的类添加新方法属性,也被称为内部类型声明。其实就是把切面用到目标类中了。
  • 目标(target) :引入中所提到的目标类,也就是要被通知的对象,也就是真正的业务逻辑,他可以在毫不知情的情况下,被咱们织入切面。而自己专注于业务本身的逻辑。由于SpringAOP是通过运行时代理实现的,所以这个对象永远是一个被代理对象。
  • 织入(weaving) :把切面应用到目标对象来创建新的代理对象的过程。有三种方式,spring采用的是运行时。

拦截器了解吗?有哪些应用场景?

spring拦截器是spring Aop的一种应用,在不修改源码的情况下,执行一段代码,以增强现有方法。写自己的拦截器一般有两种方式。

  1. 实现HandlerInterceptor接口
  2. 继承HandlerInterceptorAdapter抽象类

应用场景:

  • 记录接口响应时间

  • 判断用户是否登录

  • 判断用户的权限

  • 接口权限校验

IOC

谈一下你对Spring的IOC的理解。

IOC称之为控制反转。

之前是我们自己写代码new出来,而现在则是Spring帮我们new出来,我们无需再new。底层原理是Spring有一个容器为IOC,这个容器中开辟了很多个很重要的注解,其主要的为四大注解@Service@Controller@Respository@Component 。这四个注解会在他们的容器中开辟四个空间,还有一个注解为**@Autowired**,称之为DI,他会去找Spring容器中的bean,如果找到了就拿出来,找不到则会报一个查找不到的错误。当Spring第一次扫描,注解会帮我们new出想要的实现类,而下次使用autowired则直接拿来就用就ok了。

IOC(Inversion Of Controll,控制反转)是一种设计思想,就是将原本在程序中手动创建对象的控制权,交由给Spring框架来管理。IOC在其他语言中也有应用,并非Spring特有。IOC容器是Spring用来实现IOC的载体,IOC容器实际上就是一个Map(key, value),Map中存放的是各种对象。

将对象之间的相互依赖关系交给IOC容器来管理,并由IOC容器完成对象的注入。这样可以很大程度上简化应用的开发,把应用从复杂的依赖关系中解放出来。IOC容器就像是一个工厂一样,当我们需要创建一个对象的时候,只需要配置好配置文件/注解即可,完全不用考虑对象是如何被创建出来的。在实际项目中一个Service类可能由几百甚至上千个类作为它的底层,假如我们需要实例化这个Service,可能要每次都搞清楚这个Service所有底层类的构造函数,这可能会把人逼疯。如果利用IOC的话,你只需要配置好,然后在需要的地方引用就行了,大大增加了项目的可维护性且降低了开发难度。

Spring时代我们一般通过XML文件来配置Bean,后来开发人员觉得用XML文件来配置不太好,于是Sprng Boot注解配置就慢慢开始流行起来。

其他问题

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

设计模式

工厂模式:Spring使用工厂模式通过BeanFactoryApplicationContext创建bean对象。

单例模式Bean默认单例模式。

代理模式:SpringAOP的实现。

模板方法模式:大量的模板方法,例如JdbcTemplate等一些对数据库操作的类。

观察者模式:Spring 事件驱动模型就是观察者模式很经典的一个应用。

适配器模式:SpringMVC 中DispatcherServlet调用HandlerAdapter找到实际调用的Controller就是使用此模式,Spring AOP 的增强或通知(Advice)使用到了适配器模式。

包装器设计模式:我们的项目需要连接多个数据库,而且不同的客户在每次访问中根据需要会去访问不同的数据库。这种模式让我们可以根据客户的需求能够动态切换不同的数据源。
……

链接:Spring框架中都用到了哪些设计模式?

使用单例模式有什么好处?

  • 对于频繁使用的对象,可以省略创建对象所花费的时间,这对于那些重量级对象而言,是非常可观的一笔系统开销;

  • 由于 new 操作的次数减少,因而对系统内存的使用频率也会降低,这将减轻 GC 压力,缩短 GC 停顿时间。

Spring是如何实现事务的,原理如何。

首先要说明的是,事务一般是在操作数据库时提及。而Spring实现事务,本质上就是数据库对事务的支持。

  1. 编程式事务:在代码中硬编码(不推荐使用)。
  2. 声明式事务:在配置文件中配置(推荐使用),分为基于XML的声明式事务和基于注解的声明式事务。

Spring事务中的隔离级别有哪几种?

常量解释
ISOLATION_DEFAULT(默认使用后端数据库默认的隔离级别,Mysql默认采用的可重复读隔离级别;
Oracle默认采用的读已提交隔离级别。
ISOLATION_READ_UNCOMMITTED(读未提交最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读。
ISOLATION_READ_COMMITTED(读已提交允许读取并发事务已经提交的数据,可以阻止脏读,仍有可能幻读或不可重复读。
ISOLATION_REPEATABLE_READ(可重复读对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但仍有可能幻读。
ISOLATION_SERIALIZABLE(序列化最高的隔离级别,完全服从ACID的隔离级别。所有的事务顺序执行,将严重影响程序的性能。通常情况下也不会用到该级别。

Spring事务隔离级别与Mysql InnoDB事务隔离级别冲突了怎么办

  • Spring会在事务开始时,根据你程序中设置的隔离级别,调整数据库隔离级别与你的设置一致。
  • 当使用Serializable级别时,Mysql在执行SQL时会自动修改为select .... lock in share mode, 即使用共享锁。此时允许同时读,但只允许一个事务写,且锁的是行而不是整张表

这意味着:

  • 如果数据库不支持某种隔离级别,那么Spring设置了也无效。
  • 当使用Serializable级别时,如果两个事务读写的不是同一行,那么它们是互不影响的。如果操作同一行记录,那么允许同时读,但如果出现一个对此行的写操作,则在写事务没有提交之前,所有的读事务都会被block。

@Service注解是单例还是多例,如何做到多例?

单例在spring中是默认的,我们常用的service和dao层的对象通常都是单例的,但service或dao并不一定是单例,要产生多例,则在配置文件的bean中添加scope="prototype"

Spring中的单例bean的线程安全问题了解吗?

大部分时候我们并没有在系统中使用多线程,所以很少有人会关注这个问题。单例bean存在线程问题,主要是因为当多个线程操作同一个对象的时候,对这个对象的非静态成员变量的写操作会存在线程安全问题。

有两种常见的解决方案:

1.在bean对象中尽量避免定义可变的成员变量(不太现实)。

2.在类中定义一个ThreadLocal成员变量,将需要的可变成员变量保存在ThreadLocal中(推荐的一种方式)。

Spring中的bean生命周期?

  1. Bean容器找到配置文件中Spring Bean的定义。

  2. Bean容器利用反射创建一个Bean的实例。

  3. 如果涉及到一些属性值,利用set()方法设置一些属性值。

  4. 当要销毁Bean的时候,如果Bean实现了DisposableBean接口,执行destroy()方法

完整版 -->

  1. Spring IoC容器找到关于Bean的定义并实例化该Bean。
  2. Spring IoC容器对Bean进行依赖注入
  3. 如果Bean实现了BeanNameAware接口,则将该Bean的id传给setBeanName方法。
  4. 如果Bean实现了BeanFactoryAware接口,则将BeanFactory对象传给setBeanFactory方法。
  5. 如果Bean实现了BeanPostProcessor接口,则调用其postProcessBeforeInitialization方法。
  6. Bean实现了InitializingBean接口,则调用其afterPropertySet方法。
  7. 和Bean关联的BeanPostProcessors对象,则这些对象的postProcessAfterInitialization方法被调用。
  8. 销毁Bean实例时,如果Bean实现了DisposableBean接口,则调用其destroy方法。

Bean的作用域

类型说明
singleton默认值,当IoC容器一创建就会创建bean的实例,Spring中的bean默认都是单例的
prototype每次请求都会创建一个新的bean实例
request每次HTTP请求都会创建一个新的Bean,该bean仅在当前HTTP request内有效
session每一次HTTP请求都会产生一个新的bean,该bean仅在当前HTTP session内有效
globalSession全局session作用域,仅仅在基于Portlet的Web应用中才有意义,Spring5中已经没有了。Portlet是能够生成语义代码(例如HTML)片段的小型Java Web插件。它们基于Portlet容器,可以像Servlet一样处理HTTP请求。但是与Servlet不同,每个Portlet都有不同的会话。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值