SpringMVC

SpringMVC

文章目录

什么是SpringMVC

Spring MVC属于Spring FrameWork的一部分,是一种基于Java的实现了Web MVC设计模式的轻量级Web框架。

官方文档:https://docs.spring.io/spring-framework/docs/current/reference/html/web.html#mvc

为什么要学习SpringMVC

Spring MVC的特点:

1.轻量级,简单易学
2.高效,基于请求响应的MVC框架

3.与Spring兼容性好,无缝结合

4.约定优于配置
5.功能强大:RESTful、数据验证、格式化、本地化、主题等

6.简洁灵活
Spring的web框架围绕DispatcherServlet[调度Servlet ]设计。

回顾MVC

MVC 架构模式

​ 高内聚低耦合

MVC 模式代表 Model-View-Controller(模型-视图-控制器) 模式。这种模式用于应用程序的分层开发。

  • Model(模型) - 模型代表一个存取数据的对象或 实体类对象。它也可以带有逻辑,在数据变化时更新控制器。
  • View(视图) - 视图代表模型包含的数据的可视化。
  • Controller(控制器) - 控制器作用于模型和视图上。它控制数据流向模型对象,并在数据变化时更新视图。它使视图与模型分离开。
  • SpringMVC

MVC框架要做什么事情

Controller:控制器

  1. 取得表单数据
  2. 调用业务逻辑
  3. 转向指定的页面

Model:模型

  1. 业务逻辑
  2. 保存数据的状态(持久化)

View:视图

  1. 显示视图

IDEA搭建Web项目

步骤

  1. 新建普通的Maven项目
  2. 删除Src目录作为父项目
  3. 新建子项目,添加框架支持
  • SpringMVC

  • SpringMVC

问题描述

​ 若启动项目,tomcat正常访问,但对于Controller控制器中映射地址的访问无效,可能是打包时jar包未导入进去。

​ 组件扫描器未进行生效。

解决方案

手动导入

​ 1、选中项目,点击项目结构

  • SpringMVC

​ 2、在WEB-INF目录下,新建lib文件夹

  • SpringMVC

3、选中lib目录,选择所要导入的jar包,点击应用,重启tomcat

  • SpringMVC

回顾Servlet

​ 配置环境、编码、测试

实现步骤

  1. 创建Web项目

  2. 导入依赖

    <dependencies>
            <!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->
            <dependency>
                <groupId>javax.servlet</groupId>
                <artifactId>javax.servlet-api</artifactId>
                <version>4.0.1</version>
                <scope>provided</scope>
            </dependency>
            <!-- https://mvnrepository.com/artifact/javax.servlet/jsp-api -->
            <dependency>
                <groupId>javax.servlet</groupId>
                <artifactId>jsp-api</artifactId>
                <version>2.0</version>
                <scope>provided</scope>
            </dependency>
            <!-- https://mvnrepository.com/artifact/javax.servlet.jsp.jstl/jstl -->
            <dependency>
                <groupId>javax.servlet.jsp.jstl</groupId>
                <artifactId>jstl</artifactId>
                <version>1.2</version>
            </dependency>
        </dependencies>
    
  3. 编写Servlet

    继承HttpServlet,重写get和post方法

    package com.rui.controller;
    
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    
    public class UserServlet extends HttpServlet {
    
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp)
        				throws ServletException, IOException {
            req.setAttribute("userName", "李四");
            req.getRequestDispatcher("/WEB-INF/jsp/show.jsp").forward(req, resp);
        }
    
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp)
        														throws ServletException, IOException {
            doGet(req, resp);
        }
        
    }
    
    
    1. 编写视图
    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
    <head>
        <title>Title</title>
    </head>
    <body>
    	${userName}
    </body>
    </html>
    
    1. 启动tomcat测试

