本文共 10710 字,大约阅读时间需要 35 分钟。
1、前端控制器DispatcherServlet。
2、处理器映射器HandlerMapping。 3、处理器Handler。 4、处理器适配器HandlerAdapter 5、视图解析器 View resolver。而在springmvc中,接收页面提交的数据是通过方法形参来接收的。从客户端请求的key/value数据,经过参数绑定,将key/value数据绑定到controller方法的形参上,然后就可以在controller中使用该参数了。来看一下这个过程:
总结这个还是以需求为例吧,这样比较容易理解,假设现在有个需求:根据商品的id来修改对应点商品信息。所以前台页面肯定要传进来该商品的id,然后springmvc的controller进行处理,返回一个修改商品信息的页面。关于前台页面的东西都很简单,我就不贴代码了,主要部分截个图,具体的代码在文章最后有下载地址。 前台页面通过url将参数传递过来,请求的是editItems.action。 下面写controller中的editItems方法:@RequestMapping("/editItems")public String editItems(Model model, Integer id) throws Exception { //根据id查询对应的Items ItemsCustom itemsCustom = itemsService.findItemsById(id); model.addAttribute("itemsCustom", itemsCustom); //通过形参中的model将model数据传到页面 //相当于modelAndView.addObject方法 return "/WEB-INF/jsp/items/editItems.jsp";}
这是个很简单的demo,从上面的代码中可以看出model可以直接作为参数,springmvc默认会绑定它,然后使用model将查询到的数据放到request域中,这样就可以在前台页面取出该数据了。
要注意一点的是,简单类型的绑定中,方法形参中的参数名要和前台传进来的名一样才能完成参数的绑定。那有人要问了,如果有特殊需求(比如更好的可读性?),这里定义的参数名就是不一样,那咋整呢?有解决办法么?有!我们可以使用注解@RequestParam对简单的类型进行参数绑定,如下: 所以说,如果不使用@RequestParam,要求request传入参数名称和controller方法的形参名称一致,方可绑定成功。如果使用@RequestParam,不用限制request传入参数名称和controller方法的形参名称一致。通过@RequestParam中的required属性指定参数是否必须要传入,如果设置为true,没有传入参数就会报错。再来总结下pojo类型的绑定,继续上面的案例,当页面展示了商品详细信息后,我做了修改,然后点击提交,后台应该将我提交的这些参数全部更新到数据库的items表中,也就是说,我提交的这些参数要绑定到Items对象或者其扩展对象中。先看一下Items中都有哪些属性:
可以看到,有各种类型的属性,当我们提交后,要将这些属性全部封装到一个pojo中,然后去更新数据库。 绑定很简单,对于基本类型,要求页面中input标签的name属性值和controller的pojo形参中的属性名称一致,即可将页面中数据绑定到pojo。也就是说前台页面传进来的name要和要封装的pojo属性名一模一样,然后就可以将该pojo作为形参放到controller的方法中,如下: 这样就能将前台表单传进来的不同属性值封装到ItemsCustom中了。但是运行一下就会发现报错,报错的信息是无法将String类型转换成java.util.Date类型,因为上面Items中有一个属性是Date类型的createtime。这就需要我们自己定义转换器了,这也是这部分的重点,下面我们自己定义一个日期转换器://需要实现Converter接口,这里是将String类型转换成Date类型public class CustomDateConverter implements Converter{ @Override public Date convert(String source) { //实现 将日期串转成日期类型(格式是yyyy-MM-dd HH:mm:ss) SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); try { //转成直接返回 return simpleDateFormat.parse(source); } catch (ParseException e) { // TODO Auto-generated catch block e.printStackTrace(); } //如果参数绑定失败返回null return null; }}
定义好了转换器后,需要在springmvc.xml中进行如下配置: 现在就可以了,springmvc就能根据这个转换器将String类型正确转换成Date类型,然后封装到ItemsCustom中去了。
这里说一个小小的插曲:修改商品详细信息后提交,可能会有中文乱码问题,表达提交都是post方式,springmvc中关于post方式的中文乱码问题可以在web.xml中配置一个过滤器来解决,如下://SpringMVC的乱码过滤器encoding org.springframework.web.filter.CharacterEncodingFilter encoding utf-8 encoding //1.当配置/ 的时候,它会匹配到路径型的url,就不会匹配到模式为.jsp型的url/ //2.当配置/* 的时候,它就会匹配到所有类型的url,包括路径型的,有各种后缀的等等 /* //由于jsp很少使用了推荐第一种 /
这个包装类型pojo与上面普通的pojo有啥区别呢?包装类型pojo指的是pojo中有另一个也是pojo的属性,即pojo套pojo,为什么会设计这种pojo呢?在前面的博文中我也有提到,这种组合的设计方法对于后期程序的扩展很有用,比如复杂的查询条件就需要包装到这种包装类型中。
那么该如何绑定呢?有两个思路:1.在形参中添加HttpServletRequest request参数,通过request接收查询条件参数。2.在形参中让包装类型的pojo接收查询条件参数。
第一种方式就跟原始servlet差不多
@RequestMapping("/itemEdit") public String itemEdit(HttpServletRequest request, Model model) { //从Request中取id String strId = request.getParameter("id"); Integer id = null; //如果id有值则转换成int类型 if (strId != null && !"".equals(strId)) { id = new Integer(strId); } else { //出错 return null; } Items items = itemService.getItemById(id); //创建ModelAndView //ModelAndView modelAndView = new ModelAndView(); //向jsp传递数据 //modelAndView.addObject("item", items); model.addAttribute("item", items); //设置跳转的jsp页面 //modelAndView.setViewName("editItem"); //return modelAndView; return "editItem"; }
使用第二种方法,我们传进来一个包装类型的pojo。看一下这个包装类型的pojo:
public class Account implements Serializable { private Integer id; private String name; private Float money; //Addres是包装的pojo类,内有provinceName,cityName两个属性 private Address address; ...get/set/toString/构造函数.../*- - - - - - - - - - - - - - - - - - *///Addres类在这public class Address implements Serializable { private String provinceName; private String cityName; ...get/set/toString/构造函数...
这个包装pojo中还有一个Address 类,这个类继承了Serializable 类,并且用来扩展与Account 相关信息。所以这个Address 中有provinceName属性和cityName属性,假如我们要想将前台传进来的属性封装到Address 中,该如何传入呢?这就是包装类型的pojo参数绑定问题。
控制器代码
@Controller@RequestMapping("/account")public class AccountController { /** 保存账户 */ @RequestMapping("saveAccount") public String saveAccount(Account account) { System.out.println(account); return "success"; }}
通常在需要批量提交数据时,将提交的数据绑定到list中
public class User implements Serializable { private String username; private String password; private Integer age; //list绑定 private Listaccounts; //map绑定 private Map accountMap; ... get/set/toString ...
控制器代码
@Controller@RequestMapping("/account")public class AccountController { /** 更新账户 */ @RequestMapping("/updateAccount") public String updateAccount(User user) { System.out.println(user); return "success"; }}
js代码
js核心代码
<%-- 原始ServletAPI作为控制器参数 --%>测试访问ServletAPI
控制器代码
@Controller@RequestMapping("/account")public class AccountController { /** 测试访问testServletAPI */ @RequestMapping("/testServletAPI") public String testServletAPI(HttpServletRequest request, HttpServletResponse response, HttpSession session) { System.out.println(request); System.out.println(response); System.out.println(session); return "success"; }}
@Controller注解类型用于声明Spring类的实例是一个控制器(在讲IOC时还提到了另外3个注解);
@RequestMapping注解用于映射url到控制器类或一个特定的处理程序方法。可用于类或方法上。用于类上,表示类中的所有响应请求的方法都是以该地址作为父路径。
@Resource和@Autowired都是做bean的注入时使用,其实@Resource并不是Spring的注解,它的包是javax.annotation.Resource,需要导入,但是Spring支持该注解的注入。
public class TestServiceImpl { // 下面两种@Autowired只要使用一种即可 @Autowired private UserDao userDao; // 用于字段上 @Autowired public void setUserDao(UserDao userDao) { // 用于属性的方法上 this.userDao = userDao; }}
如果我们想使用按照名称(byName)来装配,可以结合@Qualifier注解一起使用。如下:
public class TestServiceImpl { @Autowired @Qualifier("userDao") private UserDao userDao; }
代表的是:该Controller的所有方法在调用前,先执行此@ModelAttribute方法,可用于注解和方法参数中,可以把这个@ModelAttribute特性,应用在BaseController当中,所有的Controller继承BaseController,即可实现在调用Controller时,先执行@ModelAttribute方法。
@SessionAttributes即将值放到session作用域中,写在class上面。SpringMVC 支持使用 @ModelAttribute 和 @SessionAttributes 在不同的模型(model)和控制器之间共享数据。 @ModelAttribute 主要有两种使用方式,一种是标注在方法上,一种是标注在 Controller 方法参数上。
当 @ModelAttribute 标记在方法上的时候,该方法将在处理器方法执行之前执行,然后把返回的对象存放在 session 或模型属性中,属性名称可以使用 @ModelAttribute(“attributeName”) 在标记方法的时候指定,若未指定,则使用返回类型的类名称(首字母小写)作为属性名称。关于 @ModelAttribute 标记在方法上时对应的属性是存放在 session 中还是存放在模型中,我们来做一个实验,看下面一段代码。
@Controller@RequestMapping ( "/myTest" )public class MyController { @ModelAttribute ( "hello" ) public String getModel() { System. out .println( "-------------Hello---------" ); return "world" ; } @ModelAttribute ( "intValue" ) public int getInteger() { System. out .println( "-------------intValue---------------" ); return 10; } @RequestMapping ( "sayHello" ) public void sayHello( @ModelAttribute ( "hello" ) String hello, @ModelAttribute ( "intValue" ) int num, @ModelAttribute ( "user2" ) User user, Writer writer, HttpSession session) throws IOException { writer.write( "Hello " + hello + " , Hello " + user.getUsername() + num); writer.write( "\r" ); Enumeration enume = session.getAttributeNames(); while (enume.hasMoreElements()) writer.write(enume.nextElement() + "\r" ); } @ModelAttribute ( "user2" ) public User getUser(){ System. out .println( "---------getUser-------------" ); return new User(3, "user2" ); }}
当我们请求 /myTest/sayHello.do 的时候使用 @ModelAttribute 标记的方法会先执行,然后把它们返回的对象存放到模型中。最终访问到 sayHello 方法的时候,使用 @ModelAttribute 标记的方法参数都能被正确的注入值。执行结果如下所示:
Hello world,Hello user210
@PathVariable用于将请求URL中的模板变量映射到功能处理方法的参数上。
@RequestMapping(value="/users/{userId}/topics/{topicId}")public String test( @PathVariable(value="userId") int userId, @PathVariable(value="topicId") int topicId)
@RequestParam用于将请求参数区数据映射到功能处理方法的参数上。
使用时机:返回的数据不是html标签的页面,而是其他某种格式的数据时(如json、xml等)使用;
用于注解dao层,在daoImpl类上面注解。
相当于通用的注解,当不知道一些类归到哪个层时使用,但是不建议。
转载地址:http://amrwi.baihongyu.com/