学习目标:
基于注解的控制器
学习大纲:
一、Controller注解类型
二、RequestMapping注解类型
三、编写请求处理方法
四、Controller接收请求参数的常见方式
五、重定向与转发
六、应用@Autowired进行依赖注入
七、@ModelAttribute
学习内容:
在使用Spring MVC进行Web应用开发时,Controller是Web应用的核心。Controller实现类包含了对用户请求的处理逻辑,是用户请求和业务逻辑之间的桥梁,是Spring MVC框架的核心部分,负责具体的业务逻辑处理。
一、Controller注解类型
在Spring MVC中,使用org.springframework.stereotype.Controller
注解类型声明某类的实例是一个控制器。例如,2.2.2节中的IndexController控制器类。别忘了在Spring MVC的配置文件中使用context:component-scan/元素(见【例2-1】)或在Spring MVC配置类中使用@ComponentScan
(见【例2-2】)指定控制器类的基本包,进而扫描所有注解的控制器类。
二、RequestMapping注解类型
在基于注解的控制器类中,可以为每个请求编写对应的处理方法。需要使用org.springframework.web.bind.annotation.RequestMapping
注解类型将请求与处理方法一一对应。
1.方法级别注解
package controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class IndexController {
@RequestMapping(value="/index/login")
public String login() {
/**login代表逻辑视图名称,需要根据Spring MVC配置
* 文件中internalResourceViewResolver的前缀和后缀找到对应的物理视图
*/
return "login";
}
@RequestMapping(value="/index/register")
public String register() {
return "register";
}
}
2.类级别注解
package controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
/**“@Controller”表示IndexController的实例是一个控制器
* @Controller相当于@Controller("indexController")
* 或@Controller(value = "indexController")
*/
@Controller
@RequestMapping("/index")
public class IndexController {
@RequestMapping("/login")
public String login() {
/**login代表逻辑视图名称,需要根据Spring MVC配置
* 文件中internalResourceViewResolver的前缀和后缀找到对应的物理视图
*/
return "login";
}
@RequestMapping("/register")
public String register() {
return "register";
}
}
为了方便程序维护,建议开发者采用类级别的注解,将相关处理放在同一个控制器类中,例如,对商品的增、删、改、查处理了方法都可以放在一个名为GoodsOperate的控制类中。
三、编写请求处理方法
1.请求处理方法中常出现的参数类型
Servlet API、输入输出流、表单实体类、注解类型、Model等Java类型。
在控制类中每个请求处理方法中使用Servlet API类型,那么可以将这些类型作为请求处理方法的参数类型。Servlet API参数类型示例代码如下:
package controller;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
@RequestMapping("/index")
public class IndexController1
{
@RequestMapping("/login")
public String login(HttpSession session,HttpServletRequest request) {
session.setAttribute("skey", "session范围的值");
session.setAttribute("rkey", "request范围的值");
return "login";
}
}
2.请求处理方法常见的返回类型
最常见的返回类型,就是代表逻辑视图名称的String类型。除了String类型外,还有Model、View以及其他任意的Java类型。
四、Controller接收请求参数的常见方式
Controller接收请求参数的方式有很多种,有的适合get请求方式,有的适合post请求方式,有的两者都适合。下面介绍几种方式:
1.通过实体bean接收请求参数
通过一个实体bean来接收请求参数,适用于get和post提交请求方式。需要注意的是,bean的属性名称必须与请求参数名称相同。下面通过一个实例,讲解“通过实体bean接收请求参数”
【例2-3】通过实体bean接收请求参数
具体步骤如下:
1)创建Web应用并导入JAR包
创建Web应用ch2_3,导入如图所示的JAR包。
下载Spring包的方法
2)创建视图文件
在应用ch2_3的WEB-INF/jsp/目录下有register.jsp、login.jsp和main.jsp文件
register.jsp的代码如下:
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<form action="${pageContext.request.contextPath }/user/register" method="post" name="registForm">
<table border=1>
<tr>
<td>姓名:</td>
<td>
<input type="text" name="uname" value="${user.uname }"/>
</td>
</tr>
<tr>
<td>密码:</td>
<td><input type="password" name="upass"/></td>
</tr>
<tr>
<td>确认密码:</td>
<td><input type="password" name="reupass"/></td>
</tr>
<tr>
<td colspan="2" align="center">
<input type="submit" value="注册" />
</td>
</tr>
</table>
</form>
</body>
</html>
login.jsp的代码如下:
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<form action="${pageContext.request.contextPath }/user/login" method="post">
<table>
<tr>
<td align="center" colspan="2">登录</td>
</tr>
<tr>
<td>姓名:</td>
<td><input type="text" name="uname"></td>
</tr>
<tr>
<td>密码:</td>
<td><input type="password" name="upass"></td>
</tr>
<tr>
<td colspan="2">
<input type="submit" value="提交" >
<input type="reset" value="重置" >
</td>
</tr>
</table>
${messageError }
</form>
</body>
</html>
main.jsp的代码如下:
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
主页面!
</body>
</html>
在ch2_3应用的/WebContent/目录下有index.jsp,具体代码如下:
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
没注册的用户,请<a href="user/register">注册</a>!<br>
已注册的用户,去<a href="user/login">登录</a>!
</body>
</html>
3)创建POJO实体类
在ch2_3应用的src目录下,创建pojo包,并在该包中创建实体类UserForm,具体代码如下:
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;
}
}
4)创建控制器类
在ch2_3应用的src目录下,创建controller包,并在该包中创建控制器类UserController。
package controller;
import javax.servlet.http.HttpSession;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import pojo.UserForm;
@Controller
@RequestMapping("/user")
public class UserController {
//得到一个用来记录日志的对象,这样打印信息的时候能够标记打印的是那个类的信息
private static final Log logger = LogFactory.getLog(UserController.class);
/**
* 处理登录
*/
@RequestMapping("/login")
public String login(UserForm user, HttpSession session, Model model) {
if("zhangsan".equals(user.getUname())&&"123456".equals(user.getUpass())){
session.setAttribute("u", user);
logger.info("成功");
return "main";//登录成功,跳转到main.jsp
}else{
logger.info("失败");
model.addAttribute("messageError", "用户名或密码错误");
return "login";
}
}
/**
*处理注册
*/
@RequestMapping("/register")
public String register(UserForm user,Model model) {
if("zhangsan".equals(user.getUname())&&"123456".equals(user.getUpass())){
logger.info("成功");
return "login";//注册成功,跳转到login.jsp
}else{
logger.info("失败");
//使用@ModelAttribute("user")与model.addAttribute("user", user)功能相同
//在register.jsp页面上可以使用EL表达式${user.uname}取出ModelAttribute的uname值
model.addAttribute("uname", user.getUname());
return "register";//返回register.jsp
}
}
}
5)创建Web与Spring MVC的配置类
在ch2_3应用的src目录下,创建config包,并在该包中创建Spring MVC配置类SpringMVCConfig和Web配置类WebConfig。
SpringMVCConfig的代码如下:
package config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
@Configuration
@EnableWebMvc
@ComponentScan("controller")
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/");
}
}
WebConfig的代码如下:
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);
}
}
6)发布并运行应用
右击ch2_3应用,选择Run As|Run on Server命令发布并运行应用。
2.通过处理方法的形参接收请求参数
通过处理方法的形参接收请求参数,也就是直接把表单参数写在控制器类相应方法的形参中,即形参名称与请求参数名称完全相同。该接收参数方式适用于get和post提交请求方式。可以将例2-3的控制器类UserController中的register方法的代码修改如下:
@RequestMapping("/register")
//
// 通过形参接收请求参数,形参名称与请求参数名称完全相同
//
public String register(String uname,String upass,Model model) {
if("zhangsan".equals(uname)&&"123456".equals(upass)){
logger.info("成功");
return "login";//注册成功,跳转到login.jsp
}else{
logger.info("失败");
//在register.jsp页面上可以使用EL表达式${user.uname}取出ModelAttribute的uname值
model.addAttribute("uname",uname);
return "register";//返回register.jsp
}
}
3.通过@RequestParam
接收请求参数
通过@RequestParam
接收请求参数,适用于get和post提交请求方式。通过@RequestParam
接收请求参数与“通过处理方法的形参接收请求参数”的区别是:当请求参数与接收参数名不一致时,“通过处理方法的形参接收请求参数”不会报400错误,而“通过@RequestParam
接收请求参数”会400错误。
@RequestMapping("/register")
//
// 通过`@RequestParam`接收请求参数
//
public String register(@RequestParam String uname,@RequestParam String upass,Model model) {
if("zhangsan".equals(uname)&&"123456".equals(upass)){
logger.info("成功");
return "login";//注册成功,跳转到login.jsp
}else{
logger.info("失败");
//在register.jsp页面上可以使用EL表达式${user.uname}取出ModelAttribute的uname值
model.addAttribute("uname",uname);
return "register";//返回register.jsp
}
}
4.通过@ModelAttribute
接收请求参数
@ModelAttribute
注解放在处理方法的形参上时,用于将多个请求参数封装到一个实体对象,从而简化数据绑定流程,而且自动暴露为模型数据用于视图页面展示时使用。而“通过实体bean接收请求参数”只是将多个请求参数封装到一个实体对象,并不能暴露为模型数据(需要使用model.addAttribute
语句才能暴露为模型数据)
@RequestMapping("/register")
public String register(@ModelAttribute("user")UserForm user) {
if("zhangsan".equals(user.getUname())&&"123456".equals(user.getUpass())){
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
}
}
五、重定向与转发
转发到一个请求方法(同一个控制器类里,可省略/index/)
return "forward:/index/isLogin";
重定向到一个请求方法
return "redirect:/index/isRegister";
转发到一个视图
return "register";
六、应用@Autowired进行依赖注入
可以通过org.springframework.beans.factory.annotation.Autowired
注解类型将依赖注入到一个属性(成员变量)或方法,如:
@Autowired
public UserService userService;
七、@ModelAttribute
在前面学习的控制器中,并没有体现MVC的M层,这是因为控制器既充当C层,又充当M层。这样设计程序
通过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注解的控制器的一个非请求处理方法,将在每次调用该控制器类的请求处理方法前被调用。这种特性可以用来控制登录权限。
学习时间:
学习产出:
1、 技术笔记 1 遍
2、CSDN 技术博客 1 篇