Controller注解类型
在Spring MVC中,使用org.springframework.stereotype.Controller注解类型声明某类的实例是一个控制器。
RequestMapping注解类型 在基于注解的控制器类中,可以为每个请求编写对应的处理方法。需要使用org.springframework.web.bind.annotation.RequestMapping注解类型将请求与处理方法一一对应。 1.方法级别注解 @RequestMapping(value = "/index/login") public String login() {} 注解的value属性将请求URI映射到方法,value属性是RequestMapping注解的默认属性,如果就一个value属性,则可省略该属性。可以使用如下URL访问login方法(请求处理方法)。 http://localhost:xxx/yyyy/index/login
2 类级别注解 @Controller @RequestMapping("/index") public class IndexController { @RequestMapping("/login") public String login() { return "login"; } @RequestMapping("/register") public String register() { return "register"; } } 在类级别注解的情况下,控制器类中的所有方法都将映射为类级别的请求。可以使用如下URL访问login方法。 http://localhost:xxx/yyy/index/login
编写请求处理方法 1.请求处理方法中常出现的参数类型 Servlet API、输入输出流、表单实体类、注解类型、Model等Java类型。 public String login(HttpSession session, HttpServletRequest request) 2.请求处理方法常见的返回类型 最常见的返回类型,就是代表逻辑视图名称的String类型。除了String类型外,还有Model、View以及其他任意的Java类型。 public String register(Model model) {}
Controller接收请求参数的常见方式 Controller接收请求参数的方式有很多种,有的适合get请求方式,有的适合post请求方式,有的两者都适合。 1.通过实体bean接收请求参数 通过一个实体bean来接收请求参数,适用于get和post提交请求方式。需要注意的是,bean的属性名称必须与请求参数名称相同。 2.通过处理方法的形参接收请求参数 通过处理方法的形参接收请求参数,也就是直接把表单参数写在控制器类相应方法的形参中,即形参名称与请求参数名称完全相同。该接收参数方式适用于get和post提交请求方式。
3.通过@RequestParam接收请求参数 通过@RequestParam接收请求参数,适用于get和post提交请求方式。通过@RequestParam接收请求参数与“通过处理方法的形参接收请求参数”的区别是:当请求参数与接收参数名不一致时,“通过处理方法的形参接收请求参数”不会报400错误,而“通过@RequestParam接收请求参数”会400错误。 4.通过@ModelAttribute接收请求参数 @ModelAttribute注解放在处理方法的形参上时,用于将多个请求参数封装到一个实体对象,从而简化数据绑定流程,而且自动暴露为模型数据用于视图页面展示时使用。而“通过实体bean接收请求参数”只是将多个请求参数封装到一个实体对象,并不能暴露为模型数据(需要使用model.addAttribute语句才能暴露为模型数据)
重定向与转发
重定向是将用户从当前处理请求定向到另一个视图(如JSP)或处理请求,以前的请求(request)中存放的信息全部失效,并进入一个新的request作用域;
转发是将用户对当前处理的请求转发给另一个视图或处理请求,以前的request中存放的信息不会失效。
转发是服务器行为,重定向是客户端行为。具体工作流程如下:
转发过程:客户浏览器发送http请求,Web服务器接受此请求,调用内部的一个方法在容器内部完成请求处理和转发动作,将目标资源发送给客户;在这里,转发的路径必须是同一个Web容器下的URL,其不能转向到其他的Web路径上去,中间传递的是自己的容器内的request。在客户浏览器的地址栏中显示的仍然是其第一次访问的路径,也就是说客户是感觉不到服务器做了转发的。转发行为是浏览器只做了一次访问请求。
重定向过程:客户浏览器发送http请求,Web服务器接受后发送302状态码响应及对应新的location给客户浏览器,客户浏览器发现是302响应,则自动再发送一个新的http请求,请求URL是新的location地址,服务器根据此请求寻找资源并发送给客户。在这里location可以重定向到任意URL,既然是浏览器重新发出了请求,则就没有什么request传递的概念了。在客户浏览器的地址栏中显示的是其重定向的路径,客户可以观察到地址的变化。重定向行为是浏览器做了至少两次的访问请求。
在Spring MVC框架中,控制器类中处理方法的return语句默认就是转发实现,只不过实现的是转发到视图。
//转发到一个请求方法(同一个控制器类里,可省略/index/) return "forward:/index/isLogin"; //重定向到一个请求方法 return "redirect:/index/isRegister"; //转发到一个视图 return "register"; 在Spring MVC框架中,不管重定向或转发,都需要符合视图解析器的配置,如果直接重定向到一个不需要DispatcherServlet的资源,如: return "redirect:/html/my.html"; 在Spring MVC配置文件中,需要使用mvc:resources配置: <mvc:resources location="/html/" mapping="/html/**"></mvc:resources> 在Spring MVC配置类中,需要实现WebMvcConfigurer的接口方法public void addResourceHandlers(ResourceHandlerRegistry registry),示例代码如下: @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler("/html/**").addResourceLocations("/html/"); }
应用@Autowired进行依赖注入 Spring MVC框架本身就是一个非常优秀的MVC框架,它具有一个依赖注入的优点。可以通过org.springframework.beans.factory.annotation.Autowired注解类型将依赖注入到一个属性(成员变量)或方法,如: @Autowired public UserService userService;
在Spring MVC中,为了能被作为依赖注入,服务层的类必须使用org.springframework.stereotype.Service注解类型注明为@Service(一个服务)。另外,还需要在配置文件中使用<context:component-scan base-package="基本包"/>元素或者在配置类中使用@ComponentScan("基本包")来扫描依赖基本包。 “登录”和“注册”的业务逻辑处理分离出来,使用Service层实现。 首先,创建service包,在包中创建UserService接口和UserServiceImpl实现类。 其次,将配置类中@ComponentScan("controller")修改如下: @ComponentScan(basePackages = {"controller","service"})//扫描基本包 最后,修改控制器类UserController,具体代码如下: public class UserController { //将服务层依赖注入到属性userService @Autowired public UserService userService;
@ModelAttribute 通过org.springframework.web.bind.annotation.ModelAttribute注解类型,可经常实现如下两个功能: 1.绑定请求参数到实体对象(表单的命令对象) public String register(@ModelAttribute("user") UserForm user) {} “@ModelAttribute(”user“) UserForm user”语句的功能有两个,一是将请求参数的输入封装到user对象中;一是创建UserForm实例,以“user”为键值存储在Model对象中,与“model.addAttribute(”user“, user)”语句功能一样。如果没有指定键值,即“@ModelAttribute UserForm user”,那么创建UserForm实例时,以“userForm”为键值存储在Model对象中,与“model.addAttribute(”userForm“, user)”语句功能一样。 2.注解一个非请求处理方法 被@ModelAttribute注解的控制器的一个非请求处理方法,将在每次调用该控制器类的请求处理方法前被调用。
表单标签库 表单标签库中包含了可以用在JSP页面中渲染HTML元素的标签。JSP页面使用Spring表单标签库时,必须在JSP页面开头处声明taglib指令,指令代码如下: <%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %> 表单标签库中有form、input、password、hidden、textarea、checkbox、checkboxes、radiobutton、radiobuttons、select、option、options、errors。
表单标签 表单标签,语法格式如下: <form:form modelAttribute="xxx" method="post" action="xxx"> …… </form:form> 除了具有HTML表单元素属性外,表单标签还有具有acceptCharset、commandName、cssClass、cssStyle、htmlEscape和modelAttribute等属性。 其中,commandName和modelAttribute属性功能基本一致,属性值绑定一个JavaBean对象。
input标签 input标签,语法格式如下: <form:input path="xxx"/> 该标签除了cssClass、cssStyle、htmlEscape属性外,还有一个最重要的属性path。path属性将文本框输入值绑定到form backing object的一个属性。
password标签 password标签,语法格式如下: <form:password path="xxx"/> 该标签与input标签用法完全一致
hidden标签 hidden标签,语法格式如下: <form:hidden path="xxx"/> 该标签与input标签用法基本一致,只不过它不可显示,不支持cssClass和cssStyle属性。
textarea标签 textarea基本上就是一个支持多行输入的input元素,语法格式如下: <form:textarea path="xxx"/> 该标签与input标签用法完全一致
checkbox标签 checkbox标签,语法格式如下: <form:checkbox path="xxx" value="xxx"/> 多个path相同的checkbox标签,它们是一个选项组,允许多选。选项值绑定到一个数组属性。 <form:checkbox path="friends" value="张三"/>张三 <form:checkbox path="friends" value="李四"/>李四 <form:checkbox path="friends" value="王五"/>王五 <form:checkbox path="friends" value="赵六"/>赵六 上述示例代码中复选框的值绑定到一个字符串数组属性friends(String[] friends)。
checkboxes标签
checkboxes标签渲染多个复选框,是一个选项组,等价于多个path相同的checkbox标签。它有3个非常重要的属性:items、itemLabel和itemValue。
items:用于生成input元素的Collection、Map或Array。
itemLabel:items属性中指定的集合对象的属性,为每个input元素提供label。
itemValue:items属性中指定的集合对象的属性,为每个input元素提供value。
checkboxes标签语法格式如下:
<form:checkboxes items="xxx" path="xxx"/>
<form:checkboxes items="${hobbys}" path="hobby" />
上述示例代码,是将model属性hobbys的内容(集合元素)渲染为复选框。itemLabel和itemValue缺省情况下,如果集合是数组,复选框的label和value相同;如果是Map集合,复选框的label是Map的值(value),复选框的value是Map的关键字(key)。
radiobutton标签 radiobutton标签,语法格式如下: <form:radiobutton path="xxx" value="xxx"/> 多个path相同的radiobutton标签,它们是一个选项组,只允许单选。
radiobuttons标签 radiobuttons标签渲染多个radio,是一个选项组,等价于多个path相同的radiobutton标签。radiobuttons标签,语法格式如下: <form:radiobuttons path="xxx" items="xxx"/> 该标签的itemLabel和itemValue属性与checkboxes标签的itemLabel和itemValue属性完全一样,但只允许单选。 <form:radiobuttons path="xxx" items="xxx"/> 该标签的itemLabel和itemValue属性与checkboxes标签的itemLabel和itemValue属性完全一样,但只允许单选。
select标签 select标签的选项可能来自其属性items指定的集合,或者来自一个嵌套的option标签或options标签。语法格式如下: <form:select path="xxx" items="xxx" /> 或 <form:select path="xxx" items="xxx" > <option value="xxx">xxx</option> </ form:select> 或 <form:select path="xxx"> <form:options items="xxx"/> </form:select> 该标签的itemLabel和itemValue属性与checkboxes标签的itemLabel和itemValue属性完全一样。
options标签
options标签生成一个select标签的选项列表。因此,需要与select标签一同使用,具体用法参见select标签。
errors标签 errors标签渲染一个或者多个span元素,每个span元素包含一个错误消息。它可以用于显示一个特定的错误消息,也可以显示所有错误消息。语法如下: <form:errors path="*"/> 或 <form:errors path="xxx"/> 其中,“*”表示显示所有错误消息;“xxx”表示显示由“xxx”指定的特定错误消息。
package pojo; public class UserForm { private String uname;// 与请求参数名称相同 private String upass; private String reupass; public String getUname() { return uname; } public void setUname(String uname) { this.uname = uname; } public String getUpass() { return upass; } public void setUpass(String upass) { this.upass = upass; } public String getReupass() { return reupass; } public void setReupass(String reupass) { this.reupass = reupass; } }
package interceptor; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView; public class MyInteceptor implements HandlerInterceptor { /** * 重写preHandle方法在请求发生前执行 */ @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { System.out.println("preHandle方法在请求发生前执行"); return true; } /** * 重写postHandle方法在请求完成后执行 */ @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { System.out.println("postHandle方法在请求完成后执行"); } }
package config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.web.multipart.MultipartResolver; import org.springframework.web.multipart.commons.CommonsMultipartResolver; import org.springframework.web.servlet.config.annotation.EnableWebMvc; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; import org.springframework.web.servlet.view.InternalResourceViewResolver; import interceptor.MyInteceptor; @Configuration @EnableWebMvc @ComponentScan(basePackages = { "controller", "service" }) public class SpringMVCConfig implements WebMvcConfigurer { /** * 配置视图解析器 */ @Bean public InternalResourceViewResolver getViewResolver() { InternalResourceViewResolver viewResolver = new InternalResourceViewResolver(); viewResolver.setPrefix("/WEB-INF/jsp/"); viewResolver.setSuffix(".jsp"); return viewResolver; } /** * 配置静态资源 */ @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler("/html/**").addResourceLocations("/html/"); // addResourceHandler指的是对外暴露的访问路径 // addResourceLocations指的是静态资源存放的位置 } /** * 配置拦截器Bean */ @Bean public MyInteceptor myInteceptor() { return new MyInteceptor(); } /** * 重写addInterceptors方法注册拦截器 */ @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(myInteceptor()); } /** * MultipartResolver配置 */ @Bean public MultipartResolver multipartResolver() { CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver(); // 设置上传文件的最大值,单位为字节 multipartResolver.setMaxUploadSize(5400000); // 设置请求的编码格式,默认为iso-8859-1 multipartResolver.setDefaultEncoding("UTF-8"); return multipartResolver; } }
package config; import javax.servlet.ServletContext; import javax.servlet.ServletException; import javax.servlet.ServletRegistration.Dynamic; import org.springframework.web.WebApplicationInitializer; import org.springframework.web.context.support.AnnotationConfigWebApplicationContext; import org.springframework.web.servlet.DispatcherServlet; public class WebConfig implements WebApplicationInitializer { @Override public void onStartup(ServletContext arg0) throws ServletException { AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext(); ctx.register(SpringMVCConfig.class);// 注册Spring MVC的Java配置类SpringMVCConfig ctx.setServletContext(arg0);// 和当前ServletContext关联 /** * 注册Spring MVC的DispatcherServlet */ Dynamic servlet = arg0.addServlet("dispatcher", new DispatcherServlet(ctx)); servlet.addMapping("/"); servlet.setLoadOnStartup(1); } }
package service; import pojo.UserForm; public interface UserService { boolean login(UserForm user); boolean register(UserForm user); }
package service; import org.springframework.stereotype.Service; import pojo.UserForm; //注解为一个服务 @Service public class UserServiceImpl implements UserService { @Override public boolean login(UserForm user) { if ("zhangsan".equals(user.getUname()) && "123456".equals(user.getUpass())) return true; return false; } @Override public boolean register(UserForm user) { if ("zhangsan".equals(user.getUname()) && "123456".equals(user.getUpass())) return true; return false; } }
package controller; import javax.servlet.http.HttpSession; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.RequestMapping; import pojo.UserForm; import service.UserService; @Controller @RequestMapping("/user") public class UserController { // 得到一个用来记录日志的对象,这样打印信息的时候能够标记打印的是那个类的信息 private static final Log logger = LogFactory.getLog(UserController.class); // 将服务层依赖注入到属性userService @Autowired public UserService userService; /** * 处理登录 */ @RequestMapping("/login") public String login(UserForm user, HttpSession session, Model model) { if (userService.login(user)) { session.setAttribute("u", user); logger.info("成功"); return "main";// 登录成功,跳转到main.jsp } else { logger.info("失败"); model.addAttribute("messageError", "用户名或密码错误"); return "login"; } } /** * 处理注册 */ @RequestMapping("/register") public String register(@ModelAttribute("user") UserForm user) { if (userService.register(user)) { logger.info("成功"); return "login";// 注册成功,跳转到login.jsp } else { logger.info("失败"); // 使用@ModelAttribute("user")与model.addAttribute("user", user)功能相同 // 在register.jsp页面上可以使用EL表达式${user.uname}取出ModelAttribute的uname值 return "register";// 返回register.jsp } } }