Spring MVC

Spring MVC,第1张

MVC

MVC是一种软件架构的思想,将软件按照模型、视图、控制器来划分

M:Model,模型层,指工程中的JavaBean,作用是处理数据

JavaBean分为两类:

  • 一类称为实体类Bean:专门存储业务数据的,如 Student、User 等
  • 一类称为业务处理 Bean:指 Service 或 Dao 对象,专门用于处理业务逻辑和数据访问。

V:View,视图层,指工程中的html或jsp等页面,作用是与用户进行交互,展示数据

C:Controller,控制层,指工程中的servlet,作用是接收请求和响应浏览器

Spring MVC

基于Spring的web MVC模块发展而来;底层基于Servlet。有以下三个优点:

  1. 支持RestFul风格
  2. spring家族原生产品,与IOC等基础设施无缝对接。
  3. 支持各种视图技术、支持各种请求资源映射策略。
Spring MVC核心组件及流程


 

HandlerMapping、HandlerAdapter、Handler Controller有多种定义方式:
  1. @Controller
  2. 实现Controller接口
  3. 实现Servlet接口定义的Controller
  4. ...      
HandlerMapping:根据url找到对应的Controller层方法

springmvc提供了多种不同的HandlerMapping实现类,每个实现类都可以根据请求参数(url)查找对应的Handler。这些实现类实例化对象后都放到DispatcherServlet的属性handlerMappings中,以供后续遍历时查找访问。

	//入参context是web环境下的ApplicationContext对象,
	//默认实现类是AnnotationConfigServletWebServerApplicationContext
	private void initHandlerMappings(ApplicationContext context) {
		this.handlerMappings = null;
		//detectAllHandlerMappings默认是true,
		//表示是自动检测,还是只从容器中查找名字为“handlerMapping”的对象
		if (this.detectAllHandlerMappings) {
			//下面开始自动查找HandlerMapping对象
			//下面这一行代码用于从容器中查找类型为HandlerMapping的对象,
			//包括父容器也会一并查找,scope=prototype类型的也会搜索到
			Map matchingBeans =
					BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
					
			if (!matchingBeans.isEmpty()) {
				this.handlerMappings = new ArrayList<>(matchingBeans.values());
				// We keep HandlerMappings in sorted order.
				AnnotationAwareOrderComparator.sort(this.handlerMappings);//排序
			}
		}
		else {
			try {
				//下面是从容器中查找名字为“handlerMapping”且类型为HandlerMapping的对象
				HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);
				this.handlerMappings = Collections.singletonList(hm);
			}
			catch (NoSuchBeanDefinitionException ex) {
				// Ignore, we'll add a default HandlerMapping later.
			}
		}

		// Ensure we have at least one HandlerMapping, by registering
		// a default HandlerMapping if no other mappings are found.
		//如果上述过程没有找到HandlerMapping,那么读取文件DispatcherServlet.properties,
		//从该文件中加载HandlerMapping对象
		if (this.handlerMappings == null) {
			this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
			if (logger.isTraceEnabled()) {
				logger.trace("No HandlerMappings declared for servlet '" + getServletName() +
						"': using default strategies from DispatcherServlet.properties");
			}
		}
	}

从上面代码可以看到springmvc直接从容器中获取HandlerMapping对象添加到属性handlerMappings中。那么容器中的对象从哪来?答案是WebMvcConfigurationSupport。WebMvcConfigurationSupport提供了requestMappingHandlerMapping()、viewControllerHandlerMapping()等创建HandlerMapping的方法。该类一共创建四个,分别是:

  1. RequestMappingHandlerMapping
  2. BeanNameUrlHandlerMapping
  3. RouterFunctionMapping
  4. resourceHandlerMapping

除了上面四个,还有一个是在EnableWebMvcConfiguration中创建的:

  • WelcomePageHandlerMapping

有的HandlerMapping对象,指定了order属性,这个属性用于排序使用,可以看到initHandlerMappings()里面也对这些对象做了排序,排序结果如下: 

 

DispatcherServlet遍历HandlerMapping便是以上图的顺序进行,当从其中一个HandlerMapping中找到合适的Handler,那么后面的便不再遍历,下面是遍历的代码,DispatcherServlet便是调用下面这个方法找到合适的Handler:

protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
		if (this.handlerMappings != null) {
			for (HandlerMapping mapping : this.handlerMappings) {
				HandlerExecutionChain handler = mapping.getHandler(request);
				if (handler != null) {
					return handler;//找到后直接返回
				}
			}
		}
		return null;
	}

