SpringMVC的执行流程
- 用户点击某个请求路径,发起一个request请求,此请求会被前端控制器处理。
- 前端控制器请求处理器映射器去查找Handler。可以是基于注解的、基于XML配置的或者基于Java配置的。
- 处理器映射器根据配置找到相应的Handler(可能包含若干个Interceptor拦截器),返回给前端控制器。
- 前端控制器请求处理器适配器去执行相应的Handler处理器(常称为Controller)。
- 处理器适配器执行Handler处理器。
- Handler处理器执行完毕之后会返回给处理器适配器一个ModelAndView对象(SpringMVC底层对象,包括Model数据模型和View视图信息)。
- 处理器适配器接收到Handler处理器返回的ModelAndView后,将其返回给前端控制器。
- 前端控制器接收到ModelAndView后,会请求视图解析器(ViewResolver)对视图进行解析。
- 视图解析器根据View信息匹配到相应的视图结果,反馈给前端控制器。
- 前端控制器收到View具体视图后,进行视图渲染,将Model中的模型数据填充到View视图中的request域,生成最终的视图(View)。
前端控制器向用户返回请求结果。
处理器映射器根据配置找到相应的Handler(可能包含若干个Interceptor拦截器),返回给前端控制器
在Spring MVC中,处理器映射器(HandlerMapping)负责根据HTTP请求找到相应的处理器(Handler)。处理器通常是一个控制器方法。处理器映射器通过不同的策略来查找处理器,这些策略可以是基于注解的、基于XML配置的或者基于Java配置的。
以下是一些常见的根据什么查找处理器的例子:
-
基于注解的映射:
@RequestMapping
:这是最常用的注解,可以直接标注在控制器类或方法上。例如,如果一个控制器方法被标注为@RequestMapping("/hello")
,那么当接收到路径为/hello
的请求时,这个方法就会被调用。@GetMapping
、@PostMapping
等:这些是@RequestMapping
的特定类型,用于处理特定HTTP方法的请求。
-
基于XML配置的映射:
- 在Spring的XML配置文件中,可以使用
<bean>
元素定义一个处理器映射,并通过<property>
元素设置其属性,如handler-mapping
和interceptors
。 - 使用
SimpleUrlHandlerMapping
或BeanNameUrlHandlerMapping
等具体的处理器映射实现类来配置URL到处理器的映射关系。
- 在Spring的XML配置文件中,可以使用
-
基于Java配置的映射:
- 在Java配置类中,可以使用
@EnableWebMvc
注解启用Spring MVC的自动配置功能,然后通过@Configuration
和@Bean
注解定义处理器映射。 - 使用
RequestMappingHandlerMapping
作为处理器映射的实现类,并配置其属性。
- 在Java配置类中,可以使用
无论使用哪种方式,处理器映射器都会根据请求的URL和HTTP方法找到对应的处理器。如果有多个拦截器(Interceptor)配置在处理器上,它们将在处理器执行前后按照配置的顺序被调用。
最终,前端控制器(DispatcherServlet)会接收到处理器映射器返回的处理器(可能包含若干个拦截器),然后继续后续的请求处理流程。
Spring 事务传播行为
Spring 事务传播行为定义了当一个事务方法被另一个事务方法调用时,应该如何处理事务。以下是 Spring 事务传播行为的详细介绍及各自举例说明:
-
REQUIRED(默认):如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。这是最常见的传播行为,适用于大多数业务方法。
- 举例:在一个Service类中,有一个方法A调用另一个方法B,A和B都使用
@Transactional
注解,并且没有指定传播行为。这时,B会加入到A的事务中,或者如果A没有事务,B会创建一个新的事务。
- 举例:在一个Service类中,有一个方法A调用另一个方法B,A和B都使用
-
SUPPORTS:支持当前事务,如果当前没有事务,就以非事务方式执行。这种传播行为适用于对事务要求不严格的读操作。
- 举例:如果方法A在事务中调用了方法B,但B使用了
@Transactional(propagation = Propagation.SUPPORTS)
注解,那么B将在A的事务中运行,如果A没有事务,B将以非事务方式运行。
- 举例:如果方法A在事务中调用了方法B,但B使用了
-
MANDATORY:使用当前的事务,如果当前没有事务,则抛出异常。它强制要求必须在一个已经存在的事务中执行。
- 举例:如果方法A没有事务,而方法B使用了
@Transactional(propagation = Propagation.MANDATORY)
注解,那么调用B时会抛出异常。
- 举例:如果方法A没有事务,而方法B使用了
-
REQUIRES_NEW:创建一个新的事务,并且暂停当前事务(如果存在)。新事务完成后,原始事务才能继续执行。这种传播行为通常用于需要独立事务的场景。
- 举例:方法A在事务中调用了方法B,但B使用了
@Transactional(propagation = Propagation.REQUIRES_NEW)
注解,那么B将创建一个新的事务,而A的事务会被暂停,直到B的事务完成。
- 举例:方法A在事务中调用了方法B,但B使用了
-
NOT_SUPPORTED:以非事务方式执行操作,如果存在事务,就把当前事务挂起。通常用于需要在无事务状态下运行的批处理操作。
- 举例:如果方法A在事务中调用了方法B,但B使用了
@Transactional(propagation = Propagation.NOT_SUPPORTED)
注解,那么B将以非事务方式运行,同时A的事务会被挂起。
- 举例:如果方法A在事务中调用了方法B,但B使用了
-
NEVER:以非事务方式执行,如果存在事务,则抛出异常。它确保操作不会在事务中被执行。
- 举例:如果方法A在事务中调用了方法B,但B使用了
@Transactional(propagation = Propagation.NEVER)
注解,那么调用B时会抛出异常。
- 举例:如果方法A在事务中调用了方法B,但B使用了
-
NESTED:如果当前存在事务,则在嵌套事务内执行。如果没有活动事务,则与PROPAGATION_REQUIRED类似。它允许你在一个现有的事务中开启新的“事务”,以便能够在出现异常时仅回滚部分操作。
- 举例:方法A在事务中调用了方法B,B使用了
@Transactional(propagation = Propagation.NESTED)
注解,那么B将在A的事务中开启一个嵌套事务。如果B中发生异常并标记为回滚,只有B的更改会被回滚,而A的事务仍然可以继续。
- 举例:方法A在事务中调用了方法B,B使用了
了解这些传播行为对于正确配置和管理Spring中的事务非常重要。通过合理地使用这些传播行为,可以确保业务逻辑的正确执行,同时避免事务相关的常见问题,如死锁和不一致的数据状态。在实际开发中,应根据具体的业务需求和场景选择合适的传播行为来优化事务管理。