简述:

在上一篇文章中,介绍了适配器和映射器的一些概念,这篇文章主要是介绍SpringMvc注解的使用,下面先从一个最简单注解程序开始,慢慢引入一些常用的注解(@Controller,@Component,@Service,@Repository,@RequestMapping,@InitBinder,

@RequestParam,@PathVariable,@RequestBody ,@ResponseBody)

一、第一个注解项目

1.创建项目,加入Jar包,编写web.xml

可以加入第一篇文章里面给出的那些jar包,项目结构如下:


web.xml编写如下,前面已经有介绍过了,这里直接贴图了。


2.编写springmvc-servlet.xml加入注解支持

<?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"
	xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
	xsi:schemaLocation="http://www.springframework.org/schema/beans 
		http://www.springframework.org/schema/beans/spring-beans-3.1.xsd 
		http://www.springframework.org/schema/mvc 
		http://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd 
		http://www.springframework.org/schema/context 
		http://www.springframework.org/schema/context/spring-context-3.1.xsd 
		http://www.springframework.org/schema/aop 
		http://www.springframework.org/schema/aop/spring-aop-3.1.xsd 
		http://www.springframework.org/schema/tx 
		http://www.springframework.org/schema/tx/spring-tx-3.1.xsd ">
		
		<!-- 0.指定扫描 @Controller,@Component,@Service,@Repository之类Bean的路径-->
		<context:component-scan base-package="com.billstudy.springmvc"/>
		 
		<!-- 1.注解映射器 -->
		<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"/>
	
		<!-- 2.注解适配器 -->
		<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter" />
		
		<!-- 
			小技巧:以上1、2可以直接使用 <mvc:annotation-driven />,这个配置已经包含上面两个bean配置,所以也能实现支持注解的效果  
		 -->
		 
		<!-- 3.视图解析器 
				最终路径为:prefix + Controller内返回逻辑视图名 + suffix
				如:方法返回/hello,那么实际路径为:/WEB-INF/jsp/hello.jsp
		 -->	
		<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
			<property name="prefix" value="/WEB-INF/jsp" />
			<property name="suffix" value=".jsp"/>
		</bean>
</beans>
3.编写Controller类
package com.billstudy.springmvc.controller;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

/**
 * SpringMvc/Spring 企业开发常用注解使用演示
 * @author Bill
 * @since V1.0 2015/01/23
 */

/** 标明为Controller类,可以被Spring 扫描到,一般此类注解都作用于类上 ,与此相似的还有:
 *  @Service : 一般用于MVC设计中M(model)层,也就是业务层
 *  @Repository : 一般用于DAO层,也就是数据访问层
 *  @Component : 仅仅表示一个组件 (Bean),比较泛化,当我们写了一个类不好定位其在MVC那层的时候,可以使用这个
 *  @Controller:一般用于MVC设计中C(Controller)层,也就是控制层
 * **/
@Controller	
public class AnnotationDemoController{
	
	@RequestMapping("/annotationTest01")/** 定义访问规则 **/
	public ModelAndView annotationTest01(HttpServletRequest request,HttpServletResponse response){
		ModelAndView result = new ModelAndView();
		result.setViewName("/annotationTest01");	// 这里的ViewName,我们把它看成逻辑视图名,最终结合视图解析器,路径为:/WEB-INF/jsp/annotationTest01.jsp
		result.addObject("msg", "注解使用成功了!"); 
		return result;
	}
}

4.部署,测试


二、注解应用

1.@RequestMapping

使用方法1(路径映射):

【URL】加在Controller具体方法上,称为子路径映射。如:@RequestMapping("/annotationTest01"),这个时候若没有在类上用这个注解,那么可以直接通过【项目名/annotationTest01】访问这个方法了。若是类上面写了@RequestMapping("/parent"),那么这时候访问就是【项目名/parent/annotationTest01】了,也就是说类中所有的方法,都应该带上/parent,类上面我们称其为根路径映射。
演示:
子路径:
jsp/result.jsp内容:
访问效果:

根路径:类上面写了,那么访问具体方法时,都要带上根路径。
访问测试:

使用方法2(URI 模板模式映射):

