当前位置: 首页 > news >正文

Spring常见题

题目 1:@Component@Bean 的区别是什么?

问题描述:
请解释 @Component@Bean 注解的区别,并说明它们各自的使用场景。

答案:

  • @Component

    • 是一个类级别的注解,用于将类标记为 Spring 容器中的组件。
    • Spring 会通过类路径扫描(@ComponentScan)自动发现并注册该类为 Bean。
    • 示例:
      @Component
      public class MyService {
          // ...
      }
      
  • @Bean

    • 是一个方法级别的注解,通常用在配置类中(标注了 @Configuration 的类)。
    • 用于显式地定义一个 Bean,并由方法返回值提供 Bean 实例。
    • 示例:
      @Configuration
      public class AppConfig {
          @Bean
          public MyService myService() {
              return new MyService();
          }
      }
      
主要区别
特性@Component@Bean
定义方式类级别注解方法级别注解
自动注册需要类路径扫描手动定义在配置类中
使用场景适用于简单的组件适用于复杂的对象创建逻辑或第三方库
总结
  • 如果是自己开发的类,推荐使用 @Component
  • 如果需要手动控制 Bean 的创建过程(如第三方库或复杂逻辑),推荐使用 @Bean

题目 2:@Autowired@Resource 的区别是什么?

问题描述:
请解释 @Autowired@Resource 注解的区别,并说明它们各自的使用场景。

答案:

  • @Autowired

    • 是 Spring 提供的注解,支持按类型注入(byType)。
    • 默认情况下,如果找不到匹配的 Bean,会抛出异常。
    • 可以通过 @Qualifier 指定具体的 Bean 名称。
    • 示例:
      @Autowired
      @Qualifier("myService")
      private MyService myService;
      
  • @Resource

    • 是 JSR-250 标准提供的注解,支持按名称注入(byName)。
    • 如果未指定名称,则默认按字段名查找 Bean。
    • 如果按名称找不到,则回退到按类型注入。
    • 示例:
      @Resource(name = "myService")
      private MyService myService;
      
主要区别
特性@Autowired@Resource
规范来源SpringJSR-250 标准
注入方式默认按类型注入默认按名称注入
异常处理找不到 Bean 时抛出异常找不到 Bean 时回退到按类型注入
总结
  • 如果需要更灵活的注入方式(如按名称注入),推荐使用 @Resource
  • 如果完全依赖 Spring 生态系统,推荐使用 @Autowired

题目 3:@Controller@Service@Repository 的区别是什么?

问题描述:
请解释 @Controller@Service@Repository 注解的区别,并说明它们的作用。

答案:

  • @Controller

    • 用于标记控制器层的类,通常用于处理 HTTP 请求。
    • 结合 @RequestMapping 或其他映射注解,定义请求的处理逻辑。
    • 示例:
      @Controller
      public class MyController {
          @RequestMapping("/hello")
          public String hello() {
              return "Hello, World!";
          }
      }
      
  • @Service

    • 用于标记服务层的类,通常包含业务逻辑。
    • 主要用于区分业务逻辑层和其他层(如控制器层、数据访问层)。
    • 示例:
      @Service
      public class MyService {
          public String processData() {
              return "Processed Data";
          }
      }
      
  • @Repository

    • 用于标记数据访问层的类,通常与数据库交互。
    • Spring 会自动为 @Repository 注解的类添加异常转换功能,将数据访问异常转换为 Spring 的数据访问异常。
    • 示例:
      @Repository
      public class MyRepository {
          public void saveData() {
              // 数据库操作
          }
      }
      
主要区别
注解层次作用
@Controller控制器层处理 HTTP 请求
@Service服务层包含业务逻辑
@Repository数据访问层数据库交互,自动转换异常
总结
  • 这三个注解本质上都是 @Component 的特化版本,用于分层管理代码。
  • 使用这些注解有助于提高代码的可读性和可维护性。

题目 4:Spring 中的单例 Bean 是否线程安全?为什么?

问题描述:
请解释 Spring 中的单例 Bean 是否线程安全,并说明原因。

