springmvc教程:注解开发深入解析

本人花费半年的时间总结的《Java面试指南》已拿腾讯等大厂offer,已开源在github ,欢迎star!

转载声明:转载请注明出处,本技术博客是本人原创文章

本文GitHub https://github.com/OUYANGSIHAI/JavaInterview 已收录,这是我花了6个月总结的一线大厂Java面试总结,本人已拿大厂offer,欢迎star

原文链接:blog.ouyangsihai.cn >> springmvc教程:注解开发深入解析

一、 注解开发-基础

1.1 需求

使用springmvc+mybatis架构实现商品信息维护。

1.2 商品修改

1.2.1 dao

使用逆向工程自动生成的代码:

ItemsMapper.java

ItemsMapper.xml

1.2.2 service


//根据id查询商品信息

public Items findItemById(int id) throws Exception;

//修改商品信息

public void saveItem(Items items)throws Exception;

1.2.3 controller

修改商品信息显示页面:


@RequestMapping(value = "/editItem")
public String editItem(Model model, Integer id) throws Exception {

    // 调用service查询商品信息

    Items item = itemService.findItemById(id);

    model.addAttribute("item", item);

    return "item/editItem";

}

修改商品信息提交:


// 商品修改提交

@RequestMapping("/editItemSubmit")
public String editItemSubmit(Items items) throws Exception {

    System.out.println(items);

    itemService.saveItem(items);

    return "success";

}

1.2.4 页面

/WEB-INF/jsp/item/itemsList.jsp

/WEB-INF/jsp/item/editItem.jsp

1.3 @RequestMapping

通过RequestMapping注解可以定义不同的处理器映射规则。

1.3.1 URL路径映射

