目录
Servlet概述
Java Servlet 是运行在 Web 服务器或应用服务器上的程序,它是作为来自 Web 浏览器或其他 HTTP 客户端的请求和 HTTP 服务器上的数据库或应用程序之间的中间层。使用 Servlet,您可以收集来自网页表单的用户输入,呈现来自数据库或者其他源的记录,还可以动态创建网页。
Servlet流程
搭建过程
-
创建普通web项目
-
maven引入servlet相关jar包
<!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api --> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>4.0.1</version> </dependency>
-
新建java源文件XXXServlet继承自HttpServlet,并重写其中的(doGet && doPost) || service 方法
返回视图:
@Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { this.doPost(req, resp); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { req.setAttribute("username","aaaaaa"); req.setAttribute("password","bbbbbb"); req.getRequestDispatcher("/index.jsp").forward(req, resp); }返回数据:
public class JsonServlet extends HttpServlet { @Override protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { PrintWriter printWriter = resp.getWriter(); Map<String,String> jsonData = new HashMap<>(); jsonData.put("username","service"); jsonData.put("password","service"); printWriter.println(jsonData); } } -
web.xml中注册Servlet
<servlet> <servlet-name>helloServlet</servlet-name> <servlet-class>com.fengwuj.controller.HelloServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>helloServlet</servlet-name> <url-pattern>/helloServlet</url-pattern> </servlet-mapping> <servlet> <servlet-name>jsonServlet</servlet-name> <servlet-class>com.fengwuj.controller.JsonServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>jsonServlet</servlet-name> <url-pattern>/jsonServlet</url-pattern> </servlet-mapping>
初始化过程
-
在启动Web项目时,容器(比如Tomcat)会读web.xml配置文件中的两个节点<listener>和<contex-param>。
-
接着容器会创建一个ServletContext(上下文),应用范围内即整个WEB项目都能使用这个上下文。
-
接着容器会将读取到<context-param>转化为键值对,并交给ServletContext。
-
容器创建<listener></listener>中的类实例,即创建监听(备注:listener定义的类可以是自定义的类但必须需要继承ServletContextListener)。
-
在监听的类中会有一个contextInitialized(ServletContextEvent event)初始化方法,在这个方法中可以通过event.getServletContext().getInitParameter("contextConfigLocation") 来得到context-param 设定的值。在这个类中还必须有一个contextDestroyed(ServletContextEvent event) 销毁方法.用于关闭应用前释放资源,比如说数据库连接的关闭。
-
得到这个context-param的值之后,你就可以做一些操作了.注意,这个时候你的WEB项目还没有完全启动完成.这个动作会比所有的Servlet都要早。
执行过程

Servlet--细节

Servlet解析详述:JavaWeb——Servlet(全网最详细教程包括Servlet源码分析)_扬俊的小屋-CSDN博客_servlet
接口请求-->Tomcat-->servlet的解析详述:Tomcat 9 源码解析 -- 请求如何经过tomcat到达servlet_gaohaicheng123的博客-CSDN博客
SpringMvc概述
SpringMVC,基于MVC架构,提供了模型-视图-控制的体系结构和灵活、松散耦合的 web 应用程序的组件;对Servlet进行封装、整合、管理;作为Spring框架的一部分,能 够 使 用 Spring 的 IoC 和 Aop 方 便 整 合 Strtus,MyBatis,Hiberate,JPA 等其他框架;强化了注解使用,方便灵活
SpringMvc流程
搭建过程
-
创建javaWeb项目
-
pom文件引入spring包
<properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <maven.compiler.source>1.7</maven.compiler.source> <maven.compiler.target>1.7</maven.compiler.target> <spring.version>5.0.8.RELEASE</spring.version> </properties> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.11</version> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>${spring.version}</version> </dependency> <!-- pring IOC的基础实现,包含访问配置文件、创建和管理bean等 --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-beans</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context-support</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-tx</artifactId> <version>${spring.version}</version> </dependency> <!-- https://mvnrepository.com/artifact/org.springframework/spring-aop --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aop</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aspects</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-core</artifactId> <version>2.9.8</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-annotations</artifactId> <version>2.9.8</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.9.8</version> </dependency> </dependencies> -
创建Main-servlet.xml文件,用于初始化springmvc的启动配置信息
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd"> <!-- 配置自动扫描的包 --> <context:component-scan base-package="com.fengwuj.controller"></context:component-scan> <!-- 转换器--> <mvc:annotation-driven> <mvc:message-converters> <bean class="org.springframework.http.converter.StringHttpMessageConverter"/> <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"/> </mvc:message-converters> </mvc:annotation-driven> <!-- 配置视图解析器 如何把handler 方法返回值解析为实际的物理视图 --> <!--prefix和suffix:查找视图页面的前缀和后缀(前缀[逻辑视图名]后缀), --> <!-- 比如传进来的逻辑视图名为result,则该jsp视图页面应该存放在“/WEB-INF/result.jsp” --> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name = "prefix" value="/WEB-INF/"></property> <property name = "suffix" value = ".jsp"></property> </bean> </beans>
-
web.xml注册DispatcherServlet,引入springmvc.xml
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd" > <web-app> <display-name>Archetype Created Web Application</display-name> <!-- /WEB-INF/springmvc-servlet.xml /WEB-INF/myweb-servlet.xml DispatcherServlet在创建对象的时候,在init()方法中会创建springmvc的容器对象 WebApplicationContext, 创建容器的时候会读取配置文件, 创建配置文件中的所有bean对象。 默认读取的配置文件的位置是 /WEB-INF ,默认的文件名称是 <servlet-name>-servlet.xml --> <!-- 配置DispatchcerServlet --> <servlet> <servlet-name>Main</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:Main-servlet.xml</param-value> </init-param> <!-- load-on-startup:表示启动容器时初始化该Servlet; --> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>Main</servlet-name> <!-- url-pattern:表示哪些请求交给Spring Web MVC处理, “/” 是用来定义默认servlet映射的。 --> <!-- 也可以如“*.html”表示拦截所有以html为扩展名的请求。 --> <url-pattern>/</url-pattern> </servlet-mapping> <!-- 至此请求已交给Spring Web MVC框架处理,因此我们需要配置Spring的配置文件, --> <!-- 默认DispatcherServlet会加载WEB-INF/[DispatcherServlet的Servlet名字,也就是上面的Main]-servlet.xml配置文件。 --> <!-- 即Main-servlet.xml --> </web-app>
核心组件类

①:DispatcherServlet是SpringMVC中的前端控制器(front controller),负责接收request并将request转发给对应的处理组件。
②:HanlerMapping是SpringMVC中完成url到Controller映射的组件。DispatcherServlet接收request,然后从HandlerMapping查找处理request的controller.
③:Controller处理request,并返回ModelAndView对象,Controller是SpringMVC中负责处理request的组件(类似于struts2中的Action),ModelAndView是封装结果视图的组件。
④ ⑤ ⑥:视图解析器解析ModelAndView对象并返回对应的视图给客户端。
执行流程
-
项目启动,扫描Controller,RequestMapping注解,绑定路由和方法,进行注册

RequestMappingHandlerMapping-->afterPropertiesSet()-->super.afterPropertiesSet()
AbstractHandlerMethodMapping-->initHandlerMethods()
//isHandler:判断当前bean是否有Controller注解或者RequestMapping注解 //detectHandlerMethods:注册ur-method的Mapping关系,用于后期路由匹配分发 if (beanType != null && isHandler(beanType)) { detectHandlerMethods(beanName); } //detectHandlerMethods中 //key:方法,value:RequestMappingInfo对象,patternsCondition存储url, //url格式 [/return-jsp || /test/{id}/test || /test-all/*] Map<Method, T> methods = MethodIntrospector.selectMethods(); //方法注册 //handler:Controller对象 //invocableMethod:反射得到的方法,通过了一系列校验 //mapping:RequestMappingInfo对象 registerHandlerMethod(handler, invocableMethod, mapping); //主要注册到mappingRegistry中,后续DispatcherServlet会从这里取 this.mappingRegistry.register(mapping, handler, method); //mappingLookup:key-->RequestMappingInfo对象,value-->HandlerMethod,spring封装的对象,记录了方法、对象等信息 //urlLookup:key-->精确的url,不包含*或者?,value-->RequestMappingInfo对象 //nameLookup:key-->方法的自定义规则限定名,TC#test(TestController中test方法),value-->HandlerMethod对象 //corsLookup:key-->HandlerMethod对象,value-->CorsConfiguration对象,存储CrossOrigin注解相关跨域配置 //registry:key-->RequestMappingInfo对象,value-->MappingRegistration对象,包含RequestMappingInfo,method,url,方法的自定义规则限定名 -
请求过程
DispatcherServlet拦截了所有请求,所有请求进入DispatcherServlet的doService方法
doService-->doDispatch(request, response)进行具体的请求处理
2.1 匹配Url和Method
// Determine handler for the current request. //匹配url和方法 mappedHandler = getHandler(processedRequest); //getHandler------>追踪 //handlerMappings--->RequestMappingHandlerMapping,BeanNameTypeMapping..... for (HandlerMapping hm : this.handlerMappings) { if (logger.isTraceEnabled()) { logger.trace("Testing handler map [" + hm + "] in DispatcherServlet with name '" + getServletName() + "'"); } HandlerExecutionChain handler = hm.getHandler(request); if (handler != null) { return handler; } } //hm.getHandler---->getHandlerInternal(request)--->AbstractHandlerMethodMapping#getHandlerInternal //lookupPath:url,去除掉部署指定的前缀/springmvcdemo/prexxx/return-jsp------------>/prexxx/return-jsp String lookupPath = getUrlPathHelper().getLookupPathForRequest(request); //handlerMethod:找到url匹配到的方法 HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request); //lookupHandlerMethod------>this.mappingRegistry.getMappingsByUrl(lookupPath)--------------->>urlLookup //通过urlLookup(key:url) 拿到RequestMappingInfo对象 //通过mappingLookup(key:RequestMappingInfo对象) 拿到 HandlerMethod对象 //将RM和HM封装为Match对象,添加到matches这个List中 //排序,默认第一个为最佳匹配 //这里是路由命中规则的实现原理,主要是各种情况的考虑及RequestMappingInfo#compareTo方法的重写 Comparator<Match> comparator = new MatchComparator(getMappingComparator(request)); matches.sort(comparator); if (matches.size() > 1) { //跨域校验 if (CorsUtils.isPreFlightRequest(request)) { return PREFLIGHT_AMBIGUOUS_MATCH; } if((comparator.compare(bestMatch, secondBestMatch) == 0){ //抛错,多个方法对应同一个url路由 } ...... } 2.2 封装HandlerExecutionChain,适配method相应的MappedInterceptor(默认)
protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) { HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ? (HandlerExecutionChain) handler : new HandlerExecutionChain(handler)); String lookupPath = this.urlPathHelper.getLookupPathForRequest(request); for (HandlerInterceptor interceptor : this.adaptedInterceptors) { if (interceptor instanceof MappedInterceptor) { MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor; if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) { chain.addInterceptor(mappedInterceptor.getInterceptor()); } } else { chain.addInterceptor(interceptor); } } return chain; }2.3 选择Handler适配器,SpringMVC提供RequestMappingHandlerAdapter
// Determine handler adapter for the current request. HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
2.4 Controller#Method方法调用
// Actually invoke the handler. //得到view mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); //handle---->AbstractHandlerMethodAdapter#handleInternal //------->RequestMappingHandlerAdapter#handleInternal //------->RequestMappingHandlerAdapter#invokeHandlerMethod //------->invocableMethod.invokeAndHandle(webRequest, mavContainer)-----准备调用Controller方法 //------->ServletInvocableHandlerMethod#invokeAndHandle // --->Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs); // --->getMethodArgumentValues--------获取方法参数,填充 // --->getMethodParameters--------获取Controller方法形参,并循环依次填充 // ---->doInvoke(args)-----直接反射调用,Method.invoke(obj,args) // --->this.returnValueHandlers.handleReturnValue()----对标Controller#Method封装返回参数 // --->HandlerMethodReturnValueHandlerComposite#handleReturnValue // --->1.ViewNameMethodReturnValueHandler:处理视图返回 // --->2.RequestResponseBodyMethodProcessor:处理json public boolean supportsReturnType(MethodParameter returnType) { return (AnnotatedElementUtils.hasAnnotation(returnType.getContainingClass(), ResponseBody.class) || returnType.hasMethodAnnotation(ResponseBody.class)); } @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Controller @ResponseBody public @interface RestController { /** * The value may indicate a suggestion for a logical component name, * to be turned into a Spring bean in case of an autodetected component. * @return the suggested component name, if any (or empty String otherwise) * @since 4.0.1 */ @AliasFor(annotation = Controller.class) String value() default ""; } //------->RequestMappingHandlerAdapter#getModelAndView
Servlet与SpringMvc
总结
SpringMVC基于Servlet进行演化,使用DispatcherServlet拦截所有API请求,自定义注解进行URL匹配,将请求Mapping到相应的方法上进行处理。
其他
SpringMvc 将@RequestMapping注册到HandlerMapping_shanchahua123456的博客-CSDN博客_springmvcrequestmapping的实现
SpringBoot 中DispatcherServlet请求分发流程源码分析_shanchahua123456的博客-CSDN博客_springboot 分发请求
本文深入介绍了Servlet和SpringMVC的概览、工作流程、搭建步骤以及执行过程。Servlet是JavaWeb的基础,用于处理HTTP请求,而SpringMVC作为Spring框架的一部分,提供了更高级的MVC架构,简化了Web应用的开发。文章详细阐述了两者从创建项目、配置文件、请求处理到视图解析的全过程,适合初学者和进阶开发者参考。

2319

被折叠的 条评论
为什么被折叠?