/ 和 /* 的区别

总结

​ 两者都可以匹配任何资源,只不过两者的匹配的优先级是不一样的;当请求资源与其它Servlet都无法匹配的时候,/所配置的Servlet才会匹配

注意点:

在Tomcat中conf/web.xml中有关于/和.jsp页面处理的Servlet,当自己所配的web.xml文件中配置/时,

会使Tomcat中的DefaultServlet无法访问,导致静态资源无法访问,因此在SpingMVC配置文件中要开启处理静态资源的开关。

重点:

/ 能匹配路径型URL,不能匹配后缀型URL(除静态资源类后缀的,例:.png 、.jpg 、.html)

/* 能匹配任何类型URL,在配置视图的时候尽量用/这种方式。

即:使用 / ,DispatcherServlet对于 .jsp 等带有后缀的资源不进行拦截。

​ 而使用 /* DispatcherServlet会对其进行拦截,查找对应的Controller控制器,查找不到则报404错误!

SpringMVC中解决静态资源访问问题的两种方式

  • 使用tomcat的默认Servlet进行静态资源的处理

     <mvc:default-servlet-handler/>
    
  • 声明静态资源,进行特殊处理

    <!--
    mapping:映射
    location:本地资源路径,注意必须是webapp根目录下的路径。
    两个*,它表示映射resources/下所有的URL,包括子路径(即接多个/)
    -->
    <mvc:resources mapping="/photo/**" location="/photo/"/>
    

SpringMVC执行流程

流程图示

  • SpringMVC

流程描述

  1. DispatcherServlet表示前置控制器,是整个SpringMVC的控制中心。用户发出请求,DispatcherServlet接收请求并拦截请求。

    • 我们假设请求的url为 : http://localhost:8080/SpringMVC/test

      如上url拆分成三部分:
      http://localhost:8080服务器域名

      SpringMVC部署在服务器上的web站点

      test表示控制器的映射地址

      通过分析,如上url表示为:请求位于服务器localhost:8080上的SpringMVC站点的test对应的控制器。

  2. HandlerMapping为处理器映射。DispatcherServlet调用
    HandlerMapping,HandlerMapping根据请求url查找Handler。

  3. HandlerExecution表示具体的Handler,其主要作用是根据url查找控制器,如上url被查找控制器为:test。

  4. HandlerExecution将解析后的信息传递给DispatcherServlet,如解析控制器映射等。

  5. HandlerAdapter表示处理器适配器,其按照特定的规则去执行Handler。

  6. Handler让具体的Controller执行。

  7. Controller将具体的执行信息返回给HandlerAdapter,如ModelAndView。

  8. HandlerAdapter将视图逻辑名或模型传递给DispatcherServlet。

  9. DispatcherServlet调用视图解析器(ViewResolver)来解析HandlerAdapter传递的逻辑视图名。

  10. 视图解析器将解析的逻辑视图名传给DispatcherServlet。

  11. DispatcherServlet根据视图解析器解析的视图结果,调用具体的视图。

  12. 最终视图呈现给用户。

SpringMVC的第一个程序

XML的方式开发

  1. 构建web项目,导入依赖

  2. 配置*调度器(DispatchServlet)

    <?xml version="1.0" encoding="UTF-8"?>
    <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
             version="4.0">
    
        <servlet>
            <servlet-name>springMVC</servlet-name>
            <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
            <init-param>
                <param-name>contextConfigLocation</param-name>
                <param-value>classpath:spring-MVC.xml</param-value>
            </init-param>
            <load-on-startup>1</load-on-startup>
        </servlet>
        <servlet-mapping>
            <servlet-name>springMVC</servlet-name>
            <url-pattern>/</url-pattern>
        </servlet-mapping>
    
    </web-app>
    
  3. 编写控制器(Controller)

    1. 实现Controller接口,重写handleRequest()方法
    package com.rui.controller;
    
    import org.springframework.web.servlet.ModelAndView;
    import org.springframework.web.servlet.mvc.Controller;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    public class StudentServlet implements Controller {
        @Override
        public ModelAndView handleRequest(HttpServletRequest httpServletRequest,
                                          HttpServletResponse httpServletResponse) throws Exception {
            ModelAndView  mv = new ModelAndView();
            mv.addObject("username", "学生:张三");
            mv.setViewName("/WEB-INF/jsp/show.jsp");
            return null;
        }
    }
    
    package com.rui.controller;
    
    import org.springframework.web.servlet.ModelAndView;
    import org.springframework.web.servlet.mvc.Controller;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    public class UserServlet implements Controller {
        @Override
        public ModelAndView handleRequest(HttpServletRequest httpServletRequest,
                                          HttpServletResponse httpServletResponse) throws Exception {
    
            ModelAndView  mv = new ModelAndView();
            mv.addObject("username", "用户:lisi");
            mv.setViewName("/WEB-INF/jsp/show.jsp");
    
            return mv;
        }
    }
    
  4. 编写Spring-MVC的配置,声明控制器对象

    1. 不需要额外声明BeanNameUrlHandlerMapping和SimpleControllerHandlerAdapter。
    2. Debug模式下,调试发现SpringMVC初始化时自动创建。
    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    
        <bean id="/getUser" class="com.rui.controller.UserServlet"/>
    
        <bean id="/getStudent" class="com.rui.controller.StudentServlet"/>
    </beans>
    
  5. 编写视图界面,测试

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
    <head>
        <title>Title</title>
    </head>
    <body>
    		${username}
    </body>
    </html>
    

总结:

以上代码存在很多缺点

	1.	一个控制器(Controller)只能有一个方法,返回值固定。
	2.	每创建一个控制器,则需要在SpringMVC的配置文件中进行配置。
	3.	视图路径,存在冗余。

视图解析器

​ 解决视图路径冗余问题

使用步骤

  1. 在SpringMVC的配置文件中声明视图解析器
<bean id="view" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
     <property name="prefix" value="/WEB-INF/jsp/"/>
     <property name="suffix" value=".jsp"/>
</bean>
  1. 控制器(Controller)中,编写的响应地址可以省略前缀和后缀

    package com.rui.controller;
    
    import org.springframework.web.servlet.ModelAndView;
    import org.springframework.web.servlet.mvc.Controller;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    public class UserServlet implements Controller {
        @Override
        public ModelAndView handleRequest(HttpServletRequest httpServletRequest,
                                          HttpServletResponse httpServletResponse) throws Exception {
    
            ModelAndView  mv = new ModelAndView();
            mv.addObject("username", "用户:lisi");
            mv.setViewName("show");
    
            return mv;
        }
    }
    
  2. 视图解析器原理

    内部实现是进行字符串的拼接

  3. 特定情况下,不想让视图解析器起作用

    1. forward和redirect都是关键字, 有一个共同的特点不和视图解析器一同工作(不加关键字:默认为转发操作)
    package com.rui.controller;
    
    import org.springframework.web.servlet.ModelAndView;
    import org.springframework.web.servlet.mvc.Controller;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    public class UserServlet implements Controller {
        @Override
        public ModelAndView handleRequest(HttpServletRequest httpServletRequest,
                                          HttpServletResponse httpServletResponse) throws Exception {
    
            ModelAndView  mv = new ModelAndView();
            mv.addObject("username", "用户:lisi");
            mv.setViewName("forward:/WEB-INF/jsp/show.jsp");
    
            return mv;
        }
    }
    

注解式开发

​ 解决控制器类单一方法、特定返回值的问题

@Controller

  1. 作用:声明的类为控制器类

    package com.rui.controller;
    
    import org.springframework.stereotype.Controller;
    
    @Controller
    public class UserServlet {
        
    }
    
  2. 使用:需要声明组件扫描器

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:context="http://www.springframework.org/schema/context"
           xsi:schemaLocation="http://www.springframework.org/schema/beans 
           http://www.springframework.org/schema/beans/spring-beans.xsd 
           http://www.springframework.org/schema/context 
           https://www.springframework.org/schema/context/spring-context.xsd
            ">
    
        <context:component-scan base-package="com.rui"/>
    
    </beans>
    

@RequestMapping

  1. 使用方式(类上、方法上)

    1. 声明在类上面,表示模块名称
    2. 声明在方法上面,表示映射的地址名称
    3. 则请求地址为:http://localhost:8080/项目名/模块名/请求地址
    package com.rui.controller;
    
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.servlet.ModelAndView;
    
    @Controller
    @RequestMapping("/user")
    public class UserServlet {
    
        @RequestMapping(value = "/getName",method = RequestMethod.GET)
        public ModelAndView showName(){
            ModelAndView mv = new ModelAndView();
            mv.addObject("username", "username:lisi");
            mv.setViewName("showName");
            return mv;
        }
    
        @RequestMapping("/getAge")
        public ModelAndView showAge(){
            ModelAndView mv = new ModelAndView();
            mv.addObject("age", "age:21");
            mv.setViewName("showAge");
            return mv;
        }
    
        @RequestMapping("/forward")
        public ModelAndView forward(){
            ModelAndView mv = new ModelAndView();
            mv.setViewName("forward:index.jsp");
            return mv;
        }
    
    }
    
  2. 属性

    ​ 不加method属性,请求不受限制,可以接受任意请求方式的请求

    1. value/path属性

      声明映射地址

      @RequestMapping("/forward")
      
    2. method属性

      声明请求方式类型

      @RequestMapping(value = "/forward",method = RequestMethod.GET)
      

    存在问题:

    1. 声明请求类型,代码繁琐
    2. 返回值资源浪费问题(有时不需要model,有时不需要view,使用ModelAndView浪费资源)
  3. 解决声明请求类型代码繁琐的问题

    @RequestMapping注解衍生出5中明确请求类型的mapping注解

    ​ @GetMapping

    ​ @PostMapping

    ​ @PutMapping

    ​ @DeleteMapping

    ​ @PatchMapping

@RequestParam

逐个接收请求参数中, 解决请求中参数名形参名不一样的问题

  1. 使用方式:

     @RequestMapping("/forward")
    public ModelAndView forward(@RequestParam(value = "name",required = false) String name){
      ModelAndView mv = new ModelAndView();
      mv.setViewName("forward:index.jsp");
      return mv;
    }
    
  2. 属性:

    1. value 请求中的参数名称
    2. required 是一个boolean,默认是true
      1. true:表示请求中必须包含此参数。

@ResponseBody

​ 注解 @ResponseBody,使用在控制层(controller)的方法上

作用:

  1. 将方法的返回值,以特定的格式写入到response的body区域,进而将数据返回给客户端。

  2. 当方法上面没有写ResponseBody,底层会将方法的返回值封装为ModelAndView对象。

  3. 如果返回值是字符串,那么直接将字符串写到客户端;如果是一个对象,会将对象转化为json串,然后写到客户端。

    @GetMapping(value = "/getUser",produces = "text/html;charset=utf-8")
    @ResponseBody
    public String getUser(){
        User user = new User(1,"李四");
        return user.toString();
    }
    

注意编码格式

  1. 如果返回对象,按utf-8编码。

  2. 如果返回String,默认按iso8859-1编码。

  3. 页面可能出现乱码。

  4. 因此在注解中我们可以手动修改编码格式,例如@RequestMapping(value="/hello",produces=“text/html;charset=utf-8”),前面是请求的路径,后面是编码格式

@RestController

是@ResponseBody和@Controller的组合

@ControllerAdvice

  • 通过@ControllerAdvice注解可以将对于控制器的全局配置放在同一个位置。

  • 注解了@ControllerAdvice的类的方法可以使用@ExceptionHandler、@InitBinder、@ModelAttribute注解到方法上。

    • @ExceptionHandler:用于全局处理控制器里的异常。
    • @InitBinder:用来设置WebDataBinder,用于自动绑定前台请求参数到Model中。
    • @ModelAttribute:本来作用是绑定键值对到Model中,此处让全局的@RequestMapping都能获得在此处设置的键值对
  • @ControllerAdvice注解将作用在所有注解了@RequestMapping的控制器的方法上。

@ExceptionHandler

  • 基本使用方法
    • 方法加上@ExceptionHandler注解,这个方法就会处理类中其他方法(被@RequestMapping注解)抛出的异常
  • 注解的参数
    • @ExceptionHandler注解中可以添加参数,参数是某个异常类的class,代表这个方法专门处理该类异常
  • 就近原则
    • 当异常发生时,Spring会选择最接近抛出异常的处理方法

联合使用:全局异常处理,可以跳转指定的错误页面,也可以采用@ResponseBody,返回字符串

package com.rui.controller.advice;

import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;

@ControllerAdvice
public class MyExceptionHandler {

    @ExceptionHandler(NullPointerException.class)
    public String isNullEmp(){
        return "empIsNull";
    }
    
    @ExceptionHandler(Exception.class)
    public String isNullEmp(Exception ex){
        System.out.println(ex.getMessage());
        return "error";
    }
    
}

处理器方法的形参

​ 前端数据的接收

形参类型

  1. HttpServletRequest
  2. HttpServletResponse
  3. HttpSession
  4. 用户提交的数据

框架自动进行赋值,方法中可以直接使用

请求参数的接收方式

  1. 逐个接收

    1. 处理器(控制器)方法的形参名和请求中参数名必须一致。
    2. 同名的请求参数赋值给同名的形参
    @RequestMapping("/forward")
    public ModelAndView forward(String name,String age){
        ModelAndView mv = new ModelAndView();
        System.out.println(name);
        mv.setViewName("forward:index.jsp?name="+name);
        return mv;
    }
    
  2. 对象接收

    @RequestMapping("/forward")
    public ModelAndView forward(User user){
        ModelAndView mv = new ModelAndView();
        System.out.println(name);
        mv.setViewName("forward:index.jsp?name="+name);
        return mv;
    }
    

返回值类型

​ 解决返回值资源浪费问题

ModelAndView

  1. 若处理器方法处理完后,需要跳转到其它资源,且又要在跳转的资源间传递数据

    @RequestMapping(value = "/getName",method = RequestMethod.GET)
    public ModelAndView showName(){
        ModelAndView mv = new ModelAndView();
        mv.addObject("username", "username:lisi");
        mv.setViewName("showName");
        return mv;
    }
    

String

  1. 处理器方法返回的字符串可以指定逻辑视图名,通过视图解析器解析可以将其转换为物理视图地址

  2. 也可以通过@ResponseBody,返回字符串

    @GetMapping(value = "/getIndex")
    public String getUserString(){
        return "redirect:index.jsp";
    }
    
    @GetMapping(value = "/getUserString",produces = "text/html;charset=utf-8")
    @ResponseBody
    public String getUserString(){
        User user = new User(1,"李四");
        return user.toString();
    }
    

void

  1. 若处理器对请求处理后,无需跳转到其它任何资源。例:AJAX响应

  2. 处理器方法返回值void,不能表示数据,也没有视图。

  3. 可以通过使用HttpServletResponse的输出对象,把数据输出到浏览器

    @PostMapping("/getUserJson")
    public void getUserJson(HttpServletResponse response) throws IOException {
        User user = new User(1,"李四");
        ObjectMapper om = new ObjectMapper();
        String userJson = om.writeValueAsString(user);
        PrintWriter out = response.getWriter();
        out.print(userJson);
        out.flush();
        out.close();
    }
    

Object

  1. 返回的对象不是作为逻辑视图出现的,而是作为直接在页面显示的数据出现的。

  2. 返回对象,需要使用@ResponseBody注解,将转换后的JSON数据放入到响应体中

    处理对象到json格式,需要消息转化器、json依赖

  • <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-databind</artifactId>
        <version>2.10.0</version>
    </dependency>
    
  • <mvc:annotation-driven/>
    

    执行过程源代码分析

  • 没有加入注解驱动标签时的状态
    org.springframework.http.converter.ByteArrayHttpMessageConverter 
    org.springframework.http.converter.StringHttpMessageConverter
    org.springframework.http.converter.xml.SourceHttpMessageConverter
    org.springframework.http.converter.support.AllEncompassingFormHttpMessageConverter
    
    
    加入注解驱动标签时的状态
    org.springframework.http.converter.ByteArrayHttpMessageConverter
    org.springframework.http.converter.StringHttpMessageConverter
    org.springframework.http.converter.ResourceHttpMessageConverter
    org.springframework.http.converter.ResourceRegionHttpMessageConverter
    org.springframework.http.converter.xml.SourceHttpMessageConverter 
    org.springframework.http.converter.support.AllEncompassingFormHttpMessageConverter 
    org.springframework.http.converter.xml.Jaxb2RootElementHttpMessageConverter
    org.springframework.http.converter.json.MappingJackson2HttpMessageConverter
    

中文乱码问题

解决方案

创建过滤器,进行请求、响应字符集过滤!

  • <filter>
        <filter-name>charsetEncoding</filter-name>
        <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
        <init-param>
            <param-name>encoding</param-name>
            <param-value>utf-8</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>charsetEncoding</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>	
    

源码分析

  • @Nullable
    private String encoding;
    
    private boolean forceRequestEncoding = false;
    
    private boolean forceResponseEncoding = false;
    
  • String encoding = getEncoding();
    if (encoding != null) {
        if (isForceRequestEncoding() || request.getCharacterEncoding() == null) {
            request.setCharacterEncoding(encoding);
        }
        if (isForceResponseEncoding()) {
            response.setCharacterEncoding(encoding);
        }
    }
    filterChain.doFilter(request, response);
    

RestFul风格

它是一种软件架构风格、设计风格,而不是标准,只是提供了一组设计原则和约束条件,它主要用于客户端和服务端交互类的软件。基于这个风格设计的软件可以更简介,更有层次,更易于实现缓存等机制

状态转化

HTTP协议里面,四个表示操作方式的动词:GET、POST、PUT、DELETE

分别对应四种基本操作:

1.	GET用来获取资源
2.	POST用来新建资源
3.	PUT用来更新资源
4.	DELETE用来删除资源

如何设计RESTful应用程序的API

路径设计:数据库设计完毕之后,基本上就可以确定有哪些资源要进行操作,相对应的路径也可以设计出来。

动词设计:也就是针对资源的具体操作类型,有HTTP动词表示,常用的HTTP动词如下:POST、DELETE、PUT、GET

SpringMVC对RESTful的支持

RESTful的URL路径变量

URL-PATTERN:设置为/,方便拦截RESTful请求。

@PathVariable:可以解析出来URL中的模板变量({id}/{name})

RESTful的CRUD

@RequestMapping:通过设置method属性的CRUD,可以将同一个URL映射不同HandlerMethod方法上。

@GetMapping、@PostMapping、@PutMapping、@DeleteMapping注解同@RequestMapping注解的method属性设置。

RESTful的资源表述

RESTful服务中一个重要的特性就是一种资源可以有多种表现形式,在SpringMvc中可以使用ContentNegotiatingManager这个内容协商管理器来实现这种方式。

使用

@PostMapping("/add/{a}/{b}")
public String add(@PathVariable int a,@PathVariable int b, Model model) throws IOException {
    model.addAttribute("result", a+b);
    return "test";
}

@GetMapping("/add/{a}/{b}")
public String delete(@PathVariable int a,@PathVariable int b, Model model) throws IOException {
    model.addAttribute("result", a-b);
    return "test";
}

整合SSM

搭建环境

  1. 数据库

    create DATABASE springmvc;
    
    use springmvc;
    
    drop table if  EXISTS emp;
    
    CREATE TABLE emp(
    	eNo int(10) primary KEY COMMENT '员工编号',
    	eName varchar(255) not NULL COMMENT '员工名',
    	sal double(11,2) not NULL COMMENT '薪资'
    )ENGINE=INNODB DEFAULT charset=utf8
    
    INSERT into emp(eNo,eName,sal) VALUES
    (1,'zhangsan',5000.0),
    (2,'lisi',4352.0),
    (3,'king',10000.0);
    
  2. 创建maven项目,构建为Web项目,导入依赖

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
    
        <groupId>com.rui</groupId>
        <artifactId>sprngmvc-ssm</artifactId>
        <version>1.0-SNAPSHOT</version>
    
    <!--
    依赖:
    	junit,数据库驱动,连接池,servlet ,jsp,mybatis,
    	spring,mybatis,mybatis-spring,spring-web,spring-jdbc
    	jackson,aspectj(aop)
    -->
        <dependencies>
             <!-- junit -->
         <dependency>
             <groupId>junit</groupId>
             <artifactId>junit</artifactId>
             <version>4.13.1</version>
             <scope>test</scope>
         </dependency>
         <!-- 数据库 -->
         <dependency>
             <groupId>mysql</groupId>
             <artifactId>mysql-connector-java</artifactId>
             <version>5.1.47</version>
         </dependency>
         <!--数据库连接池-->
         <dependency>
             <groupId>com.alibaba</groupId>
             <artifactId>druid</artifactId>
             <version>1.2.5</version>
         </dependency>
         <!-- servlet-jsp -->
         <dependency>
             <groupId>javax.servlet</groupId>
             <artifactId>javax.servlet-api</artifactId>
             <version>4.0.1</version>
             <scope>provided</scope>
         </dependency>
         <dependency>
             <groupId>javax.servlet.jsp</groupId>
             <artifactId>javax.servlet.jsp-api</artifactId>
             <version>2.3.3</version>
             <scope>provided</scope>
         </dependency>
         <dependency>
             <groupId>javax.servlet.jsp.jstl</groupId>
             <artifactId>jstl-api</artifactId>
             <version>1.2</version>
         </dependency>
         <dependency>
             <groupId>org.glassfish.web</groupId>
             <artifactId>jstl-impl</artifactId>
             <version>1.2</version>
             <scope>runtime</scope>
         </dependency>
         <!-- mybatis -->
         <dependency>
             <groupId>org.mybatis</groupId>
             <artifactId>mybatis</artifactId>
             <version>3.5.6</version>
         </dependency>
         <dependency>
             <groupId>org.mybatis</groupId>
             <artifactId>mybatis-spring</artifactId>
             <version>2.0.6</version>
         </dependency>
         <!-- spring -->
         <dependency>
             <groupId>org.springframework</groupId>
             <artifactId>spring-webmvc</artifactId>
             <version>5.3.5</version>
         </dependency>
         <dependency>
             <groupId>org.springframework</groupId>
             <artifactId>spring-jdbc</artifactId>
             <version>5.3.5</version>
         </dependency>
         <!--aspectj依赖-->
         <dependency>
             <groupId>org.springframework</groupId>
             <artifactId>spring-aspects</artifactId>
             <version>5.2.5.RELEASE</version>
         </dependency>
         <!--jackson-->
         <dependency>
             <groupId>com.fasterxml.jackson.core</groupId>
             <artifactId>jackson-databind</artifactId>
             <version>2.9.0</version>
         </dependency>
         <!--文件下载-->
         <dependency>
             <groupId>commons-fileupload</groupId>
             <artifactId>commons-fileupload</artifactId>
             <version>1.4</version>
         </dependency>
         <dependency>
             <groupId>commons-io</groupId>
             <artifactId>commons-io</artifactId>
             <version>2.8.0</version>
         </dependency>
         <!-- 邮件 -->
         <dependency>
             <groupId>javax.mail</groupId>
             <artifactId>mail</artifactId>
             <version>1.5.0-b01</version>
         </dependency>
        </dependencies>
    
        <build>
            <resources>
                <resource>
                    <directory>src/main/java</directory>
                    <includes>
                        <include>**/*.properties</include>
                        <include>**/*.xml</include>
                    </includes>
                    <filtering>false</filtering>
                </resource>
            </resources>
        </build>
    
    </project>
    
  3. 注册*调度器,字符集过滤器

    <?xml version="1.0" encoding="UTF-8"?>
    <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
             version="4.0">
    
        <servlet>
            <servlet-name>springMVC</servlet-name>
            <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
            <init-param>
                <param-name>contextConfigLocation</param-name>
                <param-value>classpath:spring-MVC.xml</param-value>
            </init-param>
            <load-on-startup>1</load-on-startup>
        </servlet>
        <servlet-mapping>
            <servlet-name>springMVC</servlet-name>
            <url-pattern>/</url-pattern>
        </servlet-mapping>
    
        <filter>
            <filter-name>charsetEncoding</filter-name>
            <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
            <init-param>
                <param-name>encoding</param-name>
                <param-value>utf-8</param-value>
            </init-param>
        </filter>
        <filter-mapping>
            <filter-name>charsetEncoding</filter-name>
            <url-pattern>/*</url-pattern>
        </filter-mapping>
    
        <session-config>
            <session-timeout>15</session-timeout>
        </session-config>
        
    </web-app>
    

编码

  1. 实体类(对应数据库)

    1. package com.rui.entity;
      
      public class Emp {
      
          private int eNo;
          private String eName;
          private double sal;
      
          public Emp() {
          }
      
          public Emp(int eNo, String eName, double sal) {
              this.eNo = eNo;
              this.eName = eName;
              this.sal = sal;
          }
      
          public int geteNo() {
              return eNo;
          }
      
          public void seteNo(int eNo) {
              this.eNo = eNo;
          }
      
          public String geteName() {
              return eName;
          }
      
          public void seteName(String eName) {
              this.eName = eName;
          }
      
          public double getSal() {
              return sal;
          }
      
          public void setSal(double sal) {
              this.sal = sal;
          }
      
          @Override
          public String toString() {
              return "Emp{" +
                      "eNo=" + eNo +
                      ", eName='" + eName + '\'' +
                      ", sal=" + sal +
                      '}';
          }
      }
      
  2. dao层(dao层接口,接口对应的mapper文件,mybatis的主配置文件,spring-mybatis的配置文件,数据库配置文件)

    1. package com.rui.dao;
      
      import com.rui.entity.Emp;
      import org.apache.ibatis.annotations.Param;
      
      import java.util.List;
      
      public interface EmpDao {
      
          //查询一个员工
          Emp toIdSelectEmp(@Param("eNo") int eNo);
          //删除一个员工
          int toIdDeleteEmp(@Param("eNo") int eNo);
          //更新一个员工
          int toIdUpdateEmp(Emp emp);
          //添加一个员工
          int addEmp(Emp emp);
          //展示所有员工
          List<Emp> selectAllEmp();
      
      }
      
    2. <?xml version="1.0" encoding="UTF-8" ?>
      <!DOCTYPE mapper
              PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
              "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
      <mapper namespace="com.rui.dao.EmpDao">
      
      
          <select id="toIdSelectEmp" resultType="emp">
              select * from emp where eNo =#{eNo}
          </select>
      
          <delete id="toIdDeleteEmp" >
              delete from emp where eNo =#{eNo}
          </delete>
      
          <insert id="addEmp">
              insert into emp(eNo,eName,sal) values(#{eNo},#{eName},#{sal})
          </insert>
      
          <update id="toIdUpdateEmp">
              update emp set eName=#{eName},sal=#{sal} where eNo =#{eNo}
          </update>
      
          <select id="selectAllEmp" resultType="emp">
              select * from emp;
          </select>
      </mapper>
      
    3. <?xml version="1.0" encoding="UTF-8" ?>
      <!DOCTYPE configuration
              PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
              "http://mybatis.org/dtd/mybatis-3-config.dtd">
      <configuration>
      
          <settings>
              <setting name="logImpl" value="STDOUT_LOGGING"/>
          </settings>
      
          <typeAliases>
              <package name="com.rui.entity"/>
          </typeAliases>
      
          <mappers>
              <package name="com.rui.dao"/>
          </mappers>
      
      </configuration>
      
    4. <?xml version="1.0" encoding="UTF-8"?>
      <beans xmlns="http://www.springframework.org/schema/beans"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xmlns:context="http://www.springframework.org/schema/context"
             xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
      
          <!--配置数据源-->
          <context:property-placeholder location="classpath:jdbc.properties" />
          <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"
                init-method="init" destroy-method="close">
              <property name="driverClassName" value="${jdbc.driver}"/>
              <property name="url" value="${jdbc.url}" />
              <property name="username" value="${jdbc.username}"/>
              <property name="password" value="${jdbc.password}" />
              <property name="maxActive" value="${jdbc.maxActive}" />
          </bean>
      
          <!--定义sqlSessionFactory对象-->
          <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
              <property name="dataSource" ref="dataSource" />
              <property name="configLocation" value="classpath:mybatis-config.xml" />
          </bean>
          <!--定义Mapper扫描配置器-->
          <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
              <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" />
              <property name="basePackage" value="com.rui.dao"/>
          </bean>
          
      </beans>
      
    5. jdbc.driver=com.mysql.jdbc.Driver
      jdbc.url=jdbc:mysql://localhost:3306/springmvc?useSSL=false&useUnicode=true&characterEncoding=UTF-8
      jdbc.username=root
      jdbc.password=guo12345
      jdbc.maxActive=20
      
  3. service层(接口,实现类,spring配置文件(扫描组件)),service层调dao层,进行组合

    1. package com.rui.service;
      
      import com.rui.entity.Emp;
      import java.util.List;
      
      public interface EmpService {
      
          Emp toIdSelectEmp(int eNo);
      
          int toIdDeleteEmp(int eNo);
      
          int toIdUpdateEmp(Emp emp);
      
          int addEmp(Emp emp);
      
          List<Emp> selectAllEmp();
      }
      
    2. package com.rui.service.impl;
      
      import com.rui.dao.EmpDao;
      import com.rui.entity.Emp;
      import com.rui.service.EmpService;
      import org.springframework.beans.factory.annotation.Autowired;
      import org.springframework.stereotype.Service;
      
      import java.util.List;
      
      @Service
      public class EmpServiceImpl implements EmpService {
      
          @Autowired
          private EmpDao empDao;
      
          public void setEmpDao(EmpDao empDao) {
              this.empDao = empDao;
          }
      
          @Override
          public Emp toIdSelectEmp(int eNo) {
              return empDao.toIdSelectEmp(eNo);
          }
      
          @Override
          public int toIdDeleteEmp(int eNo) {
              return empDao.toIdDeleteEmp(eNo);
          }
      
          @Override
          public int toIdUpdateEmp(Emp emp) {
              return empDao.toIdUpdateEmp(emp);
          }
      
          @Override
          public int addEmp(Emp emp) {
              return empDao.addEmp(emp);
          }
      
          @Override
          public List<Emp> selectAllEmp() {
              return empDao.selectAllEmp();
          }
      }
      
    3. <?xml version="1.0" encoding="UTF-8"?>
      <beans xmlns="http://www.springframework.org/schema/beans"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xmlns:context="http://www.springframework.org/schema/context"
             xsi:schemaLocation="http://www.springframework.org/schema/beans 
             http://www.springframework.org/schema/beans/spring-beans.xsd 
             http://www.springframework.org/schema/context 
             https://www.springframework.org/schema/context/spring-context.xsd">
      
          <context:component-scan base-package="com.rui.service"/>
      
      </beans>
      
  4. controller层(spring-mvc配置文件,控制器编写),controller层调service层,进行组合

    1. 首页

      1. <%@ page contentType="text/html;charset=UTF-8" language="java" %>
        <html>
          <head>
            <title> 员工系统 </title>
          </head>
          <body>
            <a href="${pageContext.request.contextPath}/allEmps">进入员工系统</a>
          </body>
        </html>
        
    2. 查询所有员工,跳转显示

      1. @GetMapping("/allEmps")
        public ModelAndView allEmps(){
            ModelAndView mv = new ModelAndView();
            List<Emp> emps = empService.selectAllEmp();
            mv.addObject("emps", emps);
            mv.setViewName("empList");
            return mv;
        }
        
      2. <%@ page contentType="text/html;charset=UTF-8" language="java" %>
        <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
        <html>
        <head>
            <title>员工列表</title>
        </head>
        <body>
        
        <a href="${pageContext.request.contextPath}/toAddPage">添加</a>
        <br>
        <table border="1" >
            <tr>
                <td>员工编号</td>
                <td>员工姓名</td>
                <td>员工薪资</td>
                <td colspan="2">操作</td>
            </tr>
            <c:forEach var="emp" items="${emps}">
                <tr>
                    <td>${emp.eNo}</td>
                    <td>${emp.eName}</td>
                    <td>${emp.sal}</td>
                    <td><a href="${pageContext.request.contextPath}/toUpdatePage?eNo=${emp.eNo}">修改</a></td>
                    <td><a href="${pageContext.request.contextPath}/delete?eNo=${emp.eNo}">删除</a></td>
                </tr>
            </c:forEach>
        </table>
        
        </body>
        </html>
        
    3. 添加员工

      1. @GetMapping("/toAddPage")
        public String toAddPage(){
            return "add";
        }
        
        @PostMapping("/add")
        public String addEmp(@RequestParam("eNo")int eNo,@RequestParam("eName")String eName,@RequestParam("sal")double sal){
            Emp emp = new Emp(eNo,eName,sal);
            empService.addEmp(emp);
            return "redirect:/allEmps";
        }
        
      2. <%@ page contentType="text/html;charset=UTF-8" language="java" %>
        <html>
        <head>
            <title>添加</title>
        </head>
        <body>
            <form action="${pageContext.request.contextPath}/add" method="post">
                编号<input name="eNo" type="text"/><br>
                姓名<input name="eName" type="text"/><br>
                薪资<input name="sal" type="text"/><br>
                <input  type="submit" value="添加"/>
            </form>
        </body>
        </html>
        
    4. 更新员工信息

      1. @GetMapping("/toUpdatePage")
        public String toUpdatePage(@RequestParam("eNo")int eNo,Model model){
            Emp emp = empService.toIdSelectEmp(eNo);
            model.addAttribute("emp", emp);
            return "update";
        }
        @PostMapping("/update")
        public String updateEmp(@RequestParam("eNo")int eNo,@RequestParam("eName")String eName,@RequestParam("sal")double sal){
            Emp emp = new Emp(eNo,eName,sal);
            empService.toIdUpdateEmp(emp);
            return "redirect:/allEmps";
        }
        
      2. <%@ page contentType="text/html;charset=UTF-8" language="java" %>
        <html>
        <head>
            <title>更新</title>
        </head>
        <body>
            <form action="${pageContext.request.contextPath}/update" method="post">
                编号<input name="eNo" type="text" value="${emp.eNo}" readonly/><br>
                姓名<input name="eName" type="text" value="${emp.eName}"/><br>
                薪资<input name="sal" type="text" value="${emp.sal}"/><br>
                <input  type="submit" value="添加"/>
            </form>
        </body>
        </html>
        
    5. 删除员工

      1. @GetMapping("/delete")
        public String deleteEmp(@RequestParam("eNo")int eNo){
            empService.toIdDeleteEmp(eNo);
            return "redirect:/allEmps";
        }
        
    6. spring-mvc配置

      1. <?xml version="1.0" encoding="UTF-8"?>
        <beans xmlns="http://www.springframework.org/schema/beans"
               xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
               xmlns:context="http://www.springframework.org/schema/context"
               xmlns:mvc="http://www.springframework.org/schema/mvc"
               xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd">
        
            <context:component-scan base-package="com.rui.controller"/>
            <!--视图解析器-->
            <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
                <property name="prefix" value="/WEB-INF/jsp/"/>
                <property name="suffix" value=".jsp"/>
            </bean>
            <!--处理静态资源-->
            <mvc:default-servlet-handler/>
            
            <mvc:annotation-driven/>
        
        </beans>
        
  5. 整合spring-*的配置文件,applicationContext.xml

    1. <?xml version="1.0" encoding="UTF-8"?>
      <beans xmlns="http://www.springframework.org/schema/beans"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
      
          <import resource="classpath:spring-mvc.xml"/>
          <import resource="classpath:spring-dao.xml"/>
          <import resource="classpath:spring-service.xml"/>
      
      </beans>
      

错误及解决方案

Jstl依赖导入,jsp中引入时爆红问题!

解决方法:导入jsp-api,另外导入jstl-impl

  • <dependency>
        <groupId>org.glassfish.web</groupId>
        <artifactId>jstl-impl</artifactId>
        <version>1.2</version>
        <scope>runtime</scope>
    </dependency>
    

Jackson包导入可能出现的问题

​ org.apache.catalina.startup.ContextConfig.processAnnotationsJar Unable to process Jar entry [module-info.class] from Jar

解决方法:

​ 在Maven本地仓库中找到相应的jar包,使用压缩软件打开,删除module-info.class

拦截器

​ 作用是拦截指定的用户请求,并进行相应的预处理与后处理。

拦截器的使用

自定义拦截器,需要实现HandlerInterceptor接口(三个默认实现方法)。

boolean preHandle(request,response, Object handler)

  • 处理器方法执行之前执行。若为true,则紧接着会执行处理器方法,
  • 且会将afterCompletion()方法放入到一个专门的方法栈中等待执行。

void postHandle(request,response, Object handler,modelAndView)

  • 该方法在处理器方法执行之后执行。处理器方法若最终未被执行,则该方法不会执行。
  • 由于该方法是在处理器方法执行完后执行,且该方法参数中包含ModelAndView,即该方法可以修改处理器方法的处理结果数据,且可以修改跳转方向。

void afterCompletion(request,response, Object handler, Exception ex)

  • 当preHandle()方法返回true时,会将该方法放到专门的方法栈中,等到对请求进行响应的所有工作完成之后才执行该方法。
  • 即该方法是在*调度器渲染(数据填充)了响应页面之后执行的,此时对ModelAndView再操作也对响应无济于事
  • afterCompletion最后执行的方法,清除资源,例如在Controller方法中加入数据

使用

  • package com.rui.controller.interceptor;
    
    import org.springframework.web.servlet.HandlerInterceptor;
    import org.springframework.web.servlet.ModelAndView;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    public class authorityVerify implements HandlerInterceptor {
    
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
            if("张三".equals("张三")){
                System.out.println("处理器1方法执行前");
                return true;
            }else{
                System.out.println("处理器1方法执行前");
                return false;
            }
        }
    
        @Override
        public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
            System.out.println("处理器1方法执行后");
        }
    
        @Override
        public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
            System.out.println("页面1渲染执行后");
        }
    }
    

    注册拦截器

  • <!--    <mvc:mapping/>用于指定当前所注册的拦截器可以拦截的请求路径,而/**表示拦截所有请求。-->
    <mvc:interceptors>
        <mvc:interceptor>
            <mvc:mapping path="/**"/>
            <bean class="com.rui.controller.interceptor.authorityVerify"/>
        </mvc:interceptor>
        <mvc:interceptor>
            <mvc:mapping path="/**"/>
            <bean class="com.rui.controller.interceptor.authorityVerify1"/>
        </mvc:interceptor>
    </mvc:interceptors>
    

拦截器的执行流程

SpringMVC

多个拦截器

执行结果
SpringMVC

编程注意事项

避免重复性“造*”

例如:方法的重载

  • 数据库关闭资源(以下代码造成了重复性“造*”)

    public static void close(ResultSet rs, Statement ps, Connection conn){
        if (rs == null) {
            try {
                rs.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if (ps == null) {
            try {
                ps.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if (conn == null) {
            try {
                conn.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
    
    public static void close(Statement ps, Connection conn){
        if (ps == null) {
            try {
                ps.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if (conn == null) {
            try {
                conn.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
    
  • 优化(避免了重复性“造*”)

    public static void close(ResultSet rs, Statement ps, Connection conn){
        if (rs == null) {
            try {
                rs.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if (ps == null) {
            try {
                ps.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if (conn == null) {
            try {
                conn.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
    
    public static void close(Statement ps, Connection conn){
     close(null,ps,conn);
    }
    

避免多余分工

对于一个类的相关类,若相关类只作用于该类,可以定义为内部类,降低项目的复杂度。
可以避免查看源码时不停的跳转

避免火箭式开发

​ 多层嵌套,可读性极差,可以分开判断(不需要太考虑效率的问题)

  • 	public boolean verifyUser{
            char[] chars = null ;
            if(user != null){
                if(user.name.equals("张三")){
                    chars = user.name.toCharArray();
                    for(char a :chars){
                        if(a == 'a'){
                            System.out.print(a);
                        }
                    }
             }else{
                    return false;
             }else{
                    return false;
             }
            return true;
        }
    
    
        public boolean verifyUser{
            char[] chars = null ;
            if(user == null){
                return false;
            }
            if(user.name.equals("张三")){
                chars = user.name.toCharArray();
            }else{
                return false;
            }
            for(char a :chars){
                if(a == 'a'){
                    System.out.print(a);
                }
            }
            return ture;
        }
    

注:构建项目->由上向下、排查->由下向上

扩展

文件上传与下载

  1. 导包

    <!--文件下载-->
            <dependency>
                <groupId>commons-fileupload</groupId>
                <artifactId>commons-fileupload</artifactId>
                <version>1.4</version>
            </dependency>
            <dependency>
                <groupId>commons-io</groupId>
                <artifactId>commons-io</artifactId>
                <version>2.8.0</version>
            </dependency>
    
  2. 设置表单类型及提交方式

    1. 表单类型:multipart/form-data
    2. 提交方式(必须是post请求,不能为get请求)
    <form action="${pageContext.request.contextPath }/fileUpload2" method="post" enctype="multipart/form-data">
        文件:<input type="file" name="file"/> <br>
        <input type="submit" value="上传"/>
    </form>
    
  3. 若使用SpringMVC实现,需要配置

    <!-- 用于文件上传、下载的配置 -->
    <bean id="multipartResolver"
          class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
        <property name="defaultEncoding" value="utf-8"></property>
        <property name="maxUploadSize" value="2097152"></property>
    </bean>
    

第三方jar包实现

  • 上传

  • public class RegisterServlet extends HttpServlet {
    	protected void doPost(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {
            // 判断请求是否为multipart请求
            if( !ServletFileUpload.isMultipartContent(request) ) {
                throw new RuntimeException("当前请求不支持文件上传");
            }
            try {
                // 创建一个FileItem工厂
                DiskFileItemFactory factory = new DiskFileItemFactory();
    
                // 设置使用临时文件的边界值,大于该值,上传文件会先保存在临时文件中
                //否则,上传文件将直接写入到内存。
                // 单位:字节。本例设置边界值为1M
                factory.setSizeThreshold(1024 * 1024 * 1);
    
                // 设置临时文件
                String tempPath = this.getServletContext().getRealPath("/temp");
                File temp = new File(tempPath);
                factory.setRepository(temp);
    
                // 创建文件上传核心组件
                ServletFileUpload upload = new ServletFileUpload(factory);
    
                // 设置每一个item的头部字符编码,其可以解决文件名的中文乱码问题
                upload.setHeaderEncoding("UTF-8");
    
                // 设置单个上传文件的最大边界值为2M
                upload.setFileSizeMax(1024 * 1024 * 2);
    
                // 设置一次上传所有文件的总和最大值为5M(对于上传多个文件时起作用)
                upload.setSizeMax(1024 * 1024 * 5);
    
                // 解析请求,获取到所有的item
                List<FileItem> items = upload.parseRequest(request);
                // 遍历items
                for (FileItem item : items) {
                    if(item.isFormField()) {   // 若item为普通表单项
                        String fieldName = item.getFieldName();  // 获取表单项名称
                        String fieldValue = item.getString("UTF-8"); // 获取表单项的值
                    } else {   // 若item为文件表单项
                        String fileName = item.getName();    // 获取上传文件原始名称
                        fileName = System.currentTimeMillis() + fileName;
                        // 获取输入流,其中有上传文件的内容
                        InputStream is = item.getInputStream();
                        // 获取文件保存在服务器的路径
                        String path =this.getServletContext().getRealPath("/file");
                        // 若该目录不存在,则创建这个目录
                        File dirFile = new File(path);
                        if (!dirFile.exists()) {
                            dirFile.mkdirs();
                        }
    
                        // 创建目标文件,将来用于保存上传文件
                        File descFile = new File(path, fileName);
                        // 创建文件输出流
                        OutputStream os = new FileOutputStream(descFile);
                        // 将输入流中的数据写入到输出流中
                        int len = -1;
                        byte[] buf = new byte[1024];
                        while((len = is.read(buf)) != -1) {
                            os.write(buf, 0, len);
                        }
    
                        // 关闭流
                        os.close();
                        is.close();
    
                        // 删除临时文件
                        item.delete();
                    }
                }
            } catch (FileUploadException e) {
                e.printStackTrace();
            }
    
        }
    
    }
    

下载

  • public class DownloadServlet extends HttpServlet {
    	protected void doGet(HttpServletRequest request, HttpServletResponse response)
    			throws ServletException, IOException {
    		
    		String fileName = "桌面.jpg";
    		
    		// 打散:按当前的字符编码进行打散
    		byte[] bytes = fileName.getBytes("UTF-8");
    		// 组装:按目标字符编码进行组装
    		fileName = new String(bytes, "ISO8859-1");
    		
    		// 修改响应的头部属性content-disposition值为attachment
    		response.setHeader("content-disposition", "attachment;filename=" + fileName);
    		
    		// 获取连接服务端资源文件的输入流
    		InputStream is = this.getServletContext().getResourceAsStream("/resources/aaa.jpg");
    		// 获取输出流 
    		ServletOutputStream os = response.getOutputStream();
    		// 将输入流中的数据写入到输出流中
    		int len = -1;
    		byte[] buf = new byte[1024];
    		while((len = is.read(buf)) != -1) {
    			os.write(buf, 0, len);
    		}
    		
    		// 关闭流
    		os.close();
    		is.close();
    	}
    

servlet3.0新特性

上传

  • @MultipartConfig
    public class LoadupServlet extends HttpServlet {
        protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            // 获取服务端保存上传文件的目录路径
            String path = this.getServletContext().getRealPath("/file");
            // 从请求中获取Multipart请求中的上传文件“部分”对象
            request.setCharacterEncoding("utf-8");
            Part part = request.getPart("file");
            // 解析出原始文件名
            // 获取指定的头部属性
            String header = part.getHeader("Content-Disposition");
            int index = header.lastIndexOf("=");
            String fileName = header.substring(index + 2, header.length() - 1);
            // 完成文件上传
            part.write(path + fileName);
        }
    
    }
    

springmvc实现

上传(方式一)

  • //@RequestParam("file")将name-file控件得到的文件封装成CommonsNultipartFile对象
        //批量上传CommonsMuLtipartFiLe则为数组即可
        @RequestMapping("/ upload")
        public String fileUpload(@RequestParam("file")CommonsMultipartFile file , HttpServletRequest request) throws IOException {
        //获取文件名 :file.getoriginalFiLename();
            String uploadFileName = file.getOriginalFilename();
    
        //如果文件名为空,直接回到首页!
            if ("".equals(uploadFileName)){
                return "redirect:/index.jsp";
            }
    
            //上传路径保存设置
            String path = request.getServletContext( ).getRealPath("/upload");
            //如果路径不存在,创建一个
            File realPath = new File(path);
            if (!realPath.exists()){
                realPath. mkdir();
            }
    
            InputStream is = file.getInputStream();//文件输入流
            OutputStream os = new FileOutputStream(new File(realPath,uploadFileName));//文件输出流
    
            //读取写出
            int len=0;
            byte[]	buffer = new byte[1024];
            while ( ( len=is.read( buffer))!=-1){
                os.write( buffer, 0,len);
                os.flush();
            }
            os.close();
            is.close();
            return "redirect:/index.jsp";
        } 
    

上传(方式二)

  •  /*
     * 采用file.Transto 来保存上传的文件
    */
    @RequestMapping("fileUpload2")
    public String  fileUpload2(@RequestParam("file") CommonsMultipartFile file,HttpServletRequest request) throws IOException {
        //获取文件名
        String fileName = file.getOriginalFilename();
        //生成随机编号(避免文件名相同)
        UUID uuid = UUID.randomUUID();
        String path=request.getServletContext().getRealPath("/file")+"\\"+uuid+fileName;
        System.out.println(path);
        File newFile = new File(path);
        
        //通过CommonsMultipartFile的方法直接写文件(注意这个时候)
        file.transferTo(newFile);
        return "forward:/index.jsp";
    }
    

下载

  • @RequestMapping(value="/download")
        public String downloads(HttpServletResponse response , HttpServletRequest request) throws Exception{
            //要下载的图片地址
            String path = request.getServletContext().getRealPath("/web");
            String fileName ="桌面.PNG" ;
    
            //1、设置response响应头
            response.reset();//设置页面不缓存,清空buffer
            response.setCharacterEncoding("UTF-8");//字符编码
            response.setContentType("multipart/form-data");//二进制传输数据
            //设置响应头
            response.setHeader("Content-Disposition","attachment;fileName="+ URLEncoder.encode(fileName,"UTF-8"));
    
            File file = new File(path,fileName) ;
            //2、读取文件--输入流
            InputStream input= new FileInputStream(file);
            //3、写出文件--输出流
            OutputStream out = response.getOutputStream();
    
            byte[] buff =new byte[1024];
            int index= 0;
            //4、执行写出操作
            while((index= input.read(buff))!= -1){
                out.write( buff,0,index) ;
                out.flush();
            }
            out.close();
            input.close();
            return null;
        }
    

邮件的发送

在网络上实现邮件功能,必须要有专门的邮件服务器。这些邮件服务器类似于现实生活中的邮局,它主要负责接收用户投递过来的邮件,并把邮件投递到邮件接收者的电子邮箱中。

SMTP服务器地址:一般是 smtp.xxx.com,比如qq邮箱是smtp.qq.com。

SMTP协议:通常把处理用户smtp请求(邮件发送请求)的服务器称之为SMTP服务器(邮件发送服务器)。

POP3协议:通常把处理用户pop3请求(邮件接收请求)的服务器称之为POP3服务器(邮件接收服务器)。

SpringMVC

  1. 导包
<!-- 邮件 -->
<dependency>
    <groupId>javax.mail</groupId>
    <artifactId>mail</artifactId>
    <version>1.5.0-b01</version>
</dependency>

普通邮件发送

public class test {


        public static void main(String[] args) throws MessagingException, GeneralSecurityException {
            //创建一个配置文件并保存
            Properties properties = new Properties();

            properties.setProperty("mail.host","smtp.qq.com");

            properties.setProperty("mail.transport.protocol","smtp");

            properties.setProperty("mail.smtp.auth","true");


            //QQ存在一个特性设置SSL加密
            MailSSLSocketFactory sf = new MailSSLSocketFactory();
            sf.setTrustAllHosts(true);
            properties.put("mail.smtp.ssl.enable", "true");
            properties.put("mail.smtp.ssl.socketFactory", sf);

            //创建一个session对象
            Session session = Session.getDefaultInstance(properties, new Authenticator() {
                @Override
                protected PasswordAuthentication getPasswordAuthentication() {
                    return new PasswordAuthentication("1423457753@qq.com","授权码");
                }
            });

            //开启debug模式
            session.setDebug(true);

            //获取连接对象
            Transport transport = session.getTransport();

            //连接服务器
            transport.connect("smtp.qq.com","1423457753@qq.com","16位授权码");

            //创建邮件对象
            MimeMessage mimeMessage = new MimeMessage(session);

            //邮件发送人
            mimeMessage.setFrom(new InternetAddress("1423457753@qq.com"));

            //邮件接收人
            mimeMessage.setRecipient(Message.RecipientType.TO,new InternetAddress("1423457753@qq.com"));

            //邮件标题
            mimeMessage.setSubject("Hello world");

            //邮件内容
            mimeMessage.setContent("System.out.print","text/html;charset=UTF-8");

            //发送邮件
            transport.sendMessage(mimeMessage,mimeMessage.getAllRecipients());

            //关闭连接
            transport.close();
        }


}