@RequestMapping(value="/parent/{id}"):{×××}占位符, 请求的URL可以是“/parent/001”或“/parent/abc”,通过在方法中使用@PathVariable获取{×××}中的×××变量。若是有多个,则直接@RequestMapping(value="/parent/{id}/{name}/{age}")就可以了
演示:

使用方法3(请求方式限定):

也就是规定客户端请求的类型,如get/post,不按照要求来报 http 405错误
下面使用Firefox的HttpRequest测试,我就不写form/表单测试了,效果都一样。
如果想让方法同时支持get/post,那么可以这么写:@RequestMapping(value="/postMethod",method={RequestMethod.POST,RequestMethod.GET}),就是写多个就可以了。
下面的注解解决了一些请求参数绑定,所以例子和请求参数一起测试。

三、注解配合请求实现参数绑定

默认支持(Java基本数据类型,HttpServletRequest 通过request对象获取请求信息 HttpServletResponse 通过response处理响应信息 HttpSession 通过session对象得到session中存放的对象 Model 通过model向页面传递数据),若是页面是多个表单元素使用同一个name,那么就可以直接使用String[]接收值,这个和别的框架一样。没啥特别的。如果是Date类型,则需要注册转换器了,否则会报错,如下:

1.@InitBinder

下面,创建了几个类:
package com.billstudy.springmvc.bean;

import java.util.Date;

/**
 * User Bean 
 * @author Bill
 * @since V1.0 2015/01/25
 */ 
public class User {
	
	private Integer id;
	private String name;
	private Integer age;
	private Boolean status;
	private Date birthday;
	
	public User() {
		// TODO Auto-generated constructor stub
	}

	public User(Integer id,String name, Integer age, Boolean status, Date birthday) {
		super();
		this.id = id;
		this.name = name;
		this.age = age;
		this.status = status;
		this.birthday = birthday;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public Integer getAge() {
		return age;
	}

	public void setAge(Integer age) {
		this.age = age;
	}

	@Override
	public String toString() {
		return "User [id=" + id + ", name=" + name + ", age=" + age
				+ ", status=" + status + ", birthday=" + birthday + "]";
	}

	public Boolean getStatus() {
		return status;
	}

	public void setStatus(Boolean status) {
		this.status = status;
	}

	public Date getBirthday() {
		return birthday;
	}

	public void setBirthday(Date birthday) {
		this.birthday = birthday;
	}

	public Integer getId() {
		return id;
	}

	public void setId(Integer id) {
		this.id = id;
	}
	
	
}
package com.billstudy.springmvc.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

import com.billstudy.springmvc.bean.User;

/**
 * 配合User bean演示相关注解使用
 * @author Bill
 * @since V.10 2015/01/25
 */
@Controller
@RequestMapping("/user")
public class UserController {
	/**
	 * 测试用户信息注入
	 * @param user
	 */
	@RequestMapping("/userSave")
	public void userSave(User user){
		System.out.println(user);
	}
}

WebContent/user/userEdit.jsp
<%@page pageEncoding="UTF-8" contentType="text/html; charset=UTF-8" %>
<%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt" %>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<title></title>
</head>
<body bgcolor="#C7EDCC">
	<form action="${pageContext.request.contextPath}/user/userSave.do" method="POST">
	<input type="hidden" value="${user.id}"  name="id"/>
		<div align="center"> 
			<table border="2">
				<tr>
					<td>姓名</td>
					<td><input name="name"  value="${user.name}"/></td>
				</tr>
				<tr> 
					<td>年龄</td>
					<td><input name="age"  value="${user.age}"/></td>
				</tr>
				<tr>    
					<td>状态</td>
					<td>
						<input name="status" type="radio" <c:if test="${user.status}">checked</c:if>  value="true"/>true
						<input name="status" type="radio" <c:if test="${!user.status}">checked</c:if>  value="false"/>false
					</td>
				</tr>
				<tr>  
					<td>生日</td>
					<td><input name="birthday"  value='<fmt:formatDate value="${user.birthday}" pattern="yyyy-MM-dd"/>'/></td>
				</tr>
				<tr>
					<td>操作</td>
					<td><input  value="submit" type="submit"/></td>
				</tr>
			</table>		
		</div>
	</form>
</body>
</html>
下面打开页面测试,会发现500异常,原因是Date转换不支持。

异常:

这个时候,@InitBinder派上用场了。
在UserController中加上如下代码:
@InitBinder /* register Date parse support*/
	public void bindBinbar(ServletRequestDataBinder binder){ 
		binder.registerCustomEditor(Date.class, new CustomDateEditor(new SimpleDateFormat("yyyy-MM-dd"), true));/** true:允许为空 **/
	}
再测一把,将表单提交

页面会报404错误,这个我们不管,因为方法没有返回值,它找的默认路径,默认路径没有对应的jsp页面,我们的关注点在参数自动绑定这块。可以看到参数已经绑定了,但是存在乱码,解决乱码:在web.xml加入org.springframework.web.filter.CharacterEncodingFilter的支持,如下
<!-- 解决POST 中文乱码 -->
   <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>
  </filter> 
   
