欢迎访问 SSM框架面试题 专题 | 专注SSM框架技术面试题深度解析 | 联系编辑

SSM框架面试题|全面解析Spring+SpringMVC+MyBatis核心考点与高频真题

深入剖析Spring IOC/AOP、SpringMVC请求流程、MyBatis动态SQL等核心机制;提供真实SSM框架面试题、代码示例、常见陷阱与优化策略;附带Spring Boot整合、性能调优、事务管理等延伸内容,助你构建完整知识体系,提升Java后端开发面试成功率。

⚡ Spring核心容器原理与高频面试题

1.1 Spring容器启动流程深度解析

在SSM框架面试中,Spring容器启动流程是高频考点。其核心流程可分为以下阶段:

  • ① 读取配置:通过ClassPathXmlApplicationContext或AnnotationConfigApplicationContext加载配置文件或注解配置类。
  • ② BeanDefinition解析:解析XML中的<bean>标签或@Component等注解,生成BeanDefinition对象并注册到BeanDefinitionRegistry。
  • ③ 实例化前处理:调用InstantiationAwareBeanPostProcessor的postProcessBeforeInstantiation方法,允许自定义代理逻辑(如AOP代理)。
  • ④ Bean实例化:通过反射或CGLIB创建Bean实例,触发postProcessAfterInstantiation回调。
  • ⑤ 依赖注入:解析@Autowired、@Resource等注解完成属性注入,调用postProcessProperties进行后处理。
  • ⑥ 初始化:执行InitializingBean的afterPropertiesSet方法和自定义init-method,触发BeanPostProcessor的postProcessBeforeInitialization与postProcessAfterInitialization。
  • ⑦ 使用与销毁:Bean进入可用状态;容器关闭时调用DisposableBean的destroy方法及自定义destroy-method。
⚠️ 注意:Spring容器默认采用懒加载(lazy-init=false),即容器启动时立即实例化单例Bean。若设置lazy-init="true",则首次请求时才创建实例,可能引发运行时异常(如注入链缺失)。

1.2 Spring IOC容器与BeanFactory、ApplicationContext区别

特性 BeanFactory ApplicationContext
功能层级 基础IoC容器 高级容器(扩展自BeanFactory)
Bean加载方式 懒加载(Lazy Loading) 预加载单例Bean(启动时实例化)
国际化支持 ❌ 不支持 ✅ 支持MessageSource
事件发布 ❌ 不支持 ✅ 支持ApplicationEvent与ApplicationListener
AOP支持 ❌ 需手动配置 ✅ 自动启用AnnotationAwareAspectJAutoProxyCreator
典型实现 DefaultListableBeanFactory ClassPathXmlApplicationContext、AnnotationConfigApplicationContext

在SSM框架开发中,ApplicationContext是主流选择,因其提供企业级功能(如AOP、事务、国际化),而BeanFactory适用于资源受限环境(如嵌入式系统)。

1.3 AOP(面向切面编程)实现原理与动态代理

Spring AOP基于动态代理实现,核心流程如下:

  1. 切面定义:通过@Aspect标注切面类,使用@Pointcut定义切入点(如execution(* com.example.service.*.*(..)))。
  2. 通知类型:
    • @Before:前置通知(方法执行前)
    • @After:后置通知(方法执行后,无论是否异常)
    • @AfterReturning:返回通知(方法成功返回后)
    • @AfterThrowing:异常通知(方法抛出异常后)
    • @Around:环绕通知(包裹目标方法,可控制执行流程)
  3. 代理生成:
    • 目标类实现接口 → 使用JDK动态代理(基于InvocationHandler)
    • 目标类无接口 → 使用CGLIB生成子类(通过字节码增强)
// 示例:基于@Around的事务控制切面
@Aspect
@Component
public class TransactionAspect {
    @Around("execution(* com.example.service.*.*(..))")
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        System.out.println("开启事务");
        try {
            Object result = pjp.proceed(); // 执行目标方法
            System.out.println("提交事务");
            return result;
        } catch (Exception e) {
            System.out.println("回滚事务");
            throw e;
        }
    }
}

在SSM框架面试中,常考陷阱题:“为何Spring AOP无法拦截同类中方法调用?”——答案:因Spring代理是基于接口或子类的,同类内方法调用直接通过this执行,未经过代理对象。