@RequestMapping(value="/item") @RequestMapping("/item)

value的值是数组,可以将多个url映射到同一个方法

1.3.2 窄化请求映射

在class上添加@RequestMapping(url)指定通用请求前缀, 限制此类下的所有方法请求url必须以请求前缀开头,通过此方法对url进行分类管理。

如下:

@RequestMapping放在类名上边,设置请求前缀


@Controller
@RequestMapping("/item")

方法名上边设置请求映射url:

@RequestMapping放在方法名上边,如下:


@RequestMapping("/queryItem ")

访问地址为:/item/queryItem

1.3.3 请求方法限定

1、限定GET方法


@RequestMapping(method = RequestMethod._GET_)

如果通过Post访问则报错:

HTTP Status 405 - Request method ‘POST’ not supported

例如:


@RequestMapping(value="/editItem",method=RequestMethod.GET)

2、 限定POST方法


@RequestMapping(method = RequestMethod._POST_)

如果通过Post访问则报错:

HTTP Status 405 - Request method ‘GET’ not supported

3、GET和POST都可以


@RequestMapping(method={RequestMethod.GET,RequestMethod.POST})

1.4 controller方法返回值

1.4.1 返回ModelAndView

controller方法中定义ModelAndView对象并返回,对象中可添加model数据、指定view。

1.4.2 返回void

在controller方法形参上可以定义request和response,使用request或response指定响应结果:

使用request转向页面,如下:


request.getRequestDispatcher("页面路径").forward(request, response);

通过response页面重定向:


response.sendRedirect("url")

通过response指定响应结果,例如响应json数据如下:


response.setCharacterEncoding("utf-8");

response.setContentType("application/json;charset=utf-8");

response.getWriter().write("json串");

1.4.3 返回字符串

1.4.3.1 逻辑视图名

controller方法返回字符串可以指定逻辑视图名,通过视图解析器解析为物理视图地址。


//指定逻辑视图名,经过视图解析器解析为jsp物理路径:/WEB-INF/jsp/item/editItem.jsp

return "item/editItem";

1.4.3.2 Redirect重定向

Contrller方法返回结果重定向到一个url地址,如下商品修改提交后重定向到商品查询方法,参数无法带到商品查询方法中。


//重定向到queryItem.action地址,request无法带过去
return "redirect:queryItem.action";

redirect方式相当于“response.sendRedirect()”,转发后浏览器的地址栏变为转发后的地址,因为转发即执行了一个新的request和response。

由于新发起一个request原来的参数在转发时就不能传递到下一个url,如果要传参数可以/item/queryItem.action后边加参数,如下:


/item/queryItem?...&…..

1.4.3.3 forward转发

controller方法执行后继续执行另一个controller方法,如下商品修改提交后转向到商品修改页面,修改商品的id参数可以带到商品修改方法中。


//结果转发到editItem.action,request可以带过去

return "forward:editItem.action";

forward方式相当于“request.getRequestDispatcher().forward(request,response)”,转发后浏览器地址栏还是原来的地址。转发并没有执行新的request和response,而是和转发前的请求共用一个request和response。所以转发前请求的参数在转发后仍然可以读取到。

1.5 参数绑定

1.5.1 默认支持的参数类型

处理器形参中添加如下类型的参数处理适配器会默认识别并进行赋值。

1.5.1.1 HttpServletRequest

通过request对象获取请求信息

1.5.1.2 HttpServletResponse

通过response处理响应信息

1.5.1.3 HttpSession

通过session对象得到session中存放的对象

1.5.1.4 Model

通过model向页面传递数据,如下:


//调用service查询商品信息
Items item = itemService.findItemById(id);
model.addAttribute("item", item);

页面通过${item.XXXX}获取item对象的属性值。

1.5.2 参数绑定介绍

注解适配器对RequestMapping标记的方法进行适配,对方法中的形参会进行参数绑定,早期springmvc采用PropertyEditor(属性编辑器)进行参数绑定将request请求的参数绑定到方法形参上,3.X之后springmvc就开始使用Converter进行参数绑定。

1.5.3 @RequestParam

@RequestParam用于绑定单个请求参数。

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

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


TTP Status 400 - Required Integer parameter 'XXXX' is not present

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

定义如下:


public String editItem(@RequestParam(value="item_id",required=true) String id) {

}

形参名称为id,但是这里使用value=” item_id”限定请求的参数名为item_id,所以页面传递参数的名必须为item_id。

注意:如果请求参数中没有item_id将跑出异常:


HTTP Status 500 - Required Integer parameter 'item_id' is not present

这里通过required=true限定item_id参数为必需传递,如果不传递则报400错误,可以使用defaultvalue设置默认值,即使required=true也可以不传item_id参数值

1.5.4 简单类型

当请求的参数名称和处理器形参名称一致时会将请求参数与形参进行绑定。

1.5.4.1 整型


public String editItem(Model model,Integer id) throws Exception{

}

1.5.4.4 布尔型

处理器方法:


public String editItem(Model model,Integer id,Boolean status) throws Exception

请求url:


http://localhost:8080/springmvc_mybatis/item/editItem.action?id=2&status=false

说明:对于布尔类型的参数,请求的参数值为true或false。

1.5.5 自定义参数绑定

1.5.5.1 需求

根据业务需求自定义日期格式进行参数绑定。

1.5.5.2 propertyEditor

1.5.5.2.1 使用WebDataBinder

在controller方法中通过@InitBinder标识方法为参数绑定方法,通过WebDataBinder注册属性编辑器,问题是此方法只能在单个controller类中注册。


 /**
 * 注册属性编辑器(字符串转换为日期)
 */
@InitBinder
public void initBinder(WebDataBinder binder) throws Exception {

  binder.registerCustomEditor(Date.class, new CustomDateEditor(new SimpleDateFormat("yyyy-MM-dd"),true));

}

1.5.5.2.2 使用WebDataBinder

在controller方法中通过@InitBinder标识方法为参数绑定方法,通过WebDataBinder注册属性编辑器,问题是此方法只能在单个controller类中注册。

如下:

编写CustomPropertyEditor:


public class CustomPropertyEditor implements PropertyEditorRegistrar {

@Override

public void registerCustomEditors(PropertyEditorRegistry registry) {

  registry.registerCustomEditor(Date.class, new CustomDateEditor(new

   SimpleDateFormat("yyyy-MM-dd HH-mm-ss"),true));

}

}

配置如下:


<!-- 注册属性编辑器 -->

<bean id="customPropertyEditor" class="com.sihai.ssm.propertyeditor.CustomPropertyEditor"></bean>
<!-- 自定义webBinder -->

<bean id="customBinder" class="org.springframework.web.bind.support.ConfigurableWebBindingInitializer">

    <property name="propertyEditorRegistrars">

        <list>

            <ref bean="customPropertyEditor" />

        </list>

    </property>

</bean>

<!--注解适配器 -->

<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">

    <property name="webBindingInitializer" ref="customBinder"></property>

</bean>

1.5.5.3 Converter

1.5.5.3.1 自定义Converter


public class CustomDateConverter implements Converter<String, Date> {

  @Override
  public Date convert(String source) {

    try {

      SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH-mm-ss");

      return simpleDateFormat.parse(source);

    } catch (Exception e) {

      e.printStackTrace();

    }

    return null;

  }

}

1.5.5.3.2 配置方式1


<!--注解适配器 -->

<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">

    <property name="webBindingInitializer" ref="customBinder"></property>

</bean>
<!-- 自定义webBinder -->

<bean id="customBinder" class="org.springframework.web.bind.support.ConfigurableWebBindingInitializer">

    <property name="conversionService" ref="conversionService" />

</bean>
<!-- conversionService -->

<bean id="conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">

    <!-- 转换器 -->

    <property name="converters">

        <list>

            <bean class="cn.itcast.ssm.controller.converter.CustomDateConverter" />

        </list>

    </property>

</bean>

1.5.5.3.3 配置方式2


<mvc:annotation-driven conversion-service="conversionService"></mvc:annotation-driven>
<!-- conversionService -->

<bean id="conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">

    <!-- 转换器 -->

    <property name="converters">

        <list>

            <bean class="cn.itcast.ssm.controller.converter.CustomDateConverter" />

        </list>

    </property>

</bean>

1.5.6 pojo

1.5.6.1 简单pojo

将pojo对象中的属性名于传递进来的属性名对应,如果传进来的参数名称和对象中的属性名称一致则将参数值设置在pojo对象中

页面定义如下;


<input type="text" name="name"/>

<input type="text" name="price"/>

Contrller方法定义如下:


@RequestMapping("/editItemSubmit")
public String editItemSubmit(Items items)throws Exception{

  System.out.println(items);
  }

请求的参数名称和pojo的属性名称一致,会自动将请求参数赋值给pojo的属性。

1.5.6.2 包装pojo

如果采用类似struts中对象.属性的方式命名,需要将pojo对象作为一个包装对象的属性,action中以该包装对象作为形参。

包装对象定义如下:


Public class QueryVo {

  private Items items;

}

页面定义:


<input type="text" name="items.name" />

<input type="text" name="items.price" />

Controller方法定义如下:


public String useraddsubmit(Model model,QueryVo queryVo)throws Exception{

  System.out.println(queryVo.getItems());
}

1.5.6.3 ModelAttribute

@ModelAttribute作用如下:

1、绑定请求参数到pojo并且暴露为模型数据传到视图页面

此方法可实现数据回显效果。


// 商品修改提交
@RequestMapping("/editItemSubmit")
public String editItemSubmit(@ModelAttribute("item") Items items,Model model)

页面:


<tr>

<td>商品名称</td>

<td><input type="text" name="name" value="${item.name }"/></td>

</tr>

<tr>

<td>商品价格</td>

<td><input type="text" name="price" value="${item.price }"/></td>

</tr>

如果不用@ModelAttribute可以使用model.addAttribute(“item”, items)完成数据回显。

2、将方法返回值暴露为模型数据传到视图页面


//商品分类
@ModelAttribute("itemtypes")
public Map<String, String> getItemTypes(){

  Map<String, String> itemTypes = new HashMap<String,String>();

  itemTypes.put("101", "数码");

  itemTypes.put("102", "母婴");

  return itemTypes;

}

页面:

商品类型:


<select name="itemtype">

<c:forEach items="${itemtypes }" var="itemtype">

<option value="${itemtype.key }">${itemtype.value }</option>

</c:forEach>

</select>

1.5.7 集合类

1.5.7.1 字符串数组

页面定义如下:

页面选中多个checkbox向controller方法传递


<input type="checkbox" name="item_id" value="001"/>

<input type="checkbox" name="item_id" value="002"/>

<input type="checkbox" name="item_id" value="002"/>

传递到controller方法中的格式是:001,002,003

Controller方法中可以用String[]接收,定义如下:


public String deleteitem(String[] item_id)throws Exception{

  System.out.println(item_id);

1.5.7.2 List

List中存放对象,并将定义的List放在包装类中,action使用包装对象接收。

List中对象:

成绩对象


Public class QueryVo {

Private List<Items> itemList;//订单明细

  //get/set方法..

}

包装类中定义List对象,并添加get/set方法如下:

页面:


<tr>

<td>

<input type="text" name=" itemList[0].id" value="${item.id}"/>

</td>

<td>

<input type="text" name=" itemList[0].name" value="${item.name }"/>

</td>

<td>

<input type="text" name=" itemList[0].price" value="${item.price}"/>

</td>

</tr>

<tr>

<td>

<input type="text" name=" itemList[1].id" value="${item.id}"/>

</td>

<td>

<input type="text" name=" itemList[1].name" value="${item.name }"/>

</td>

<td>

<input type="text" name=" itemList[1].price" value="${item.price}"/>

</td>

</tr>

Contrller方法定义如下:


public String useraddsubmit(Model model,QueryVo queryVo)throws Exception{

  System.out.println(queryVo.getItemList());

}

1.5.7.3 Map

在包装类中定义Map对象,并添加get/set方法,action使用包装对象接收。

包装类中定义Map对象如下:


Public class QueryVo {

  private Map<String, Object> itemInfo = new HashMap<String, Object>();

  //get/set方法..

}

页面定义如下:


<tr>

<td>学生信息:</td>

<td>

姓名:<inputtype="text"name="itemInfo['name']"/>

年龄:<inputtype="text"name="itemInfo['price']"/>

.. .. ..

</td>

Contrller方法定义如下:


public String useraddsubmit(Model model,QueryVo queryVo)throws Exception{

  System.out.println(queryVo.getStudentinfo());

}

1.6 问题总结

1.6.1 404

页面找不到,视图找不到。

HandlerMapping根据url没有找到Handler。

1.6.2 Post时中文乱码

在web.xml中加入:


<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>

以上可以解决post请求乱码问题。

对于get请求中文参数出现乱码解决方法有两个:

修改tomcat配置文件添加编码与工程编码一致,如下:


<Connector URIEncoding="utf-8" connectionTimeout="20000" port="8080" protocol="HTTP/1.1" redirectPort="8443"/>

另外一种方法对参数进行重新编码:


String userName = new String(request.getParamter("userName").getBytes("ISO8859-1"),"utf-8")

ISO8859-1是tomcat默认编码,需要将tomcat编码后的内容按utf-8编码

1.7 与struts2不同

1、 springmvc的入口是一个servlet即前端控制器,而struts2入口是一个filter过虑器。

2、 springmvc是基于方法开发(一个url对应一个方法),请求参数传递到方法的形参,可以设计为单例或多例(建议单例),struts2是基于类开发,传递参数是通过类的属性,只能设计为多例。

3、 Struts采用值栈存储请求和响应的数据,通过OGNL存取数据,springmvc通过参数解析器是将request请求内容解析,并给方法形参赋值,将数据和视图封装成ModelAndView对象,最后又将ModelAndView中的模型数据通过reques域传输到页面。Jsp视图解析器默认使用jstl。

本人花费半年的时间总结的《Java面试指南》已拿腾讯等大厂offer,已开源在github ,欢迎star!

转载声明:转载请注明出处,本技术博客是本人原创文章

本文GitHub https://github.com/OUYANGSIHAI/JavaInterview 已收录,这是我花了6个月总结的一线大厂Java面试总结,本人已拿大厂offer,欢迎star

原文链接:blog.ouyangsihai.cn >> springmvc教程:注解开发深入解析


 上一篇
springMVC教程:拦截器详解 springMVC教程:拦截器详解
1 拦截器1.1 定义Spring Web MVC 的处理器拦截器类似于Servlet 开发中的过滤器Filter,用于对处理器进行预处理和后处理。 1.2 拦截器定义实现HandlerInterceptor接口,如下: public c
下一篇 
springmvc教程:整合mybatis开发(spring+springMVC+mybatis整合开发) springmvc教程:整合mybatis开发(spring+springMVC+mybatis整合开发)
一、整合mybatis为了更好的学习 springmvc和mybatis整合开发的方法,需要将springmvc和mybatis进行整合。 整合目标:控制层采用springmvc、持久层使用mybatis实现。 1.1 需求实现商品查询列表