详解Spring mvc工作原理及源码分析
Model 模型层 (javaBean组件 = 领域模型(javaBean) + 业务层 + 持久层)
View 视图层( html、jsp…)
Controller 控制层(委托模型层进行数据处理)
springmvc是一个web层mvc框架,类似struts2。
springmvc是spring的部分,其实就是spring在原有基础上,又提供了web应用的mvc模块。
实现机制:
struts2是基于过滤器实现的。
springmvc是基于servlet实现的。
运行速度:
因为过滤器底层是servlet,所以springmvc的运行速度会稍微比structs2快。
struts2是多例的
springmvc单例的
参数封装:
struts2参数封装是基于属性进行封装。
springmvc是基于方法封装。颗粒度更细。
⑴ 用户发送请求至DispatcherServlet。
⑵ DispatcherServlet收到请求调用HandlerMapping查询具体的Handler。
⑶ HandlerMapping找到具体的处理器(具体配置的是哪个处理器的实现类),生成处理器对象及处理器拦截器(HandlerExcutorChain包含了Handler以及拦截器集合)返回给DispatcherServlet。
⑷ DispatcherServlet接收到HandlerMapping返回的HandlerExcutorChain后,调用HandlerAdapter请求执行具体的Handler(Controller)。
⑸ HandlerAdapter经过适配调用具体的Handler(Controller即后端笑巧控制器)。
⑹ Controller执行完成返回ModelAndView(其中包含逻辑视图和数据)给HandlerAdaptor。
⑺ HandlerAdaptor再将ModelAndView返回给DispatcherServlet。
⑻ DispatcherServlet请求视图解析器ViewReslover解析ModelAndView。
⑼ ViewReslover解析后返胡迟回具体View(物理视图)到DispatcherServlet。
⑽ DispatcherServlet请求渲染视图(即将模型数据填充至视图中) 根据View进行渲染视图。
⑾ 将渲染后的视图返回给DispatcherServlet。
⑿ DispatcherServlet将响应结果返回给用户。
(1)前端控制器DispatcherServlet(配置即可)
功能:中央处理器,接收请求,自己不做任何处理,而是将请求发送给其他组件进行处理。DispatcherServlet 是整个流程的控制中心。
(2)处理器映射器HandlerMapping(配置即可)
功能:根据DispatcherServlet发送的url请求路径查找Handler
常见的处理器映射器:BeanNameUrlHandlerMapping,SimpleUrlHandlerMapping,
ControllerClassNameHandlerMapping,DefaultAnnotationHandlerMapping(不建议使用)
(3)处理器适配器HandlerAdapter(配置即可)
功能:按照特定规裤升李则(HandlerAdapter要求的规则)去执行Handler。
通过HandlerAdapter对处理器进行执行,这是适配器模式的应用,通过扩展多个适配器对更多类型的处理器进行执行。
常见的处理器适配器:HttpRequestHandlerAdapter,SimpleControllerHandlerAdapter,AnnotationMethodHandlerAdapter
(4)处理器Handler即Controller(程序猿编写)
功能:编写Handler时按照HandlerAdapter的要求去做,这样适配器才可以去正确执行Handler。
(5)视图解析器ViewReslover(配置即可)
功能:进行视图解析,根据逻辑视图名解析成真正的视图。
ViewResolver负责将处理结果生成View视图,ViewResolver首先根据逻辑视图名解析成物理视图名即具体的页面地址,再生成View视图对象,最后对View进行渲染将处理结果通过页面展示给用户。
springmvc框架提供了多种View视图类型,如:jstlView、freemarkerView、pdfView...
(6)视图View(程序猿编写)
View是一个接口,实现类支持不同的View类型(jsp、freemarker、pdf...)
引入相关依赖:spring的基本包、springmvc需要的spring-webmvc,日志相关的slf4j-log4j12,jsp相关的jstl、servlet-api、jsp-api。
因为DispatcherServlet本身就是一个Servlet,所以需要在web.xml配置。
一、使用默认加载springmvc配置文件的方式,必须按照以下规范:
①命名规则:-servlet.xml ==== springmvc-servlet.xml
②路径规则:-servlet.xml必须放在WEB-INF下边
二、如果要不按照默认加载位置,则需要在web.xml中通过标签来指定springmvc配置文件的加载路径,如上图所示。
将自定义的 Controller 处理器配置到 spring 容器中交由 spring 容器来管理,因为这里的 springmvc.xml 配置文件中处理器映射器配置的是 BeanNameUrlHandlerMapping ,根据名字可知这个处理器映射器是根据 bean (自定义Controller) 的 name 属性值url去寻找执行类 Handler(Controller) , 所以bean的name属性值即是要和用户发送的请求路径匹配的 url 。
根据视图解析路径:WEB-INF/jsps/index.jsp
功能:根据bean(自定义Controller)的name属性的url去寻找执行类Controller。
功能:自定义的处理器(Controller)实现了Controller接口时,适配器就会执行Controller的具体方法。
SimpleControllerHandlerAdapter会自动判断自定义的处理器(Controller)是否实现了Controller接口,如果是,它将会自动调用处理器的handleRequest方法。
Controller接口中有一个方法叫handleRequest,也就是处理器方法。
因此,自定义的Controller要想被调用就必须实现Controller接口,重写Controller接口中的处理器方法。
源码分析spring-mvc启动流程
spring-mvc 的启动流程
1、request 请求到达 dispatchServlet- doService()-doDispatch() 开始处理请求
2、根据doDispatch() 再去调用getHandler() 目的是获取包含 处理器Handler和处理器拦截器 AdapterIntercepers 的处理器拦截链 HandlerExecutionChain
2.1 getHandler(HttpServletRequest request) 通过HandlerMappping对象获取HandlerExecutionChain
3、再通过getHandlerAdapter() 在拦截链中获取handler对应的处理器适配器 handleAdapter
4、ha.handler(processedRequest, response, mappedHandler.getHandler()) 通过handlerAdapter来睁尘调用具体的处理器 完成对请求的处理
4.1 hanler 调用 SimpleServletHandlerAdapter.service()-HttpServlet.service()-HttpServlet.service()-doGet(req, resp);
在这里我们发现不只是调用doGet()请求,而是根据请求方法来决定调用doGet()还是doPost(),或者其他
这里发现请求有如下:doGet(req, resp);doHead(req, resp);doPost(req, resp);
doPut(req, resp);doDelete(req, resp);doOptions(req,resp);doTrace(req,resp);
5、根据handler返回的ModleAndView来决定是否渲染试图 ModleAndView 将modle 和 view 封装在一起
6、ViewResolver:视图解析器 负责将处理结果生成view 试图
具体操作:ViewResolver首先根据逻辑视图名解析成物理视图名即具体的页面地址,再生成View视图对象,最后对View 进行渲染将处理结果通过页面展示给用户
7、view 是spring-mvc 的封装对象,是一个接口
springmvc框架提供了很多的View视图类型,包括:jspview,pdfview,jstlView、freemarkerView、pdfView等。一般情况下需要通过页面标签或页面模版技术将模型数据通过页面展示给用户,需要由程序员根据业务需求开发具体的页面。
在阅读源码的过程中,发现在启动流程中,用到了适配器模式。。。。
适配器模式:大致对它的理解是这样的,假如现在有一个三方系统M,我们现在有个系统A需要和他交互,那么需要双方定义通信协议。那么问题来了猛早清,如果后期我们有很多系统都想于这个M来交互,那么都需要和M去定义通信协议,对于M来说,M会答应么?
解决方案:这个时候,我们来找一个C,来兼容所有的系统,而最后和M交互的只有C,其他系统想要和枝前M交互,那么只需要和C交互在这里C就相当于我们的适配器,也及就是说适配各种系统的调用
Spring Tx源码解析(二)
上一篇 我们介绍了 spring-tx 中的底层抽象,本篇我们一起来看看围绕这些抽象概念 spring-tx 是如何打造出声明式事务的吧。笼统的说, spring-tx-5.2.6.RELEASE 的实现主要分为两个部分:
这两部分彼此独立又相互成就,并且每个部分都有着大量的源码支撑,本篇我们先来分析 spring-tx 中的AOP部分吧。
EnableTransactionManagement 注解想必大家都很熟悉了,它是启用 Spring 中注释驱动的事务管理功能的关键。
EnableTransactionManagement 注解的主要作用是向容器中导入 TransactionManagementConfigurationSelector ,至于注解中定义的几个属性在 Spring AOP源码解析 中有过详细分析,这里就不再赘述嫌纤缓了。
由于我们并没有使用 AspectJ ,因此导入容器的自然是 ProxyTransactionManagementConfiguration 这个配置类。
这个配置类的核心是向容器中导入一个类型为 BeanFactoryTransactionAttributeSourceAdvisor 的Bean。这是一个 PointcutAdvisor ,它的 Pointcut 是 TransactionAttributeSourcePointcut , Advice 是 TransactionInterceptor 。
TransactionAttributeSourcePointcut 利用 TransactionAttributeSource 解析 @Transactional 注解的能力来选取标注了 @Transactional 注解的芹模方法,而 TransactionInterceptor 则根据应用提出的需求(来自对 @Transactional 注解的解析)将方法增强为事务方法,因此 BeanFactoryTransactionAttributeSourceAdvisor 可以识别出那些标注了 @Transactional 注解的方法,为它们应用上事务相关功能。
TransactionInterceptor 能对方法进行增强,但是它竖岩却不知道该如何增强,比如是为方法新开一个独立事务还是沿用已有的事务?什么情况下需要回滚,什么情况下不需要?必须有一个『人』告诉它该如何增强,这个『人』便是 TransactionAttributeSource 。
@Transactional 注解定义了事务的基础信息,它表达了应用程序期望的事务形态。 TransactionAttributeSource 的主要作用就是解析 @Transactional 注解,提取其属性,包装成 TransactionAttribute ,这样 TransactionInterceptor 的增强便有了依据。
前面我们已经见过, spring-tx 使用 AnnotationTransactionAttributeSource 来做具体的解析工作,其父类 AbstractFallbackTransactionAttributeSource 定义了解析 TransactionAttribute 的优先级,核心方法是 computeTransactionAttribute(...) 。
AnnotationTransactionAttributeSource 默认只解析 public 修饰的方法,这也是导致 @Transactional 注解失效的一个原因,除此之外它还实现了父类中定义的两个模板方法:
同时为了支持 EJB 中定义的 javax.ejb.TransactionAttribute 和 JTA 中定义的 javax.transaction.Transactional 注解, AnnotationTransactionAttributeSource 选择将实际的提取工作代理给 TransactionAnnotationParser 。Spring 提供的 @Transactional 注解由 SpringTransactionAnnotationParser 进行解析。
SpringTransactionAnnotationParser 的源码还是很简单的,它使用 AnnotatedElementUtils 工具类定义的 find 语义来获取 @Transactional 注解信息。 RuleBasedTransactionAttribute 中 rollbackOn(...) 的实现还是挺有意思的,其它的都平平无奇。
RollbackRuleAttribute 是用来确定在发生特定类型的异常(或其子类)时是否应该回滚,而 NoRollbackRuleAttribute 继承自 RollbackRuleAttribute ,但表达的是相反的含义。 RollbackRuleAttribute 持有某个异常的名称,通过 getDepth(Throwable ex) 算法来计算指定的 Throwable 和持有的异常在继承链上的距离。
程序猿只有在拿到需求以后才能开工, TransactionInterceptor 也一样,有了 TransactionAttributeSource 之后就可以有依据的增强了。观察类图, TransactionInterceptor 实现了 MethodInterceptor 接口,那么自然要实现接口中的方法:
可以看到, TransactionInterceptor 本身是没有实现任何逻辑的,它更像一个适配器。这样分层以后, TransactionAspectSupport 理论上就可以支持任意类型的 Advice 而不只是 MethodInterceptor 。实现上 TransactionAspectSupport 确实也考虑了这一点,我们马上就会看到。
invokeWithinTransaction(...) 的流程还是非常清晰的:
第一步前文已经分析过了,我们来看第二步。
TransactionInfo 是一个非常简单的类,我们就不费什么笔墨去分析它了。接着看第三步,这一步涉及到两个不同的操作——提交或回滚。
至此, TransactionInterceptor 于我们而言已经没有任何秘密了。
本篇我们一起分析了 spring-tx 是如何通过 spring-aop 的拦截器将普通方法增强为事务方法的,下篇就该说道说道 PlatformTransactionManager 抽象下的事务管理细节啦,我们下篇再见~~
Spring源码解析之BeanPostProcessor
源腔庆码版本4.3.10-RELEASE
我们都知道,实现BeanPostProcessor接口,在bean初始化前后,spring会分别回调postProcessBeforeInitialization和postProcessAfterInitialization。目的是保留扩展接口修改bean的属性,甚伍晌握至可以替换bean :
那spring究竟是如何做到的呢?我们一起来看看吧。
从spring容器启动开始看起:new ClassPathXmlApplicationContext("classpath:application.xml");
看AbstractApplicationContext.refresh();方法。
1、在 obtainFreshBeanFactory 里会解析xml配置,把所有bean注册到beanDefinitionNames,包含我们自定义的BeanPostProcessor。
2、先看 prepareBeanFactory:
这里添加了三个spring默认的BeanPostProcessor。
3、再看 postProcessBeanFactory:
这个是AbstractRefreshableWebApplicationContext(AbstractApplicationContext的子类)的方法,它同样也添加了一个默认的BeanPostProcessor。
4、接着看 invokeBeanFactoryPostProcessors:
这里设置一下TempClassLoader,并重新添加LoadTimeWeaverAwareProcessor,后面会单独开个文章分析内建的这些类的作用。
5、继续看 registerBeanPostProcessors:
registerBeanPostProcessors 会继续添加一个内建Processor:
然后从容器取出所有Processor(包括我们自定义的)进行排序,再添加回beanFactory(先移除后添加),最后重新把ApplicationListenerDetector添加到末尾:
6、最后看一下调用过程吧, finishBeanFactoryInitialization:
在这里beanFactory.preInstantiateSingletons();(在子类DefaultListableBeanFactory里实现)
再到其父类的AbstractBeanFactory.doGetBean-AbstractAutowireCapableBeanFactory. createBean -resolveBeforeInstantiation
就是在这里回调接口的2个方法,其实这里允许直接返回bean了。继续看resolveBeforeInstantiation后面的 doCreateBean ,同样会在applyMergedBeanDefinitionPostProcessors里回调,不过只是针对某一类型的Processor:
doCreateBean 的另一个谨脊方法initializeBean里也会做回调:
执行完 createBean 后,再回到其父类的AbstractBeanFactory. getObjectForBeanInstance (该方法就是处理FactoryBean类型的bean)
调到其父类FactoryBeanRegistrySupport.getObjectFromFactoryBean
到AbstractAutowireCapableBeanFactory.postProcessObjectFromFactoryBean(在doGetObjectFromFactoryBean后执行,该方法正是初始化bean的过程)-applyBeanPostProcessorsAfterInitialization
至此,完.
Spring 源码分析 —— 服务优雅关闭
我态搏们想停掉一帆轮祥个spring的进程,一般通过kill命令完成,常用的命令如 kill -2 pid(ctrl + C) 、 kill -9 pid 、 kill -15 pid 。
kill -9 可以认为操作系统从内核级别直接强行kill进程,对服务来说没有任何的准备时间和清场时间,直接被kill掉,且无法被监听。
kill -2 和 -15 则是操作系统给该进程发送一个信号通知,告知应用主动关闭,应用可以监听并接收到信号,可以完成一些关闭回收等动作,然后自我停止。
准确来说,这些关闭信号并不是由spring感知,而是由java线程 Signal Dispatcher 监听,此线程将收到的信号交给JVM,JVM判断信号种类,如果是-2/-15等关闭类型,则交由 java.lang.Shutdown 完成关闭,桐大关闭前会触发所有的shutdown hook。
Runtime.getRuntime().addShutdownHook() 可以添加自定义的shutdown hook。
关于jvm关闭详细的原理分析过程请参考 java进程关闭事件监听
spring在启动过程中也是通过 Runtime.getRuntime().addShutdownHook() 来注册hook回调。
SpringApplication
在 SpringApplication 的 run 方法中,会调用 refreshContext ,默认情况下会调用 context.registerShutdownHook();
AbstractApplicationContext
在 AbstractApplicationContext. registerShutdownHook() ,实现了添加shutdown hook,对应的回调实现为 doClose() ;
其中 发布ContextClosedEvent事件 是非常关键的一步,所有希望完成清场操作的业务或组件都可以监听此事件来完成触发。
整个回收过程还是比较简单的。