【问题】Spring支持哪几种Bean作用域?默认是什么?

Spring提供5种作用域:

作用域 说明 适用场景
singleton 单例(容器启动时创建,全局唯一) 无状态Service、DAO
prototype 多例(每次请求创建新实例) 有状态Bean(如会话数据)
request HTTP请求范围(每个请求新建实例) Web应用中请求级数据
session HTTP会话范围(每个会话新建实例) 用户登录状态管理
application ServletContext范围(全局唯一) 应用配置参数

默认作用域为singleton。若配置@Scope("prototype"),需注意:与@Lazy组合可延迟初始化;与@Scope("prototype") + @Scope("request")嵌套时,需通过ScopedProxyMode.TARGET_CLASS生成代理。

【问题】Spring如何解决单例Bean的循环依赖?三级缓存机制是什么?

Spring通过三级缓存解决构造器注入和setter注入的循环依赖:

  • 一级缓存(singletonObjects):存放完全初始化好的单例Bean
  • 二级缓存(earlySingletonObjects):存放早期暴露的Bean(尚未完成属性注入)
  • 三级缓存(singletonFactories):存放ObjectFactory,用于生成早期Bean引用

流程示例(A→B→A):

  1. A开始创建 → 放入三级缓存
  2. A依赖B → B开始创建 → 放入三级缓存
  3. B依赖A → 从三级缓存获取A的ObjectFactory → 提前暴露A的引用(放入二级缓存)
  4. B完成创建 → A继续完成创建
⚠️ 注意:仅支持singleton作用域;prototype作用域Bean无法解决循环依赖;构造器注入无法解决循环依赖(因Bean尚未实例化)。

【问题】@Conditional注解如何实现条件化Bean注册?

@Conditional是Spring 4.0引入的注解,用于根据条件决定是否注册Bean:

  • 常用元注解:
    • @ConditionalOnClass:类路径存在指定类
    • @ConditionalOnMissingBean:容器中不存在指定Bean
    • @ConditionalOnProperty:配置文件中存在指定属性
  • 自定义条件类:
    public class LinuxCondition implements Condition {
        @Override
        public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
            return context.getEnvironment().getProperty("os.name").contains("Linux");
        }
    }

在SSM框架整合中,Spring Boot自动配置大量使用@Conditional(如@ConditionalOnWebApplication),实现按需加载模块。

⚙️ SpringMVC请求处理流程与核心组件

2.1 请求处理全流程图解

SpringMVC基于前端控制器模式,核心流程如下:

① 客户端发送HTTP请求
请求到达DispatcherServlet
② HandlerMapping查找处理器
根据URL匹配Controller方法(如@RequestMapping)
③ HandlerAdapter适配执行
调用Controller方法,处理参数绑定、注解解析
④ 返回ModelAndView
包含视图名与模型数据
⑤ ViewResolver解析视图
将逻辑视图名(如"list")转为物理视图(如"/WEB-INF/jsp/list.jsp")
⑥ 渲染视图并响应
将模型数据填充到JSP/Thymeleaf模板,生成HTML返回客户端
💡 关键点:DispatcherServlet是单例的;HandlerMapping/Adapter/Resolver均通过接口设计支持扩展(如自定义HandlerMapping实现RESTful路由)。

2.2 关键组件源码级解析

组件 作用 默认实现 扩展方式
DispatcherServlet 前端控制器(入口) org.springframework.web.servlet.DispatcherServlet 继承重写doDispatch()
HandlerMapping 查找处理器(URL→Controller) RequestMappingHandlerMapping 实现HandlerMapping接口
HandlerAdapter 适配执行处理器 RequestMappingHandlerAdapter 实现HandlerAdapter接口
ViewResolver 解析视图 InternalResourceViewResolver 实现ViewResolver接口
MultipartResolver 文件上传解析 StandardServletMultipartResolver 配置StandardServletMultipartResolver Bean

在SSM框架面试中,常考题:“如何实现RESTful风格的URL映射?”——答案:通过@RequestMapping(value="/users/{id}", method=RequestMethod.GET)绑定URL参数,结合@PathVariable获取路径变量。

2.3 参数绑定与数据校验机制

