简介
Spring MVC 属于Spring Framework项目中的一部分。主要用于组建MVC结构的项目。比较类似于Struts的功能。而且Spring MVC对于目前比较流行的RESTful架构风格支持的也是比较好的。
官方网站:http://projects.spring.io/spring-framework/
原理
DispatcherServlet类是spring mvc的核心控制器,负责分发和响应用户请求。
DispatcherServlet请求处理工作流程如下图所示:
1、用户将request请求发送至前端控制器(Front controller)
2、前端控制器将请求转发至具体的业务控制器(Controller)
3、业务控制器创建业务逻辑实现(Service)和数据
4、业务控制器(Controller)将结果返回给前端控制器(Front controller)
5、前端控制器(Front controller)选择展示模板(View template)
6、最后将结果通过前端控制器(Front controller)返回给客户
DispatcherServlet继承于HttpServlet,所以它拥有HttpServlet所有特征。
加载依赖
由于本人惯用spring boot进行项目构建,所以配置默认都是spring boot的方式
只要在项目中依赖springframework的包就可以使用Spring MVC
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.9.RELEASE</version>
</dependency>
</dependencies>
如果你使用spring boot来搭建项目,那么是不需要以上依赖的,因为springboot的依赖已经包含他了
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
<version>1.3.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
DispatcherServlet拦截配置
DispatcherServlet的url-pattern默认情况下为“/”根路径,所有的url请求都会被DispatcherServlet处理。但是我们可以对url-pattern进行配置。
在spring boot的配置文件application.properties中进行配置
#拦截所有以.do结尾的路径
server.servlet-path=*.do
#拦截所有springmvc目录下的路径
server.servlet-path=/springmvc/*
这里需要注意,项目中的RequestMapping请求路径不能包含拦截内容。比如 拦截路径为“server.servlet-path=/springmvc/*“,RequestMapping为“/index”,那么请求路径为http://localhost:8090/springmvc/index
注解
@Controller
此注解声明了一个Controller层的类,使用此注解的类会被识别为一个控制器。
@Controller
public class StartController {
}
在此例子中我们不用将类声明为一个bean,因为spring boot在初始化时使用了@ComponentScan注解进行自动扫描并注入。
@RestController
此注解声明一个类为Restful风格的控制器。其实就是@Controller和@ResponseBody两个注解叠加的效果。
@RestController
public class StartController {
@PostMapping(value = "/login1")
public User login1(@RequestBody User user1) {
User user = new User();
user.setUsername(user1.getUsername());
user.setPassword(user1.getPassword());
return user;
}
}
- @RestController注释的类中,所有的方法默认添加@ResponseBody,会将内容放在http主体中返回。
@RequestMapping
此注解声明了一个可访问的资源映射,url可以通过RequestMapping配置的映射路径来访问这个方法。
参数:
value(默认) | 访问该资源的URI路径。资源路径支持通配符访问。(如例5) 此参数可以不写则为默认访问。(如例1) 该参数可以设置URI形式的入参,入参使用{}进行表示。在方法参数中使用@PathVariable可以将URI参数映射给一个方法入参。(如例4) |
method | 指定http的提交类型。一般使用RequestMethod类来指定类型。 常用的基本上就是RequestMethod.GET和RequestMethod.POST。(如例2、3) |
consumes | 指定处理请求的提交内容类型(Content-Type),例如application/json, text/html; |
produces | 指定返回的内容类型,仅当request请求头中的(Accept)类型中包含该指定类型才返回;(如例6) 在做接口时往往使用此参数指定接口出参数据结构类型。 |
params | 指定request中必须包含某些参数值是,才让该方法处理。 |
headers | 指定request中必须包含某些指定的header值,才能让该方法处理请求。 |
括号内只写参数值,则代表此参数是默认参数的值。
例子:
@RequestMapping("/home")
public class StartController {
@RequestMapping
public String index(Model model) {
return "index";
}
@RequestMapping(value = "/login",method = RequestMethod.GET)
public String login(Model model) {
return "login";
}
@RequestMapping(value = "/login",method = RequestMethod.POST)
public String loginPost(Model model) {
return "login";
}
@RequestMapping(value = "/{parameter}",method = RequestMethod.GET)
public String parameter(@PathVariable("parameter") String urlParameter, Model model) {
return "login";
}
@RequestMapping(value = "/*/login",method = RequestMethod.GET)
public String login(Model model) {
return "login";
}
@RequestMapping(value = "/login1", method = RequestMethod.POST, produces = MediaTypes.JSON_UTF_8)
public String login1(Model model) {
return "login";
}
}
- RequestMapping可以标识一个类或一个方法,在类上时代表这个类内所有资源都在该路径下。在方法上时代表该方法的访问路径。如例2的路径为“/home/login”
- RequestMapping有一些变种注解,如@GetMapping、@PostMapping等。此类注解省去了method参数。@GetMapping相当于@RequestMapping(method = RequestMethod.GET)
@PathVariable
此注解用于获取URI形式的参数,参数作为路径的一部分被传递过来。
参数:
value(默认) | URI形式参数的名称。也就是@RequestMapping注解使用“{}”来标注的参数名称。 此参数不写,则要求方法入参名称与URI参数名称一致。(如例3) |
括号内只写参数值,则代表此参数是默认参数的值。(如例二)
例子:
@RequestMapping(value = "/{parameter}")
public String parameter(@PathVariable(value = "parameter") String urlParameter, Model model) {
return "login";
}
@RequestMapping(value = "/{parameter}")
public String parameter(@PathVariable("parameter") String urlParameter, Model model) {
return "login";
}
@RequestMapping(value = "/{parameter}")
public String parameter(@PathVariable String parameter, Model model) {
return "login";
}
- 更多关于URI参数的说明请参考官方文档:http://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#mvc-ann-requestmapping-uri-templates
@RequestParam
获得request请求参数并绑定到方法参数上,get请求时使用此注解获取参数。
参数:
value(默认) | 请求参数名称。 此参数不写,则要求方法入参名称与请求参数名称一致。(如例2) |
required | 是否必输。默认情况下是true必输,如果希望那个参数为非必输就设置成false(如例3) |
括号内只写参数值,则代表此参数是默认参数的值。(如例1)
例子:
//以下例子的url参数为?username=andy&password=111111
@GetMapping(value = "/login1")
public String login1(@RequestParam("username") String username ,@RequestParam("password") String password) {
return "login";
}
@GetMapping(value = "/login2")
public String login2(@RequestParam String username ,@RequestParam String password) {
return "login";
}
@GetMapping(value = "/login3")
public String login3(@RequestParam String username ,@RequestParam(required=false) String password) {
return "login";
}
@GetMapping(value = "/login4")
public String login4(@RequestParam Map user) {
return "login";
}
- @RequestParam的参数可以使用map接收,url的入参名和值,会以键值对的形式存储在map中(如例4)
@RequestHeader
获取request头信息。
参数:
value(默认) | 请求头参数名称。 此参数不写,则要求方法入参名称与请求头参数名称一致。(如例2) |
required | 是否必输。默认情况下是true必输,如果希望那个参数为非必输就设置成false(如例5) |
括号内只写参数值,则代表此参数是默认参数的值。(如例1)
例子:
@GetMapping(value = "/login1")
public String login1(@RequestHeader("Host") String host) {
return "login";
}
@GetMapping(value = "/login2")
public String login2(@RequestHeader String Host) {
return "login";
}
@GetMapping(value = "/login3")
public String login3(@RequestHeader("Accept-Encoding") List<String> encoding ) {
return "login";
}
@GetMapping(value = "/login4")
public String login4(@RequestHeader Map<String,String> header) {
return "login";
}
@GetMapping(value = "/login5")
public String login5(@RequestHeader(value = "Host",required=false) String host) {
return "login";
}
- @RequestHeader可以使用list接收一组用逗号分割的值(如例3)。如“gzip,deflate” 就会被list拆分接收。
- @RequestHeader可以使用Map来接收所有的头信息,头信息会以键值对的形式存储在map中(如例4)。
@RequestBody
获取request请求,http主体内的参数,post请求时使用此注解获取参数。
参数:
required | 是否必输。默认情况下是true必输,如果希望那个参数为非必输就设置成false(如例2) |
例子:
@PostMapping(value = "/login1")
public String login1(@RequestBody User user) {
return "login";
}
@PostMapping(value = "/login2")
public String login2(@RequestBody(required=false) User user) {
return "login";
}
- @RequestBody将http主体中的数据转换到入参对象中(如例1)。
- http主体的形式由http Content Type定义。一般为application/json或application/xml
- @RequestBody入参接收对象中的属性名要求与http主体中的变量名一致。如:User类定义了usernam和password两个属性,那么json 形式的http body就应该写成{"username":"myuser","password":"111111"}
@ResponseBody
将响应内容直接放在http主体中返回。通常的@Controller方法返回的是视图名称,并跳转到这个视图。
@PostMapping(value = "/login1")
@ResponseBody
public User login1(@RequestBody User user1) {
User user = new User();
user.setUsername(user1.getUsername());
user.setPassword(user1.getPassword());
return user;
}
- @ResponseBody经常用于ajax请求和接口方法。因为ajax请求和接口方法要求直接返回内容而不是跳转到某一视图。
@SessionAttributes(不常用)
用于获取session中的参数。
@SessionAttributes作用于类的时候,会在方法执行结束时将对应名字的model内容存储到session中。(如例1)
@SessionAttributes作用于参数的时候,会取出session中对应名字的内容,赋值给当前的参数。(如例2)
参数:
value(默认) | session中的参数名称。 此参数不写,则要求方法入参名称与请求参数名称一致。(如例3) |
括号内只写参数值,则代表此参数是默认参数的值。(如例2)
例子:
@Controller
@RequestMapping(value = "/home")
@SessionAttributes("username")
public class StartController {
@PostMapping(value = "/login1")
public String login1(HttpSession httpSession,Model model) {
model.addAttribute("username","woshicanshu");
return "login";
}
@PostMapping(value = "/login2")
public String login2(@SessionAttribute("user") User user, HttpSession httpSession) {
return "login";
}
@PostMapping(value = "/login3")
public String login3(@SessionAttribute User user, HttpSession httpSession) {
return "login";
}
}
@RequestAttribute(不常用)
获取request的参数。@RequestAttribute作用于参数的时候,会取出request中对应名字的内容,赋值给当前的方法入参。
参数:
value(默认) | request中的参数名称。 此参数不写,则要求方法入参名称与请求参数名称一致。(如例2) |
括号内只写参数值,则代表此参数是默认参数的值。(如例1)
例子:
@PostMapping(value = "/login1")
public String login1(@RequestAttribute("username") String username,HttpRequest request) {
return "login";
}
@PostMapping(value = "/login2")
public String login2(@RequestAttribute String username,HttpRequest request) {
return "login";
}
@ModelAttribute(不常用)
将参数保存到Model模型中,供视图页面使用。
- 作用于参数时表示这个参数保存到model中。(如例1)
- 作用于方法时表示将这个方法的出参保存到model中,此方法会在执行任意一个@RequestMapping之前执行一次。(如例2)
参数:
value(默认) | model中的参数名称。 |
括号内只写参数值,则代表此参数是默认参数的值。(如例1)
例子:
//本例请求参数为 ?username=pltx
@GetMapping(value = "/login1")
public String login1(@ModelAttribute("username") String username,Model model) {
return "login";
}
@ModelAttribute("password")
public String login2(String username) {
return "000000";
}
@RequestPart(不常用)
用于获取使用multipart/form-data格式传递的http请求的请求体,通常用于获取上传文件。
@Autowired
用于在类中注入bean。
@Autowired
private Activity2CompanyDao activity2CompanyDao;
@Repository
用于声明此类为一个数据库持久化类(DAO层)。
@Repository
public class Activity2ConsumerCouponDaoImpl implements Activity2ConsumerCouponDaoCustom{
}
@Service
用于声明一个业务层的类。
@Service
public class Activity2ConsumerService {
}
Controller层
Controller层是项目的转向控制层,主要负责接收请求、转发请求到业务层(service)进行处理、将处理结果进行响应、控制页面跳转等职责。
需要注意涉及到业务逻辑的代码尽量不要在Controller进行编写,将业务逻辑合理的封装在service层对项目的结构和复用性都是非常有益的。
Controller层主要常用的注解有@Controller、@RequestMapping、@Autowired、@RequestParam、@RequestHeader、@RequestBody、@ResponseBody等。
默认绑定的参数
Controller层的方法入参可以获得默认绑定的类的实例,这实例我们可以通过形参的声明直接使用。其中主要包括HttpServletRequest、HttpservletResponse、HTTPSession、Model、ModelMap以及他们的衍生类。
HttpServletRequest
此参数可以获得一个HttpServletRequest实例。
@RequestMapping("/unauthorized")
public String unauthorized(HttpServletRequest request, HttpServletResponse response) {
}
HttpServletResponse
此参数可以获得一个HttpservletResponse实例。
@RequestMapping("/unauthorized")
public String unauthorized(HttpServletRequest request, HttpServletResponse response) {
}
HTTPSession
此参数可以获得一个HTTPSession实例。
@RequestMapping("/unauthorized")
public String unauthorized(HTTPSession session) {
}
Model/ModelMap
Model用于将模型数据传递到展示层,保存在Model中的数据可以直接在页面使用获取。
@RequestMapping(value = "/list", method = RequestMethod.GET)
public String list(String name, Integer openType, Model model) {
model.addAttribute("name", name);
model.addAttribute("openType", openType);
return "/activity2_company/list";
}
RedirectAttributes
model的Attribute参数传递的声明周期为一次请求。所以如果方法采用重定向到其他资源则Attribute保存的参数将会丢失,在页面是无法获取的。如果想要在重定向后在页面获取参数则需要使用 RedirectAttributes进行保存。
下面的例子在login页面仍然可以拿到message参数值。
@RequestMapping(value = "/logout", method = RequestMethod.GET)
public String logout(RedirectAttributes redirectAttributes) {
redirectAttributes.addFlashAttribute("message", "您已安全退出");
return "redirect:login";
}
@RequestMapping(value = "/login", method = RequestMethod.GET)
public String loginInput() {
return "login";
}
RedirectAttributes中有两个添加参数的方法addAttribute和addFlashAttribute ,addAttribute参数会被放在url中进行传递,所以长度和类型会受到限制。而addFlashAttribute 参数放在flashmap内部对象中传递,对于长度和类型没有限制。
在重定向的方法中如果想要拿到,前面方法存入的addAttribute参数,只要在重定向方法入参加入这个参数即可
@GetMapping(value = "/login")
public String login(RedirectAttributes redirectAttributes) {
redirectAttributes.addAttribute("username","andy");
return "redirect:login1";
}
@GetMapping(value = "/login1")
public String login1(String username) {
System.out.println(username);
return "login";
}
在重定向的方法中如果想要拿到,前面方法存入的addFlashAttribute参数,则需要使用@ModelAttribute注解
@GetMapping(value = "/login")
public String login(RedirectAttributes redirectAttributes) {
redirectAttributes.addFlashAttribute("username","andy");
return "redirect:login1";
}
@GetMapping(value = "/login1")
public String login1(@ModelAttribute("username") String username) {
System.out.println(username);
return "login";
}
响应方式
返回到页面
Controller方法的出参直接写视图名称,则代表执行结束后调转到此视图。(加ResponseBody不行)
如下例将访问login页面
@GetMapping(value = "/login")
public String login() {
return "login";
}
跳转到其他Controller
Controller方法的出参可以通过增加forward和redirect前缀的方式进行转发或重定向到,其他Controller。
@GetMapping(value = "/login")
public String login() {
return "login";
}
@GetMapping(value = "/login1")
public String login1() {
return "redirect:login";
}
@GetMapping(value = "/login2")
public String login2() {
return "forward:login";
}
强调一下forward和redirect区别。forward是服务器转发,浏览器地址不变,服务器将请求的新地址内容返回给刘蓝鳍。redirect是浏览器重定向,浏览器地址改变,服务器告诉浏览器要访问新的地址后浏览器重新访问新地址。
输出到页面
我们可以使用HttpServletResponse对象将内容直接输出到页面
@GetMapping(value = "/login")
public void login(HttpServletResponse response) {
try {
response.setHeader("Content-type", "text/html;charset=UTF-8");
response.getWriter().println("<h1>我是标题</h1>");
} catch (IOException e) {
e.printStackTrace();
}
}