答案:

  • 单例 Bean 的线程安全性

    • Spring 容器中的单例 Bean 默认是线程不安全的。
    • 原因:Spring 容器只会创建一个实例,并将其共享给所有线程使用。如果该 Bean 中有可变状态(如成员变量),则可能会导致线程安全问题。
  • 示例

    @Component
    public class MySingletonBean {
        private int count = 0;
    
        public void increment() {
            count++;
            System.out.println("Count: " + count);
        }
    }
    

    如果多个线程同时调用 increment() 方法,count 的值可能会出现竞争条件。

  • 解决方法

    1. 无状态设计:避免在单例 Bean 中使用可变状态(推荐)。
      @Component
      public class MySingletonBean {
          public void process() {
              // 无状态逻辑
          }
      }
      
    2. 同步机制:使用 synchronized 或锁确保线程安全。
    3. ThreadLocal:为每个线程维护独立的状态副本。
总结
  • 单例 Bean 的线程安全性取决于其设计。如果 Bean 是无状态的,则天然线程安全;否则需要额外的同步措施。

题目 5:Spring 的 @Transactional 注解有哪些常见的坑?

问题描述:
请列举 Spring 中使用 @Transactional 注解时常见的坑,并说明如何避免。

答案:

  1. 事务方法必须是 public 的

    • Spring 的 AOP 代理机制要求事务方法必须是 public 的,否则事务不会生效。
    • 解决方法:确保事务方法是 public
  2. 事务传播行为未正确设置

    • 默认的传播行为是 PROPAGATION_REQUIRED,可能不符合实际需求。
    • 解决方法:根据业务需求明确设置传播行为。
  3. 自调用问题

    • 如果一个类中的方法调用了另一个带有 @Transactional 注解的方法,事务不会生效。
    • 原因:Spring 的事务管理是基于代理的,自调用绕过了代理。
    • 解决方法:将事务方法抽取到另一个 Bean 中,或者使用 AopContext.currentProxy()
  4. 异常未被捕获导致事务提交

    • 默认情况下,只有未捕获的运行时异常(RuntimeException)和错误(Error)才会触发回滚。
    • 解决方法:使用 rollbackFor 属性指定需要回滚的异常类型。
      @Transactional(rollbackFor = Exception.class)
      public void myMethod() throws Exception {
          // ...
      }
      
  5. 事务隔离级别未正确设置

    • 默认隔离级别是 ISOLATION_DEFAULT,可能导致脏读、不可重复读等问题。
    • 解决方法:根据业务需求明确设置隔离级别。
总结
  • 使用 @Transactional 注解时需要特别注意方法签名、传播行为、异常处理等细节,避免陷入常见陷阱。

题目 6:解释 Spring 中的循环依赖问题,并说明如何解决

问题描述:
请详细解释 Spring 中的循环依赖问题(Circular Dependency),并说明 Spring 如何解决它。如果无法解决,有哪些替代方案?

答案:

循环依赖问题
  • 循环依赖是指两个或多个 Bean 相互依赖,形成一个闭环。例如:
    @Component
    public class A {
        private final B b;
    
        @Autowired
        public A(B b) {
            this.b = b;
        }
    }
    
    @Component
    public class B {
        private final A a;
    
        @Autowired
        public B(A a) {
            this.a = a;
        }
    }
    
    在这种情况下,A 依赖 B,而 B 又依赖 A,导致 Spring 容器无法完成注入。
Spring 的解决方式
  • Spring 使用三级缓存机制来解决循环依赖问题:

    1. 一级缓存(Singleton Objects):存储完全初始化好的单例 Bean。
    2. 二级缓存(Early Singleton Objects):存储提前暴露的 Bean(尚未完全初始化)。
    3. 三级缓存(Singleton Factories):存储 Bean 的工厂对象,用于生成早期引用。
  • 当 Spring 检测到循环依赖时:

    1. 它会先将未完全初始化的 Bean 放入二级缓存中。
    2. 其他 Bean 可以从二级缓存中获取该 Bean 的引用,完成注入。
    3. 最终,所有 Bean 都会被完全初始化并放入一级缓存。