SpringMVC支持多种参数绑定方式:

  • 基本类型:自动转换(String→int/long/Boolean等)
  • POJO对象:按字段名匹配(需提供setter方法)
  • 集合类型:如List<User>,需通过@InitBinder注册自定义编辑器
  • 注解参数:
    • @RequestParam:处理Query String/FormData参数
    • @PathVariable:处理RESTful路径参数
    • @RequestBody:处理JSON/XML请求体(结合Jackson)
    • @RequestHeader:获取HTTP请求头
// 示例:POJO对象绑定 + 数据校验
@PostMapping("/user")
public String saveUser(@Valid @ModelAttribute User user, BindingResult result) {
    if (result.hasErrors()) {
        return "user-form"; // 返回错误页面
    }
    userService.save(user);
    return "redirect:/user/list";
}

数据校验需在User类中添加JSR-303注解(如@NotNull、@Size(min=2)),并依赖hibernate-validator库。

【问题】SpringMVC拦截器执行顺序是怎样的?

拦截器(HandlerInterceptor)有三个核心方法:

  • preHandle():Controller执行前调用,返回true继续,false中断
  • postHandle():Controller执行后、视图渲染前调用
  • afterCompletion():视图渲染后调用(用于资源清理)

多拦截器执行顺序:

  • preHandle:按配置顺序执行
  • postHandle与afterCompletion:按配置顺序的逆序执行

示例(配置A→B):

执行流程:A.preHandle → B.preHandle → Controller → B.postHandle → A.postHandle → 视图渲染 → B.afterCompletion → A.afterCompletion

【问题】@RequestBody与@RequestParam如何选择?

特性 @RequestBody @RequestParam
数据来源 请求体(Body) Query String / FormData
Content-Type application/json、application/xml application/x-www-form-urlencoded、multipart/form-data
参数解析 通过HttpMessageConverter(如Jackson) 直接类型转换
支持对象绑定 ✅ 直接绑定POJO ❌ 仅支持简单类型

在SSM框架开发中,RESTful API推荐用@RequestBody,传统表单提交用@RequestParam。

【问题】如何配置JSP视图解析器?

在SpringMVC配置文件中添加:

<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <property name="prefix" value="/WEB-INF/jsp/" />
    <property name="suffix" value=".jsp" />
    <property name="viewClass" value="org.springframework.web.servlet.view.JstlView" />
</bean>

当Controller返回"user/list"时,最终路径为/WEB-INF/jsp/user/list.jsp。

⚡ MyBatis核心机制与动态SQL

3.1 MyBatis执行流程与四大核心对象

MyBatis执行流程可分为以下步骤:

  1. 加载配置:通过SqlSessionFactoryBuilder解析mybatis-config.xml生成Configuration对象。
  2. 创建SqlSessionFactory:构建SqlSessionFactory(线程安全,单例)。
  3. 获取SqlSession:通过SqlSessionFactory.openSession()创建SqlSession(非线程安全)。
  4. 执行SQL:调用SqlSession.selectOne()等方法,触发四大对象协作:
Executor
执行器(处理SQL执行、缓存、事务)
StatementHandler
操作JDBC Statement(预编译、参数设置、结果集处理)
ParameterHandler
参数处理器(设置SQL参数)
ResultSetHandler
结果集处理器(映射Java对象)

在SSM框架面试中,常考题:“MyBatis如何防止SQL注入?”——答案:使用#{} 占位符(预编译),避免${}字符串拼接。

3.2 动态SQL与常用标签详解

MyBatis动态SQL通过XML标签实现条件拼接,核心标签如下:

标签 功能 示例
<if> 条件判断 <if test="name != null">name = #{name}</if>
<choose>/<when>/<otherwise> 类似switch-case <choose><when test="id != null">...</when><otherwise>...</otherwise></choose>
<where> 自动添加WHERE并去除多余AND/OR <where><if test="id != null">id = #{id}</if></where>
<set> UPDATE语句中自动添加SET并去除逗号 <set>name = #{name}</set>
<foreach> 遍历集合(IN查询) <foreach collection="ids" item="id" open="(" separator="," close=")">#{id}</foreach>
<select id="selectUsers" resultType="User">
  SELECT * FROM user
  <where>
    <if test="name != null">
      AND name LIKE CONCAT('%', #{name}, '%')
    </if>
    <if test="ids != null and ids.size > 0">
      AND id IN
      <foreach collection="ids" item="id" open="(" separator="," close=")">
        #{id}
      </foreach>
    </if>
  </where>
</select>

在SSM框架开发中,动态SQL可避免拼接字符串导致的SQL注入风险,同时提升SQL可读性。

3.3 一级缓存与二级缓存机制

MyBatis提供两层缓存:

  • 一级缓存(SqlSession级别):
    • 默认开启,基于PerpetualCache(HashMap实现)
    • 同一SqlSession中,相同SQL+参数仅执行一次
    • 调用sqlSession.clearCache()或执行增删改操作时清除
  • 二级缓存(Mapper级别):
    • 需在mybatis-config.xml中启用<cache>标签
    • Mapper接口对应缓存,需实体类实现Serializable
    • 多线程环境下需注意数据一致性(推荐使用Ehcache等外部缓存)
⚠️ 注意:二级缓存存在脏读风险!在分布式系统中,建议关闭二级缓存,改用Redis等分布式缓存。

【问题】#{}与${}有何本质区别?

特性 #{} ${}
处理方式 预编译(?占位符) 字符串拼接
SQL注入风险 ❌ 安全 ✅ 高风险
适用场景 参数值(如WHERE条件) 动态表名、列名(如ORDER BY字段)

正确用法:ORDER BY ${sortColumn} ${sortDirection}(需校验sortColumn合法性)。

【问题】ResultMap与ResultType有何区别?

ResultType:直接指定返回类型(适用于字段名与属性名一致的简单映射)。

ResultMap:自定义映射规则(适用于字段名与属性名不一致、关联查询)。

<resultMap id="userResultMap" type="User">
  <id property="id" column="user_id"/>
  <result property="userName" column="user_name"/>
</resultMap>
<select id="selectUser" resultMap="userResultMap">
  SELECT user_id, user_name FROM user WHERE user_id = #{id}
</select>

在SSM框架面试中,常考关联映射(如<association>、<collection>)。

【问题】PageHelper分页插件原理是什么?

PageHelper通过MyBatis插件机制实现物理分页:

  1. 拦截StatementHandler.query()方法
  2. 解析原始SQL,添加LIMIT子句(MySQL)或ROWNUM(Oracle)
  3. 执行COUNT查询获取总记录数
  4. 返回PageInfo对象(包含分页数据与元信息)
// 示例:使用PageHelper分页
PageHelper.startPage(1, 10);
List list = userMapper.selectAll();
PageInfo pageInfo = new PageInfo<>(list); // 包含total, pages等信息

⚡ SSM框架整合与企业级实践

4.1 整合Spring与SpringMVC

在web.xml中配置核心监听器与前端控制器:

<!-- 加载Spring容器配置 -->
<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>/WEB-INF/applicationContext.xml</param-value>
</context-param>
<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- SpringMVC前端控制器 -->
<servlet>
    <servlet-name>dispatcher</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/dispatcher-servlet.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
    <servlet-name>dispatcher</servlet-name>
    <url-pattern>/</url-pattern>
</servlet-mapping>

关键点:ContextLoaderListener创建根容器(父容器),DispatcherServlet创建Web容器(子容器),子容器可访问父容器Bean。

4.2 整合Spring与MyBatis

通过SqlSessionFactoryBean集成MyBatis:

<!-- 数据源 -->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
    <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
    <property name="url" value="jdbc:mysql://localhost:3306/test"/>
    <property name="username" value="root"/>
    <property name="password" value="123456"/>
</bean>
<!-- SqlSessionFactory -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
    <property name="dataSource" ref="dataSource"/>
    <property name="mapperLocations" value="classpath:mapper/*.xml"/>
</bean>
<!-- 扫描Mapper接口 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
    <property name="basePackage" value="com.example.mapper"/>
</bean>

注意事项:

  • Mapper接口无需实现类,由MyBatis动态代理生成
  • 事务管理通过@Transactional注解(需配置DataSourceTransactionManager)
  • 避免在Service层直接使用SqlSession(破坏事务一致性)

4.3 事务管理深度解析

Spring支持声明式事务与编程式事务,推荐使用声明式:

// 配置事务管理器
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"/>
</bean>
// 启用注解驱动事务
<tx:annotation-driven transaction-manager="transactionManager"/>

@Transactional常用属性:

属性 说明 示例
propagation 事务传播行为 REQUIRED(默认)、REQUIRES_NEW
isolation 事务隔离级别 READ_COMMITTED、REPEATABLE_READ
rollbackFor 指定异常回滚 rollbackFor = Exception.class
noRollbackFor 指定异常不回滚 noRollbackFor = RuntimeException.class
⚠️ 注意:@Transactional仅对public方法生效;同类内方法调用需通过代理对象(如注入自身)才能触发事务。

4.4 SSM框架演进时间轴

2004年:Spring 1.0发布
引入IOC容器与AOP,奠定Java企业级开发基础
2005年:SpringMVC诞生
基于DispatcherServlet的MVC框架,替代Struts1
2008年:MyBatis 3.0发布
替代iBATIS,支持注解与动态SQL,简化数据访问层
2012年:SSM整合成为主流
Spring + SpringMVC + MyBatis成为Java Web开发标配
2016年:Spring Boot兴起
简化配置,推动SSM向Spring Boot迁移(MyBatis-Plus兴起)

⚡ 网友最关心的10个SSM框架面试真题

Q1:Spring事务传播行为有哪些?举例说明REQUIRED与REQUIRES_NEW。

答案:Spring定义7种传播行为:

传播行为 说明
REQUIRED 有事务则加入,无则新建(默认)
REQUIRES_NEW 总是新建独立事务(挂起当前事务)
NESTED 在当前事务内创建保存点(JDBC 3.0+)
SUPPORTS 有事务则执行,无则非事务执行
NOT_SUPPORTED 以非事务方式执行,挂起当前事务
NEVER 以非事务方式执行,有事务则抛异常
MANDATORY 必须在事务中执行,无事务则抛异常

示例:

@Service
public class OrderService {
    @Transactional(propagation = Propagation.REQUIRED)
    public void createOrder() {
        // 主事务
        orderMapper.insert(order);
        // 调用支付服务(新建独立事务)
        paymentService.pay(order);
    }
}
@Service
public class PaymentService {
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void pay(Order order) {
        // 独立事务,不影响主事务
        paymentMapper.updateStatus(order);
    }
}

若支付失败,主事务仍可提交(订单创建成功)。

Q2:SpringMVC中如何实现文件上传?

答案:步骤如下:

  1. 添加依赖:
    • commons-fileupload(Spring 5.3+推荐)
    • commons-io
  2. 配置MultipartResolver:
    <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
        <property name="defaultEncoding" value="UTF-8"/>
        <property name="maxUploadSize" value="10485760"/>
    </bean>
  3. Controller处理:
    @PostMapping("/upload")
    public String upload(@RequestParam("file") MultipartFile file) {
        if (!file.isEmpty()) {
            String fileName = file.getOriginalFilename();
            file.transferTo(new File("/uploads/" + fileName));
        }
        return "success";
    }
⚠️ 注意:表单需设置enctype="multipart/form-data";生产环境需校验文件类型与大小。

Q3:MyBatis中如何实现批量插入?

答案:有三种方式:

方式 优点 缺点
<foreach> + INSERT 单次执行,效率高 SQL长度受限(MySQL 64MB)
ExecutorType.BATCH 支持大批量数据 需手动提交,可能积压内存
数据库批量语法(如MySQL INSERT ... VALUES (), ()) 最高效 依赖数据库特性
<insert id="batchInsert">
  INSERT INTO user (name, age) VALUES
  <foreach collection="users" item="user" separator=",">
    (#{user.name}, #{user.age})
  </foreach>
</insert>

Q4:Spring AOP中@Around与@Before/@After区别?

答案:

  • 执行时机:
    • @Before:目标方法执行前
    • @Around:包裹目标方法,可控制执行流程(甚至跳过)
  • 返回值:
    • @Before:无返回值
    • @Around:必须返回目标方法结果(或自定义结果)
  • 异常处理:
    • @Before:无法捕获目标方法异常
    • @Around:可捕获并处理异常

适用场景:

  • @Around:日志记录、性能监控、权限校验(需动态决策)
  • @Before:参数校验、简单日志

Q5:如何解决Spring循环依赖?

答案:见前文1.2节,核心是三级缓存机制:

  • 一级缓存:单例池(完全初始化的Bean)
  • 二级缓存:早期Bean引用(尚未完成依赖注入)
  • 三级缓存:ObjectFactory(用于生成早期Bean)

注意:仅支持singleton作用域;prototype无法解决;构造器注入无法解决。

Q6:MyBatis一级缓存何时失效?

答案:以下情况清除一级缓存:

  • 执行增删改操作(insert/update/delete)
  • 手动调用sqlSession.clearCache()
  • SqlSession关闭或提交
  • 配置flushCache="true"(针对单条SQL)

默认情况下,SELECT语句不刷新缓存。

Q7:SpringMVC中如何自定义异常处理器?

答案:使用@ControllerAdvice + @ExceptionHandler:

@ControllerAdvice
public class GlobalExceptionHandler {
    @ExceptionHandler(Exception.class)
    public ResponseEntity<ErrorResponse> handleException(Exception e) {
        ErrorResponse error = new ErrorResponse(500, "服务器错误");
        return ResponseEntity.status(500).body(error);
    }
    @ExceptionHandler(NullPointerException.class)
    public ResponseEntity<ErrorResponse> handleNPE(NullPointerException e) {
        ErrorResponse error = new ErrorResponse(404, "数据不存在");
        return ResponseEntity.status(404).body(error);
    }
}

注意:优先级按异常类层次结构匹配(子类优先)。

Q8:MyBatis中如何实现分页?物理分页与逻辑分页区别?

答案:

方式 实现 适用场景
物理分页 SQL添加LIMIT子句 大数据量(推荐)
逻辑分页 使用RowBounds在内存中截取 小数据量(内存消耗大)

推荐使用PageHelper插件实现物理分页。

Q9:Spring事务失效的常见原因?

答案:

  • @Transactional标注在非public方法
  • 同类内方法调用(未经过代理)
  • 异常被catch且未重新抛出
  • 事务传播行为为NOT_SUPPORTED或NEVER
  • 数据源未配置事务管理器
💡 解决方案:通过AopContext.currentProxy()获取代理对象调用方法。

Q10:MyBatis动态SQL中<trim>标签作用?

答案:<trim>是通用字符串截取标签,可替代<where>和<set>:

<select id="selectUsers" resultType="User">
  SELECT * FROM user
  <trim prefix="WHERE" prefixOverrides="AND |OR ">
    <if test="name != null">
      AND name = #{name}
    </if>
    <if test="age != null">
      AND age = #{age}
    </if>
  </trim>
</select>

属性说明:prefix(添加前缀)、prefixOverrides(移除前缀冗余)、suffix(添加后缀)、suffixOverrides(移除后缀冗余)。

⚠️ SSM框架开发常见误区与避坑指南

7.1 误区:Spring Bean默认是线程安全的

纠正:Spring容器本身不保证线程安全!singleton作用域Bean若包含可变状态,需自行处理并发问题:

  • 避免在Bean中定义实例变量
  • 使用ThreadLocal存储线程隔离数据
  • 对共享资源加锁(synchronized或ReentrantLock)

推荐设计无状态Bean(仅包含Service方法),状态由请求参数传递。

7.2 误区:MyBatis的#{}能完全防SQL注入

纠正:#{}仅防止参数注入,但若动态SQL拼接了用户输入的表名/列名,仍可能注入:

SELECT * FROM ${tableName} WHERE id = #{id}

正确做法:对${}参数做白名单校验(如只允许字母数字下划线)。

7.3 误区:Spring事务会自动回滚所有异常

纠正:默认仅回滚RuntimeException和Error,对checked Exception不回滚:

@Transactional(rollbackFor = Exception.class) // 显式指定回滚所有异常

生产环境务必显式配置rollbackFor。

7.4 误区:SpringMVC拦截器可拦截所有请求

纠正:拦截器不拦截静态资源(需额外配置<mvc:interceptors> + <mvc:exclude-mapping>):

<mvc:interceptors>
  <bean class="com.example.interceptor.AuthInterceptor"/>
  <mvc:interceptor>
    <mvc:mapping path="/admin/**"/>
    <mvc:exclude-mapping path="/admin/login"/>
    <bean class="com.example.interceptor.LoginInterceptor"/>
  </mvc:interceptor>
</mvc:interceptors>
斌知网
蜀ICP备2026017620号-5