  <filter-mapping>
	<filter-name>characterEncodingFilter</filter-name>
	<url-pattern>/*</url-pattern>
  </filter-mapping>
若是get乱码则可以修改Tomcat/server.xml的编码,或者在代码中对具体中文参数进行ISO-8859-1 To UTF-8的方法,个人偏向修改Tomcat编码,简单省事。
加入Filter之后,效果如下:

可以看到,我的名字没有乱码了。 这里可以看出,springmvc会将表单中的name绑定到方法参数中,和struts2不同的是。这里没有用对象.属性的方式,当然这里也支持。那就是复杂对象里面包含复杂对象的时候。如下面这个类的定义:
package com.billstudy.springmvc.bean;

import java.util.Arrays;
import java.util.List;
import java.util.Map;

/**
 * List , Map , String[] Test Bean
 * @author Bill
 * @since V1.0 2015/01/25
 */
public class Other {
	
	private List<User> tUsers;
	
	private Map<String,Object> tMaps;
	
	private String[] tStrs;
	
	private User mainUser;
	
	public List<User> gettUsers() {
		return tUsers;
	}

	public void settUsers(List<User> tUsers) {
		this.tUsers = tUsers;
	}

	public Map<String, Object> gettMaps() {
		return tMaps; 
	}

	public void settMaps(Map<String, Object> tMaps) {
		this.tMaps = tMaps;
	}

	public String[] gettStrs() {
		return tStrs;
	}

	public void settStrs(String[] tStrs) {
		this.tStrs = tStrs;
	}

	public Other(List<User> tUsers, Map<String, Object> tMaps, String[] tStrs) {
		super();
		this.tUsers = tUsers;
		this.tMaps = tMaps;
		this.tStrs = tStrs;
	}

	public User getMainUser() {
		return mainUser;
	}

	public void setMainUser(User mainUser) {
		this.mainUser = mainUser;
	}
	public Other() {
	}
}

这个对象中包含了User对象,我们暂且把它称为复杂对象。那么在页面想要给这里面的user注入值时,就可以使用mainUser.name/mainUser.age的方式了。可以看到,里面还有List,Map,String[]等类型的属性,这个下面也对其参数绑定进行演示。

添加页面:/WebContent/user/injectTest.jsp
<%@page pageEncoding="UTF-8" contentType="text/html; charset=UTF-8" %>
<%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt" %>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<title></title>
</head>
<body bgcolor="#C7EDCC">
	<form action="${pageContext.request.contextPath}/user/inject.do" method="POST">
	<input type="hidden" value="${user.id}"  name="id"/>
		<div align="center" style="background-color: #1B7D00"> 
				<h1>List test</h1><br/> 
					<!-- List 使用[下标]的方式绑定值 -->
					姓名 <input name="tUsers[0].name"  /><br/> 
					年龄 <input name="tUsers[0].age"  /><br/>
					状态
						<input name="tUsers[0].status" type="radio"  value="true"/>true
						<input name="tUsers[0].status" type="radio"  value="false"/>false<br/>
					生日 <input name="tUsers[0].birthday" /><br/>
					<hr/> 
					