局限性
  • Spring 只能解决单例(Singleton)作用域的循环依赖。
  • 对于原型(Prototype)作用域的 Bean,Spring 无法解决循环依赖。
替代方案
  • 重构代码:通过引入中间层(如接口或服务类)来打破循环依赖。
  • 使用 Setter 注入:相比于构造器注入,Setter 注入可以延迟依赖注入的时间,从而避免部分循环依赖问题。

题目 7:Spring AOP 的底层实现原理是什么?Proxy 和 CGLIB 的区别是什么?

问题描述:
请解释 Spring AOP 的底层实现原理,并比较 Proxy 和 CGLIB 的区别。

答案:

Spring AOP 的底层实现
  • Spring AOP 是基于动态代理实现的,支持两种代理方式:

    1. JDK 动态代理
      • 基于接口实现,要求目标对象必须实现至少一个接口。
      • Spring 创建一个实现了相同接口的代理对象,在调用方法时拦截并应用切面逻辑。
    2. CGLIB 动态代理
      • 基于子类实现,不要求目标对象实现接口。
      • Spring 创建一个目标对象的子类,在子类中重写方法以应用切面逻辑。
  • 代理选择规则

    • 如果目标对象实现了接口,Spring 默认使用 JDK 动态代理。
    • 如果目标对象没有实现接口,Spring 使用 CGLIB。
Proxy 和 CGLIB 的区别
特性JDK 动态代理CGLIB 动态代理
实现代理的方式基于接口基于子类
性能较慢(反射调用)较快(字节码生成)
目标对象要求必须实现接口不需要实现接口
方法拦截只能拦截接口中的方法可以拦截所有非 final 方法
注意事项
  • CGLIB 不能代理 final 方法,因为子类无法重写 final 方法。
  • 在 Spring Boot 中,默认使用 CGLIB(即使目标对象实现了接口),可以通过配置切换回 JDK 动态代理。

题目 8:Spring 中的事务传播行为有哪些?请举例说明 PROPAGATION_REQUIREDPROPAGATION_REQUIRES_NEW 的区别

问题描述:
请列举 Spring 中的事务传播行为,并说明 PROPAGATION_REQUIREDPROPAGATION_REQUIRES_NEW 的区别。

答案:

事务传播行为

Spring 提供了以下七种事务传播行为:

  1. PROPAGATION_REQUIRED:如果当前存在事务,则加入该事务;否则创建一个新事务。
  2. PROPAGATION_REQUIRES_NEW:总是创建一个新事务,如果当前存在事务,则挂起当前事务。
  3. PROPAGATION_SUPPORTS:如果当前存在事务,则加入该事务;否则以非事务方式运行。
  4. PROPAGATION_NOT_SUPPORTED:以非事务方式运行,如果当前存在事务,则挂起当前事务。
  5. PROPAGATION_MANDATORY:如果当前存在事务,则加入该事务;否则抛出异常。
  6. PROPAGATION_NEVER:以非事务方式运行,如果当前存在事务,则抛出异常。
  7. PROPAGATION_NESTED:如果当前存在事务,则在嵌套事务中运行;否则创建一个新事务。
PROPAGATION_REQUIREDPROPAGATION_REQUIRES_NEW 的区别
  • PROPAGATION_REQUIRED

    • 如果外层方法已经开启了一个事务,则内层方法会复用该事务。
    • 如果外层方法没有事务,则内层方法会创建一个新事务。
    • 示例:
      @Transactional(propagation = Propagation.REQUIRED)
      public void outerMethod() {
          innerMethod(); // 复用同一个事务
      }
      
      @Transactional(propagation = Propagation.REQUIRED)
      public void innerMethod() {
          // 执行数据库操作
      }
      
  • PROPAGATION_REQUIRES_NEW

    • 内层方法总是会创建一个新事务,即使外层方法已经有一个事务。
    • 外层事务会被挂起,直到内层事务完成。
    • 示例:
      @Transactional(propagation = Propagation.REQUIRED)
      public void outerMethod() {
          innerMethod(); // 内层方法启动新事务
      }
      
      @Transactional(propagation = Propagation.REQUIRES_NEW)
      public void innerMethod() {
          // 启动独立事务
      }
      
