Spring 框架面试题

目录

一、Spring是什么?

二、Spring的AOP理解?

三、Spring的IOC理解?

[四、BeanFactory 和 ApplicationContext 有什么区别?](#四、BeanFactory 和 ApplicationContext 有什么区别?)

[五、请解释Spring Bean 的生命周期?](#五、请解释Spring Bean 的生命周期?)

[六、解释 Spring 支持的几种 bean 的作用域。](#六、解释 Spring 支持的几种 bean 的作用域。)

七、使用注解之前要开启自动扫描功能

[八、Spring 框架中都用到了哪些设计模式?](#八、Spring 框架中都用到了哪些设计模式?)

九、Spring事务的实现方式和实现原理


一、Spring是什么?

Spring 是一个轻量级的 IoC 和 AOP 容器框架。是为 Java 应用程序提供基础性服务的一套框架,目的是用于简化企业应用程序的开发,它使得开发者只需要关心业务需求。常见的配置方式有三种:基于 XML 的配置.基于注解的配置.基于 Java 的配置。

主要由以下几个模块组成:

Spring Core :核心类库,提供 IOC 服务;

Spring Context :提供框架式的 Bean 访问方式以及企业级功能 (JNDI.定时任务等);

Spring AOP · AOP 服务;

Spring DAO :对 JDBC 的抽象,简化了数据访问异常的处理;

Spring ORM :对现有的 ORM 框架的支持;

Spring web 提供了基本的面向 web 的综合特性,例如多方文件上传;

Spring MVC :提供面向 Web 应用的 Model-View-Controller 实现。

二、Spring的AOP理解?

OOP 面向对象,允许开发者定义纵向的关系,但并适用于定义横向的关系,导致了大量代码的重复,而不利于各个模块的重用。从而衍生出AOP。

AOP ,一般称为面向切面,作为面向对象的一种补充,用于将那些与业务无关,但却对多个对象产生影响的公共行为和逻辑,抽取并封装为一个可重用的模块,这个模块被命名为"切面'( Aspect ),减少系统中的重复代码,降低了模块间的耦合度,同时提高了系统的可维护性。可用于权限认证.日志.事务处理。

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

( 1 ) AspectJ 是静态代理的增强,所谓静态代理,就是 AOP 框架会在编译阶段生成 AOP 代理类,因此也称为编译时增强,他会在编译阶段将 AspectJ (切面)织入到Java 字节码中,运行的时候就是增强之后的 AOP 对象。

( 2 ) Spring AOP 使用的动态代理,所谓的动态代理就是说 AOP 框架不会去修改字节码,而是每次运行时在内存中临时为方法生成一个 AOP 对象,这个AOP 对象包含了目标对象的全部方法,并且在特定的切点做了增强处理,并回调原对象的方法。

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

(1)JDK 动态代理只提供接口的代理,不支持类的代理。核心InvocationHandler 接口和 Proxy 类, InvocationHandler 通过 invoke() 方法反射来调用目标类中的代码,动态地将横切逻辑和业务编织在一起;接着, Proxy利用 InvocationHandler 动态创建一个符合某一接口的的实例,生成目标类的代理对象。

( 2 )如果代理类没有实现 InvocationHandler 接口,那么 Spring AOP 会选择使用 CGLIB 来动态代理目标类。 CGLIB ( Code Generation Library ),是一个代码生成的类库,可以在运行时动态的生成指定类的一个子类对象,并覆盖其中特定方法并添加增强代码,从而实现 AOP。 CGLIB 是通过继承的方式做的动态代理,因此如果某个类被标记为 final ,那么它是无法使用 CGLIB 做动态代理的。

( 3 )静态代理与动态代理区别在于生成 AOP 代理对象的时机不同,相对来说 AspectJ 的静态代理方式具有更好的性能,但是 AspectJ 需要特定的编译器进行处理而 Spring AOP 则无需特定的编译器处理。InvocationHandIer 的invoke(ObJect proxy,Method method,Object[] a rgs): proxy 是最终生成的代理实例; method 是被代理目标实例的某个具体方法; args 是被代理目标实例某个方法的具体入参,在方法反射调用时使用。

步骤 1: 添加依赖

<dependencies>
    <!-- Spring AOP -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-aop</artifactId>
        <version>5.3.10</version> <!-- Use the appropriate version -->
    </dependency>
    <!-- AspectJ -->
    <dependency>
        <groupId>org.aspectj</groupId>
        <artifactId>aspectjweaver</artifactId>
        <version>1.9.7</version> <!-- Use the appropriate version -->
    </dependency>
</dependencies>

步骤 2: 定义一个简单的服务

定义一个简单的服务类,我们将在其方法上应用AOP。

public class SimpleService {
    public void performTask() {
        System.out.println("Performing task...");
    }
}

步骤 3: 定义一个Aspect

创建一个Aspect类,其中包含通知(Advice)和切点(Pointcut)。

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;

@Aspect
public class LoggingAspect {

    // 定义一个切点,匹配SimpleService类的performTask方法
    @Pointcut("execution(* SimpleService.performTask(..))")
    public void logPointcut() {
    }

    // 在匹配的切点上应用前置通知
    @Before("logPointcut()")
    public void logBefore() {
        System.out.println("Method is about to be called...");
    }
}

步骤 4: 配置Spring以启用AOP

在Spring配置文件中启用AOP并声明Aspect。

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

@Configuration
@EnableAspectJAutoProxy // 启用AspectJ自动代理
public class AppConfig {

    @Bean
    public SimpleService simpleService() {
        return new SimpleService();
    }

    @Bean
    public LoggingAspect loggingAspect() {
        return new LoggingAspect();
    }
}

步骤 5: 运行应用程序

最后,运行你的应用程序并调用SimpleServiceperformTask方法,你将看到AOP生效,控制台打印了日志信息。

Method is about to be called...
Performing task...

三、Spring的IOC理解?

( 1 ) IOC 就是控制反转,是指创建对象的控制权的转移,以前创建对象的主动权和时机是由自己把控的,而现在这种权力转移到 Spring 容器中,并由容器根据配置文件去创建实例和管理各个实例之间的依赖关系,对象与对象之间松散耦合,也利于功能的复用。 DI 依赖注入,和控制反转是同一个概念的不同角度的描述,即应用程序在运行时依赖 IoC 容器来动态注入对象需要的外部资源。

( 2 )最直观的表达就是 ,IOC 让对象的创建不用去 new 了可以由 spring自动生产,使用 java 的反射机制,根据配置文件在运行时动态的去创建对象以及管理对象,并调用对象的方法。

( 3 ) Spring 的 IOC 有三种注入方式:构造器注入 .setter 方法注入.根据注解注入。IOC 让相互协作的组件保持松散的耦合,而 AOP 编程允许你把遍布于应用各层的功能分离出来形成可重用的功能组件。

四、BeanFactory 和 ApplicationContext 有什么区别?

BeanFactory 和 ApplicationContext 是 Spring 的两大核心接口,都可以当做 Spring 的容器。其中 ApplicationContext 是 BeanFactory 的子接口。

( 1 ) BeanFactory :是 Spring 里面最底层的接口包含了各种 Bean 的定义,读取 bean 配置文档,管理 bean 的加载·实例化,控制 bean 的生命周期,维护 bean 之间的依赖关系。 ApplicationContext 接口作为 BeanFactory 的派生,除了提供 BeanFactory 所具有的功能外,还提供了更完整的框架功能:

  1. 继承 MessageSource ,因此支持国际化。
  2. 统一的资源文件访问方式。
  3. 提供在监听器中注册 bean 的事件。
  4. 同时加载多个配置文件。
  5. 载入多个(有继承关系)上下文,使得每一个上下文都专注于一个特定的层次,比如应用的 web 层。

( 2 ) BeanFactroy 采用的是延迟加载形式来注入 Bean 的,即只有在使用到某个 Bean 时(调用 getBean()) ,才对该 Bean 进行加载实例化。这样,我们就不能发现一些存在的 Spring 的配置问题。如果 Bean 的某一个属性没有注入,BeanFacotry 加载后,直至第一次使用调用 getBean 方法才会抛出异常。

ApplicationContext ,它是在容器启动时,一次性创建了所有的 Bean。 这样,在容器启动时,我们就可以发现 Spring 中存在的配置错误,这样有利于检查所依赖属性是否注入。 ApplicationContext 启动后预载入所有的单实例 Bean通过预载入单实例 bean ,确保当你需要的时候,你就不用等待,因为它们已经创建好了。

相对于基本的 BeanFactory , ApplicationContext 唯一的不足是占用内存空间。当应用程序配置 Bean 较多时,程序启动较慢。

( 3 ) BeanFactory 通常以编程的方式被创建, ApplicationContext 还能以声明的方式创建,如使用 ContextLoader。

( 4 ) BeanFactory 和 ApplicationContext 都支持BeanPostProcessor.BeanFactoryPostProcessor 的使用,但两者之间的区别是·:BeanFactory 需要手动注册,而 ApplicationContext 则是自动注册。

五、请解释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(String beanld) 方法,此处传递的就是 Spring 配置文件中 Bean的 id 值,如果这个 Bean 已经实现了 BeanFactoryAware 接口,则会调用它实现的setBeanFactory() 方法,传递的是 Spring 工厂自身。如果这个 Bean 已经实现了 ApplicationContextAware 接口,会调用setApplicationContext(ApplicationContext) 方法,传入 Spring 上下文;

( 4 ) BeanPostProcessor :如果想对 Bean 进行一些自定义的处理,那么可以让 Bean 实现了BeanPostProcessor 接口,那将会调用postProcessBeforeInitialization(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 属性,会自动调用其配置的销毁方法。

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

Spring 容器中的 bean 可以分为 5 个范围

( 1 ) singleton :默认,每个容器中只有一个 bean 的实例,单例的模式由BeanFactory 自身来维护。

( 2 ) prototype :为每一个 bean 请求提供一个实例。

( 3 ) request :为每一个网络请求创建一个实例,在请求完成以后, bean会失效并被垃圾回收器回收。

( 4 ) session :与 request 范围类似,确保每个 session 中有一个 bean 的实例,在 session 过期后, bean 会随之失效。

( 5 ) global-session :全局作用域, global-session 和 Portlet 应用相关。当你的应用部署在 Portlet 容器中工作时,它包含很多 portlet。 如果你想要声明让所有的 portlet 共用全局的存储变量的话,那么这全局变量需要存储在global-session 中。全局作用域与 Servlet 中的 session 作用域效果相同。

七、使用注解之前要开启自动扫描功能

步骤一:创建配置类并开启组件扫描

创建一个 Java 配置类并使用 @Configuration 注解标记它,表示这是一个Spring 配置类;在配置类中使用@ComponentScan注解来指定Spring应该扫描的包路径。

import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ComponentScan;

@Configuration
@ComponentScan(basePackages = "com.example") // 指定要扫描的包路径
public class AppConfig {
    // 配置其他bean
}

步骤二:使用注解定义组件

在指定的包路径下创建组件类,并使用相应的注解标记它们:

import org.springframework.stereotype.Component;

@Component // 表示这是一个Spring组件
public class MyComponent {
    // 组件实现
}

@Configuration 把一个类作为一个 IoC 容器,它的某个方法头上如果注册 了@Bean ,就会作为这个 Spring 容器中的 Beano

@Scope 注解作用域

@Service 用于标注业务层组件

@Controller 用于标注控制层组件(如 struts 中的 action )

@Repository 用于标注数据访问组件,即 DA0 组件。

@Component 泛指组件,当组件不好归类的时候,我们可以使用这个注解进行标注。@Scope 用于指定 scope 作用域的(用在类上)

@Autowired 默认按类型装配

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

( 1 )工厂模式: BeanFactory 就是简单工厂模式的体现,用来创建对象的实例;

( 2 )单例模式: Bean 默认为单例模式。

( 3 )代理模式: Spring 的 AOP 功能用到了 JDK 的动态代理和 CGLIB 字节码生成。( 4 )模板方法:用来解决代码重复的问题。比如. RestTemplate, JmsTemplate, JpaTemplate。

( 5 )观察者模式:定义对象键一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都会得到通知被制动更新,如 Spring 中 listener 的实现--ApplicationListener。

九、Spring事务的实现方式和实现原理

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

( 1 ) Spring 事务的种类:spring 支持编程式事务管理和声明式事务管理两种方式:A.编程式事务管理使用 TransactionTemplate。

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

声明式事务最大的优点就是不需要在业务逻辑代码中掺杂事务管理的代码,只需在配置文件中做相关的事务规则声明 或通过@Transactional 注解的方式,便可以将事务规规则应用到业务逻辑中。声明式事务管理要优于编程式事务管理,这正是 spring 倡导的非侵入式的发方式,使业务代码不受污染,只要加上注解就可以获得完全的事务支持。唯不一足地方是,最细粒度只能作用到方法级别,无法却故到像编程式事务那样可以作用到代码块级别。

( 2 ) 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 属性执行。

( 3 ) Spring 中的隔离级别:

  1. ISOLATION_DEFAULT :这是个 PlatfromTransactionManager 默认的隔离级别,使用数据库默认的事务隔离级别。
  2. ISOLATION-READ-UNCOMMITTED :读未提交,允许另外一个事务可以看到这个事务未提交的数据。
  3. ISOLATION_READ_COMMITTED :读已提交,保证一个事务修改的数据提交后才能被另一事务读取,而且能看到该事务对已有记录的更新。
  4. ISOLATION_REPEATABLE_READ :可重复读,保证一个事务修改的数据提交后才能被另一事务读取,但是不能看到该事务对已有记录的更新。
  5. ISOLATION_SERIALIZABLE :一个事务在执行的过程中完全看不到其他事务对数据库所做的更新。
相关推荐
计算机毕设指导614 分钟前
基于 SpringBoot 的作业管理系统【附源码】
java·vue.js·spring boot·后端·mysql·spring·intellij-idea
paopaokaka_luck31 分钟前
[371]基于springboot的高校实习管理系统
java·spring boot·后端
捂月2 小时前
Spring Boot 深度解析:快速构建高效、现代化的 Web 应用程序
前端·spring boot·后端
瓜牛_gn2 小时前
依赖注入注解
java·后端·spring
Estar.Lee2 小时前
时间操作[取当前北京时间]免费API接口教程
android·网络·后端·网络协议·tcp/ip
喜欢猪猪2 小时前
Django:从入门到精通
后端·python·django
一个小坑货2 小时前
Cargo Rust 的包管理器
开发语言·后端·rust
bluebonnet272 小时前
【Rust练习】22.HashMap
开发语言·后端·rust
uhakadotcom3 小时前
如何实现一个基于CLI终端的AI 聊天机器人?
后端
Iced_Sheep3 小时前
干掉 if else 之策略模式
后端·设计模式