Spring MVC总结

1.理论

1.1执行过程及原理

在这里插入图片描述
简要分析执行流程

  • 1.DispatcherServlet表示前置控制器,是整个SpringMVC的控制中心。用户发出请求,
    • DispatcherServlet接收请求并拦截请求。
      我们假设请求的url为 : http://localhost:8080/hello
      如上url拆分成三部分:
      http://localhost:8080服务器域名
      SpringMVC部署在服务器上的web站点
      hello表示控制器
      通过分析,如上url表示为:请求位于服务器localhost:8080上的SpringMVC站点的hello控制器。
  • 2.HandlerMapping为处理器映射。DispatcherServlet调用HandlerMapping,HandlerMapping根据请求url查找Handler。
  • 3.HandlerExecution表示具体的Handler,其主要作用是根据url查找控制器,如上url被查找控制器为:hello。
  • 4.HandlerExecution将解析后的信息传递给DispatcherServlet,如解析控制器映射等。
  • 5.HandlerAdapter表示处理器适配器,其按照特定的规则去执行Handler。
  • 6.Handler让具体的Controller执行。
  • 7.Controller将具体的执行信息返回给HandlerAdapter,如ModelAndView。
  • 8.HandlerAdapter将视图逻辑名或模型传递给DispatcherServlet。
  • 9.DispatcherServlet调用视图解析器(ViewResolver)来解析HandlerAdapter传递的逻辑视图名。
  • 10.视图解析器将解析的逻辑视图名传给DispatcherServlet。
  • 11.DispatcherServlet根据视图解析器解析的视图结果,调用具体的视图。
  • 12.最终视图呈现给用户。

2.Spring MVC组件

1、前端控制器DispatcherServlet。
2、处理器映射器HandlerMapping。
3、处理器Handler。
4、处理器适配器HandlerAdapter
5、视图解析器 View resolver。

3.请求参数绑定

而在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,没有传入参数就会报错。

3.1pojo类绑定
3.1.1 普通pojo类型

再来总结下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<String, Date> {

    @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的乱码过滤器
<filter>
    <filter-name>encoding</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>encoding</filter-name>
    //1.当配置<url-pattern>/</url-pattern>的时候,它会匹配到路径型的url,就不会匹配到模式为.jsp型的url
    <url-pattern>/</url-pattern>
    //2.当配置<url-pattern><url-pattern>/*<url-pattern>的时候,它就会匹配到所有类型的url,包括路径型的,有各种后缀的等等
    <url-pattern><url-pattern>/*<url-pattern>
    //由于jsp很少使用了推荐第一种<url-pattern>/</url-pattern>
</filter-mapping>
3.1.2 包装的pojo类型

这个包装类型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参数绑定问题。

<form action="account/saveAccount" method="post">
    账户名称: <input type="text" name="name"> <br>
    账户金额: <input type="text" name="money"> <br>
    账户省份: <input type="text" name="address.provinceName"> <br>
    账户城市: <input type="text" name="address.cityName"> <br>
    <input type="submit" value="保存">
</form>

控制器代码

@Controller
@RequestMapping("/account")
public class AccountController {

    /** 保存账户 */
    @RequestMapping("saveAccount")
    public String saveAccount(Account account) {
        System.out.println(account);
        return "success";
    }
}
3.2 list绑定和map绑定

通常在需要批量提交数据时,将提交的数据绑定到list中

public class User implements Serializable {
    private String username;
    private String password;
    private Integer age;
    //list绑定
    private List<Account> accounts;
    //map绑定
    private Map<String, Account> accountMap;
    ... get/set/toString ...

控制器代码

@Controller
@RequestMapping("/account")
public class AccountController {

    /** 更新账户 */
    @RequestMapping("/updateAccount")
    public String updateAccount(User user) {
        System.out.println(user);
        return "success";
    }
}

js代码

<form action="account/updateAccount" method="post">
    用户名称:<input type="text" name="username"> <br>
    用户密码:<input type="password" name="password"> <br>
    用户年龄:<input type="text" name="age"> <br>

    <%-- List --%>
    账户1名称:<input type="text" name="accounts[0].name"> <br>
    账户1金额: <input type="text" name="accounts[0].money"> <br>
    账户1省份:<input type="text" name="accounts[0].address.provinceName"> <br>
    账户1城市: <input type="text" name="accounts[0].address.cityName"> <br>
     <%-- Map --%>
    账户2名称:<input type="text" name="accountMap['one'].name"> <br>
    账户2金额:<input type="text" name="accountMap['one'].money"> <br>
    账户2省份:<input type="text" name="accountMap['one'].address.provinceName"> <br>
    账户2城市:<input type="text" name="accountMap['one'].address.cityName"> <br>
    <input type="submit" value="保存">
</form>
HttpSession绑定,HttpServletResponse绑定,HttpServletRequest绑定等等

js核心代码

<%-- 原始ServletAPI作为控制器参数 --%>
<a href="account/testServletAPI">测试访问ServletAPI</a>

控制器代码

@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";
    }
}

HttpServiceRequest、HttpSession、注解(@SessionAttributes):设置、获取session

4.常用注解

@Controller

@Controller注解类型用于声明Spring类的实例是一个控制器(在讲IOC时还提到了另外3个注解);

@RequestMapping

@RequestMapping注解用于映射url到控制器类或一个特定的处理程序方法。可用于类或方法上。用于类上,表示类中的所有响应请求的方法都是以该地址作为父路径。

  1. @RequestMapping(value={"/get","/fetch"} )即 /get或/fetch都会映射到该方法上。
    method:指定请求的method类型, GET、POST、PUT、DELETE等;
  2. @RequestMapping(value="/get/{bookid}",method={RequestMethod.GET,RequestMethod.POST})
    params:指定request中必须包含某些参数值是,才让该方法处理。
  3. @RequestMapping(params=“action=del”),请求参数包含“action=del”,如:http://localhost:8080/book?action=del
    headers:指定request中必须包含某些指定的header值,才能让该方法处理请求。
  4. @RequestMapping(value="/header/id", headers = “Accept=application/json”):表示请求的URL必须为“/header/id 且请求头中必须有“Accept =application/json”参数即可匹配。
@Resource和@Autowired

@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; 
}
@ModelAttribute和 @SessionAttributes

代表的是:该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

@PathVariable用于将请求URL中的模板变量映射到功能处理方法的参数上。

@RequestMapping(value="/users/{userId}/topics/{topicId}")
public String test(
        @PathVariable(value="userId") int userId,
        @PathVariable(value="topicId") int topicId)
@requestParam

@RequestParam用于将请求参数区数据映射到功能处理方法的参数上。

@ResponseBody

使用时机:返回的数据不是html标签的页面,而是其他某种格式的数据时(如json、xml等)使用;

@Repository

用于注解dao层,在daoImpl类上面注解。

@Component

相当于通用的注解,当不知道一些类归到哪个层时使用,但是不建议。

5.Restful风格

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值