SpringMVC


SpringMVC

问题:

  1. 一个Servlet处理一个请求
  2. 请求参数获取与转换
  3. 硬编码了视图结果

概述

  1. 是spring框架一个模块,与Spring无缝集成(SSH,Struts2)
  2. 是基于MVC设计模式实现的
  3. 是目前最流行的MVC框架
  4. Spring3.0之后,全面超越了Struts2

使用

  1. 导入jar

    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-webmvc</artifactId>
        <version>4.3.8.RELEASE</version>
    </dependency>
    
  2. 添加了一个SpringMVC组件配置类

    @Configuration
    @EnableWebMvc//启用mvc注解驱动
    @ComponentScan(basePackages = "com.neu.controller")
    public class SpringMVCConfig {
    
    }
    
  3. 修改了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);
        }
    
    }
    
  4. 配置了一个视图解析器

    @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;
        }
    }
    
  5. 创建业务逻辑控制器和处理器方法

    @Controller
    public class HelloController {
    
        @RequestMapping("hello")
        public String hello() {
            System.out.println("hello!");
            return "hello";
        }
    }
    

SpringMVC详细配置

  1. @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";
          }
      
  2. GetMapping:只处理get请求

  3. PostMapping:只处理post请求

  4. 请求参数绑定

    • 定义:把请求参数的key/value绑定到处理器方法的形参上,默认形参名与请求参数名相同,就会自动绑定
    • @RequestParam:手工映射请求参数
      • value:请求参数名,默认属性
      • required:必须的,默认为:true,
      • defaultValue:默认值

请求参数中文处理

  1. 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>
    
  2. post:修改web初始化器

    //注册编码过滤器
    javax.servlet.FilterRegistration.Dynamic filter 
                = servletContext.addFilter("charsetFilter", new CharacterEncodingFilter("utf-8"));
            
    filter.addMappingForUrlPatterns(null, false, "/*");
    

日期格式处理

  1. 定义了一个转换器

    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;
        }
    
    }
    
  2. 注册转换器

    @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";
    

处理器形参

  1. HttpServletRequest

    @Controller
    public class HelloController {
    
        @RequestMapping("hello")
        public String hello(HttpServletRequest request) {
            String name = request.getParameter("name");
            System.out.println(name);
            return "hello";
        }
        
    }
    
  2. 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";
        }	
        
    }
    
  3. 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分页插件

  1. 导入依赖

    <dependency>
        <groupId>com.github.pagehelper</groupId>
        <artifactId>pagehelper</artifactId>
        <version>5.1.10</version>
    </dependency>
    
  2. 在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();
        }
    }
    
  3. 修改业务逻辑类

    @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;
        }
    
  4. 控制器

    @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";
        }
    
  5. 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>&nbsp;</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>
    

请求参数验证

  1. JSR303是JavaBean数据合法性验证标准框架

  2. Hibernate Validator 是JSR303的标准的实现

  3. 导入包

    <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>
    
  4. 在实体类的属性上添加验证注解

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public class Dept {
        @NotNull(message = "部门编号不能为空")
        private Integer deptno;
        private String dname;
        private String loc;
    }
    
  5. 在控制器中的处理器方法的形参前添加@Valid注解

    @RequestMapping("insert")
    public String insert(@Valid Dept dept,Errors errors,Model model) {
        if(errors.hasErrors()) {
            model.addAttribute("errorList", errors.getAllErrors());
            return "dept/insert";
        }
        //。。。
    }
    
  6. jsp页面显示错误消息

    <c:if test="${ !(empty errorList) }">
        <c:forEach items="${ errorList }" var="error">
            ${ error.defaultMessage }<br>
        </c:forEach>		
    </c:if>
    
  7. 验证注解

    • @NotNull:不能为空,能验证任何对象
    • @NotBlank:表示注解的属性不能为null和空串,只能验证字符串
    • @Size(min=2,max=10,message=””):验证字符串的长度
    • @Email:验证邮箱
    • @Post():必须是一个过去的日期
    • @Futrue:必须是一个未来的日期
    • @Min():最小值
    • @Max():最大值
    • @Pattern(regexp="^\\d{3,8}$"):使用正则表达式验证

文章作者: FFFfrance
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 FFFfrance !