• 所谓SpringMVC的注解式开发是指,处理器是基于注解的类的开发。对于每一个定义的处理器,无需在配置文件中逐个注册,只需要在代码中通过对类和方法的注解,便可完成注册。即注解替换的是配置文件中对处理器的注册部分。

    1 第一个注解式开发程序

    1.1 注册组件扫描器

  • 这里的组件即处理器,需要指定处理器所在基本包。
    
    
        
        
    
    
    

    1.2 定义处理器

  • 此时的处理器类无需继承任何父类,实现任何接口。只需要在类上和方法上添加相应注解即可。
  • @Controller:表示当前类为处理器。
  • @RequestMapping:表示当前方法为处理器方法。该方法要对value属性所指定的URL进行处理与响应。被注解的方法的方法名可以随意。
    @Controller
    public class MyController  {
    @RequestMapping("/test/hello.do")
    public ModelAndView handleSome(HttpServletRequest request,
            HttpServletResponse response) throws Exception {
        ModelAndView mv = new ModelAndView("welcome");
        mv.addObject("welcome", "welcome to SpringMVC world!");
        return mv;
    }
    }
  • 当然,若有多个请求路径均可匹配该处理器方法的执行,则@RequestMapping的value属性中可以写上一个数组。
    @RequestMapping({"/test/hello.do", "/test/world.do"})

    2 处理器的请求映射规则的定义

  • 通过@RequestMapping注解可以定义处理器对于请求的映射规则。该注解可以注解在方法上,也可以注解在类上,但是意义不同。

    2.1 对于请求URI的命名空间的定义

  • @RequestMapping的value属性用于定义所匹配请求的URI。但是对于注解在方法上与类上,其value属性值所指定的URI,意义是不同的。
  • 一个@Controller所注解的类中,可以定义多个处理器方法。当然,不同的处理器方法所匹配的URI是不同的。这些不同的URI被指定在注解于方法之上的@RequestMapping的value属性中。但若这些请求具有相同的URI部分,则这些相同的URI,可以被抽取到注解在类之上的@RequestMapping的value属性中。此时的这个URI被称之为命名空间。
  • 换个角度说,要访问处理器的指定方法,必须要在方法指定URI之前加上处理器类前定义的命名空间。
    SSM框架之SpringMVC4专题3:SpringMVC注解式开发_第1张图片

    2.2 请求URI中通配符的应用

  • 在处理器方法所映射的URI中,可以使用通配符,有两种用法。

    2.2.1 资源名称中使用通配符

  • 在请求的资源名称中使用通配符,表示请求的资源名称中只要包含指定的字符即可完成匹配。
  • 例如,下面的写法中/some.do表示的意思是,只要请求的资源名称以some开口即可;/other.do表示的意思是,只要请求的资源名称以other结尾即可。
    @RequestMapping("/some*.do")
    public ModelAndView handleSome(HttpServletRequest request,
            HttpServletResponse response) throws Exception {
        ModelAndView mv = new ModelAndView("welcome");
        mv.addObject("welcome", "welcome to SpringMVC world!");
        return mv;
    }

    2.2.2 资源路径中使用通配符

  • 在资源路径中使用通配符,有两种用法:路径级数的精确匹配,与路径级数的可变匹配。
    /xxx/*/show.do:表示在show.do的资源名称前面,只能有两级路径,第一即必须为/xxx,
    而第二级随意。这种称之为路径级数的精确匹配。
    /***/**/show.do:表示在show.do的资源名称前面,必须以/xxx路径开头,
    而其他级的路径是否包含,若包含有包含几级,各级又叫什么名称,均随意。
    这种称之为路径级数的可变匹配。
    @RequestMapping("*/some.do")
    public ModelAndView handleSome(HttpServletRequest request,
            HttpServletResponse response) throws Exception {
        ModelAndView mv = new ModelAndView("welcome");
        mv.addObject("welcome", "welcome to SpringMVC world!");
        return mv;
    }
    @RequestMapping("**/other.do")
    public ModelAndView handleOther(HttpServletRequest request,
            HttpServletResponse response) throws Exception {
        ModelAndView mv = new ModelAndView("other");
        mv.addObject("other", "Other to SpringMVC world!");
        return mv;
    }

    2.3 对请求提交方式的定义

  • 对于@RequestMapping,其有一个属性method,用于对被注解方法所处理请求的提交方式进行限制,即只有满足该method属性指定的提交方式的请求,才会执行该被注解方法。
  • Method属性的取值为RequestMethod枚举常量。常用的为RequestMethod.GET与RequestMethod.POST,分别表示提交方式的匹配规则为GET与POST请求。
    @RequestMapping(value="/register.do", method=RequestMethod.POST)
  • 以上处理器方法只能够处理POST方式提交的请求。客户端浏览器常用的请求方式,以及提交方式有以下几种:
    SSM框架之SpringMVC4专题3:SpringMVC注解式开发_第2张图片
  • 也就是说,只要指定了处理器方法匹配的请求提交方式为POST,则相当于指定了请求发送的方式:要么使用表单请求,要么使用AJAX请求。其他请求方式被禁用。

    2.4 对请求中携带参数的定义

  • @RequestMapping中params属性中定义了请求中必须携带的参数的要求。以下是几种情况的说明。
    @RequestMapping(value="/xxx.do", params={"name", "age"}):要求请求中必须携带请求参数name和age
    @RequestMapping(value="/xxx.do", params={"!name", "age"}):要求请求中必须携带请求参数age,但是不能够携带参数name
    @RequestMapping(value="/xxx.do", params={"name=zs", "age=23"}):要求请求中必须携带请求参数name,且其值必须为zs;必须携带参数age,且其值必须为23
    @RequestMapping(value="/xxx.do", params="name!=zs"):要求请求中必须携带请求参数name,且其值必须不能够为zs

    3 处理器方法的参数

  • 处理器方法可以包含以下五类参数,这些参数会在系统调用时有系统自动赋值,即程序员可在方法内直接使用。
  • HttpServletRequest、HttpServletResponse、HttpSession、用于承载数据Model、请求中所携带的请求参数。

    3.1 逐个参数接收

  • 只要保证请求参数名和该请求处理方法的参数名相同即可。
  • 修改index页面:
    姓名:
    年龄:
  • 修改处理器类MyController:
    @Controller
    @RequestMapping("/test")
    public class MyController  {
    @RequestMapping(value="/register.do", method=RequestMethod.POST)
    public ModelAndView register(String name, int age) throws Exception {
        ModelAndView mv = new ModelAndView();
        mv.addObject("name", name);
        mv.addObject("age", age);
        mv.setViewName("show");
        return mv;
    }
    }
  • 添加show页面,在/WEB-INF/jsp下添加show.jsp页面:
    
    服务端信息为: 
    name = ${name } age = ${age }

    3.2 请求参数中文乱码问题

  • 对于前面所接收的请求参数,若含有中文,则会出现中文乱码问题。Spring对于请求参数中的中文乱码问题,给出了专门的字符集过滤器:spring-web-4.2.1.RELEASE.jar的org.springframework.web.filter包下的CharacterEncodingFilter类。
    SSM框架之SpringMVC4专题3:SpringMVC注解式开发_第3张图片

    3.2.1 解决方案

  • 在web.xml中注册字符集过滤器,即可解决Spring的请求参数的中文乱码问题。不过,最好将该过滤器注册在其他过滤器之前。因为过滤器的执行时按照其注册顺序进行的。
    
    
        characterEncodingFilter
        
            org.springframework.web.filter.CharacterEncodingFilter
        
        
        
            encoding
            utf-8
        
        
        
            forceEncoding
            true
        
    
    
        characterEncodingFilter
        /*
    

    3.2.2 源码分析

  • 打开CharacterEncodingFilter类的源码,发现有两个set属性:encoding与forceEncoding。
    SSM框架之SpringMVC4专题3:SpringMVC注解式开发_第4张图片
  • encoding:String类型,指定字符集。
  • forceEncoding:boolean类型,指定是否强制使用指定的字符集。若为true,则代码中指定的字符集将不起作用;若为false,则代码中若指定了字符集,就使用代码指定字符集;若代码中没有指定字符集,则使用这里指定的字符集。

    3.3 校正请求参数名@RequestParam

  • 所谓校正请求参数名,是指若请求URL所携带的参数名称与处理方法中指定的参数名称不相同时,则需在处理方法参数前,添加一个注解@RequestParam("请求参数名"),指定请求URL所携带参数的名称,该注解是对处理器方法参数进行修饰的。
  • @RequestParam()有三个属性:
  • 1、value:指定请求参数的名称;
  • 2、required:指定该注解所修饰的参数是否是必须的,boolean类型。若为true,则表示请求中所携带的参数中必须包含当前参数。若为false,则表示有没有均可;
  • 3、defaultValue:指定当前参数的默认值。若请求URI中没有给出当前参数,则当前方法参数将取该默认值。即使required为true,其URI中没有给出当前参数,该处理器方法参数会自动取该默认值,而不会报错。
  • 修改index页面:
    姓名:
    年龄:
  • 修改处理器类MyController:
    @RequestMapping(value="/register.do", method=RequestMethod.POST)
    public ModelAndView register(@RequestParam(name="rname") String name, 
            @RequestParam(name="rage")int age) throws Exception {
        ModelAndView mv = new ModelAndView();
        mv.addObject("name", name);
        mv.addObject("age", age);
        mv.setViewName("show");
        return mv;
    }

    3.4 整体参数接收

  • 将处理器方法的参数定义为一个对象,只要保证请求参数名与这个对象的属性同名即可。参数名称中不用写“对象.属性”的形式。
  • 定义PO类Student:
    public class Student {
    private String name;
    private int age;
    //setter and getter()
    //toString()
    }
  • 修改处理器类MyController:
    @RequestMapping("/register.do")
    public ModelAndView doRegister(Student student) {
        ModelAndView mv = new ModelAndView();
        mv.addObject("student", student);
        mv.setViewName("/WEB-INF/jsp/show.jsp");
        return mv;
    }
  • 修改show页面:
    服务端信息为: 
    Student = ${student }

    3.5 域属性参数的接收

  • 所谓域属性,即对象属性。当请求参数中的数据为某类对象域属性的属性值时,要求请求参数名为“域属性名.属性“。
  • 定义PO类:
    public class School {
    private String name;
    private  String address;
    //setter and getter()
    //toString()
    }   
    public class Student {
    private String name;
    private int age;
    private School school;
    // setter and getter()
    //toString()
    }
  • 修改index页面:
    姓名:
    年龄:
    学校:
    地址:

    3.6 路径变量@PathVariable

  • 对于处理器方法中所接收的请求参数,可以来自于请求中所携带的参数,也可以来自于请求的URI中所携带的变量,即路径变量。不过此时,需要借助于@PathVariable注解。
  • @PathVariable在不指定参数的情况下,默认其参数名,即路径变量名与用于接收其值的属性名相同。若路径变量名与用于接收其值的属性名不同,则@PathVariable可通过参数指出路径变量名称。
  • 修改index页面:不需要index页面中的表单。
    
    index page
    
  • 修改处理器MyController:
    @Controller    
    @RequestMapping("/test")    
    public class MyController {
    @RequestMapping("/{pname}/{age}/register.do")
    public ModelAndView doRegister(@PathVariable("pname") String name, @PathVariable int age) {
        ModelAndView mv = new ModelAndView();
        mv.addObject("myname", name);
        mv.addObject("myage", age);
        mv.setViewName("/WEB-INF/jsp/welcome.jsp");
        return mv;
    }   
    }
  • 浏览器地址栏中发出的请求:
    SSM框架之SpringMVC4专题3:SpringMVC注解式开发

    4 处理器方法的返回值

  • 使用@Controller注解的处理器的处理器方法,其返回值常用的有四种类型:
  • 第一种:ModelAndView;第二种:String;第三种:无返回值void;第四种:返回自定义类型对象。
  • 根据不同的情况,使用不同的返回值。

    4.1 返回ModelAndView

  • 若处理器方法处理完后,需要跳转到其他资源,且又要在跳转的资源间传递数据。此时处理器方法返回ModelAndView比较好。当然,若要返回ModelAndView,则处理器方法中需要定义ModelAndView对象。
  • 在使用时,若该处理器方法只是进行跳转而不传递数据,或者只是传递数据而并不向任何资源跳转(如对页面的Ajax异步响应),此时若返回ModelAndView,则将总是有一部分多余:要么Model多余,要么View多余,即此时返回ModelAndView将不合适。

    4.2 返回String

  • 处理器方法返回的字符串可以指定逻辑视图名,通过视图解析器解析可以将其转换为物理视图地址。

    4.2.1 返回内部资源逻辑视图名

  • 若要跳转的资源为内部资源,则视图解析器可以使用InternalResourceViewResolver内部资源视图解析器。此时处理器方法返回的字符串就是要跳转页面的文件名去掉文件扩展名后的部分。这个字符串与视图解析器中的prefix、suffix相结合,即可形成要访问的URI。
    
    
        
        
    
  • 直接修改处理器类MyController:
    @Controller    
    @RequestMapping("/test")    
    public class MyController {
    @RequestMapping("/register.do")
    public String register(HttpServletRequest request, Student student) {
        request.setAttribute("myStudent", student);
        return "welcome";
    }   
    }
  • 当然,也可以直接返回资源的物理视图名。不过,此时就不需要再在视图解析器中再配置前缀和后缀。
    @Controller    
    @RequestMapping("/test")    
    public class MyController {
    @RequestMapping("/register.do")
    public String register(HttpServletRequest request, Student student) {
        request.setAttribute("myStudent", student);
        return "/WEB-INF/jsp/welcome.jsp";
    }   
    }

    4.2.2 返回View对象名

  • 若要跳转的资源为外部资源,则视图解析器可以使用BeanNameViewResolver,然后在配置文件中再定义一些外部资源视图View对象,此时处理器方法返回的字符串就是要跳转资源视图View的名称。当然,这些视图View对象,可以是内部资源视图View对象。
  • 修改配置文件:
    
    
        
    
    
    
        
    
    
    
        
    
  • 修改处理器:
    @Controller    
    @RequestMapping("/test")    
    public class MyController {
    @RequestMapping("/register.do")
    public String doSome() {
        return "taobao";
    }   
    }

    4.3 返回void

  • 对于处理器方法返回void的应用场景,主要由两种:

    4.3.1 通过ServletAPI传递数据并完成跳转

  • 通过在处理器方法的参数中放入的SerlvetAPI参数,来完成资源跳转时所要传递的数据以及跳转。
  • 可在方法参数中放入HttpServletRequest或者是HttpSession,使方法中可以直接将数据放入到request、session的域中,也可以通过request.getServletContext()获取到ServletContext,从而将数据放入到application的域中。
  • 可在方法参数中放入HttpServletRequest与HttpServletResponse,使方法可以完成请求转发和重定向。注意,重定向时无法完成对/WEB-INF/下资源的访问的。
  • 请求转发:request.getRequestDispatcher("目标页面").forward(request, response);
  • 重定向:response.sendRedirect(“目标页面")
  • 修改处理器:
    @Controller    
    @RequestMapping("/test")    
    public class MyController {
    @RequestMapping("/register.do")
    public void register(HttpServletRequest request, 
                           HttpServletResponse response, Student student) throws Exception {
        request.setAttribute("myStudent", student);
        //请求转发
        request.getRequestDispatcher("/WEB-INF/jsp/show.jsp").forward(request, response);
    }   
    }
  • 修改SpringMVC配置文件:删除视图解析器的配置。因为处理器方法无视图返回,所以也就不需要视图解析器。
    
    

    4.3.2 AJAX响应

  • 若处理器对请求处理后,无需跳转到其他任何资源,此时可以让处理器方法返回void。例如对于AJAX的异步请求的响应。
  • 导入Jar包:由于本项目中服务器向浏览器返回的是JSON(JavaScript Object Notation, JS对象符号)数据,需要使用一个工具类将字符串包装为JSON格式,所以需要导入JSON的Jar包。
    SSM框架之SpringMVC4专题3:SpringMVC注解式开发_第5张图片
  • 引入jQuery库:由于要使用jQuery的ajax()方法提交AJAX请求,所以项目中需要引入jQuery的库。在WebRoot下新建一个Folder(文件夹),命名为js,并将jquery-1.8.3.js文件放入其中。
    SSM框架之SpringMVC4专题3:SpringMVC注解式开发_第6张图片
  • 当然,该jQuery库文件,需要在使用ajax()方法的index页面中引入。
    SSM框架之SpringMVC4专题3:SpringMVC注解式开发
  • 定义index页面:index页面由两部分内容构成:一个是
  • 修改处理器类MyController:处理器对于AJAX请求中所提交的参数,可以使用逐个接收的方式,也可以以对象的方式整体接收。只要保证AJAX请求参数与接收的对象类型属性同名即可。
  • 以对象方式整体接收:
    @Controller    
    @RequestMapping("/test")    
    public class MyController {
    @RequestMapping("/myAjax.do")
    public void doAjax(Student student, HttpServletResponse response) throws IOException {
        Map map = new HashMap();
        map.put("pname", student.getName());
        map.put("page", student.getAge());  
        JSONObject myJson = JSONObject.fromObject(map);
        String jsonStr = myJson.toString(); 
        PrintWriter out = response.getWriter();
        out.print(jsonStr);
        out.close();
    }
    }
  • 逐个接收
    @Controller    
    @RequestMapping("/test")    
    public class MyController {
    @RequestMapping("/myAjax.do")
    public void doAjax(String name, int age, HttpServletResponse response) throws IOException {
        Map map = new HashMap();
        map.put("pname", name);
        map.put("page", age);
        JSONObject myJson = JSONObject.fromObject(map);
        String jsonStr = myJson.toString();
        PrintWriter out = response.getWriter();
        out.print(jsonStr);
        out.close();
    }
    }
  • 删除视图页面:由于是服务器直接向浏览器发回数据,所以也就不需要视图页面了,所以需要删除WEB-INF中的jsp目录以及其中的show页面。

    4.4 返回Object

  • 处理器方法也可以返回Object对象。但是返回的这个Object对象不是作为逻辑视图出现的,而是作为直接在页面中显示的数据出现的。
  • 返回Object对象,需要使用@ResponseBody注解,将转换后的JSON数据放入到响应体中。

    4.4.1 环境搭建

  • 导入jar包:由于返回Object数据,一般都是将数据转换为了JSON对象后传递给浏览器页面的。而这个由Object转换为JSON,是由Jackson工具完成的。所以需要导入Jackson的相关jar包。
    SSM框架之SpringMVC4专题3:SpringMVC注解式开发_第7张图片
  • 注册注解驱动:将Object数据转化为JSON数据,需要由Http消息转换器HttpMessageConverter完成。而转换器的开启,需要由完成。
  • 当Spring容器进行初始化过程中,在处创建注解驱动时,默认创建了七个HttpMessageConverter对象。也就是说,我们注册,就是为了让容器为我们创建HttpMessageConverter对象。
    
    

    4.4.2 返回数值型对象

  • 修改index页面:
    
    
    
    
    
  • 修改处理器:在处理器方法上添加@ResponseBody注解,将数据放入到响应体中。
    @Controller    
    @RequestMapping("/test")    
    public class MyController {
    @RequestMapping("/myAjax.do")
    @ResponseBody
    public Object doAjax(){
        return 123.456;
    }
    }
  • 修改配置文件:在配置文件中注册mvc的注解驱动。
    
    
    
    

    4.4.3 返回字符串对象

  • 若要返回非中文字符串,将前面返回数值型数据的返回值直接修改为字符串即可。但是若返回的字符串中带有中文字符,则接受方页面将会出现乱码。此时需要使用@RequestMapping的produces属性执行字符集。
  • produces,产品,结果,即该属性用于设置输出结果类型。
  • 直接修改处理器类即可:
    @Controller    
    @RequestMapping("/test")    
    public class MyController {
    @RequestMapping(value="/myAjax.do", produces="text/html;charset=utf-8")
    @ResponseBody
    public Object doAjax(){
        return "China GuangZhou";
    }
    }

    4.4.4 返回自定义类型对象

  • 返回自定义类型对象时,不能够以对象的形式直接返回给客户端浏览器,而是将对象转换成JSON格式的数据发送给浏览器的。
  • 由于转换器底层使用Jackson转换方式将对象转换成JSON数据,所以需要导入Jackson的相关Jar包。
  • 定义PO类:
    public class Student {
    private String name;
    private int age;
    //setter and getter()
    //toString()
    }
  • 修改处理器MyController:
    @Controller    
    @RequestMapping("/test")    
    public class MyController {
    @RequestMapping(value="/myAjax.do", produces="text/html;charset=utf-8")
    @ResponseBody
    public Object doAjax(){
        Student student = new Student();
        student.setAge(23);
        student.setName("张三");
        return student;
    }
    }
  • 修改index页面:
    
    
    
    
    

    4.4.5 返回Map集合

  • 修改处理器类:
    @Controller    
    @RequestMapping("/test")    
    public class MyController {
    @RequestMapping("/myAjax.do")
    @ResponseBody   // 将返回的数据放入到响应体中
    public Object doAjax(){
        Map map = new HashMap();
        map.put("student1", new Student("张三", 23));
        map.put("student2", new Student("李四", 24));
        return map;
    }
    }
  • 修改index页面:
    
    
    
    
    

    4.4.5 返回List集合

  • 修改处理器MyController:
    @Controller    
    @RequestMapping("/test")    
    public class MyController {
    @RequestMapping("/myAjax.do")
    @ResponseBody   // 将返回的数据放入到响应体中
    public Object doAjax(){
        List list = new ArrayList<>();
        list.add(new Student("张三", 23));
        list.add(new Student("李四", 24));
        return list;
    }
    }
  • 修改index页面: