lotus

贵有恒何必三更眠五更起 最无益只怕一日曝十日寒

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

目录 

1.Spring是什么?作用是什么?
2.你对IoC(控制反转 Inversion of Control)是怎么理解的?
3.什么是AOP(面向切面编程 Aspect Oriented Programming)?
4.Spring中Bean之间的依赖关系是怎样的?
5.Spring框架中的单例模式是怎样实现的?
6.Spring中的事务管理是如何实现的?
7.Spring的优势是什么?
8.解释一下Spring中的BeanFactory和ApplicationContext有什么区别。
9.Spring中的注解有哪些?
10.Spring MVC框架的流程是怎样的?
11.Spring Boot是什么?与传统的Spring框架有什么区别?
12.解释一下Spring的生命周期。
13.Spring中的IOC如何管理对象?
14.Spring中的AOP的具体实现方式有哪些?
15.Spring中的JdbcTemplate是什么?使用JdbcTemplate需要做哪些配置?
16.解释一下Spring中的循环依赖问题。
17.Spring中的事件驱动机制是怎样的?有哪些默认事件?
18.Spring的MVC框架中,HandlerAdapter的作用是什么?
19.@Autowired和@Resource注解用来干嘛的?
20.Spring中的事务传播属性有哪些?

 

 

Spring作为现代开发中最流行的框架之一,让开发者可以更加方便高效地构建应用系统。但是对于初学者而言,常常会遇到很多困惑。本篇文章收集了50道常见的Spring面试题,并对每道题进行了详细的解释,希望能够对读者有所帮助。

1.Spring是什么?作用是什么?

Spring是一个轻量级的JavaEE框架,它主要解决企业应用中的复杂性问题。

Spring框架有三个核心部分:IoC容器、AOP和数据访问/集成层。

  • Spring中的IoC容器提供了一种对象创建和对象之间关系管理的机制,以实现松散耦合和可扩展性。
  • AOP提供了一种很好的方式来实现横向关注点的处理,如事务管理,安全检查,缓存等。
  • 数据访问/集成层则提供了许多针对不同数据持久化技术的实现,比如JDBC,ORM和NoSQL。

 

横向关注点是指跨越应用程序多个模块的方法或功能,与业务逻辑无关,但是需要关注的部分¹³。比如日志、安全、缓存、事务等等¹²。横向关注点往往难以用面向对象编程的方式模块化,因为它们会散布在多个类或方法中¹²。面向切面编程(AOP)就是一种解决横向关注点的编程范式,它可以把横向关注点从业务逻辑中分离出来,通过切面、切点、通知等概念来定义和织入横向关注点¹²。

2.你对IoC(控制反转 Inversion  of Control)是怎么理解的?

  • IoC是一种设计思想,通过将对象之间的依赖关系交给容器来管理,从而实现松散耦合。
  • 在Spring中,IoC容器扮演了一个中心角色,负责创建对象,装配对象之间的依赖关系,并管理它们的整个生命周期。
  • IoC还提供了一些高级功能,如AOP和事件驱动等,可以进一步增强应用程序的可维护性和可扩展性。

 

3.什么是AOP(面向切面编程 Aspect Oriented Programming)?

  • AOP是一种编程范式,它能够在程序运行期间动态地将代码切入到原有代码的流程中,从而实现横向关注点的处理,如
    • 日志记录、
    • 事务管理
    • 安全检查等。
  • 在Spring中,
    • AOP是通过代理模式来实现的,在创建Bean时为Bean生成一个代理对象,代理对象能够拦截指定方法的调用,并在执行前或执行后执行特定操作。

4.Spring中Bean之间的依赖关系是怎样的?

在Spring中,Bean之间的依赖关系主要通过构造函数注入、Setter方法注入和自动注入来实现。

  • 构造函数注入是通过Bean的构造方法注入其他Bean作为参数来实现;
  • Setter方法注入是通过Bean的Setter方法来注入其他Bean;
  • 自动注入则是通过IoC容器在Bean创建时自动发现并注入其他Bean,可以通过设置@Autowired或@Resource注解来完成。

 