					姓名 <input name="tUsers[1].name" /><br/> 
					年龄 <input name="tUsers[1].age" /><br/>
					状态
						<input name="tUsers[1].status" type="radio"  value="true"/>true
						<input name="tUsers[1].status" type="radio"   value="false"/>false<br/>
					生日 <input name="tUsers[1].birthday" /><br/>
					<hr/> 
					
					姓名 <input name="tUsers[2].name"  /><br/> 
					年龄 <input name="tUsers[2].age"  /><br/>
					状态
						<input name="tUsers[2].status" type="radio"   value="true"/>true
						<input name="tUsers[2].status" type="radio" value="false"/>false<br/>
					生日 <input name="tUsers[2].birthday" /><br/>
					<hr/> 
					
					 
					<h1>Map test</h1><br/> 
					<!-- Map key value的形式,比较容易理解吧 -->
					Map key flyName :<input name="tMaps['flyName']" /><br/>
					Map key flyAge :<input name="tMaps['flyAge']" /><br/>
					Map key flyAddress :<input name="tMaps['flyAddress']" /><br/>
					
					<h1>String Array test</h1><br/> 
					<!-- 这个就是同名的表单元素,在Controller中就是一个字符串数组,和request.getParameterValues()一个效果 -->
					<input name="tStrs" />
					<input name="tStrs" />
					<input name="tStrs" />
					<input name="tStrs" />
					<h2>mainUser 填写</h2>
					<div align="center"> 
						<table border="2">
							<tr>
								<td>姓名</td>
								<td><input name="mainUser.name" /></td>
							</tr>
							<tr> 
								<td>年龄</td>
								<td><input name="mainUser.age" /></td>
							</tr>
							<tr>    
								<td>状态</td>
								<td>
									<input name="mainUser.status" type="radio" />true
									<input name="mainUser.status" type="radio" />false 
								</td>
							</tr>
							<tr>  
								<td>生日</td>
								<td><input name="mainUser.birthday"  /></td>
							</tr>
						</table>		
					</div> 
					操作 <input  value="submit" type="submit"/><br/> 
		</div>
	</form>
</body>
</html>

录入参数如下:


控制台输出如下:

List<User> tUsers:[User [id=null, name=姓名1, age=10, status=false, birthday=Mon Nov 11 00:00:00 CST 1991], User [id=null, name=姓名2, age=22, status=true, birthday=Tue Nov 12 00:00:00 CST 1991], User [id=null, name=姓名3, age=33, status=false, birthday=Wed Nov 13 00:00:00 CST 1991]]
Map<String,Object> tMaps:{flyAddress=上海, flyAge=20, flyName=飞机}
String[] tStrs:[数组值01, 数组值02, 数组值03, 数组值04]
User mianUser:User [id=null, name=mainUser用户, age=100, status=true, birthday=Sun Jan 03 00:00:00 CST 1993]

2.@RequestParam

绑定单个请求参数

value参数名字,即入参的请求参数名字,如value=“id表示请求的参数区中的名字为id的参数的值将传入;

required是否必须,默认是true,表示请求中一定要有相应的参数,否则将报400错误码;

defaultValue默认值,表示如果请求中没有同名参数时的默认值


添加方法如下(报错的测试,大家可以自己去做):

/**
	 * 用了defaultValue,其实required=true也不会报错,因为有默认值。请求时需要将id的值以userId=xx的形式提交,会自动将值绑定到形参id中
	 * @param model
	 * @param id
	 * @return
	 */ 
	@RequestMapping("/requestParamTest")
	public String requestParamTest(Model model,@RequestParam(defaultValue="520",required=true,value="userId")String id){
		model.addAttribute("msg", "requestParamTest id:"+id); 
		return "/result";
	}

测试如下:

不传递值,使用默认值

传递值:

3.@RequestBody

@RequestBody注解用于读取http请求的内容(字符串),通过springmvc提供的HttpMessageConverter接口将读到的内容转换为json、xml等格式的数据并绑定到controller方法的参数上,下面演示将客户端传递过来的JSON转成Java对象。

要使用这个注解,需要先加入两个jar包(jackson-core-asl-1.9.11.jar,jackson-mapper-asl-1.9.11.jar),同时需要在springmvc-servlet.xml适配器中加入对jackson的支持,修改之后如下:

	<!-- 2.注解适配器 -->
		<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter" >
			<span style="color:#ff0000;"><property name="messageConverters"> 
				<list>
					<bean class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter" />
				</list> 
			</property></span>
		</bean>


在UserController中加入方法:

/**
	 * 演示JSON 	TO Java Object
	 * @param user
	 */
	@RequestMapping("/requestBodyTest")
	public void requestBodyTest(@RequestBody User user){
		System.out.println("user:"+user);
	}


利用HttpRequest测试:


控制台输出:


4.@ResponseBody

该注解用于将Controller的方法返回的对象,通过HttpMessageConverter接口转换为指定格式的数据如:json,xml等,通过Response响应给客户端

加入方法如下:

测试效果:

5.@ModelAttribute

Model一般可以用于在方法中放置变量在View取值,这个注解可以帮助我们将一些通用的变量返回到View(页面)。很方便取值,如下演示。
添加如下三个方法,2个添加参数,1个直接跳转到指定页面。测试能否取值
方法:
/** 
	 * 将key 为citys的数组对象传递到页面
	 * @return
	 */
	@ModelAttribute("citys")
	public String[] getCitys(){
		return new String[]{"中国","美国","伊拉克","日本"};
	}
	/** 
	 * 将key 为users的List<User>对象传递到页面
	 * @return
	 */
	@ModelAttribute("users")
	public List<User> getUsers(){
		List<User> users = new ArrayList<User>(); 
		for (int i = 0; i < 3; i++) {
			// 构造函数 : User(Integer id,String name, Integer age, Boolean status, Date birthday)
			users.add(new User(i,"测试姓名"+i,10+i,true,Calendar.getInstance().getTime()));
		}
		return users; 
	}
	/**
	 * 直接跳转到页面,不存放任何数据
	 * @return
	 */ 
	@RequestMapping("/toShowValue") 
	public String toShowValue(){
		return "/showValue"; 
	}

页面:

<%@page pageEncoding="UTF-8" contentType="text/html; charset=UTF-8" %>
<%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<title>测试ModelAttribute</title>
</head>
<body>
	citys:<br/>
	
	<c:forEach items="${citys}" var="city">
		${city}   
	</c:forEach><br/>
	
	Users:<br/>
	<c:forEach items="${users}" var="user">
		${user} <br/>   
	</c:forEach>
</body>
</html>
访问结果:

三、forward/redirect

转发/重定向。也就是在方法内部设置ViewName或者直接返回String时可以达到转发/重定向的效果。
转发:方式相当于“request.getRequestDispatcher().forward(request,response)”,转发后浏览器地址栏还是原来的地址。转发并没有执行新的request和response,而是和转发前的请求共用一个request和response。所以转发前请求的参数在转发后仍然可以读取到
重定向:方式相当于“response.sendRedirect()”,转发后浏览器的地址栏变为转发后的地址,因为转发即执行了一个新的request和response。
使用演示:
/**
	 * 演示转发,重定向用法,看注释部分就好了
	 * @param request
	 * @param response
	 * @return
	 * @throws ServletException
	 * @throws IOException
	 */
	public String demoForwardRedirect(HttpServletRequest request,HttpServletResponse response) throws ServletException, IOException{
		// request.getRequestDispatcher("").forward(request,response);	// 转发
		// return "forward:/xx/xx.jsp";									// 转发
		
		// response.sendRedirect("/xx/xx.jsp");							// 重定向
		// return "redirect:/xx/xx.jsp";								// 重定向
		return null;
	}



注解部分还有@CookieValue,@RequestHeader@RequestPart,这些就小伙伴们自己去研究看看吧。 哈哈,收工。读书去了

本文所有代码:点击下载本文代码

上面分享的这些注解你会用了吗? 这里是 热爱生活,热爱技术,喜欢交友的大彪 .

版权声明:本文为博主原创文章,未经博主允许不得转载。

Copyright © 2024 LuckyBill
Powered by .NET 8.0 on Kubernetes