2.注解式开发
Posted 小马Mark
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了2.注解式开发相关的知识,希望对你有一定的参考价值。
@RequestMapping的规则
指定模块名称
通过@RequestMapping 注解可以定义处理器对于请求的映射规则。
该注解可以注解在方法上,也可以注解在类上,但意义是不同的。
@RequestMapping 的 value 属性用于定义所匹配请求的 URI,但是注解在方法上与在类上,其value属性所指定的URI的意义是不同的。(value 属性值常以“/”开始)
一个@Controller 所注解的类中,可以定义多个处理器方法。不同的处理器方法所匹配的 URI 是不同的。不同的 URI 被指定在注解于方法之上的@RequestMapping 的value 属性中。但若这些请求具有相同的 URI 部分,则这些相同的 URI,可以被抽取到注解在类之上的@RequestMapping 的 value 属性中。此时的这个 URI 表示模块的名称。
/*
控制器类的部分代码
*/
@Controller
@RequestMapping(value = "/test") // 在类上使用其注解就是给类里面的所有方法上的value属性加上模块名
public class MyController
@RequestMapping(value = "/some.do") // 实际这部分的完整uri是/test/some.do
public ModelAndView doSome()
ModelAndView mv = new ModelAndView();
mv.addObject("msg", "doSome方法");
mv.setViewName("show");
return mv;
请求提交的方式
对于@RequestMapping,其有一个属性 method,用于对被注解方法所处理请求的提交方式进行限制,即只有满足该 method 属性指定的提交方式的请求,才会执行该被注解方法。
Method 属性的取值为 RequestMethod 枚举常量。
常用的为 RequestMethod.GET 与RequestMethod.POST,分别表示提交方式的匹配规则为 GET 与 POST 提交。
客户端浏览器常用的请求方式,及其提交方式有以下几种:
请求方式 | 提交方式 |
---|---|
表单请求 | 默认get,可以指定post |
AJAX请求 | 默认get,可以指定post |
地址栏请求 | get请求 |
超链接请求 | get请求 |
src资源路径请求 | get请求 |
只要指定了处理器方法匹配的请求提交方式为 POST,则相当于指定了请求发送的方式:要么使用表单请求,要么使用 AJAX 请求。其它请求方式被禁用。
若不指定 method 属性,则无论是 GET 还是 POST 提交方式,均可匹配。(对任何请求都有效)
@RequestMapping(value = "/first.do", method = RequestMethod.POST) // 指定了post请求,这个方法就只能被该路径的post请求访问
public ModelAndView doFirst()
ModelAndView mv = new ModelAndView();
mv.addObject("msg", "doFirst方法");
mv.setViewName("show");
return mv;
处理器方法的参数
处理器方法可以包含以下四类参数,这些参数会在系统调用时由系统自动赋值,即程序员可在方法内直接使用。
- HttpServletRequest
- HttpServletResponse
- HttpSession
- 请求中所携带的请求参数
HttpServletRequest、HttpServletResponse、HttpSession可以这样使用:
@RequestMapping(value = "/first.do", method = RequestMethod.POST)
public ModelAndView doFirst(HttpServletRequest request, HttpServletResponse response, HttpSession session)
ModelAndView mv = new ModelAndView();
mv.addObject("msg", "doFirst方法==="+ request.getParameter("username"));
mv.setViewName("show");
return mv;
逐个参数接收
只要保证请求参数名与该请求处理方法的参数名相同即可。顺序都无所谓
<form action="test/some.do" method="post">
姓名:<input type="text" name="name"><br>
年龄:<input type="text" name="age"><br>
<input type="submit">
</form>
@RequestMapping(value = "/some.do")
public ModelAndView doSome(String name,Integer age) // 参数名称必须与请求参数一样
ModelAndView mv = new ModelAndView();
mv.addObject("myName", name);
mv.addObject("myAge", age);
mv.setViewName("show");
return mv;
补充:框架会自动将Request请求传过来的参数值(String)转变为你想要的类型,但是一旦转换不成功也不会报错,只是会有日志警告,controller中的方法也不会执行。
校正请求参数名
当请求 URL 所携带的参数名称与处理方法中指定的参数名不相同时,处理方法参数前,添加一个注解@RequestParam(“请求参数名”)
,指定请求 URL 所携带参数的名称。
-
该注解是对处理器方法的参数进行修饰
-
属性:
- value = 指定请求参数名(例前端传来的参数的参数名)(String类型)
- required = 表示请求是否必须包含value指定的参数名(boolean类型)
- 默认是true,表示必须包含value指定的参数名,否则报400错误
- false,表示可以不包含,不会报错
@RequestMapping(value = "/first.do")
public ModelAndView doFirst(@RequestParam(value = "rname", required = false) String name,
@RequestParam(value = "rage", required = false) Integer age)
ModelAndView mv = new ModelAndView();
mv.addObject("myName", name);
mv.addObject("myAge", age);
mv.setViewName("show");
return mv;
中文乱码问题
在 web.xml 中注册字符集过滤器,即可解决 Spring 的请求参数的中文乱码问题。不过,最好将该过滤器注册在其它过滤器之前。因为过滤器的执行是按照其注册顺序进行的。
<filter>
<filter-name>characterEncodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<!-- 设置编码 -->
<param-name>encoding</param-name>
<param-value>utf-8</param-value>
</init-param>
<init-param>
<!--强制Request使用字符集encoding-->
<param-name>forceRequestEncoding</param-name>
<param-value>true</param-value>
</init-param>
<init-param>
<!--强制Response使用字符集encoding-->
<param-name>forceResponseEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>characterEncodingFilter</filter-name>
<!--设置在所有路径之前都要过滤-->
<url-pattern>/*</url-pattern>
</filter-mapping>
对象参数接收
将处理器方法的参数定义为一个对象,只要保证请求参数名与这个对象的属性同名即可。
自己创建一个接收请求参数的普通bean类,类中的属性需要与请求参数的参数名相同(自己无需创建对象)
框架会自动给使用无参构造方法new一个对象,使用set方法把请求参数的值赋值给对应的属性
public class Person
private String name; // 与视图传来的参数名一致
private Integer age;
public Person()
// .....代码省略
// 自己创建一个Person普通类,类中的属性需要与请求参数的参数名相同
@RequestMapping(value = "/second.do")
public ModelAndView doSecond(Person person)
ModelAndView mv = new ModelAndView();
mv.addObject("myName", person.getName());
mv.addObject("myAge", person.getAge());
mv.addObject("myPerson", person);
mv.setViewName("show");
return mv;
处理器方法的返回值
使用@Controller 注解的处理器的处理器方法,其返回值常用的有四种类型:
- 第一种:ModelAndView
- 第二种:String
- 第三种:无返回值 void
- 第四种:返回自定义类型对象
根据不同的情况,使用不同的返回值。
返回ModelAndView
若处理器方法处理完后,需要跳转到其它资源,且又要在跳转的资源间传递数据,此时处理器方法返回 ModelAndView 比较好。
若要返回 ModelAndView,则处理器方法中需要定义 ModelAndView 对象。
在使用时,若该处理器方法只是进行跳转而不传递数据,或只是传递数据而并不向任何资源跳转(如对页面的 Ajax 异步响应),此时若返回 ModelAndView,则将总是有一部分多余:要么 Model 多余,要么 View 多余。即此时返回 ModelAndView 将不合适。
返回String
处理器方法返回的字符串可以指定逻辑视图名,通过视图解析器解析可以将其转换为物理视图地址
返回内部资源逻辑视图名
若要跳转的资源为内部资源,则视图解析器可以使用 InternalResourceViewResolver 内部资源视图解析器。此时处理器方法返回的字符串就是要跳转页面的文件名去掉文件扩展名后的部分。这个字符串与视图解析器中的 prefix、suffix 相结合,即可形成要访问的 URI。
<!--视图解析器,设置视图文件的路径-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!--前缀:视图文件的路径-->
<property name="prefix" value="/WEB-INF/view/"/>
<!--后缀:视图文件的后缀-->
<property name="suffix" value=".jsp" />
</bean>
@RequestMapping(value = "/some.do")
public String doSome(String name,Integer age, HttpServletRequest request)
System.out.println("处理器方法使用String表示视图名称");
System.out.println("name = " + name + ", age = " + age);
request.setAttribute("myName", name);
request.setAttribute("myAge", age);
// 框架对视图执行的是forward转发操作
return "show"; // 这是使用了视图解析器,所以这里直接写逻辑名称,无需写全uri
如果写全uri,就不能需要使用视图解析器,否则会产生冲突。
返回void
对于处理器方法返回 void 的应用场景,AJAX 响应。
若处理器对请求处理后,无需跳转到其它任何资源,此时可以让处理器方法返回 void。
使用返回void的处理器方法,来进行ajax请求会有许多重复的事要做,比如使用 Jackson依赖将数据转为json数据,然后用response相应给浏览器都会产生非常多的重复。
返回对象Object
处理器方法也可以返回 Object 对象。这个 Object 可以是 Integer,String,自定义对象,Map,List 等。但返回的对象不是作为逻辑视图出现的,而是作为直接在页面显示的数据出现的。
返回对象,需要使用@ResponseBody
注解,将转换后的 JSON 数据放入到响应体中。
由于返回 Object 数据,一般都是将数据转化为了 JSON 对象后传递给浏览器页面的。而这个由 Object 转换为 JSON,是由 Jackson 工具完成的。所以需要导入 Jackson 的相关 Jar 包。
<!-- jackson依赖 -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.9.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.0</version>
</dependency>
@ResponseBody
:
- 作用:把处理器方法返回对象转为json后,通过HttpMessageConverter输出给浏览器
- 位置:在处理器方法的上面,与其他注解没有顺序的关系
@RequestMapping(value = "/ajax1.do")
@ResponseBody
public Person doAjax1(String name, Integer age)
Person person = new Person();
person.setName(name);
person.setAge(age);
return person;
声明注解驱动
将Object数据转换为Json数据,需要有消息转换器HttpMessageConverter完成
开启消息转换器:
<mvc:annotation-driven />
<!--
约束文件:http://www.springframework.org/schema/mvc
https://www.springframework.org/schema/mvc/spring-mvc.xsd
别搞错了
-->
注解驱动实现的功能:
-
完成java对象到json、xml、text、二进制等数据格式的转换
-
SpringMVC 使用消息转换器实现请求数据和对象,处理器方法返回对象和响应输出之间的自动转换
当 Spring 容器进行初始化过程中,在<mvc:annotation-driven/>
处创建注解驱动时,默认创建了七个 HttpMessageConverter 对象。(补充:spring5.x现在好像是八个了)
HttpMessageConverter 接口
:
HttpMessageConverter<T>
是 Spring3.0 新添加的一个接口,负责将请求信息转换为一个对象(类型为 T),将对象(类型为 T)输出为响应信息
HttpMessageConverter<T>
接口定义的方法:
boolean canRead(Class<?> clazz,MediaType mediaType): 指定转换器可以读取的对象类型,即转换器是否可将请求信息转换为clazz类型的对象,同时指定支持MIME类型(text/html,applaiction/json 等)
boolean canWrite(Class<?> clazz,MediaType mediaType):指定转换器是否可将 clazz 类型的对象写到响应流中,响应流支持的媒体类型在 MediaType 中定义。
LIst<MediaType> getSupportMediaTypes():该转换器支持的媒体类型。
T read(Class<? extends T> clazz,HttpInputMessage inputMessage):将请求信息流转换为T类型的对象。
void write(T t,MediaType contnetType,HttpOutputMessgae outputMessage):将 T 类型的对象写到响应流中,同时指定相应的媒体类型为 contentType
HttpMessageConverter接口实现类 | 作用 |
---|---|
ByteArrayHttpMessageConverter | 负责读取二进制格式的数据和写出二进制格式的数据 |
StringHttpMessageConverter | 负责读取字符串格式的数据和写出字符串格式的数据 |
ResourceHttpMessageConverter | 负责读取资源文件和写出资源文件数据 |
SourceHttpMessageConverter | 能够读 / 写 来 自 HTTP 的 请 求 与 响 应 的javax.xml.transform.Source ,支持DOMSource,SAXSource, 和 StreamSource 的 XML 格式 |
AllEncompassingFormHttpMessageConverter | 负责处理表单(form)数据 |
Jaxb2RootElementHttpMessageConverter | 使用 JAXB 负责读取和写入 xml 标签格式的数据 |
MappingJackson2HttpMessageConverter | 负责读取和写入 json 格式的数据。利用Jackson 的 ObjectMapper 读写 json 数据,操作Object 类型数据,可读取 application/json,响应媒体类型为 application/json |
spring5.x好像多了一个ResourceRegionHttpMessageConverter(用处以后再了解)
补充:
-
没有加入注册驱动
<mvc:annotation-driven/>
时容器会创建四个对象:ByteArrayHttpMessageConverter StringHttpMessageConverter SourceHttpMessageConverter AllEncompassingFormHttpMessageConverter
-
加入之后才全部创建
返回String类型
注意:这里的返回值String,是加注解@ResponseBody
后的,含义是返回一个字符串类型的数据,而不加注解就是返回一个String类型的视图uri路径(或逻辑路径)
若要返回非中文字符串,将前面返回数值型数据的返回值直接修改为字符串即可。但若返 回 的 字 符 串 中 带 有 中文字符 ,则接收方页面将会出现乱码 。此时需要使用@RequestMapping
的produces属性指定字符集。
- produces:产品,结果,即该属性用于设置输出结果类型。
例如:
ajax请求:接收返回的数据是text类型时,默认字符集是ISO-8859-1
Content-Type: text/plain;charset=ISO-8859-1
$.ajax(
url:"ajax2.do",
type:"post",
dataType:"text", // 这里如果用其他json以为其他类型,可能就会有编码问题
data:
"name":"何鼎东",
"age":33
,
success:function (data)
alert(data);
)
所以我们要使用@RequestMapping
的produces属性进行指定编码
@RequestMapping(value = "/ajax2.do",produces = "text/plain;charset=utf-8")
@ResponseBody
public String doAjax2(String name, Integer age)
return name + ":" + age;
注册中央调度器
在web.xml文件里
<!--
声明注册springmvc的核心对象DispatcherServlet,注册中央调度器
-->
<servlet>
<servlet-name>myMvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!--自定义springmvc读取的配置文件的位置
不自定义读取位置,springmvc默认是读取的WEB-INF/<servlet-name>-servlet.xml
(WEB-INF/myMvc-servlet.xml)
默认读取的是
-->
<init-param>
<!--指定springmvc的配置文件的位置的属性-->
<param-name>contextConfigLocation</param-name>
<!--指定自定义文件位置classpath:*** ,从类路径开始,即resources目录下-->
<param-value>classpath:springmvc.xml</param-value>
</init-param>
<!--
让DispatcherServlet随着Tomcat服务器的启动而创建对象
load-on-startup:表示Tomcat启动后创建对象的顺序
值是一个非负整数(>=0),数值越小,Tomcat创建其对象越早
-->
<load-on-startup>1</load-on-startup>
</servlet>
<!--指定哪些路径下的使用myMvc配置的对象-->
<servlet-mapping>
<servlet-name>myMvc</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
解读<url-pattern/>
配置详解
- 使用
*.do
:- 在没有特殊要求的情况下,SpringMVC 的中央调度器 DispatcherServlet 的
<url-pattern/>
常使用后辍匹配方式,如写为*.do 或者 *.action, *.mvc 等。静态资源交给Tomcat处理。
- 在没有特殊要求的情况下,SpringMVC 的中央调度器 DispatcherServlet 的
- 使用
/
:- 配置是
/
的话,DispatcherServlet 会将向静态资源的获取请求,例如.css、.js、.jpg、.png等资源的获取请求,当作是一个普通的 Controller 请求。 - 中央调度器会调用处理器映射器为其查找相应的处理器。当然也是找不到的,所以在这种情况下,所有的静态资源获取请求也均会报 404 错误。
- 主要原因:Tomcat会有一个默认的Servlet,用来处理静态资源和处理未映射到其他Servlet的请求,当中央调度器设置为
/
时,就会让中央调度器来进行处理静态资源和未映射的Servlet请求,默认情况下中央调度器没有能力处理静态资源。
- 配置是
静态资源访问
第一种
<url-pattern/>
的值并不是说写为/后,静态资源就无法访问了。经过一些配置后,该问题也是可以解决的。
声明了<mvc:default-servlet-handler />
后,springmvc 框架会在容器中创建DefaultServletHttpRequestHandler
处理器对象。
原理:对进入 DispatcherServlet的URL 进行筛查,如果发现是静态资源的请求,就将该请求转由 Web 应用服务器(tomcat)默认的Servlet 处理。一般的服务器都有默认的 Servlet。
缺点:依赖服务器
<!--声明注解驱动-->
<mvc:annotation-driven />
<mvc:default-servlet-handler />
第二种
在 Spring3.0 版本后,Spring 定义了专门用于处理静态资源访问请求的处理器ResourceHttpRequestHandler。并且添加了<mvc:resources/>
标签,专门用于解决静态资源无法访问问题
需要在 springmvc 配置文件中添加如下形式的配置:
<!--声明注解驱动-->
<mvc:annotation-driven />
<mvc:resources mapping="/img/**" location="/img/" />
说明:
- location:表示静态资源所在目录(目录不要使用/WEB-INF/及其子目录)
- mapping:表示对该资源的请求(访问静态资源的uri的地址,使用通配符
**
,使用**
后表示目录下的所有子目录及其文件)
补充:
- 以上两种方式都会与@RequestMapping有冲突,即动态资源与静态资源有冲突
- 需要在springmvc配置文件中声明注解驱动
<mvc:annotation-driven />
以上是关于2.注解式开发的主要内容,如果未能解决你的问题,请参考以下文章