5.Spring框架中的单例模式是怎样实现的?

在Spring中,默认情况下,所有的Bean都是单例的。

  • 当IoC容器创建一个Bean时,它会缓存该Bean的实例,并在后续请求中返回相同的实例。
  • 这种机制可以提高应用程序的性能并减少内存消耗,但也需要注意线程安全和状态管理等问题。

6.Spring中的事务管理是如何实现的?

  • Spring通过AOP在运行时动态地将事务相关的逻辑切入到指定的方法中,从而实现事务管理。
  • 通常情况下,Spring使用声明式事务管理来管理事务。
  • 开发者可以通过@Transactional注解或XML配置文件来定义事务范围、传播属性以及回滚规则等。

 

拓展-Spring中的事务管理有两种方式,

  • 一种是编程式事务管理,即通过编写代码来控制事务的开始、提交和回滚;
  • 另一种是声明式事务管理,即通过配置或注解来声明事务的规则,让Spring框架来管理事务¹²。
    • 声明式事务管理有两种实现方式,
      • 一种是基于XML文件的方式,需要在配置文件中定义事务通知和切点,并开启tx命名空间和aop命名空间;
      • 另一种是基于注解的方式,需要在Spring容器中注册事务注解驱动,并在需要使用事务的类或方法上添加@Transactional注解

7.Spring的优势是什么?

Spring的主要优势有:

1)提高了应用程序的可扩展性和可维护性。

2)提供了一种松散耦合的方式来管理对象之间的依赖关系。

3)提供了完整的数据访问/集成层,支持多种持久化技术。

4)提供了AOP支持,可以很方便地实现横向关注点的处理。

5)提供了很好的集成支持,可以与其他框架无缝协作。

8.解释一下Spring中的BeanFactory和ApplicationContext有什么区别。

BeanFactory是Spring IoC容器的最基本形式,

提供了基本的IoC功能。它是延迟初始化的,即当一个Bean被实际使用时才被创建。

ApplicationContext是BeanFactory的子接口,提供了更多的企业级功能,如AOP,事件驱动,国际化等。

ApplicationContext是预初始化的,即在容器启动时就已经创建了所有的Bean。

9.Spring中的注解有哪些?

Spring中的常用注解有:

  • 1)@Component、@Service、@Repository、@Controller:用于标识Bean的角色。
  • 2)@Autowired、@Resource:用于自动装配Bean。
  • 3)@PostConstruct、@PreDestroy:用于指定初始化和销毁方法。
  • 4)@Transactional:用于声明式事务管理。
  • 5)@Scope:用于指定Bean作用域。

10.Spring MVC框架的流程是怎样的?

Spring MVC的处理流程如下:

  • 1)客户端向服务器发起请求。
  • 2)DispatcherServlet接收到请求并将请求发送给处理器映射器进行处理器的映射查找。
  • 3)处理器映射器将请求的URL映射到相应的控制器(Controller)上,并返回处理器及其拦截器(如果有)。
  • 4)DispatcherServlet调用处理器适配器(Adapter)执行映射的处理器(Controller)。
  • 5)处理器(Controller)执行后返回ModelAndView对象。
  • 6)DispatcherServlet将ModelAndView对象交给视图解析器(ViewResolver)进行解析。
  • 7)视图解析器(ViewResolver)解析出对应的视图(View)。
  • 8)DispatcherServlet将Model数据和View视图进行渲染后返回给客户端。

11.Spring Boot是什么?与传统的Spring框架有什么区别?

Spring Boot是一种基于Spring的快速应用开发框架,

  • 它能够让开发者更加快速地搭建基于Spring的应用程序,并提供了自动化配置、快速启动等便利功能。
  • 相比传统的Spring框架,Spring Boot具有更简洁的配置方式和更快的启动速度,同时还提供了更多的功能和特性支持。

12.解释一下Spring的生命周期。