实际影响
  • 如果 innerMethod 抛出异常,PROPAGATION_REQUIRED 会导致整个事务回滚,而 PROPAGATION_REQUIRES_NEW 只会回滚内层事务,外层事务不受影响。

题目 9:Spring Boot 的自动配置是如何工作的?请解释其核心原理

问题描述:
请解释 Spring Boot 自动配置的工作原理,并说明其核心组件和实现细节。

答案:

自动配置的核心原理
  • Spring Boot 的自动配置是基于条件化配置(Conditional Configuration)实现的。
  • 它的核心思想是根据项目的依赖、环境变量等条件,自动注册适当的 Bean。
关键组件
  1. @EnableAutoConfiguration

    • 启用自动配置功能,通常与 @SpringBootApplication 注解一起使用。
    • 它会扫描 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 文件,加载所有符合条件的自动配置类。
  2. @Conditional 注解族

    • 例如 @ConditionalOnClass@ConditionalOnMissingBean 等,用于根据条件决定是否加载某个配置类。
    • 示例:
      @Configuration
      @ConditionalOnClass(DataSource.class)
      public class DataSourceAutoConfiguration {
          @Bean
          @ConditionalOnMissingBean
          public DataSource dataSource() {
              return new EmbeddedDatabaseBuilder().build();
          }
      }
      
  3. spring.factories 文件(Spring Boot 2.7 之前)

    • spring.factories 文件中定义了自动配置类的列表。
    • 示例:
      org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
      com.example.MyAutoConfiguration
      
  4. AutoConfiguration.imports 文件(Spring Boot 3.0+)

    • 替代了 spring.factories,更高效地加载自动配置类。
工作流程
  1. Spring Boot 启动时,会扫描类路径下的所有依赖库。
  2. 根据 spring.factoriesAutoConfiguration.imports 文件加载自动配置类。
  3. 自动配置类通过条件注解判断是否满足条件,如果满足则注册相应的 Bean。
http://www.dtcms.com/a/108436.html

相关文章:

  • Go语言学习(15)结构体标签与反射机制
  • ES6(8) Fetch API 详解
  • C#:base 关键字
  • 铂卡梭 智能羽翼 AI 系统:交易科技的未来引擎
  • php8 match表达式使用教程
  • 软件重构与项目进度的矛盾如何解决
  • Node.js全局生效的中间件
  • pytorch中Dropout
  • Vue + Scss项目中实现自定义颜色主题的动态切换
  • 深入解析Translog机制:Elasticsearch的数据守护者
  • MySQL 服务基础介绍
  • 第二十章:Python-Matplotlib库实现函数可视化
  • 日本IT|浅谈intramart现状及分析
  • 玛卡巴卡的k8s知识点问答题(六)
  • QT软件设计可考虑回答
  • GFS论文阅读笔记
  • 《二叉树:二叉树的顺序结构->堆》
  • Linux基础命令:开启系统操作之旅
  • 将一个新的机器人模型导入最新版isaacLab进行训练(以unitree H1_2为例)
  • 2025最新WordPress网站被挂码的原因与解决方案
  • 透视投影(Perspective projection)与等距圆柱投影(Equirectangular projection)
  • 学习笔记 - Flask - 02
  • Sam Altman 表示 OpenAI 的性能问题将导致产品延迟
  • [Linux]从硬件到软件理解操作系统
  • 学习笔记—数据结构—二叉树(算法题)
  • fyrox 2D和3D游戏的制作
  • 【云计算物理网络】数据中心网络架构设计
  • 蓝桥杯备考:完全二叉树的节点个数
  • mysql and redis简化版
  • 【Easylive】视频在线人数统计系统实现详解 WebSocket 及其在在线人数统计中的应用