Marco's Java【SpringMVC入门(三) 之 SpringMVC的映射器和适配器详解】

前言

上节我们讲到SpringMVC的基本配置,并做了一个Demo项目,实现数据层,到业务层,再到控制层,最后在浏览器上显示我们数据库中查询到的内容,"一条龙"的服务已经到位了,但是关于映射器和拦截器的概念仍然比较模糊,那么这节我们针对于这一部分来做一个详解吧~

映射器和适配器

之前我们提到过映射器就好比"花名册",通过花名册找到对应的我们要找的类的地址,并且我们之前也讲到过,控制器是需要我们自己去创建的,而其他部分是Spring内部就已经存在的结构,那么这样会有个什么问题呢?
就是我们自己创建的类怎么去适配Spring的接口呢?
在这里插入图片描述
大家看我们上面的图,DispatcherServlet在查询花名册的时候,找到了这个类的完全限定名之后,就需要找这个类对应的适配器,去适配Spring为我们提供的接口,通过查看源码我们发现,所有适配器的顶级接口是HandlerAdapter
在这里插入图片描述
而在HandlerAdapter下面又有很多个不同的适配器,大家有没有发现其中有两个名字我们有点熟悉。
一个是HttpRequestHandlerAdapter还有一个是SimpleControllerHandlerAdapter,我们把最后的Adapter字样去掉,那么他们分别就变成HttpRequestHandler和SimpleControllerHandler,是不是和我们之前自定义的Controller分别两种方式实现的接口很相像(HttpRequestHandler和Controller)
在这里插入图片描述
这也就印证了,当我们使用不同的控制器接口时,适配器也就不一样,这个很容易理解,Controller就好比我们的笔记本LapTop,而HttpRequestHandler就好比我们的台式机,那么不同的电脑的接口是不一样的(只是打个比方),那么当我们想接入硬件设施,比如说打印机,那么就需要一个适配器去适配它才能运行。

非注解的映射器和适配器

了解了什么是映射器和适配器,那么我们来看看SpringMVC的映射器和适配器具体有哪些吧~
我们打开spring-webmvc-4.3.24 这个jar包,最下面有个文件DispatcherServlet.properties,这里面列举了Spring的映射器、适配器和视图处理器
在这里插入图片描述

# Default implementation classes for DispatcherServlet's strategy interfaces.
# Used as fallback when no matching beans are found in the DispatcherServlet context.
# Not meant to be customized by application developers.

org.springframework.web.servlet.LocaleResolver=org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver

org.springframework.web.servlet.ThemeResolver=org.springframework.web.servlet.theme.FixedThemeResolver

org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\
	org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping

org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\
	org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\
	org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter

org.springframework.web.servlet.HandlerExceptionResolver=org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerExceptionResolver,\
	org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver,\
	org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver

org.springframework.web.servlet.RequestToViewNameTranslator=org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator

org.springframework.web.servlet.ViewResolver=org.springframework.web.servlet.view.InternalResourceViewResolver

org.springframework.web.servlet.FlashMapManager=org.springframework.web.servlet.support.SessionFlashMapManager

非注解的映射器
1)BeanNameUrlHandlerMapping
我们的Controller能够正常的运行,并且在浏览器上正常显示也要归功于这三个"组件"。那么我们来配置看看吧
之前我们使用的是这种方式配置大家还有印象吗?

<bean id="listUserController" class="com.marco.servlet.ListUserController" name="/list.action"></bean>

这种方式配置得话对应的映射器是,

<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"></bean>

BeanNameUrlHandlerMapping这种映射方式使用bean的name属性值做为url的映射的key,一个name只能对应一个url,映射器的本质其实就是一个map

2)SimpleUrlHandlerMapping
这种方式配置就不一样了,我们之前在学习Servlet的时候有讲过,servlet-name和url-pattern是一对多的关系,但是通过BeanNameUrlHandlerMapping的话一个name只能对应一个class,实现不了一对多这种关系,因此,就衍生出来SimpleUrlHandlerMapping这种映射方式。

<?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:mvc="http://www.springframework.org/schema/mvc"
	xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
		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.xsd">
		
	<bean id="listUserController" class="com.marco.servlet.ListUserController"></bean>
	<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
		<property name="mappings">
			<props>
				<prop key="/user.action">listUserController</prop>
				<prop key="/user1.action">listUserController</prop>
				<prop key="/user2.action">listUserController</prop>
			</props>
		</property>
	</bean>
	<bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"></bean>
</beans>

SimpleUrlHandlerMapping可以配置多个url并指向同一个Controller,因此当我们访问上面任意的url地址,都可以访问到我们listUserController对象
注意: BeanNameUrlHandlerMapping和SimpleUrlHandlerMapping这两种映射器是可以共存的!