在Spring中,Bean的生命周期包括以下几个阶段:

  • 1)实例化:当IoC容器读取到Bean的定义后,创建一个新的Bean实例。
  • 2)属性注入:IoC容器将Bean中声明的依赖关系和配置信息注入到Bean中。
  • 3)BeanPostProcessor前置处理:如果Bean中定义了BeanPostProcessor接口的实现类,它们将在Bean初始化之前被调用。
  • 4)初始化:如果Bean实现了InitializingBean接口,则在所有属性都被设置完毕后调用afterPropertiesSet()方法。同时,也可以通过@Bean(initMethod = "init")来指定Bean的初始化方法。
  • 5)BeanPostProcessor后置处理:如果Bean中定义了BeanPostProcessor接口的实现类,它们将在Bean初始化之后被调用。
  • 6)销毁:如果Bean实现了DisposableBean接口,则在容器关闭或者该Bean被从容器中移除时调用destroy()方法。同时,也可以通过@Bean(destroyMethod = "destroy")来指定Bean的销毁方法。

 

拓展-BeanPostProcessor

BeanPostProcessor是Spring的一个接口,它可以在Bean的初始化前后对Bean进行额外的处理。它有两个方法,分别是**postProcessBeforeInitialization**和**postProcessAfterInitialization**。

  • postProcessBeforeInitialization方法是在Bean的属性注入之后,初始化方法(如afterPropertiesSet或自定义的init-method)之前执行的。
  • postProcessAfterInitialization方法是在Bean的初始化方法之后执行的。

这两个方法都可以对Bean进行修改或增强,比如添加代理或其他逻辑。

 

拓展-使用BeanPostProcessor的例子

  • 一个例子是使用BeanPostProcessor来处理自定义注解。³

例如,可以定义一个@Log注解,用于在Bean的方法上打印日志。然后,可以实现一个LogPostProcessor类,继承BeanPostProcessor接口,重写postProcessAfterInitialization方法,在这个方法中,扫描Bean的所有方法,如果有@Log注解,就用动态代理生成一个代理对象,增加日志打印的逻辑。

  • 另一个例子是使用BeanPostProcessor来实现AOP(面向切面编程)。²

例如,可以定义一个@Aspect注解,用于标记切面类,和一些其他的注解,用于指定切点和通知。然后,可以实现一个AspectPostProcessor类,继承BeanPostProcessor接口,重写postProcessAfterInitialization方法,在这个方法中,扫描所有的切面类,解析切点和通知的信息,然后用动态代理或者字节码增强技术生成代理对象,织入切面逻辑。

这些例子只是展示了BeanPostProcessor的一些可能的应用场景,实际上还有很多其他的用法。你可以根据你的需求来自定义你的BeanPostProcessor类。

 

拓展-Spring框架本身有使用BeanPostProcessor的。

Spring框架提供了很多实现了BeanPostProcessor接口的类,用于实现各种功能,比如:

  • - **AutowiredAnnotationBeanPostProcessor**:用于处理@Autowired, @Value, @Inject等注解,实现自动注入和属性赋值。¹⁴
  • - **CommonAnnotationBeanPostProcessor**:用于处理@PostConstruct, @PreDestroy, @Resource等注解,实现初始化和销毁方法的调用和资源的注入。¹⁴
  • - **ApplicationListenerDetector**:用于检测Bean是否实现了ApplicationListener接口,如果是,就将其添加到事件监听器列表中。¹⁴
  • - **ConfigurationClassPostProcessor**:用于处理@Configuration, @Bean, @Import, @ImportResource等注解,实现配置类的解析和注册。²⁵
  • - **RequiredAnnotationBeanPostProcessor**:用于处理@Required注解,检查依赖属性是否已经设置。²⁵

这些只是一部分例子,还有很多其他的BeanPostProcessor类,你可以在Spring的文档或源码中查看更多详情。

 

拓展-InitializingBean接口应用场景

InitializingBean接口可以用在一些需要在Bean初始化之后执行一些特定的业务逻辑的场景中。¹²³

