
1. 框架篇面试题的核心考察点在技术面试中框架相关的考察从来都不是简单的API记忆测试。面试官真正想了解的是你对框架设计思想的理解深度以及在实际项目中运用框架解决问题的能力。根据我参与过的数百场技术面试经验框架类问题通常会从以下三个维度展开首先是框架的核心机制。比如Spring的IoC容器工作原理、Hibernate的一二级缓存机制、MyBatis的SQL映射实现等。这类问题需要你不仅能说出表面现象更要理解背后的设计考量。以Spring的依赖注入为例你需要清楚BeanFactory和ApplicationContext的区别知道各种作用域的实现原理了解循环依赖的解决策略。其次是框架的实践应用。面试官会关注你如何用框架解决具体业务问题。比如在Spring Boot中如何实现多数据源动态切换、MyBatis如何优化大批量数据插入等问题。回答这类问题时要结合具体场景展示你的实战经验。我建议准备2-3个典型案例用STAR法则情境-任务-行动-结果来组织回答。最后是框架的扩展与定制。高级开发者需要展示对框架的深度掌控能力。比如如何扩展Spring MVC的功能、如何自定义MyBatis插件等问题。这类回答要体现你的框架二次开发能力展示你不仅会用框架还能根据业务需求改造框架。2. Spring框架深度解析2.1 IoC容器工作机制Spring框架的核心是IoC容器其工作流程可以分为以下几个关键阶段资源定位与加载容器首先需要找到配置文件的位置。在传统XML配置方式中ClassPathXmlApplicationContext会通过ClassLoader定位资源文件。而在注解配置中AnnotationConfigApplicationContext会扫描指定包路径下的组件。Bean定义解析解析器(BeanDefinitionReader)将配置信息转换为BeanDefinition对象。这个过程涉及XML解析(如DOM4J)或注解处理(如ASM字节码分析)。我曾经遇到一个性能问题当Bean定义过多时解析阶段会成为启动瓶颈。解决方案是使用ComponentScan的excludeFilters属性减少不必要的扫描。依赖注入处理容器根据BeanDefinition中的依赖关系完成注入。这里有个关键点Spring默认使用按类型自动装配这可能导致多个候选Bean的冲突。解决方法包括使用Qualifier指定具体Bean名称使用Primary标记首选Bean实现自定义的Conditional条件装配生命周期回调Bean的初始化过程遵循严格的顺序PostConstruct → InitializingBean.afterPropertiesSet() → init-method在面试中能清晰描述这个顺序的候选人通常对Spring有较深理解。2.2 AOP实现原理Spring AOP的底层是基于动态代理实现的但具体机制根据目标类有所不同JDK动态代理适用于实现了接口的类。代理类会实现相同的接口并将方法调用转发给InvocationHandler。这种方式的限制是只能代理接口方法。CGLIB代理通过生成目标类的子类来实现代理。由于是继承方式无法代理final方法和类。在性能上CGLIB的创建过程比JDK代理慢但调用效率更高。实际项目中我推荐优先使用JDK动态代理因为遵循面向接口编程原则避免CGLIB的类加载问题更符合Spring的设计哲学但要注意一个常见陷阱同一个Bean内的自调用不会触发AOP拦截。这是因为自调用绕过了代理对象。解决方案包括将方法拆分到不同Bean使用AopContext.currentProxy()获取代理对象改为基于AspectJ的编译时织入3. MyBatis框架核心机制3.1 SQL映射实现MyBatis的SQL映射是其最核心的功能理解其工作原理对优化性能至关重要映射文件解析MyBatis启动时会解析所有Mapper XML文件将SQL语句转换为MappedStatement对象。这个过程会进行以下处理参数映射解析将#{}和${}转换为参数占位符结果集映射建立列名与Java属性的对应关系缓存配置根据useCache和flushCache设置初始化缓存策略SQL执行过程当调用Mapper方法时MyBatis会通过SqlSession获取对应的MappedStatement创建BoundSql对象完成参数绑定通过Executor执行查询可能涉及一级缓存检查使用ResultSetHandler处理结果集在实际项目中我遇到过一个典型问题当使用${}进行字符串替换时存在SQL注入风险。正确的做法是尽量使用#{}参数化查询如果必须使用${}要严格过滤输入值考虑使用MyBatis的SQL Provider机制动态构建安全SQL3.2 缓存机制详解MyBatis提供两级缓存来提高查询性能一级缓存SqlSession级别的缓存默认开启。特点是生命周期与SqlSession一致执行update/insert/delete操作时会自动清空在分布式环境下无效常见误区是认为一级缓存可以跨Session共享。实际上即使查询相同数据不同SqlSession也会访问数据库。我曾经通过以下方式优化一级缓存使用对只读操作使用同一个SqlSession合理控制Session生命周期避免在循环中重复创建Session二级缓存Mapper级别的缓存需要显式配置。实现要点缓存实现可插拔(默认使用PerpetualCache)需要序列化缓存对象事务提交后才生效在集群环境中建议使用Redis等分布式缓存实现。配置示例cache typeorg.mybatis.caches.redis.RedisCache evictionLRU flushInterval60000 size1024/4. Spring Boot自动配置原理4.1 条件化装配机制Spring Boot的自动配置基于一系列条件注解实现核心机制包括Conditional系列注解Spring Boot扩展了Spring的条件注解提供了ConditionalOnClass类路径存在指定类时生效ConditionalOnMissingBean容器中不存在指定Bean时生效ConditionalOnProperty配置属性满足条件时生效理解这些注解的工作时机很重要。它们是在Bean定义阶段生效而不是运行时。这意味着条件判断基于类加载时的环境无法根据运行时动态条件切换配置自动配置加载过程Spring Boot启动时会加载META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports文件过滤掉不满足条件的配置类按AutoConfigureOrder顺序处理剩余配置我曾经通过自定义自动配置解决了多数据源问题。关键步骤包括创建自动配置类并指定Conditional条件在META-INF/spring/下创建org.springframework.boot.autoconfigure.AutoConfiguration.imports文件使用AutoConfigureOrder控制加载顺序4.2 启动过程分析Spring Boot应用的启动过程可以分为几个关键阶段SpringApplication初始化推断web应用类型(Servlet/Reactive/None)初始化ApplicationContextInitializer准备Environment环境配置上下文准备阶段创建对应的ApplicationContext实例准备Bean定义读取器和扫描器执行初始化器刷新阶段调用AbstractApplicationContext.refresh()完成Bean工厂后处理注册Bean后处理器初始化消息源和事件广播器在面试中经常会被问到如何扩展启动过程。常见方式包括实现ApplicationRunner或CommandLineRunner接口使用ApplicationEvent监听启动事件自定义EnvironmentPostProcessor修改环境配置5. 高频面试题精讲5.1 Spring事务传播机制Spring定义了7种事务传播行为但实际项目中最常用的是REQUIRED(默认)如果当前存在事务则加入该事务否则新建一个事务。这是最常用的传播行为适合大多数业务场景。REQUIRES_NEW总是新建事务如果当前存在事务则将其挂起。适用于需要独立提交的子操作比如日志记录。NESTED如果当前存在事务则在嵌套事务内执行否则新建事务。与REQUIRES_NEW不同嵌套事务会随外部事务一起提交或回滚。常见误区是在同一个类的方法调用中期望传播行为生效。由于Spring AOP的代理机制自调用不会触发代理导致传播行为失效。解决方案包括将方法拆分到不同Bean使用AopContext.currentProxy()改为基于AspectJ的编译时织入5.2 MyBatis动态SQL实践MyBatis提供了强大的动态SQL功能常用元素包括条件判断适合简单的非此即彼场景select idfindActiveBlogWithTitleLike resultTypeBlog SELECT * FROM BLOG WHERE state ACTIVE if testtitle ! null AND title like #{title} /if /select多条件选择类似Java的switch-caseselect idfindActiveBlogLike resultTypeBlog SELECT * FROM BLOG WHERE state ACTIVE choose when testtitle ! null AND title like #{title} /when when testauthor ! null AND author_name like #{author} /when otherwise AND featured 1 /otherwise /choose /select集合遍历特别适合IN查询select idselectPostIn resultTypedomain.blog.Post SELECT * FROM POST P WHERE ID in foreach itemitem indexindex collectionlist open( separator, close) #{item} /foreach /select在实际项目中我建议将复杂的动态SQL拆分为多个片段使用 定义通过 引用。这能提高SQL的可维护性。6. 框架选型与性能优化6.1 ORM框架对比在选择ORM框架时需要从多个维度进行评估维度HibernateMyBatisJPA学习曲线较陡峭适中取决于实现灵活性较低很高中等性能中等(有优化空间)较高取决于实现缓存机制完善简单取决于实现适合场景简单CRUD复杂SQL标准规范根据我的项目经验对简单业务系统JPAHibernate组合开发效率最高对报表类复杂查询系统MyBatis更合适微服务架构下可以考虑Spring Data JPA QueryDSL组合6.2 Spring Boot性能调优Spring Boot应用常见的性能瓶颈及解决方案启动速度优化使用spring-context-indexer加速组件扫描延迟初始化(Lazy)排除不必要的自动配置内存占用优化调整JVM参数(-Xmx, -Xms)使用Spring Boot的Actuator监控内存使用分析heap dump找出内存泄漏数据库访问优化合理配置连接池参数使用Transactional(readOnlytrue)标记只读事务启用MyBatis二级缓存我曾经通过以下配置将应用启动时间从15秒缩短到8秒spring.main.lazy-initializationtrue spring.autoconfigure.excludeorg.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration spring.jmx.enabledfalse7. 实际项目经验分享7.1 分布式事务解决方案在微服务架构下传统的本地事务不再适用。常见的解决方案包括2PC/XA协议适合传统单体应用拆分场景但对性能影响较大。实现要点使用Atomikos或Narayana等事务管理器配置XA数据源注意数据库驱动对XA的支持程度TCC模式通过Try-Confirm-Cancel三个阶段实现最终一致性。优点是性能较好缺点是实现复杂。典型实现步骤定义Try接口预留资源Confirm接口确认操作Cancel接口取消预留Saga模式将大事务拆分为多个本地事务通过补偿机制保证一致性。我最近的项目中使用Axon Framework实现了Saga定义Saga流程实现事件处理器设计补偿机制7.2 多数据源动态切换在大型系统中经常需要访问多个数据源。Spring提供了AbstractRoutingDataSource来实现动态切换定义数据源路由键上下文public class DataSourceContextHolder { private static final ThreadLocalString CONTEXT new ThreadLocal(); public static void setDataSource(String name) { CONTEXT.set(name); } public static String getDataSource() { return CONTEXT.get(); } public static void clear() { CONTEXT.remove(); } }实现AbstractRoutingDataSourcepublic class DynamicDataSource extends AbstractRoutingDataSource { Override protected Object determineCurrentLookupKey() { return DataSourceContextHolder.getDataSource(); } }配置多个数据源BeanBean ConfigurationProperties(spring.datasource.master) public DataSource masterDataSource() { return DataSourceBuilder.create().build(); } Bean ConfigurationProperties(spring.datasource.slave) public DataSource slaveDataSource() { return DataSourceBuilder.create().build(); }使用AOP在方法执行前切换数据源Aspect Component public class DataSourceAspect { Before(annotation(targetDataSource)) public void before(JoinPoint point, TargetDataSource targetDataSource) { DataSourceContextHolder.setDataSource(targetDataSource.value()); } After(annotation(targetDataSource)) public void after(JoinPoint point, TargetDataSource targetDataSource) { DataSourceContextHolder.clear(); } }在实际项目中还需要考虑事务管理器的配置、连接池参数的优化等问题。我曾经遇到过连接泄漏问题最终通过以下方式解决使用Druid的监控功能检测泄漏连接在finally块中确保清除上下文设置合理的超时时间