复杂的邮件

MIME(多用途互联网邮件扩展类型)

MimeBodyPart类

javax.mail.internet.MimeBodyPart类 表示的是一个MIME消息,它和MimeMessage类一样都是从Part接口继承过来。

MimeMultipart类

javax.mail.internet.MimeMultipart是抽象类 Multipart的实现子类,它用来组合多个MIME消息。一个MimeMultipart对象可以包含多个代表MIME消息的MimeBodyPart对象

SpringMVC

public class test {

    public static void main(String[] args) throws GeneralSecurityException, MessagingException {
        Properties prop = new Properties();
        prop.setProperty("mail.host", "smtp.qq.com"); // 设置QQ邮件服务器
        prop.setProperty("mail.transport.protocol", "smtp"); // 邮件发送协议
        prop.setProperty("mail.smtp.auth", "true"); // 需要验证用户名密码

        // QQ邮箱设置SSL加密
        MailSSLSocketFactory sf = new MailSSLSocketFactory();
        sf.setTrustAllHosts(true);
        prop.put("mail.smtp.ssl.enable", "true");
        prop.put("mail.smtp.ssl.socketFactory", sf);

        //1、创建定义整个应用程序所需的环境信息的 Session 对象
        Session session = Session.getDefaultInstance(prop, new Authenticator() {
            @Override
            protected PasswordAuthentication getPasswordAuthentication() {
                //传入发件人的姓名和授权码
                return new PasswordAuthentication("1423457753@qq.com","tvqczbqkyrfabbeh");
            }
        });

        //2、通过session获取transport对象
        Transport transport = session.getTransport();

        //3、通过transport对象邮箱用户名和授权码连接邮箱服务器
        transport.connect("smtp.qq.com","1423457753@qq.com","tvqczbqkyrfabbeh");

        //4、创建邮件,传入session对象
        MimeMessage mimeMessage = complexEmail(session);

        //5、发送邮件
        transport.sendMessage(mimeMessage,mimeMessage.getAllRecipients());

        //6、关闭连接
        transport.close();

    }