例如,一些常见的应用场景有:

  • - 在项目初始化时对常用数据进行缓存,比如从数据库或者配置文件中读取一些数据,然后存储到内存或者Redis中。¹
  • - 在项目初始化时对一些资源进行加载,比如读取一个XML文件,或者创建一个连接池。²⁴
  • - 在项目初始化时对一些配置进行检查,比如检查是否有必要的属性被注入,或者是否有冲突的配置。²⁴

当然,这些场景并不是只能使用InitializingBean接口来实现,你也可以使用其他的方式,比如XML配置文件中的init-method属性,或者注解中的@PostConstruct注解。²³

使用InitializingBean接口的好处是可以直接在Java代码中控制初始化逻辑,不需要依赖外部的配置文件或者注解。²⁴

使用InitializingBean接口的坏处是会增加对Spring框架的依赖,如果你想在没有Spring容器的环境下使用你的类,就需要额外的处理。²⁴

 

拓展-DisposableBean 应用场景

DisposableBean 是一个接口,为 Spring bean 提供了一种释放资源的方式,只有一个扩展方法 destroy()。实现 DisposableBean 接口,并重写 destroy(),可以在 Spring 容器销毁 bean 的时候获得一次回调。¹²

应用场景:在实际项目中,某些时候我们需要在程序 stop,对象销毁的的时候做一些销毁工作。例如在项目中,有大量的使用 redis 分布式锁的场景。²

 

13.Spring中的IOC如何管理对象?

  • 在Spring中,IOC通过Bean工厂来管理对象,
    • Bean工厂是提供了一种对象创建和对象之间关系管理的机制。
    • 它在读取配置文件时会自动创建和装配对象,并缓存这些对象以便后续使用。
    • 开发者可以通过构造器注入、Setter方法注入和自动注入等方式来管理对象之间的依赖关系。

 

14.Spring中的AOP的具体实现方式有哪些?

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

  • JDK动态代理又叫接口代理,它是基于Java反射机制实现的,要求目标对象必须实现至少一个接口;
  • CGLIB代理则是基于字节码操作实现的,当目标对象没有实现接口时,它就会使用CGLIB代理。

通常情况下,Spring默认使用JDK动态代理来实现AOP但如果目标对象没有实现任何接口,则会自动切换到使用CGLIB代理

 

拓展-CGLIB代理

CGLIB代理则是基于字节码操作的一种动态代理技术。

它可以在运行时为没有实现接口的类生成代理对象,通过继承被代理类并重写其非final方法来实现方法拦截。

CGLIB使用ASM框架来操作字节码,生成新的类。

CGLIB相比于JDK动态代理,具有更强的功能和更高的性能,但是也有一些缺点,比如不能代理final方法。

 

CGLIB的应用场景有以下几种:

  • - 当要代理的类没有实现接口或者为了更好的性能,可以使用CGLIB来创建代理对象。
  • - CGLIB被许多AOP框架使用,例如Spring AOP和dynaop,用于实现方法的拦截和织入横切逻辑。
  • - Hibernate使用CGLIB来代理单端single-ended (多对一和一对一)关联,用于实现持久化对象的字节码动态生成。

 

CGLIB的原理是这样的:

  • - CGLIB动态生成一个要代理类的子类,子类**重写**要代理的类的所有**不是final**的方法。
  • - 在子类中采用**方法拦截**的技术拦截所有父类方法的调用,顺势织入横切逻辑。
  • - CGLIB使用**字节码处理框架ASM**,来转换字节码并生成新的类。

 

 

15.Spring中的JdbcTemplate是什么?使用JdbcTemplate需要做哪些配置?

  • JdbcTemplate是Spring提供的一个对JDBC访问的封装,它简化了JDBC操作的繁琐和冗长,能够提高开发效率。
  • 使用JdbcTemplate需要在Spring的配置文件中
    • 配置基本数据源信息、
    • 配置JdbcTemplate Bean
    • 以及为JdbcTemplate Bean配置数据源。

16.解释一下Spring中的循环依赖问题。

循环依赖指两个或多个Bean之间相互引用,形成了一个无限循环调用的情况。

  • 在Spring容器中,如果存在循环依赖,但是都是单例模式的Bean,则可以通过Spring容器提前暴露正在创建的Bean,从而避免循环依赖问题。
  • 如果存在循环依赖且其中一方是原型模式的Bean,则Spring无法处理这种情况。

 

