SpringMVC
问题:
- 一个Servlet处理一个请求
- 请求参数获取与转换
- 硬编码了视图结果
概述
- 是spring框架一个模块,与Spring无缝集成(SSH,Struts2)
- 是基于MVC设计模式实现的
- 是目前最流行的MVC框架
- Spring3.0之后,全面超越了Struts2
使用
导入jar
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>4.3.8.RELEASE</version> </dependency>
添加了一个SpringMVC组件配置类
@Configuration @EnableWebMvc//启用mvc注解驱动 @ComponentScan(basePackages = "com.neu.controller") public class SpringMVCConfig { }
修改了web初始化器
Servlet 3.0 之后,web容器启动后会调用 META-INF/services/javax.servlet.ServletContainerInitializer文件中的类
在Tomcat8以上版本支持一个配置启动的东西,意思就是tomcat启动时会自动去扫描所有 jar 中目录为「 META-INF/services/ 」中是否有个名字是「javax.servlet.ServletContainerInitializer」的配置文件,然后根据里面内容反编译启动等等一系列操作
如上图配置后tomcat启动时就会自动生成HelloServlet了,大概原理就这样,不过SpringMVC已经把上图中的基本配置已经搞定直接使用即可
在 spring-web的jar包中查看
org.springframework.web.SpringServletContainerInitializer
public class WebInitializer implements WebApplicationInitializer { @Override public void onStartup(ServletContext servletContext) throws ServletException { //创建spring容器 AnnotationConfigWebApplicationContext c = new AnnotationConfigWebApplicationContext(); //注册配置类 c.register(DBConfig.class); c.register(MybatisConfig.class); c.register(MybatisMapperScannerConfig.class); //把spring容器的引用放到ServletContext servletContext.addListener(new ContextLoaderListener(c)); //创建SpringMVC组件的容器(子容器,可以访问父容器) AnnotationConfigWebApplicationContext childContext = new AnnotationConfigWebApplicationContext(); childContext.register(SpringMVCConfig.class); Dynamic servlet = servletContext.addServlet("dispatcher", new DispatcherServlet(childContext)); servlet.addMapping("/"); //在启动web服务器的时候,直接创建这个前端控制器 servlet.setLoadOnStartup(1); } }
配置了一个视图解析器
@Configuration @EnableWebMvc//启用mvc注解驱动 @ComponentScan(basePackages = "com.neu.controller") public class SpringMVCConfig { //物理路径:/WEB-INF/jsp/hello.jsp ///WEB-INF/jsp/hello.jsp @Bean public InternalResourceViewResolver viewResolver() { InternalResourceViewResolver r = new InternalResourceViewResolver(); //设置前缀 r.setPrefix("/WEB-INF/jsp/"); //设置后缀 r.setSuffix(".jsp"); return r; } }
创建业务逻辑控制器和处理器方法
@Controller public class HelloController { @RequestMapping("hello") public String hello() { System.out.println("hello!"); return "hello"; } }
SpringMVC详细配置
@RequestMapping
既可以添加到类上,也可以添加到处理器方法上
- 添加到类上的时候,提供初步的请求映射,路径相对于web应用程序根目录
- 添加到方法上,提供进一步的细分映射信息,相对于类定义处的url,若类定义处没有注解,则相对于web应用根目录
method:表示能处理的请求方法,默认能处理get和post请求
@RequestMapping(value="insert",method = {RequestMethod.POST,RequestMethod.GET}) public String insert() { return "dept/getAll"; }
value:指定请求的url,默认属性
params:指定请求中必须包含某些请求参数或参数值
@RequestMapping(value="insert",params = {"deptno=1","dname"}) public String insert() { return "dept/getAll"; }
GetMapping:只处理get请求
PostMapping:只处理post请求
请求参数绑定
- 定义:把请求参数的key/value绑定到处理器方法的形参上,默认形参名与请求参数名相同,就会自动绑定
- @RequestParam:手工映射请求参数
- value:请求参数名,默认属性
- required:必须的,默认为:true,
- defaultValue:默认值
请求参数中文处理
get
<plugin> <groupId>org.apache.tomcat.maven</groupId> <artifactId>tomcat7-maven-plugin</artifactId> <version>2.2</version> <configuration> <path>/</path> <port>8089</port> <uriEncoding>utf-8</uriEncoding> </configuration> </plugin>
post:修改web初始化器
//注册编码过滤器 javax.servlet.FilterRegistration.Dynamic filter = servletContext.addFilter("charsetFilter", new CharacterEncodingFilter("utf-8")); filter.addMappingForUrlPatterns(null, false, "/*");
日期格式处理
定义了一个转换器
public class DateConverter implements Converter<String, Date> { @Override public Date convert(String source) { SimpleDateFormat f = new SimpleDateFormat("yyyy-MM-dd"); Date date = null; try { date = f.parse(source); } catch (ParseException e) { // TODO Auto-generated catch block e.printStackTrace(); } return date; } }
注册转换器
@Configuration @EnableWebMvc//启用mvc注解驱动 @ComponentScan(basePackages = "com.neu.controller") public class SpringMVCConfig implements WebMvcConfigurer { @Override public void addFormatters(FormatterRegistry registry) { //注册日期转换器 registry.addConverter(new DateConverter()); } }
跳转到其他请求的方式
//请求转发 // return "forward:/dept/getAll"; //重定向 return "redirect:/dept/getAll";
处理器形参
HttpServletRequest
@Controller public class HelloController { @RequestMapping("hello") public String hello(HttpServletRequest request) { String name = request.getParameter("name"); System.out.println(name); return "hello"; } }
HttpServletResponse
@Controller public class HelloController { @RequestMapping("hello") public void hello(HttpServletRequest request,HttpServletResponse response) throws IOException { String name = request.getParameter("name"); System.out.println(name); response.getWriter().append(name); // return "hello"; } }
HttpSession
@Controller public class HelloController { @RequestMapping("/") public String getLogin() { return "login"; } @RequestMapping("login") public String login(String username,String password,HttpSession session) { if("admin".equals(username) && "111".equals(password)) { session.setAttribute("username", username); return "forward:/dept/getAll"; }else { return "redirect:/"; } } }
分页:使用Mybatis分页插件
导入依赖
<dependency> <groupId>com.github.pagehelper</groupId> <artifactId>pagehelper</artifactId> <version>5.1.10</version> </dependency>
在mybatis配置类中,注册分页插件(拦截器)
@Configuration @ComponentScan(basePackages = {"com.neu.mapper","com.neu.service","com.neu.utils"}) public class MybatisConfig { @Autowired private DataSource dataSource; @Bean public SqlSessionFactory sqlSessionFactory() throws Exception { SqlSessionFactoryBean factory = new SqlSessionFactoryBean(); factory.setDataSource(dataSource); //创建一个分页拦截器 PageInterceptor interceptor = new PageInterceptor(); Properties prop =factory new Properties(); interceptor.setProperties(prop); //注册拦截器 factory.setPlugins(new Interceptor[] {interceptor}); return factory.getObject(); } }
修改业务逻辑类
@Override public PageInfo<Dept> getPaged(int pageNum, int pageSize) { //分页方法,该方法会拦截该语句后的第一个查询,对该查询进行分页操作 PageHelper.startPage(pageNum, pageSize); List<Dept> list = deptMapper.getAll(); PageInfo<Dept> pageInfo = new PageInfo<Dept>(list); return pageInfo; }
控制器
@RequestMapping("getPaged") public String getPaged(@RequestParam(defaultValue = "1") int pageNum,@RequestParam(defaultValue = "3") int pageSize,Model model) { PageInfo<Dept> pageInfo = deptService.getPaged(pageNum, pageSize); System.out.println(pageInfo); model.addAttribute("pageInfo", pageInfo); return "dept/paged"; }
jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Insert title here</title> </head> <body> 用户名:${ username }<br> <a href="${ pageContext.request.contextPath }/dept/getinsertpage">添加部门</a> <form action="${ pageContext.request.contextPath }/dept/batchDelete" method="post"> <input type="submit" value="批量删除"> <table border="1" width="900"> <thead> <tr> <th> </th><th>编号</th><th>名称</th><th>地址</th><th>操作</th> </tr> </thead> <tbody> <c:forEach items="${ pageInfo.list }" var="dept"> <tr> <td> <input type="checkbox" name="deptno" value="${ dept.deptno }"> </td> <td>${ dept.deptno }</td> <td>${ dept.dname }</td> <td>${ dept.loc }</td> <td> <a href="${ pageContext.request.contextPath }/dept/delete?deptno=${ dept.deptno }">删除</a> <a href="${ pageContext.request.contextPath }/dept/getById?deptno=${ dept.deptno }">编辑</a> </td> </tr> </c:forEach> </tbody> <tfoot> <td colspan="5"> 共${ pageInfo.total }记录 第 ${ pageInfo.pageNum } 页/共${ pageInfo.pages }页 <c:if test="${ !pageInfo.isFirstPage }"> <a href="${ pageContext.request.contextPath }/dept/getPaged?pageNum=1&pageSize=${pageInfo.pageSize}">第一页</a> <a href="${ pageContext.request.contextPath }/dept/getPaged?pageNum=${pageInfo.pageNum-1}&pageSize=${pageInfo.pageSize}">上一页</a> </c:if> <c:forEach items="${ pageInfo.navigatepageNums }" var="num"> <c:if test="${ num == pageInfo.pageNum }"> <a>[${ num }]</a> </c:if> <c:if test="${ num != pageInfo.pageNum }"> <a href="${ pageContext.request.contextPath }/dept/getPaged?pageNum=${ num }&pageSize=${pageInfo.pageSize}">[${ num }]</a> </c:if> </c:forEach> <c:if test="${ pageInfo.hasNextPage }"> <a href="${ pageContext.request.contextPath }/dept/getPaged?pageNum=${pageInfo.pageNum+1}&pageSize=${pageInfo.pageSize}">下一页</a> </c:if> <c:if test="${ !pageInfo.isLastPage }"> <a href="${ pageContext.request.contextPath }/dept/getPaged?pageNum=${pageInfo.navigateLastPage}&pageSize=${pageInfo.pageSize}">最后一页</a> </c:if> 跳转到<input type="text" style="width:25px;" id="page">页 <input type="button" value="go" onclick="goPage()"> <select onchange="changePageSize()" id="pageSize"> <option ${ pageInfo.pageSize == 3?"selected":"" }>3</option> <option ${ pageInfo.pageSize == 5?"selected":"" }>5</option> <option ${ pageInfo.pageSize == 10?"selected":"" }>10</option> </select> </td> </tfoot> </table> </form> <script type="text/javascript"> function goPage(){ //得到文本框的值 var pageNum = document.getElementById("page").value; //修改地址栏地址 location.href = "${ pageContext.request.contextPath }/dept/getPaged?pageNum="+pageNum; } function changePageSize(){ var pageSize = document.getElementById("pageSize").value; location.href = "${ pageContext.request.contextPath }/dept/getPaged?pageNum=1&pageSize="+pageSize; } </script> </body> </html>
请求参数验证
JSR303是JavaBean数据合法性验证标准框架
Hibernate Validator 是JSR303的标准的实现
导入包
<dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-validator</artifactId> <version>5.4.1.Final</version> </dependency> <dependency> <groupId>com.fasterxml</groupId> <artifactId>classmate</artifactId> <version>1.3.3</version> </dependency>
在实体类的属性上添加验证注解
@Data @AllArgsConstructor @NoArgsConstructor public class Dept { @NotNull(message = "部门编号不能为空") private Integer deptno; private String dname; private String loc; }
在控制器中的处理器方法的形参前添加@Valid注解
@RequestMapping("insert") public String insert(@Valid Dept dept,Errors errors,Model model) { if(errors.hasErrors()) { model.addAttribute("errorList", errors.getAllErrors()); return "dept/insert"; } //。。。 }
jsp页面显示错误消息
<c:if test="${ !(empty errorList) }"> <c:forEach items="${ errorList }" var="error"> ${ error.defaultMessage }<br> </c:forEach> </c:if>
验证注解
- @NotNull:不能为空,能验证任何对象
- @NotBlank:表示注解的属性不能为null和空串,只能验证字符串
- @Size(min=2,max=10,message=””):验证字符串的长度
- @Email:验证邮箱
- @Post():必须是一个过去的日期
- @Futrue:必须是一个未来的日期
- @Min():最小值
- @Max():最大值
@Pattern(regexp="^\\d{3,8}$")
:使用正则表达式验证