大家好,感谢邀请,今天来为大家分享一下java访问网站返回源码分享的问题,以及和打开浏览器访问特定网址的一些困惑,大家要是还不太明白的话,也没有关系,因为接下来将为大家分享,希望可以帮助到大家,解决大家的问题,下面就开始吧!
上图是SpringMVC的框架结构图示,中间涉及到多个组件,根据组件所处定位和功能,我把他们分为三个类别:中心组件、核心组件、功能组件。中心组件DispatcherServlet是整个MVC的核心,负责接收客户端的请求,调度组件处理请求,作为一个中心的调度控制,DispatcherServlet有效降低组件间的耦合性。核心组件是被DispatcherServlet直接调用的组件,而功能组件则依附于核心组件,构成并丰富了核心组件的功能
tips:关于组件的分类,你在网上搜索可能每一篇文章都有所不同。不必过于纠结详细的分类,关键能够理解记忆这个东西即可。
中心组件:前端控制器DispatcherServlet:处于SpringMVC框架的核心位置,实际上是个Servlet。它负责协调组织不同的组件完成请求处理并返回响应工作
核心组件(部分):
处理器映射器HandlerMapping:它的内部维护了请求的访问路径和处理器(controller这种)的对应关系。当请求过来的时候,通过请求的url找到对应的处理器。处理器适配器HandlerAdapter:用来执行处理器。因为处理器的类别不止一种,并且他们执行处理器调用的方法也不同。处理器适配器通过一个接口来对执行处理器的方法进行规范(handle方法)处理器Handler:由开发人员自己编写的代码,涉及业务请求。视图解析器ViewResolver:进行视图解析,根据逻辑视图名解析成真正的视图(本文不会涉及相关的内容)
功能组件(部分):
参数解析器ArgumentResolver:用于解析request请求参数并绑定数据到Controller的入参上,有时候直接参数不是我们想要,可以自定义参数解析器转化为我们想要的参数返回值处理器ReturnValueHandler:用于将处理器执行完后的返回值转化为发送请求的客户端能够接收的形式内容协商管理器ContentNegotiationManager:能够确定客户端请求所需要的返回类型。消息转换器MessageConveter:对不同类型的对象进行转换拦截器HandlerInterceptor:类似过滤器的功能,做的事情与请求响应相关,例如对特定请求做拦截并进行额外处理。
在SpringMVC内部一次web请求会涉及到各式各样的组件,例如处理器映射器、处理器适配器、参数解析器、返回值处理器、消息转换器等,这些功能作为SpringMVC框架的基础设施丰富了MVC框架,使其具有完整的Web请求解析处理的功能,当然这些组件我们也可以自己定义。实际上SpringMVC就是这样一种模式,框架(DispatcherServlet)先给你搭好,对于框架的每个部件则给与开发者充分的发挥空间。
拿台式电脑做比喻:主板就相当于SpringMVC的框架(中心组件DispatcherServlet),CPU、内存条、硬盘器件相当于组件(包括核心组件和功能组件),组件+框架组成了一个可用的电脑。我们可以对各个组件进行定制,例如更换可以发光的神光同步内存条(内存这个核心组件没变,定制化了带光效外壳这个功能组件)、更大容量的硬盘以及水冷散热等。我们从品牌商购买的整机相当于使用了SpringBoot的自动配置,帮我们配置好了基础的Web开发功能。
上面提到,DispatcherServlet是SpringMVC的核心,就SpringMVC而言,DispatcherServlet是客户端请求的入口,因此我们的源码阅读也应该从这里开始。本文主要分为三个章节:1.组件引入Spring容器的过程是怎样的?2.DispatcherServlet的初始化3.请求在DispatcherServlet中的执行过程。
1.组件引入Spring容器的过程是怎样的?
DispatcherServlet涉及组件的调度,被DispatcherServlet调度的组件是在DispatcherServlet初始化时从Spring容器中获取的,那么思考下,这些组件如如何引入Spring容器中的?关于这个问题将在本章中得到解答。
先来看一个知识扩展:设计模式之组合模式
组合模式是一种结构型模式,树形结构:树枝节点和叶子节点都拥有相似的功能,树枝节点可以有自己的叶子节点或树枝节点
树枝节点(Composite)和叶子节点(Leaf)都实现相同的接口或继承相同的类(抽象构件component),树枝节点里面有一个列表List<Component>,保存子节点
组合模式又分为安全组合模式和透明组合模式,他们的具体区别可参考这篇文章进行扩展juejin.cn/post/708521…,以下代码以安全组合模式为例,涉及以下三个构件:Component抽象构件(树枝节点和叶子节点都具备的属性)、Leaf叶子节点构件(叶子节点下面不具备其他分支)、Composite树枝构件。
java复制代码//———————————–抽象构件————————————–\n\npublicinterfaceComponent{\nvoiddoSomething();\n}\n\n//————————————–树枝构件————————————–\n\npublicstaticclassCompositeimplementsComponent{\n\nprivateStringname;\n\nComposite(Stringname){\nthis.name=name;\n}\n\n//构件容器\nprivateList<Component>components=newArrayList<Component>();\n\n//增加一个叶子构件或树枝构件\npublicvoidadd(Componentcomponent){\nthis.components.add(component);\n}\n@Override\n\npublicvoiddoSomething(){\n\n//容器构件具体业务方法的实现,将递归调用成员构件的业务方法\nSystem.out.println(&34;+name);\n\nfor(Componentc:components){\nc.doSomething();\n}\n\n}\n\n}\n\n//————————————–叶子构件————————————–\n\npublicstaticclassLeafimplementsComponent{\n\nprivateStringname;\n\nLeaf(Stringname){\nthis.name=name;\n}\n\n@Override\n\npublicvoiddoSomething(){\n\n//叶子节点执行具体的业务\nSystem.out.println(&34;+name);\n\n}\n\n}\n\n//————————————–调用示例————————————–\n\npublicstaticvoidmain(String[]args){\n\nCompositeroot=newComposite(&34;);\nCompositebranchOne=newComposite(&34;);\nCompositebranchTwo=newComposite(&34;);\n\nLeafleafOne=newLeaf(&34;);\nLeafleafTwo=newLeaf(&34;);\nLeafleafThree=newLeaf(&34;);\nLeafleafFour=newLeaf(&34;);\n\nroot.add(branchOne);\nroot.add(branchTwo);\nbranchOne.add(leafOne);\nbranchOne.add(leafTwo);\nbranchTwo.add(leafThree);\nbranchTwo.add(leafFour);\n\nroot.doSomething();\n\n}\n
那么组合模式有什么优势呢?组合模式使得调用者可以一致地处理单个对象和组合对象,无须关心自己处理的是单个对象,还是组合对象,这简化了调用者代码的处理。在Spring源码中经常见到xxxComposite,就是应用了这种设计模式,例如下面将要提到的WebMvcConfigurerComposite,这个类是组合模式的简单应用,因为它只有两层,子节点全为叶子节点,并且叶子节点的泛型类型都是:WebMvcConfigurer。
我们知道,通常配置或加入一些MVC组件都是通过这种方式:
实现WebMvcConfigurer接口加入@Configuration注解标记为配置类交给Spring进行管理
例如下面示例是Spring中配置HandlerInterceptor拦截器的一种方式
java复制代码//实现WebMvcConfigurer接口、加入@Configuration注解标记为配置类\n@Configuration\npublicclassMyConfigimplementsWebMvcConfigurer{\n\n@Resource\nprivateMyInterceptormyInterceptor;\n\n@Override\npublicvoidaddInterceptors(InterceptorRegistryregistry){\nregistry.addInterceptor(myInterceptor);\n}\n}\n
WebMvcConfigurerComposite实现了对系统中WebMvcConfigurer的统一处理下面是WebMvcConfigurerComposite的部分源码内容,WebMvcConfigurerComposite运用了组合模式,叶子节点和树枝节点都实现了WebMvcConfigurer接口。
java复制代码packageorg.springframework.web.servlet.config.annotation;\n\n//WebMvcConfigurerComposite是树枝构件WebMvcConfigurer是通用抽象构件\nclassWebMvcConfigurerCompositeimplementsWebMvcConfigurer{\n\n//delegates是叶子构件列表\nprivatefinalList<WebMvcConfigurer>delegates=newArrayList<>();\n\npublicvoidaddWebMvcConfigurers(List<WebMvcConfigurer>configurers){\nif(!CollectionUtils.isEmpty(configurers)){\nthis.delegates.addAll(configurers);\n}\n}\n\n//—省略代码—\n\n//以添加拦截器为例,外部调用者需要添加拦截器时,传入一个InterceptorRegistry注册对象,然后遍历叶子构件,将系统中定义的满足要求的拦截器添加到InterceptorRegistry对象内部维护的列表上面。这里有点绕=-=\n@Override\npublicvoidaddInterceptors(InterceptorRegistryregistry){\n\nfor(WebMvcConfigurerdelegate:this.delegates){\ndelegate.addInterceptors(registry);\n}\n\n}\n//—省略代码—\n}\n
总结一下以上小节内容:WebMvcConfigurer接口里面定义了许多方法让我们能够添加或者配置定制化的组件,我们可以根据自己的需求,建立多个实现了WebMvcConfigurer的配置类,例如MyConfig或者TheirConfig…。然后这些配置类在SpringMVC内部被类WebMvcConfigurerComposite统一管理,WebMvcConfigurerComposite实现了设计模式中的组合模式。
当需要批量添加某一类组件,例如一个核心组件HandlerMapping需要将系统中的功能组件HandlerInterceptor(拦截器)添加到自己内部维护的HandlerInterceptor列表供后续的业务使用,HandlerMapping实现类的源码中是这样操作的(这个过程需要理解,在本章后半部分也会有体现):
定义一个InterceptorRegistry注册器对象registry,调用WebMvcConfigurerComposite的addInterceptors(InterceptorRegistryregistry)方法,参数传入registry在addInterceptors()方法中遍历WebMvcConfigurer实现类列表(Listdelegates),逐个调用WebMvcConfigurer实现类的addInterceptors()方法。registry进入WebMvcConfigurer实现类自己实现的addInterceptors方法中,根据用户自定义的业务逻辑,添加拦截器HandlerMapping获取registry的所有拦截器添加自己内部维护的拦截器列表里面。后续进行自己的业务处理
简单总结下这个步骤:A组件需要B类组件,A组件内部维护了一个B类组件列表。注册器R内部也维护了一个B类组件列表。A组件创建注册器R,R通过WebMvcConfigurerComposite将系统中所有满足要求的B类组件添加到,R自己内部维护的B类组件列表中。然后这个R内部维护的B类组件列表赋值给A内部维护的B类组件列表。
上述步骤是SpringMVC自己内部的调用管理,通常不需要我们关心,单纯使用的话,了解如何定制组件并加入Spring管理即可
现在还有一个问题:WebMvcConfigurerComposite用来管理系统中的WebMvcConfigurer,它的addWebMvcConfigurers方法是用来添加系统中WebMvcConfigurer的,那么这个add方法又是何时被调用的?
至此引出了一个同样较为重要的类DelegatingWebMvcConfiguration,下面是此类的部分代码。我们可以看到,这个类继承了WebMvcConfigurationSupport,并且内部维护了一个上面提到的WebMvcConfigurerComposite对象。类中其他方法基本都是直接调用了内部WebMvcConfigurerComposite对象进行处理,这种方式将WebMvcConfigurerComposite对象与外部隔离开来,对WebMvcConfigurerComposite对象的调用在中间加了一层,也呼应了DelegatingWebMvcConfiguration名称的开头:Delegating->委派、代理。
DelegatingWebMvcConfiguration类是一个被Spring管理的配置类,并且其中有个@Autowired注解标记的setConfigurers()方法,就是在这里,Setter注入方式将系统中实现了WebMvcConfigurer的Bean自动注入,并且添加到WebMvcConfigurerComposite对象身上。
java复制代码packageorg.springframework.web.servlet.config.annotation;\n\n@Configuration(proxyBeanMethods=false)\npublicclassDelegatingWebMvcConfigurationextendsWebMvcConfigurationSupport{\n\nprivatefinalWebMvcConfigurerCompositeconfigurers=newWebMvcConfigurerComposite();\n\n//setter方式注入系统中的WebMvcConfigurer\n@Autowired(required=false)\npublicvoidsetConfigurers(List<WebMvcConfigurer>configurers){\nif(!CollectionUtils.isEmpty(configurers)){\nthis.configurers.addWebMvcConfigurers(configurers);\n}\n}\n//——省略部分代码\n@Override\nprotectedvoidaddInterceptors(InterceptorRegistryregistry){\n\tthis.configurers.addInterceptors(registry);\n}\n\n@Override\nprotectedvoidaddArgumentResolvers(List<HandlerMethodArgumentResolver>argumentResolvers){\n\tthis.configurers.addArgumentResolvers(argumentResolvers);\n}\n//——省略部分代码\n}\n
DelegatingWebMvcConfiguration类继承了WebMvcConfigurationSupport类,那么它就拥有了WebMvcConfigurationSupport类的全部能力,而WebMvcConfigurationSupport类在SpringMVC里面是一个非常重要的类,它里面定义了web场景下相关的核心组件,这些组件包括但不限于:HandlerMapping(处理器映射)、HandlerAdapter(处理器适配器)、HandlerExceptionResolver(处理器异常解析器)等等。以及新建这些核心组件时用到的功能组件,包括但不限于:ArgumentResolver(参数解析器)、ReturnValueHandler(返回值处理器)等等。
以下是WebMvcConfigurationSupport类中定义核心组件处理器映射器的方法,以及其中为处理器映射器添加功能组件拦截器时调用的方法:
java复制代码packageorg.springframework.web.servlet.config.annotation;\n\npublicRequestMappingHandlerMappingrequestMappingHandlerMapping(\n\t\t@Qualifier(&34;)ContentNegotiationManagercontentNegotiationManager,\n\t\t@Qualifier(&34;)FormattingConversionServiceconversionService,\n\t\t@Qualifier(&34;)ResourceUrlProviderresourceUrlProvider){\n\n\tRequestMappingHandlerMappingmapping=createRequestMappingHandlerMapping();\n\tmapping.setOrder(0);\n//设置拦截器\n\tmapping.setInterceptors(getInterceptors(conversionService,resourceUrlProvider));\n\tmapping.setContentNegotiationManager(contentNegotiationManager);\n\tmapping.setCorsConfigurations(getCorsConfigurations());\n\n\tPathMatchConfigurerpathConfig=getPathMatchConfigurer();\n\tif(pathConfig.getPatternParser()!=null){\n\t\tmapping.setPatternParser(pathConfig.getPatternParser());\n\t}\n\telse{\n\t\tmapping.setUrlPathHelper(pathConfig.getUrlPathHelperOrDefault());\n\t\tmapping.setPathMatcher(pathConfig.getPathMatcherOrDefault());\n\n\t\tBooleanuseSuffixPatternMatch=pathConfig.isUseSuffixPatternMatch();\n\t\tif(useSuffixPatternMatch!=null){\n\t\t\tmapping.setUseSuffixPatternMatch(useSuffixPatternMatch);\n\t\t}\n\t\tBooleanuseRegisteredSuffixPatternMatch=pathConfig.isUseRegisteredSuffixPatternMatch();\n\t\tif(useRegisteredSuffixPatternMatch!=null){\n\t\t\tmapping.setUseRegisteredSuffixPatternMatch(useRegisteredSuffixPatternMatch);\n\t\t}\n\t}\n\tBooleanuseTrailingSlashMatch=pathConfig.isUseTrailingSlashMatch();\n\tif(useTrailingSlashMatch!=null){\n\t\tmapping.setUseTrailingSlashMatch(useTrailingSlashMatch);\n\t}\n\tif(pathConfig.getPathPrefixes()!=null){\n\t\tmapping.setPathPrefixes(pathConfig.getPathPrefixes());\n\t}\n\n\treturnmapping;\n}\n\n//—-获取拦截器\n\nprotectedfinalObject[]getInterceptors(\n\t\tFormattingConversionServicemvcConversionService,\n\t\tResourceUrlProvidermvcResourceUrlProvider){\n\tif(this.interceptors==null){\n//增加一个注册器registry,调用子类实现的addInterceptors方法,将子类中找到的拦截器加入注册器中\n\t\tInterceptorRegistryregistry=newInterceptorRegistry();\n\t\taddInterceptors(registry);\n\t\tregistry.addInterceptor(newConversionServiceExposingInterceptor(mvcConversionService));\n\t\tregistry.addInterceptor(newResourceUrlProviderExposingInterceptor(mvcResourceUrlProvider));\n//获取注册器registry中的拦截器并设置给内部对象,并返回\n\t\tthis.interceptors=registry.getInterceptors();\n\t}\n\treturnthis.interceptors.toArray();\n}\n//空方法实现,交给子类实现\nprotectedvoidaddInterceptors(InterceptorRegistryregistry){\n}\n
WebMvcConfigurationSupport的requestMappingHandlerMapping方法获取拦截器时调用的getInterceptors方法是一个典型的模板方法,常见的设计模式之模板方法模式:模板方法通常是一个final修饰的方法,子类无法重写(override)。在这个final修饰的方法里面有至少一个抽象方法或者空方法让子类去实现。
java复制代码//模板方法模式示意\npublicabstractclassAbstractClass{\n//由子类实现具体业务\npublicabstractvoiddoSomething();\npublicabstractvoiddoAnything();\n\n//模板方法\npublicfinalvoidtemplateMethod(){\nthis.doSomething();\nthis.doAnything();\n}\n}\n\n
上面介绍了核心组件HandlerMapping初始化并添加功能组件Interceptor的过程,其他核心组件添加其他功能组件的过程大同小异,感兴趣的话可以对自行阅读源码(位置:packageorg.springframework.web.servlet.config.annotation;的WebMvcConfigurationSupport类)
图2这是一张类的继承关系图,我们根据这张图来对前面两小节做一个总结:
我们开发人员通过实现WebMvcConfigurer接口,定制化组件(拦截器、参数解析器、返回值处理器……),类WebMvcConfigurerComposite对实现了WebMvcConfigurer接口的配置类进行统一管理。(组合模式)类WebMvcConfigurationSupport中实现了对Web场景下核心组件的缺省配置,并且用户自定义功能组件的获取是一个空实现,需要由子类重写**(模板方法模式)**DelegatingWebMvcConfiguration类中维护了一个WebMvcConfigurerComposite对象,并继承了WebMvcConfigurationSupport,且重写了父类的空实现方法(例如父类的抽象方法addInterceptors在这里重写),并且在重写方法里面调用了WebMvcConfigurerComposite的相关方法获取组件(例如调用addInterceptors,不要跟上一句的addInterceptors搞混了!)。另外,DelegatingWebMvcConfiguration类中有个setConfigurers方法,Setter注入的方式将spring容器中的WebMvcConfigurer进行导入。
如果阅读源码你会发现,WebMvcConfigurationSupport中的配置的处理器适配器RequestMappingHandlerAdapter设置参数解析器和返回值处理器这两个组件时调用的setCustomArgumentResolvers和setCustomReturnValueHandlers方法,他们名字中都有个单词Custom:定制的。这说明这里设置的功能组件是用户自定义的。这两个功能组件既然有用户自定义的肯定也有默认实现的,如下面代码所示HandlerMethodArgumentResolverComposite和HandlerMethodReturnValueHandlerComposite这两个对象存储了默认的参数解析器和返回值处理器。那么这两个对象是在哪里初始化的呢?
afterPropertiesSet方法,这个方法来自实现的接口InitializingBean。InitializingBean是Spring提供的拓展性接口,InitializingBean接口为bean提供了属性初始化后的处理方法,它只有一个afterPropertiesSet方法,凡是继承该接口的类,在bean的属性初始化后都会执行该方法。RequestMappingHandlerAdapter的afterPropertiesSet方法里面创建了默认的参数解析器和返回值处理器。创建方式是通过直接new的形式。
java复制代码\npublicclassRequestMappingHandlerAdapterextendsAbstractHandlerMethodAdapter\n\t\timplementsBeanFactoryAware,InitializingBean{\n//……\n@Nullable\nprivateHandlerMethodArgumentResolverCompositeargumentResolvers;\n\n@Nullable\nprivateHandlerMethodArgumentResolverCompositeinitBinderArgumentResolvers;\n\n@Nullable\nprivateList<HandlerMethodReturnValueHandler>customReturnValueHandlers;\n\n@Nullable\nprivateHandlerMethodReturnValueHandlerCompositereturnValueHandlers;\n\n//……\n@Override\npublicvoidafterPropertiesSet(){\n\t//Dothisfirst,itmayaddResponseBodyadvicebeans\n\tinitControllerAdviceCache();\n\n\tif(this.argumentResolvers==null){\n//\n\t\tList<HandlerMethodArgumentResolver>resolvers=getDefaultArgumentResolvers();\n\t\tthis.argumentResolvers=newHandlerMethodArgumentResolverComposite().addResolvers(resolvers);\n\t}\n\tif(this.initBinderArgumentResolvers==null){\n\t\tList<HandlerMethodArgumentResolver>resolvers=getDefaultInitBinderArgumentResolvers();\n\t\tthis.initBinderArgumentResolvers=newHandlerMethodArgumentResolverComposite().addResolvers(resolvers);\n\t}\n\tif(this.returnValueHandlers==null){\n\t\tList<HandlerMethodReturnValueHandler>handlers=getDefaultReturnValueHandlers();\n\t\tthis.returnValueHandlers=newHandlerMethodReturnValueHandlerComposite().addHandlers(handlers);\n\t}\n}\n//……\n}\n
OK,如果以上内容都明白了,那就应该知道WebMvcConfigurationSupport这个类拥有SpringMVC的缺省配置,而他的子类DelegatingWebMvcConfiguration更进一步将开发者定制化的组件也加入框架中,那么我们来思考下一个问题:在SpringBoot环境下,DelegatingWebMvcConfiguration这个配置类是如何交给Spring管理的?
我们都知道SpringBoot基本的自动配置原理:启动时扫描扫描所有jar路径下META-INF/spring.factories文件,这个文件里面是需要加载的配置。并且可能你已经注意到,图2-1继承关系图底部还有一个类EnableWebMvcConfiguration。
spring-boot-autoconfigurer-xxx.jar的spring.factories文件里面配置了一个类叫:WebMvcAutoConfiguration。这个类是自动装配webMVC的入口。而其中有个内部类正是EnableWebMvcConfiguration。如继承图2所示,它继承了DelegatingWebMvcConfiguration,也就拥有了DelegatingWebMvcConfiguration的全部功能。EnableWebMvcConfiguration中对WebMvcConfigurationSupport中定义的一些组件的创建和配置进行了扩展,具体可以阅读源码
现在回到本章最开始的问题:这些组件是如何引入到Spring容器中的?想必答案已经呼之欲出~~
总结下,组件加入Spring容器的过程实际上有一条线索:加入功能组件–>加入核心组件–>交给Spring管理
再详细一点就是:加入功能组件(1、类实现WebMvcConfigurer接口定制化组件,2、Setter注入组件配置类)–>加入核心组件(1、创建核心组件、同时配置核心组件上的功能组件)–>交给Spring管理(通过SpringBoot的自动配置功能实现)
至此,DispatcherServlet在调度过程中需要用到的组件就已经添加到Spring容器当中…
2.DispatcherServlet的初始化
图3
观看DispatcherServlet的继承体系,会发现它与它的父类都跟Servlet有关。这一条继承体系里面,每一层都有它独有的功能规范。一个抽象类或接口只负责一类事情,这种编码方式符合设计模式中的SRP(SingleResponsibilityPrinciple)原则,也就是单一职责原则。
Servlet接口
从顶层的Servlet接口开始,它是Servlet的框架的核心。所有的Servlet都必须实现这一接口。在Servlet接口中定义了5个方法,其中有3个方法代表了Servlet的生命周期
init方法,负责初始化Servlet对象service方法,负责响应客户的请求,最重要的方法,请求响应走的这里。外部的调用者是Servlet容器destory方法,当Servlet对象退出生命周期时,负责释放占有的资源
java复制代码packagejavax.servlet;\n\npublicinterfaceServlet{\npublicvoidinit(ServletConfigconfig)throwsServletException;\npublicServletConfiggetServletConfig();\npublicvoidservice(ServletRequestreq,ServletResponseres)throwsServletException,IOException;\npublicStringgetServletInfo();\npublicvoiddestroy();\n}\n
GenericServlet抽象类(默认生命周期管理)
GenericServlet实现Servlet、ServletConfig接口。提供了默认的生命周期管理,是一个通用的、协议无关的Servlet。并且包括一些额外的功能,比如说log、servlet的name,这部分能力是因为实现了ServletConfig接口
HttpServlet抽象类(Http协议的Servlet实现)
HttpServlet是一个抽象类,它进一步继承并封装了GenericServlet,使得使用更加简单方便,由于是扩展了Http的内容,所以还需要使用HttpServletRequest和HttpServletResponse,这两个类分别是ServletRequest和ServletResponse的子类
HttpServlet中对原始的Servlet中的方法都进行了默认的操作,不需要显式的销毁初始化以及service(),在HttpServlet中,实现了父类的service抽象方法,在其中将ServletRequest和ServletResponse封装为HttpServletRequest和HttpServletResponse,然后再调用自定义的一个新的service()方法,其中通过getMethod()方法判断请求的类型,从而调用doGet()或者doPost()等方法处理get,post等各种类型的http请求,使用者只需要继承HttpServlet,然后重写doPost()或者doGet()等方法处理请求即可。
HttpServletBean抽象类
进行一些创建工作,将init-param(servlet的web.xml的配置)参数注入到子类属性中
FrameworkServlet抽象类(SpringWeb框架的基础Servlet)
FrameworkServlet中实现了HttpServlet中的各种请求类型(如doGet、doPost、doPut等等),大多数请求类型的处理,都指向了一个模板方法:processRequest()。模板方法里面通常有抽象方法用于子类实现,processRequest中也不例外:doService
DispatcherServlet实现类
核心类,用于请求的处理。doService方法进行一些处理,然后调用类方法doDispatch进行真正的请求处理!!!
以上是DispatcherServlet继承体系里面涉及到Servlet类的一些基本概念,这个继承体系中主要有两条路线需要关注:①处理请求的方法从Servlet接口的service()到DispatcherServlet的doService()②初始化方法从Servlet的**init()到DispatcherServlet的onRefresh()**方法
路线①:Servlet接口的service()—>HttpServlet抽象类的service()(在这个方法中将请求参数转化为了HttpServletRequest和HttpServletResponse,并且针对Http请求的类型将请求分发给不同的方法处理:doGet、doPost。并且子类必须实现这些方法否则抛异常)—>FrameworkServlet的doGet()、doPost()等方法。(这些方法中又都调用了一个processRequest方法,进行统一处理,相当于把HttpServlet针对不同Http请求调用不同方法的模式又给还原回去了。所有请求都会调用一个统一的方法:processRequest()。processRequest是一个模板方法,里面调用了一个供子类实现的抽象方法:doService())—>DispatcherServlet类的doService()
路线②:Servlet接口的init()—>GenericServlet抽象类的init(此方法存储了它从servlet容器接收到的ServletConfig对象,以供以后使用)—>HttpServletBean抽象类的init(将init-param参数注入到子类属性中,末尾预留了initServletBean()方法给子类初始化使用)—>FrameworkServlet抽象类的initServletBean(初始化了当前servlet的应用上下文或者说容器更合理:WebApplicationContext。上下文刷新后会调用由子类实现的方法onRefresh()。方法末尾调用了initFrameworkServlet给子类初始化使用)->DispatcherServlet类的onRefresh()。
这样,当DispatcherServlet初始化时,onRefresh方法就会被调用,而onRefresh()方法里面对DispatcherServlet需要的组件进行了注入:通过传入的参数,spring容器,获取到对应组件的Bean。
图4总之,当初始化之后,DispatcherServlet中就会挂上一开始我们注册到Spring容器中的组件,包括HandlerMapping处理器映射器、HandlerAdapter处理器适配器、ViewResolver视图解析器等,后续业务需要时直接使用。
3.请求在DispatcherServlet中的执行过程
到这里,我们应该有了一个概念:第一章讲了被DispatcherServlet调度的组件是如何交给Spring容器进行管理的;第二章讲了DispatcherServlet的初始化过程,这个过程里面从Spring容器获取已经注册好的组件,添加到DispatcherServlet内部。经过以上过程,万事俱备,就等待客户端的请求进入啦。
tips:下面的源码阅读不会过于详细,实际上对于这部分源码而已个人认为也没有必要每一句都去刨析读懂,知晓基本的流程和原理即可。
进入正题之前简要串一下请求过程中会涉及到的组件:
请求进入doDispatch方法,调用MultipartResolver文件上传解析器判断是否为文件上传请求,并保留是否文件上传请求的标记,以便请求过后可以进行资源清理操作根据HandlerMapping处理器映射器找到本次请求要用到的Handler处理器(一般开发人员自己的业务实现:controller这种),并且再将匹配的HandlerInterceptor拦截器和处理器一起组装成处理器执行链。根据上一步找到的处理器再去找到合适的HandlerAdapter处理器适配器执行拦截器的前置处理,再调用处理器适配器的handle方法,执行处理处理器实现各不相同,此处以@Controller注解修饰的类为例,这种处理器一般匹配的是RequestMappingHandlerAdapter处理器适配器,在这个处理器适配器中会调用ArgumentResolver参数解析器解析参数,参数解析里面也会使用到MessageConverter消息转换器来对请求参数进行一个转化获取实际传入处理器的参数后,使用反射执行处理器处理器返回值被ReturnValueHandlers返回值处理器调用,返回值处理器内部结合ContentNegotiationManager内容协商管理器和消息转换器返回合适的内容给浏览器
下面是一个普通的controller方法,以这个方法为例,让我们一步一步的打断点,看下究竟一次请求过程在SpringMVC内部是如何流转的
图5首先这个方法有两个参数,HttpServletRequest和一个自定义的DTO,包含了我们业务需要的字段。并且这个DTO被@RequestBody注解修饰。
使用Postman往本地服务发送一个请求,并且在DispatcherServlet的doDispatch()方法入口处打断点(至于为何在这里打断点,第二章中提到过:Servlet接口的service方法负责处理客户端的请求,而存在一条从Servlet接口的service方法到DispatcherServlet的doService方法的路线。因此客户端的请求最终都会进入doService方法。doDispatch则是doService方法中真正执行请求处理的方法,因此doDispatch是我们阅读请求过程源码的初始位置)
这是doDispatch方法的所有代码,因为并不算多,所以全部贴在这里:
java复制代码protectedvoiddoDispatch(HttpServletRequestrequest,HttpServletResponseresponse)throwsException{\nHttpServletRequestprocessedRequest=request;\nHandlerExecutionChainmappedHandler=null;\nbooleanmultipartRequestParsed=false;\n\nWebAsyncManagerasyncManager=WebAsyncUtils.getAsyncManager(request);\ntry{\nModelAndViewmv=null;\nExceptiondispatchException=null;\n\ntry{\nprocessedRequest=checkMultipart(request);\nmultipartRequestParsed=(processedRequest!=request);\n\n//Determinehandlerforthecurrentrequest.\nmappedHandler=getHandler(processedRequest);\nif(mappedHandler==null){\nnoHandlerFound(processedRequest,response);\nreturn;\n}\n\n//Determinehandleradapterforthecurrentrequest.\nHandlerAdapterha=getHandlerAdapter(mappedHandler.getHandler());\n\n//Processlast-modifiedheader,ifsupportedbythehandler.\nStringmethod=request.getMethod();\nbooleanisGet=HttpMethod.GET.matches(method);\nif(isGet||HttpMethod.HEAD.matches(method)){\nlonglastModified=ha.getLastModified(request,mappedHandler.getHandler());\nif(newServletWebRequest(request,response).checkNotModified(lastModified)&&isGet){\nreturn;\n}\n}\n\nif(!mappedHandler.applyPreHandle(processedRequest,response)){\nreturn;\n}\n\n//Actuallyinvokethehandler.\nmv=ha.handle(processedRequest,response,mappedHandler.getHandler());\n\nif(asyncManager.isConcurrentHandlingStarted()){\nreturn;\n}\n\napplyDefaultViewName(processedRequest,mv);\nmappedHandler.applyPostHandle(processedRequest,response,mv);\n}\ncatch(Exceptionex){\ndispatchException=ex;\n}\ncatch(Throwableerr){\n//Asof4.3,we&34;Handlerdispatchfailed&34;Handlerprocessingfailed&getCandidateBeanNames()\n*@seehandlerMethodsInitialized\n*/\nprotectedvoidinitHandlerMethods(){\nfor(StringbeanName:getCandidateBeanNames()){\nif(!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)){\n//\nprocessCandidateBean(beanName);\n}\n}\nhandlerMethodsInitialized(getHandlerMethods());\n}\n\n
在initHandlerMethods方法中,首先获取了容器中的所有Bean的名称,然后调用了processCandidateBean方法,这个方法源码如下,最重要的是它的isHandler()方法,是一个子类实现的抽象方法。由子类来判断当前Bean是否是一个Handler处理器。
java复制代码protectedvoidprocessCandidateBean(StringbeanName){\nClass<?>beanType=null;\ntry{\nbeanType=obtainApplicationContext().getType(beanName);\n}\ncatch(Throwableex){\n//Anunresolvablebeantype,probablyfromalazybean-let&34;Couldnotresolvetypeforbean&34;+beanName+&39;&34;HandlerInterceptor.afterCompletionthrewexception&34;Arguments:&34;handlerdoesnotexist&34;”;\n\n}\n
当返回值被处理后,就会一路返回构建ModelAndView,直到返回到DispatcherServlet类。再执行拦截器的postHandle后置处理,以及afterCompletion等方法。至此一个请求在SpringMVC中的流转过程介绍,就接近尾声。当然,实际肯定不会这么简单,SpringMVC还为我们做了许多额外的操作,就由感兴趣的读者去自行探究了~
最后再进行一个扩展,现在有这样一个问题:常规的web网页请求场景,处理器上有@ResponseBody注解,序列化默认使用jackson,消息转换器是MappingJackson2HttpMessageConverter。在拦截器的postHandle后置处理中对HttpServletResponse操作有效吗?
应该是无效的,因为返回值处理器在调用MappingJackson2HttpMessageConverter这个消息转换器时,内部会调用一个outputMessage.getBody().flush()方法将返回数据刷出去。具体而言是将response里面的commited状态修改了(源码调用较深,建议自己实地操作一次),此时response里面的信息也就已经确定,后面不能再改变。
对于一次请求在SpringMVC的流转过程,本文就写到这里。文章内容较多,我写这篇博客的初衷是想对自己过往的知识做个总结,文中不免有许多跳跃之处,使文章不够顺畅,逻辑不够清晰。当然,发出这篇文章时,并不表示这篇文章已经定型,应该会在后续的时间里,遣词造句做出一些优化,知识点查漏补缺,让这篇文章更加通俗易懂且可靠。
关于本次java访问网站返回源码分享和打开浏览器访问特定网址的问题分享到这里就结束了,如果解决了您的问题,我们非常高兴。