Spring中的循环依赖问题是指两个或多个Bean互相依赖对方,形成一个闭环,导致无法正常初始化Bean的情况。

例如,A依赖于B,B依赖于C,C又依赖于A,这就是一个循环依赖的例子。

Spring采用了一些解决方案来解决循环依赖问题。

例如,

  • 使用构造函数注入可以避免Setter注入的循环依赖问题,因为这种方式可以保证Bean在实例化时已经完成了依赖关系的设置。
  • 使用@Lazy注解可以使Bean延迟初始化,从而避免循环依赖问题的发生。
  • 对于单例的Bean,Spring还使用了三级缓存的机制,将Bean的实例化和属性注入分为两步进行,并在不同的缓存中保存Bean的引用,从而在遇到循环依赖时能够提前暴露Bean的引用,打破循环。

 

17.Spring中的事件驱动机制是怎样的?有哪些默认事件?

  • Spring中的事件驱动机制基于观察者模式实现。
    • 当一个Bean发生事件时,它会创建一个事件对象并将其发布到Spring容器上。容器中所有对该事件对象感兴趣的Bean都会收到事件通知并进行相应的处理。
  • Spring中默认的事件有:
    • ContextRefreshedEvent、
    • ContextStartedEvent、
    • ContextStoppedEvent
    • 和ContextClosedEvent。

18.Spring的MVC框架中,HandlerAdapter的作用是什么?

HandlerAdapter是Spring MVC框架中的一个重要组件,

它的作用是

  • 将请求交给相应的处理器(Controller)进行处理,
  • 并负责调用处理器的方法来生成ModelAndView对象。

 

19.@Autowired和@Resource注解用来干嘛的?

@Autowired和@Resource注解是用于自动装配Bean的。

  • @Autowired是Spring提供的注解,可以在Setter方法上、构造函数上、类属性上进行标注,用于自动注入匹配类型的Bean。
  • @Resource是JavaEE提供的注解,可以在Setter方法上或类属性上进行标注,用于自动注入指定名称和类型匹配的Bean。

20.Spring中的事务传播属性有哪些?

Spring中的事务传播属性是指在多个事务方法相互调用时,事务如何在这些方法间传播的行为¹²³。Spring中定义了七种事务传播属性,分别是:

  • - PROPAGATION_REQUIRED:如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。这是最常见的选择,也是Spring的默认设置。
  • - PROPAGATION_SUPPORTS:支持当前事务,如果当前没有事务,就以非事务方式执行。
  • - PROPAGATION_MANDATORY:使用当前的事务,如果当前没有事务,就抛出异常。
  • - PROPAGATION_REQUIRES_NEW:新建事务,如果当前存在事务,把当前事务挂起。
  • - PROPAGATION_NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
  • - PROPAGATION_NEVER:以非事务方式执行,如果当前存在事务,则抛出异常。
  • - PROPAGATION_NESTED:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作。

不同的事务传播属性会影响到事务的提交和回滚的范围和效果。

例如,PROPAGATION_REQUIRED会使多个方法共享同一个事务,那么任何一个方法发生异常都会导致整个事务回滚;

而PROPAGATION_REQUIRES_NEW会使每个方法拥有自己的独立事务,那么一个方法发生异常不会影响其他方法的提交。

 

不同的事务传播属性会影响到事务的提交和回滚的范围和效果

下面举一些例子来说明:

假设有两个方法A和B,方法A执行会在数据库ATable插入一条数据,方法B执行会在数据库BTable插入一条数据,然后抛出异常。伪代码如下:

public void A(String name) {
insertIntoATable(name);
}

public void B(String name) {
insertIntoBTable(name);
throw new RuntimeException();
}

假设有一个主方法mainTest,调用了A和B两个方法,伪代码如下:

public void mainTest(String name) {
A("a1");
B("b1");
}

 

 

posted on 2023-06-12 18:39  白露~  阅读(17)  评论(0编辑  收藏  举报