Servlet方式
Servlet
是我们很熟悉的一个类。Spring MVC也是对这种实现方式提供了支持,也把它能够当作一个Spring MVC的Bean,作为一个Handler来实现的~~
@Controller("/servletController") public class ServletController extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("this my servlet controller"); } }
小细节:即使你使用了@EnableWebMvc,Spring MVC默认也不会给你注册SimpleServletHandlerAdapter这个适配器(其余三个都给注册了),因此此处我们自己注册一下即可。
@Configuration @EnableWebMvc public class WebMvcConfig extends WebMvcConfigurerAdapter { // 让支持Servlet这种Handler的方式~~你 SpringMVC默认是不予支持的 @Bean public SimpleServletHandlerAdapter simpleServletHandlerAdapter() { return new SimpleServletHandlerAdapter(); } }
这样子是也是能够正常访问的,因为它就是个Handler了。
从Spring MVC的意图中我们也可以看出,Spring并不推荐我们再使用源生的Servlet来处理请求了~~~
@RequestMapping注解方式
是当下最为广泛使用的方式。
这种方式此处就不做介绍了,不介绍并不是它不重要,反而是它太重要了此处篇幅不够,比如它的数据绑定可以完全屏蔽Servlet源生的API,为后续的WebFlux做了充分的准备~后面还有大篇幅讲解它相关的内容
小知识
- Spring2.5之前,我们都是通过实现Controller接口或其实现来定义我们的处理器类。显然现在已经不推荐这么做了
- Spring2.5引入注解式处理器支持,通过@Controller 和 @RequestMapping注解定义我们的处理器类。并且有一批注解都是这个时候出来的:
1. @RequestMapping:请求到处理器功能方法的映射规则;
2. @RequestParam:请求参数到处理器功能处理方法的方法参数上的绑定;
3. @ModelAttribute:请求参数到命令对象的绑定;
4. @SessionAttributes:用于声明session级别存储的属性,放置在处理器类上,通常列出模型属性(如@ModelAttribute)对应的名称,则这些属性会透明的保存到session中;
5. @InitBinder:自定义数据绑定注册支持,用于将请求参数转换到命令对象属性的对应类型;
3.Spring3.0引入RESTful架构风格支持(通过@PathVariable注解和一些其他特性支持),且又引入了更多的注解支持:
1. @CookieValue:cookie数据到处理器功能处理方法的方法参数上的绑定;
2. @RequestHeader:请求头(header)数据到处理器功能处理方法的方法参数上的绑定;
3. @RequestBody:请求的body体的绑定(通过HttpMessageConverter进行类型转换);
4. @ResponseBody:处理器功能处理方法的返回值作为响应体(通过HttpMessageConverter进行类型转换);
5. @ResponseStatus:定义处理器功能处理方法/异常处理器返回的状态码和原因;
6. @ExceptionHandler:注解式声明异常处理器;
7. @PathVariable:请求URI中的模板变量部分到处理器功能处理方法的方法参数上的绑定,从而支持RESTful架构风格的URI;
4.Spring3.1使用新的HandlerMapping 和 HandlerAdapter来支持@Contoller和@RequestMapping注解处理器。
1. 处理器映射RequestMappingHandlerMapping 和 处理器适配器RequestMappingHandlerAdapter组合
2. 对应代替了Spring2.5开始有的DefaultAnnotationHandlerMapping和AnnotationMethodHandlerAdapter
命令对象/模式:来自客户端的请求传入一个对象,从而使你可用不同的请求对客户进行参数化。用于“行为请求者”与“行为实现者”解耦,可实现二者之间的松耦合,以便适应变化。分离变化与不变的因素。一般可以实现命令的执行和撤销操作。 比如:遥控器给灯可以发送命令:开灯
总结
在使用Spring MVC的开发过程中,Handler(就是Controller)是我们需要手动开发的主要内容(其余的都是Spring MVC自动去处理的,开发者基本不用关心~),注解的配置方式比较固定,可以限定请求方式,请求映射到方法级,基本可以满足我们的日常需求。
但是如果知道这些controller的模式,比如UrlFilenameViewController这种,可以不用开发或者非常少量开发的情况下,极其快速的定位到handler到页面的映射关系,也是大大的提升了我们的效率有木有~
附:
Spring MVC中对静态资源的访问
1.当静态资源放在webapp下面的时候,可直接通过浏览器访问,不需要配置映射,安全性略低,对应的访问效率就略高。
但是静态资源若很多,访问频率很高的话,强烈建议放在静态服务器或者CDN上,不要放在tomcat里,这不是它擅长的
2.WEB-INF是Java的WEB应用的安全目录。所谓安全就是客户端无法直接访问,只有服务端可以访问的目录(所以理论上必须经过controller)
对于第一点这里需要强调一下:其实这么说是有问题的。因为我们一般会把DispatcherServlet映射为 /从而拦截所有的请求(包括静态资源的请求),所以如果直接访问就找不到Handler还是会报404的。后面会解释原因~
那么现在问题就来了,Spring MVC下怎么让访问WEB-INF下面的静态资源呢?
比如现在我有如下静态资源:
通过浏览器直接访问肯定是会404的,但是因为它是html文件,我们也不能通过controller直接转发,那肿么办呢?
如果我们之前的Spring MVC
项目是基于xml的,相信很多人都看到过如下的配置项:
<mvc:default-servlet-handler/> ... <mvc:resources mapping="/js/**" location="/js/"/> <mvc:resources mapping="/css/**" location="/css/"/> <mvc:resources mapping="/html/**" location="/html/"/> // 备注两种只需要配其一,其中<mvc:resources />是被推荐的方式
那么现在我们是java代码的方式,怎么弄呢?
DefaultServlet方式:DefaultServletHttpRequestHandler
对应xml的<mvc:default-servlet-handler/>方式。
此时会注册一个默认的Handler:DefaultServletHttpRequestHandler,这个Handler是用来处理静态文件的。(tomcat等容器里都一个名字为defaultServlet的Servlet来默认处理这些,当然默认名称各个容器都不一样,你可以自己指定)
当我们的请求倒带DispatcherServelt,当并没有找到合适的Handler来处理请求时,就会交给DefaultServletHttpRequestHandler来处理。
注意注意注意:这里的静态资源是放置在web根目录下,而非WEB-INF下,若在该目录下只能下面方案解决~
简单的举个例子描述下这个情况:
在webroot目录下有一个图片:1.png。我们知道的是:Servlet规范中web根目录(webapp目录)下的文件我们是可以直接访问的(不需要经过Servlet处理)。
但是但是但是由于DispatcherServlet配置了映射路径是:/ ,它几乎把所有的请求都拦截了,从而导致1.png 访问不到。这时注册一个DefaultServletHttpRequestHandler就可以解决这个问题。
DispatcherServlet破坏了Servlet的这个特性规范(根目录下的文件可以直接访问),DefaultServletHttpRequestHandler是帮助回归这个特性的。可谓也没啥损失(只是Spring MVC默认并没有开启哦~~)
因此若要开启此特性,我们只需要这么做即可:
@Configuration @EnableWebMvc public class WebMvcConfig extends WebMvcConfigurerAdapter { @Override public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) { configurer.enable(); //configurer.enable("default"); } }
这样非WEB-INF下的静态资源就可以正常反问啦~
ResourceHandler方式:ResourceHttpRequestHandler
这种对应的是XML中的<mvc:resources />
方式。
我们Java代码的方式一般都这么做:
@Configuration @EnableWebMvc public class WebMvcConfig extends WebMvcConfigurerAdapter { @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler("/resource/**").addResourceLocations("/WEB-INF/static/"); // 备注这里addResourceLocations(“file:D:/”)这样都是支持的~~~~~ } }
这样只要我们访问路径中是匹配/resource/**的,那就能够访问了。比如上面截图中的1.html我们这样访问:http://localhost:8080/demo_war_war/resource/1.html就能正常访问到了。(相当于路径中给加个/resource/)
那它的基本原理是什么呢?
它的原理也还行,就是向容器注册了一个ResourceHttpRequestHandler,它是一个HttpRequestHandler。关于HttpRequestHandler前面文章是有重点讲述的,具体参考:
【小家Spring】Spring MVC控制器中Handler的四种实现方式:Controller、HttpRequestHandler、Servlet、@RequestMapping
DefaultServletHttpRequestHandler它也是一个HttpRequestHandler
/ 和 /*有什么区别?
/会拦截除了jsp以外的所有url,/* 会拦截所有url,包括jsp。
例如:在webroot下面有一个test.jsp,当DispatcherServlet 配置映射/ 时,浏览器输入:http://localhost:8080/test.jsp 这个jsp是可以直接访问的,并且不经过DispatcherServlet ;而当DispatcherServlet 配置映射/* 时,这个请求就会被DispatcherServlet 拦截。
Spring Boot中静态资源的访问
它就比Spring稍微简单点,因为Boot已经做好了很多事。
在 Spring Boot 中,默认情况下,一共有5个位置可以放静态资源,五个路径分别是如下5个:
- classpath:/META-INF/resources/
- classpath:/resources/
- classpath:/static/
- classpath:/public/
- /
前四个目录好理解,分别对应了resources目录下不同的目录,第5个 / 是啥意思呢?稍微解释下:在 Spring Boot 项目中,默认是没有 webapp 这个目录的,当然我们也可以自己添加(例如在需要使用JSP的时候),这里第5个 / 其实就是表示 webapp 目录中的静态资源也不被拦截。如果同一个文件分别出现在五个目录下,那么优先级也是按照上面列出的顺序。
所以在SpringBoot中问问静态资源默认情况下我们并不需要做什么。
具体原理参考类:ResourceProperties,它定义了这5个路径以及顺序~
WebMvcProperties.staticPathPattern属性值定义了访问的url的pattern。