    public static MimeMessage complexEmail(Session session) throws MessagingException {
        //消息的固定信息
        MimeMessage mimeMessage = new MimeMessage(session);

        //发件人
        mimeMessage.setFrom(new InternetAddress("1423457753@qq.com"));
        //收件人
        mimeMessage.setRecipient(Message.RecipientType.TO,new InternetAddress("1423457753@qq.com"));
        //邮件标题
        mimeMessage.setSubject("带图片和附件的邮件");

        //邮件内容
        //准备图片数据
        MimeBodyPart image = new MimeBodyPart();
        DataHandler handler = new DataHandler(new FileDataSource("D:\\电脑备份\\桌面.png"));
        image.setDataHandler(handler);
        image.setContentID("photo.png"); //设置图片id

        //准备文本
        MimeBodyPart text = new MimeBodyPart();
        text.setContent("一段文字中插入图片<img src='cid:photo.png'>下一段文字", "text/html;charset=utf-8");

        //附件
        MimeBodyPart appendix = new MimeBodyPart();
        appendix.setDataHandler(new DataHandler(new FileDataSource("D:\\电脑备份\\settings.xml")));
        appendix.setFileName("settings.xml");

        //拼装邮件正文
        MimeMultipart mimeMultipart = new MimeMultipart();
        mimeMultipart.addBodyPart(image);
        mimeMultipart.addBodyPart(text);
        mimeMultipart.setSubType("related");//文本和图片内嵌成功

        //将拼装好的正文内容设置为主体
        MimeBodyPart contentText = new MimeBodyPart();
        contentText.setContent(mimeMultipart);

        //拼接附件
        MimeMultipart allFile = new MimeMultipart();
        allFile.addBodyPart(appendix);//附件
        allFile.addBodyPart(contentText);//正文
        allFile.setSubType("mixed"); //正文和附件都存在邮件中,所有类型设置为mixed

        //放到Message消息中
        mimeMessage.setContent(allFile);
        mimeMessage.saveChanges();//保存修改

        return mimeMessage;
    }

}

上一篇:SpringMVC---数据响应


下一篇:SpringMVC_2