最终HandlerMapping会返回一个handler{ map;其中key是url,value是对应handler(我们的Controller层中的方法)}的执行链HanlerExcutionChain对象。

上面第一个handler是 HanlerExcutionChain对象,第二个handler是我们对应的Controller中方法。

HandlerAdapter :根据Controller层中方法的实现方式找到对应适配器

不同的HandlerAdapter处理不同的的Controller实现方式,通过HandlerAdapter去适配当前Controller对应的handler。


Spring MVC拦截器

请求拦截:

  1. 对congtroller拦截:Spring MVC拦截。
  2. 对Controller之外Bean的拦截:Spring Aop
  3. 对静态资源的拦截:Filter

基于注解的拦截器实现

  1. 实现HandlerIntercepter接口,重写三个方法:
    1. preHandle:控制器方法执行之前执行preHandle(),其boolean类型的返回值表示是否拦截或放行,返回true为放行,即调用控制器方法;返回false表示拦截,即不调用控制器方法。

    2. postHandle:控制器方法执行之后执行postHandle()。

    3. afterComplation:处理完视图和模型数据,渲染视图完毕之后执行afterComplation()

  2. 注册拦截器:定义配置类,实现WebMvcConfigurer接口,在接口的addInterceptors方法中注册拦截器、设置拦截路径、设置放行路径。

基于配置的拦截器实现





    
    
    


3、多个拦截器的执行顺序

a>若每个拦截器的preHandle()都返回true

此时多个拦截器的执行顺序和拦截器在SpringMVC的配置文件的配置顺序有关:

preHandle()会按照配置的顺序执行,而postHandle()和afterComplation()会按照配置的反序执行

b>若某个拦截器的preHandle()返回了false

preHandle()返回false和它之前的拦截器的preHandle()都会执行,postHandle()都不执行,返回false的拦截器之前的拦截器的afterComplation()会执行





    
    
    


多个拦截器执行顺序

  1. 若每个拦截器的preHandle()都返回true;此时多个拦截器的执行顺序和拦截器在SpringMVC的配置文件的配置顺序有关:preHandle()会按照配置的顺序执行,而postHandle()和afterComplation()会按照配置的反序执行。

  2. 若某个拦截器的preHandle()返回了false;preHandle()返回false和它之前的拦截器的preHandle()都会执行,postHandle()都不执行,返回false的拦截器之前的拦截器的afterComplation()会执行。


转发和重定向 转发(服务端行为) 转发的特点是:
  1. 地址栏不会改变
  2. 转发只能转发到当前Web应用内的资源
  3. 在转发过程中,可以将数据保存到request域对象当中去
  4. 转发只有一次请求
  5. 转发是服务器端行为
转发的过程:
  1. 客户端浏览器发送http
  2. web浏览器接收请求
  3. 调用内部的一个方法在容器内部完成请求处理和转发工作

需注意的是:转发的路径必须是同一个web容器下的url。在客户端浏览器路径栏显示的仍然是第一次访问的路径。转发行为是浏览器只做了一次访问请求。

重定向(客户端行为) 重定向的特点:
  1. .重定向地址栏会改变
  2. 重定向可以跳转到当前web应用,甚至是外部域名的网站
  3. 不能在重定向的过程中,将数据保存到request域对象中
重定向的过程:
  1. 客户端浏览器发送http请求
  2. web服务器接收后,发送302状态码响应,并且发送location给客户端服务器
  3. .客户端服务器发现是302响应后,则自动发送一个http请求,请求url为重定向的地址,响应的服务器根据此请求寻找资源并发送给客户
两者区别:
  1. 请求转发是一次请求一次响应,而重定向是两次请求两次响应
  2. 请求转发地址栏不会改变,而重定向地址栏会显示第二次请求的地址
  3. 请求转发只能转发给本项目的其他资源,而重定向不仅可以重定向到本项目的其他资源, 还可以重定向到其他项目
  4. 请求转发是服务器端的行为,转发时只需要给出转发的资源路径即可,如Servlet的访问路径;而重定向需要给出全路径,即路径要包含项目名
  5. 请求转发比重定向的效率高,因为请求转发是一个请求。在以后的开发中,如果需要地址栏的地址发生改变,就选择重定向;如果需要在Servlet之间通过request域进行数据通信,就选择请求转发
表单重读提交问题

问题:当用转发提交表单后,点击刷新页面,页面会提示表单重复提交。

原因:Servlet处理请求以后,直接转发到目标页面,整个业务只发生一次请求,点击刷新会一直刷新之前的请求。

解决:使用重定向。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/725502.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-04-26
下一篇 2022-04-26

发表评论

登录后才能评论

评论列表(0条)

保存