尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

Java 面试高频题精讲:Spring 核心原理与分布式技术实战

Java 面试高频题精讲:Spring 核心原理与分布式技术实战 171. 谈一谈你对面向对象设计原则的理解。答面向对象设计原则是指导我们进行软件设计的一系列经验法则主要包括以下几个方面单一职责原则SRP一个类只负责一个职责只有一个引起它变化的原因。如果一个类承担的职责过多那么这些职责耦合在一起一个职责的变化可能会削弱或抑制这个类完成其他职责的能力。开闭原则OCP软件实体类、模块、函数等应该对扩展开放对修改关闭。即当需求变化时应该通过扩展来实现新的功能而不是修改已有的代码。这是面向对象设计的核心原则。里氏替换原则LSP所有引用基类的地方必须能透明地使用其子类的对象。即子类可以扩展父类的功能但不能改变父类原有的功能。依赖倒置原则DIP高层模块不应该依赖低层模块二者都应该依赖抽象抽象不应该依赖细节细节应该依赖抽象。即面向接口编程而不是面向实现编程。接口隔离原则ISP使用多个专门的接口比使用单一的总接口要好。即客户端不应该依赖它不需要的接口。迪米特法则LoD一个对象应该对其他对象保持最少的了解。即一个类应该尽量减少对其他类的依赖降低类之间的耦合度。172. 什么是依赖注入DISpring 框架是如何实现依赖注入的答依赖注入是控制反转IoC的一种实现方式它是指将对象所依赖的其他对象的创建和装配过程从对象自身中分离出来由外部容器负责完成。这样做的好处是降低了对象之间的耦合度使得对象更容易被测试和复用。Spring 框架实现依赖注入主要有以下几种方式Setter 注入通过调用对象的 setter 方法注入依赖。示例代码如下ComponentpublicclassUserService{privateUserDaouserDao;// 通过 setter 方法注入依赖AutowiredpublicvoidsetUserDao(UserDaouserDao){this.userDaouserDao;}}关键点Setter 注入通过Autowired标注在 setter 方法上Spring 容器在创建 Bean 后会调用该方法完成依赖注入。这种方式灵活但依赖可能在对象创建后才被注入存在依赖未就绪的风险。构造器注入通过构造器参数注入依赖这种方式可以保证对象在创建时就被完整地初始化。示例代码如下ComponentpublicclassUserService{privatefinalUserDaouserDao;// 通过构造器参数注入依赖AutowiredpublicUserService(UserDaouserDao){this.userDaouserDao;}}关键点构造器注入将依赖作为构造器参数传入配合final关键字可以保证依赖不可变且对象创建时依赖必然已就绪。这是 Spring 官方推荐的注入方式尤其适合必选依赖。注解注入使用Autowired、Resource等注解由 Spring 容器自动完成依赖的装配。示例代码如下ComponentpublicclassUserService{// 直接在字段上标注注解由 Spring 自动注入AutowiredprivateUserDaouserDao;}关键点注解注入直接在字段上标注Autowired代码最简洁。Autowired默认按类型装配Resource默认按名称装配。缺点是依赖对反射可见且字段无法声明为final不利于单元测试。Spring 容器在创建 Bean 时会根据配置或注解信息自动完成依赖的查找和注入从而实现了对象之间的解耦。173. Spring 中 Bean 的作用域有哪些答Spring 中 Bean 的作用域主要有以下几种singleton默认作用域容器中只存在一个 Bean 实例所有对该 Bean 的请求都返回同一个实例。prototype每次请求都会创建一个新的 Bean 实例。request每次 HTTP 请求都会创建一个新的 Bean 实例仅在 Web 应用上下文中有效。session每个 HTTP 会话都会创建一个新的 Bean 实例仅在 Web 应用上下文中有效。application每个 ServletContext即整个 Web 应用共享一个 Bean 实例仅在 Web 应用上下文中有效。websocket每个 WebSocket 连接都会创建一个新的 Bean 实例仅在 Web 应用上下文中有效。其中singleton和prototype是最常用的两种作用域request、session、application和websocket作用域仅在 Web 环境中可用。174. 什么是 AOPSpring AOP 的实现原理是什么答AOPAspect Oriented Programming面向切面编程是一种编程范式它通过将横切关注点如日志、事务、安全等从业务逻辑中分离出来以模块化的方式进行管理从而降低代码的耦合度提高代码的复用性和可维护性。Spring AOP 的实现原理主要有两种基于代理Proxy的方式Spring AOP 默认使用 JDK 动态代理或 CGLIB 代理来为目标对象创建代理对象。如果目标对象实现了接口则使用 JDK 动态代理如果目标对象没有实现接口则使用 CGLIB 代理。在代理对象中Spring 会在调用目标方法的前后织入增强逻辑如前置通知、后置通知、环绕通知等。基于 AspectJ 的方式Spring 也支持使用 AspectJ 注解如Aspect、Before、After等来定义切面通过 AspectJ 的织入机制在编译期或加载期将切面织入目标类中。Spring AOP 主要支持方法级别的切面适用于 Spring 管理的 Bean。175. 什么是事务Spring 是如何管理事务的答事务是一组逻辑上的操作单元这些操作要么全部成功要么全部失败回滚从而保证数据的一致性和完整性。事务具有 ACID 四大特性原子性Atomicity、一致性Consistency、隔离性Isolation和持久性Durability。Spring 管理事务的方式主要有两种编程式事务管理通过TransactionTemplate或PlatformTransactionManager手动控制事务的提交和回滚。这种方式灵活但代码侵入性较强。声明式事务管理通过Transactional注解或 XML 配置来声明事务的边界由 Spring 容器在运行时自动管理事务的开启、提交和回滚。这种方式代码侵入性低是实际开发中最常用的方式。Spring 声明式事务的底层原理是基于 AOP 的通过代理对象在方法调用前后自动开启和提交/回滚事务。需要注意的是Transactional注解默认只对RuntimeException和Error进行回滚对受检异常Checked Exception默认不回滚如果需要回滚需要显式指定rollbackFor属性。此外在实际开发中Transactional注解还容易出现失效的情况常见场景及解决方案如下方法内部自调用同一个类中的方法 A 调用方法 B且方法 B 标注了Transactional此时事务不会生效。因为 Spring 事务基于 AOP 代理实现自调用走的是this引用而非代理对象事务切面无法被织入。解决方案将方法 B 拆分到另一个 Bean 中通过注入该 Bean 来调用或者注入自身的代理对象如通过Lazy注入ApplicationContext获取代理来调用。方法非 publicTransactional标注在private、protected或包私有方法上时事务不会生效。Spring 的代理机制只能拦截 public 方法。解决方案将方法改为public或将事务逻辑提取到 public 方法中。异常被捕获未抛出方法内部使用try-catch捕获了异常但没有重新抛出事务无法感知异常也就不会回滚。解决方案在catch块中重新抛出异常或使用TransactionAspectSupport.currentTransactionStatus().setRollbackOnly()手动标记回滚。数据库引擎不支持事务如 MySQL 的 MyISAM 引擎不支持事务即使配置了Transactional也不会生效。解决方案将数据库表引擎改为 InnoDB支持事务。类未被 Spring 管理如果类没有标注Component、Service等注解或没有被 XML 配置扫描到Spring 不会为其创建代理事务自然失效。解决方案确保类被 Spring 容器管理。176. 什么是 MyBatisMyBatis 和 Hibernate 有什么区别答MyBatis 是一款优秀的持久层框架它支持自定义 SQL、存储过程以及高级映射。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集的过程它通过 XML 或注解的方式将 SQL 语句与 Java 方法进行映射使得开发者可以灵活地控制 SQL 语句。MyBatis 和 Hibernate 的主要区别如下-SQL 控制MyBatis 需要开发者自己编写 SQL 语句SQL 控制更加灵活Hibernate 则通过对象关系映射ORM自动生成 SQL开发者不需要编写 SQL但 SQL 的优化空间较小。学习曲线MyBatis 的学习曲线相对平缓因为 SQL 是开发者熟悉的技能Hibernate 的学习曲线较陡峭需要理解 ORM 的映射机制和缓存机制。性能MyBatis 由于 SQL 由开发者控制更容易进行 SQL 优化性能通常更高Hibernate 的自动 SQL 生成可能导致性能问题需要开发者进行调优。缓存两者都支持一级缓存和二级缓存但 MyBatis 的二级缓存需要手动配置Hibernate 的二级缓存相对成熟。适用场景MyBatis 适合 SQL 复杂、需要精细控制 SQL 的项目Hibernate 适合业务逻辑简单、以 CRUD 为主的项目。## 177. 什么是 RedisRedis 支持哪些数据类型答Redis 是一个开源的高性能键值对Key-Value存储系统它基于内存运行支持持久化通常被用作缓存、消息队列、分布式锁等场景。Redis 支持的数据类型主要有以下几种-String字符串最基本的数据类型可以存储字符串、整数或浮点数。Hash哈希类似于 Java 中的 Map适合存储对象。List列表双向链表支持从两端插入和弹出元素。Set集合无序的字符串集合支持交集、并集、差集等操作。ZSet有序集合带权重的字符串集合每个元素关联一个分数可以按分数排序。Bitmap位图基于 String 类型可以进行位操作。HyperLogLog用于基数统计可以估算集合中不重复元素的数量。GEO地理信息用于存储地理位置信息支持距离计算等操作。其中String、Hash、List、Set、ZSet是最常用的五种数据类型。## 178. 什么是分布式锁如何用 Redis 实现分布式锁答分布式锁是用于在分布式系统中协调多个节点对共享资源的访问的一种锁机制它需要保证在同一时刻只有一个节点能够获取到锁。使用 Redis 实现分布式锁的常见方式如下- 使用SET命令的NX和EX参数通过SET lock_key unique_value NX EX 30命令其中NX表示只有当键不存在时才设置成功EX表示设置过期时间单位秒unique_value用于标识锁的持有者防止误删他人的锁。释放锁时使用Lua脚本释放锁时需要先判断锁的持有者是否是自己然后删除锁这两个操作需要保证原子性因此使用Lua脚本实现。使用Redisson框架Redisson是Redis官方推荐的Java客户端它提供了封装好的分布式锁实现支持可重入锁、公平锁、读写锁等使用起来更加方便。示例代码如下// 1. 创建 Redisson 客户端连接 Redis 服务ConfigconfignewConfig();config.useSingleServer().setAddress(redis://127.0.0.1:6379);RedissonClientredissonRedisson.create(config);// 2. 获取分布式锁默认是可重入锁RLocklockredisson.getLock(myLock);try{// 3. 尝试获取锁最多等待 10 秒锁自动释放时间为 30 秒booleanisLockedlock.tryLock(10,30,TimeUnit.SECONDS);if(!isLocked){// 获取锁失败说明其他线程/节点正在持有锁thrownewRuntimeException(获取分布式锁失败);}// 4. 获取锁成功后执行业务逻辑如扣减库存、更新订单等System.out.println(获取锁成功开始执行业务逻辑...);// doBusiness();}catch(InterruptedExceptione){Thread.currentThread().interrupt();thrownewRuntimeException(获取锁过程中被中断,e);}finally{// 5. 释放锁Redisson 会先判断锁的持有者是否是自己再删除锁保证原子性if(lock.isHeldByCurrentThread()){lock.unlock();}// 6. 关闭 Redisson 客户端通常在应用关闭时执行redisson.shutdown();}关键点tryLock(waitTime, leaseTime, unit)中waitTime表示获取锁的最大等待时间leaseTime表示锁的自动释放时间到期自动释放避免死锁。Redisson 内部通过看门狗Watchdog机制自动续期默认每 10 秒续期一次确保业务未执行完时锁不会提前过期。释放锁时 Redisson 会先校验持有者身份再删除避免误删他人的锁。## 179. 什么是消息队列你在项目中如何使用消息队列答消息队列是一种用于在分布式系统中传递消息的中间件它采用生产者-消费者模式生产者将消息发送到队列中消费者从队列中取出消息进行处理。消息队列的主要作用包括异步处理、削峰填谷、系统解耦和日志处理等。常见的消息队列产品有 RabbitMQ、Kafka、RocketMQ、ActiveMQ 等。在项目中消息队列的典型使用场景包括-异步处理将耗时的操作如发送邮件、短信通知、生成报表等异步化提高系统的响应速度。削峰填谷在秒杀、促销等瞬时高并发场景下将请求先写入消息队列再由消费者按一定的速率处理避免对数据库造成冲击。系统解耦将不同系统之间的调用关系通过消息队列解耦生产者不需要关心消费者的处理结果降低了系统之间的耦合度。日志处理将日志消息发送到消息队列由专门的日志消费者进行收集和分析。在使用消息队列时需要注意消息的可靠性消息不丢失、消息的幂等性消费者重复消费时不会产生错误以及消息的顺序性等问题。180. 什么是微服务微服务架构有哪些优缺点答微服务是一种软件架构风格它将一个大型的单体应用拆分成多个小的、独立的服务每个服务围绕特定的业务能力构建可以独立开发、部署和扩展。微服务之间通过轻量级的通信机制如 HTTP RESTful API、消息队列等进行交互。微服务架构的优点包括-独立部署每个服务可以独立开发、测试和部署提高了发布的频率和灵活性。技术异构不同的服务可以使用不同的技术栈选择最适合该业务场景的技术。故障隔离单个服务的故障不会影响整个系统提高了系统的可用性。弹性伸缩可以根据每个服务的负载情况独立地进行水平扩展。微服务架构的缺点包括运维复杂度高服务数量多部署、监控、日志收集等运维工作量大。分布式事务复杂跨服务的事务处理比单体应用复杂得多。服务间通信开销服务间通过网络通信存在网络延迟和通信失败的风险。测试难度大端到端测试需要启动多个服务测试成本较高。因此微服务架构更适合业务复杂、团队规模较大、需要频繁发布的大型系统对于小型项目或团队单体应用可能是更合适的选择。
返回列表