第三章 控制器的实现(一)

    控制器是一个服务层的接口,通过它来访问应用程序的业务逻辑。控制器解析用户输入并把它转换为模型最后通过视图将模型数据展示给用户。Spring对控制器的实现是在一个非常抽象的层面上,这使得我们在具体实现控制器时有了非常大的灵活性和自主权。Spring 2.5引入了基于注解的控制器编码模型,比如@RequestMapping@RequestParam@ModelAttribute等等。这种控制器的实现方式就无需继承一个基类或是实现一个接口,而且通常也不再直接依赖Servlet API,但是也仍然能够方便地使用Servlet API。下面是一个使用注解的控制器的简单示例:

@Controller

public class HelloWorldController {

    @RequestMapping("/helloWorld")

    public String helloWorld(Model model) {

        model.addAttribute("message""Hello World!");

        return "helloWorld";

    }

}


    从上面的例子可以看出,使用@Controller@RequestMapping注解使得方法定义更加灵活。上面的方法接收一个Model模型对象参数,返回一个字符串形式的视图名称。后面将会讲到对于方法的入参和出参可以有多种不同的形式。@Controller@RequestMapping以及其他相关的注解构成了Spring MVC的实现基础。本章将重点讲解这些注解的含义和使用。

3.1 使用@Controller注解定义一个控制器

    从@Controller注解的命名我们可以知道它定义了一个类充当控制器的角色。Spring MVC中,我们无需去继承一个基类或引用Servlet API来实现控制器,但是如果需要我们仍然可以使用Servlet特有的特性。使用@Controller注解实现了一个控制器后,DispatcherServlet就会扫描这些注解类找到@RequestMapping注解的方法来处理客户端的请求。我们可以显式地在上下文文件中定义控制器Bean,然而,通过@Controller这种注解,Spring提供了一种自动扫描的机制,扫描类路径中的组件类并自动注册这些Beans。为启动对注解类的自动扫描,我们需要在上下文文件中进行配置。这里需要用到spring-context模式。下面为一个示例:

<?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:p="http://www.springframework.org/schema/p"

xmlns:context="http://www.springframework.org/schema/context"

xsi:schemaLocation="

    http://www.springframework.org/schema/beans

    http://www.springframework.org/schema/beans/spring-beans.xsd

    http://www.springframework.org/schema/context

    http://www.springframework.org/schema/context/spring-context.xsd">

    <context:component-scan base-package="org.springframework.samples.petclinic.web"/>

    <!-- ... -->

</beans>

3.2 使用@RequestMapping注解映射客户端请求

    使用@RequestMapping注解来将诸如/appointmentsURL请求映射到某个控制器类或处理方法。通常地,类层面的注解会将请求路径或路径模式映射到一个表单控制器,而方法层面的注解则会进一步过滤请求,比如根据HTTP请求的方式(“GET”或“POST”请求等)或请求参数的条件。下面是一个典型的示例:

@Controller

@RequestMapping("/appointments")

public class AppointmentsController {

    private final AppointmentBook appointmentBook;

    @Autowired

    public AppointmentsController(AppointmentBook appointmentBook) {

        this.appointmentBook = appointmentBook;

    }

    @RequestMapping(method = RequestMethod.GET)

    public Map<String, Appointment> get() {

        return appointmentBook.getAppointmentsForToday();

    }

    @RequestMapping(value="/{day}", method = RequestMethod.GET)

    public Map<String, Appointment> getForDay(@PathVariable @DateTimeFormat(iso=ISO.DATE) Date day, Model model) {

        return appointmentBook.getAppointmentsForDay(day);

    }

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

    public AppointmentForm getNewForm() {

        return new AppointmentForm();

    }

    @RequestMapping(method = RequestMethod.POST)

    public String add(@Valid AppointmentForm appointment, BindingResult result) {

        if (result.hasErrors()) {

            return "appointments/new";

        }

        appointmentBook.addAppointment(appointment);

        return "redirect:/appointments";

    }

}

    上面的例子中,多个地方使用到了@RequestMapping注解。第一个地方是在类上,表明该控制器类中的所有处理方法将会处理相对于/appointments路径的请求。其中的get()方法只会接收GET类型的请求,即以/appointments结尾的HTTP GET请求将会调用该方法,post()方法与此类似。getNewForm()方法则综合了过滤HTTP请求方式和请求路径两种方式,因此GET类型的以appointments/new结尾的请求将会调用该方法处理。getForDay()方法展示了@RequestMapping注解的一种URI模板参数的用法,后面将会详细说明。@RequestMapping注解并非必须使用在类层面上,没有的话,则所有的请求路径都是绝对的,而不是相对的了。此时,该控制器就相当于一个多动作的控制器,相当于Struts中的多个Action。下面即为一个例子:

@Controller

public class ClinicController {

    private final Clinic clinic;

    @Autowired

    public ClinicController(Clinic clinic) {

        this.clinic = clinic;

    }

    @RequestMapping("/")

    public void welcomeHandler() {

    }

    @RequestMapping("/vets")

    public ModelMap vetsHandler() {

        return new ModelMap(this.clinic.getVets());

    }

}

URI模板参数

    通过@RequestMapping注解的URI模板参数我们可以访问到URL中的信息。所谓的URI模板指的是一个类似URI的字符串,但是包括了一个或多个变量名。当把这些变量名替换为实际值时,URI模板便成了标准的URI。比如,http://www.example.com/users/{userId}便是一个URI模板,它包含了一个变量名userId,当我们为变量userId赋上一个值fredhttp://www.example.com/users/fred,这时它就成了一个实际的URI。在Spring MVC中,我们可以通过给方法的参数加上@PathVariable注解来将它与URI模板变量的值进行绑定。如下面的示例:

@RequestMapping(value="/owners/{ownerId}", method=RequestMethod.GET)

public String findOwner(@PathVariable String ownerId, Model model) {

    Owner owner = ownerService.findOwner(ownerId);

    model.addAttribute("owner", owner);

    return "displayOwner";

}

    在上面的例子中,“/owners/{ownerId}”便是一个URI模板,它有个变量名ownerId,当该方法所在的控制器处理这个路径的请求时,方法入参ownerId的值便会从URI路径中获得,比如对于“/owners/fred”这样的请求,ownerId的值便会为fred

    为解析@PathVariable注解,Spring MVC需要通过名称来找到匹配的URI模板变量,我们可以为@PathVariable注解附加上一个参数来显式地指明,如下所示:

@RequestMapping(value="/owners/{ownerId}", method=RequestMethod.GET)

public String findOwner(@PathVariable("ownerId") String theOwner, Model model) {

    // implementation omitted

}

    或者,如果URI模板变量名与方法参数名一致,则可以省略。但这样做有个限制,就是编译时必须开启了调试信息才可以。因此不建议这样做。方法可以有多个加上了@PathVariable注解的参数,如下面的代码所示:

@RequestMapping(value="/owners/{ownerId}/pets/{petId}", method=RequestMethod.GET)

public String findPet(@PathVariable String ownerId, @PathVariable String petId, Model model) {

    Owner owner = ownerService.findOwner(ownerId);

    Pet pet = owner.getPet(petId);

    model.addAttribute("pet", pet);

    return "displayPet";

}

    当@PathVariable注解被用在一个Map<String, String>类型的参数上时,该map就会被填充上所有的URI模板变量键值对。URI模板会汇总类和方法两个层面变量的值,比如下面的例子:

@Controller

@RequestMapping("/owners/{ownerId}")

public class RelativePathUriTemplateController {

    @RequestMapping("/pets/{petId}")

    public void findPet(@PathVariable String ownerId, @PathVariable String petId, Model model) {

        // implementation omitted

    }

}

    对于“/owners/42/pets/21”这样的URL请求将会调用findPet()方法,此时参数ownerIdpetId会分别从类层面的URI模板变量ownerId和方法层面的URI模板变量petId中获取到值。使用@PathVariable注解的参数可以为任何简单类型,如intlongDate等。框架会自动进行类型转化,如果转化不了则会抛出TypeMismatchException。我们可以自定义复杂类型的转化,这一点将会在后面的章节中详细说明。

URI模板模式中使用正则表达式

    有时我们需要对URI模板变量进行更精确的定义,考虑“/spring-web/spring-web-3.0.5.jar”这个URL请求该如何拆解?

    @RequestMapping注解提供了对在URI模板变量中使用正则表达式的支持,语法格式为{varName:regex},冒号左侧的部分定义了变量名,右侧则为正则表达式,比如下面的例子:

@RequestMapping("/spring-web/{symbolicName:[a-z-]}-{version:\\d\\.\\d\\.\\d}{extension:\\.[a-z]}")

    public void handle(@PathVariable String version, @PathVariable String extension) {

        // ...

    }

}

路径模式

    除了URI模板,@RequestMapping注解还支持Ant风格的路径模式,例如“/myPath/*.do”。当然这两者同时使用也是支持的,比如“/owners/*/pets/{petId}”。

模式中使用占位符

    在@RequestMapping注解中使用的模式还支持以${…}这种占位符的形式关联本应用的属性文件或系统属性文件或环境变量。当需要通过配置文件来自定义请求路径时,我们就可能需要使用到占位符。关于占位符的更多信息,请参考PropertyPlaceholderConfigurer类的API文档。



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值