目录
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");
}