非注解适配器
1)SimpleControllerHandlerAdapter
适配器则有两种选择,上面也有提到过,实现Controller接口的处理器使用SimpleControllerHandlerAdapter

<bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"></bean>

2)HttpRequestHandlerAdapter
而实现HttpRequestHandler接口的的处理器需要使用HttpRequestHandlerAdapter

<bean class="org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter"></bean>

注意: SimpleControllerHandlerAdapter和HttpRequestHandler这两种适配器也是可以共存的!

测试阶段
那我们刚刚讲到,控制器如果没有相匹配的适配器的话就无法正常运行,真的是这样吗?
我们就拿SimpleControllerHandlerAdapter的案例,把刚刚的配的这个适配器先给注掉
然后替换成HttpRequestHandlerAdapter看看是什么效果

<!-- <bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"></bean> -->
<bean class="org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter"></bean>

显示的不全,不过我们可以看到有这么个错误的信息,说没有ListUserController处理器的适配器,需要一个支持这个处理器运行的适配器,那这不正是印证了我们之前提到的控制器需要有相匹配的适配器才能正常运行的观点吗?

No adapter for handler [com.marco.servlet.ListUserController@2070467e]: 
The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler

在这里插入图片描述

注解的映射器和适配器

在讲解具体配置之前呢,先给大家介绍一下,注解都有哪些映射器和适配器

映射器
在spring3.1之前使用
org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping注解映射器。
在spring3.1之后使用
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping注解映射器。

适配器
在spring3.1之前使用
org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter注解适配器。
在spring3.1之后使用
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter注解适配器

先列出来的原因是spring3.1之前的版本的映射器和适配器都被@Depecated(过时了)注解了,而且配上去也不能使用,那么我们就使用spring3.1之后的版本的映射器和适配器测试一下吧

第一步:勾选namespace的mvc选项
在这里插入图片描述
第二步:配置springmvc.xml
这里我把之前没有讲到过的视图解析器加上了,视图解析器只有一个InternalResourceViewResolver

<?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:mvc="http://www.springframework.org/schema/mvc"
	xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
		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.xsd">
	<!-- 这一步很关键,一定要对servlet包进行扫描才能进行IoC/DI操作!! -->
	<context:component-scan base-package="com.marco.servlet"></context:component-scan>
	<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"></bean>
	<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"></bean>
	<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"/>
</beans>

第三步:在ListUserController上添加注解,@RequestMapping(“user”)就替代了我们之前的url-pattern和class的映射操作,是不是和我们在Servlet学习中所使用到的@WebServlet注解很类似呢?
只不过,在@RequestMapping这个注解中,缺省/.action,因为只有当url的后缀是.action的时候,DispatcherServlet才会放行,反过来想既然能放行通过,那么url后缀必然有.action,因此可以缺省。

@Controller
@RequestMapping("user")
public class ListUserController{
	@RequestMapping("query")
	public ModelAndView query() throws Exception {
		ApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
		UserService userService = context.getBean(UserService.class);
		List<User> users = userService.selectAll();
		ModelAndView modelAndView = new ModelAndView();
		modelAndView.setViewName("/list.jsp");
		modelAndView.addObject("users", users);
		modelAndView.setStatus(HttpStatus.OK);
		return modelAndView;
	}

测试后发现没有问题
在这里插入图片描述
其实在实际开发中,上面的写法都不会用!惊不惊喜,意不意外?

ListUserController类上的注解还是不用动,其他的xml配置全部删除掉,然后只用加上

<context:component-scan base-package="com.marco.servlet"></context:component-scan>
<mvc:annotation-driven></mvc:annotation-driven>

那如果我跟你说,其实<mvc:annotation-driven></mvc:annotation-driven>也不用配置,你是不是会疯掉?

哈哈,别急,虽然这样也可以,但是在开发中我们还是要将<mvc:annotation-driven></mvc:annotation-driven>加上,
<mvc:annotation-driven></mvc:annotation-driven>本质上就是帮我们开启了注解的映射器、适配器和视图解析器,除了这些,它还有一些参数转换的辅助功能,譬如说将页面上的String转换为Integer,亦或者说将对象以json字符串的形式输出到页面等等,这些功能虽然都很简单,但是却很重要,因此,建议使用这种方式!

后语

其实本节遗留下来一个问题还没有被解答,就是为什么我们映射器、适配器都没有配置的情况下,程序依然可以正常运转,这其中的奥秘究竟是什么?
有兴趣的朋友可以看我的番外篇,关于映射器和适配器底层的分析和自己的一些理解
Marco’s Java【SpringMVC番外篇 之 映射器及适配器运行原理源码解析】

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值