Java框架(SpringMvc)学习笔记

SpringMVC

一.简介

1.框架特征

MVC框架

mvc是一种设计模式

  • 天生就与Spring整合
  • 提供了大量的注解来代替原有的配置文件
  • 提供了非常简单的web支持
  • 提供了大量的扩展点

m:model:模型 vo对象 java代码

v:view :视图 html,jsp

c:controller 控制器 controller servlet

控制器根据用户请求决定调用哪个模型去处理请求,然后决定调用哪个视图来显示模型处理的返回数据

2.运行机制

客户端发送请求到达*控制器DispatcherServlet

由*控制器负责解析整个SpringMVC的运行流程

当*控制器接收到请求之后,会将请求先交给HandlerMapping进行处理

HandlerMapping负责解析整个SpringMVC的业务流程,将请求分发给合适的处理器

处理完成之后将处理的结果返回给*控制器

*控制器将处理结果交给HandlerAdapter

由HandlerAdapter负责适配不同类型的处理器,找到对应的Handler进行处理

Handler负责处理核心业务逻辑并返回响应的视图与模型

将响应结果封装成一个对象,ModelAndView

ModelAndView负责处理模型与视图

最终会将ModelAndView交给*控制器

*控制器将结果交给ViewResolver

ViewResolver负责解析响应结果,将其解析为具体的视图技术

根据解析结果找到View,由View使用具体的视图技术来进行具体的实现

最终将处理完成的结果返回给*控制器

由*控制器将结果返回给客户端

最终在客户端生成对应的效果

二. 第一个SpringMVC程序

1.环境搭建

1-1 POM配置

<?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.itany.springmvc</groupId>
  <artifactId>springmvc</artifactId>
  <version>1.0-SNAPSHOT</version>
  <packaging>war</packaging>

  <properties>
    <spring.version>5.2.5.RELEASE</spring.version>
  </properties>

  <dependencies>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-webmvc</artifactId>
      <version>${spring.version}</version>
    </dependency>

    <!-- J2EE 环境依赖 begin -->
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>javax.servlet-api</artifactId>
      <version>3.1.0</version>
      <scope>provided</scope>
    </dependency>

    <!-- J2EE 环境依赖 end -->
  </dependencies>

</project>

1-2 web.xml配置

在web.xml中对*控制器进行配置

*控制器DispatcherServlet是一个Servlet

<!-- 配置*控制器 -->
<servlet>
    <!--
        此处的servlet-name理论上可以随便写
        但是此处的值是用于控制SpringMVC默认的配置文件的名字的
        假设此处的值是xxx,则其配置文件的文件名就叫做:xxx-servlet.xml
        默认情况下,读取的配置文件是存放在WEB-INF根目录下的
    -->
    <servlet-name>controller</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
  
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:spring.xml</param-value>
        </init-param>
    <!--希望在容器启动的时候创建servlet,并且创建spring容器-->
    <load-on-startup>0</load-on-startup>
</servlet>

<servlet-mapping>
    <servlet-name>controller</servlet-name>
    <url-pattern>*.do
    </url-pattern>
</servlet-mapping>

2.xml配置实现

1.导入依赖

2.创建controller 新建一个普通类,实现ctroller接口,重写方法

3.spring配置文件中配置一个handlerMapping,HandlerAdapter

BeanNameUrlHandlerMapping SimpleControllerHandlerAdapter

4.配置handler

5.配置视图解析器

<!--1.处理器映射器,作用就是根据url找到对应的处理器   /hello -->
<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"></bean>
<!--2.处理器适配器  作用就是调用处理器的方法    -->
<bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"></bean>
<!-- 3.视图解析器
    获取modelAndView的数据
    获取视图名 比如说success
    根据视图名去解析出视图 /WEB-INF/pages/success.htm
    把数据渲染到视图
   -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <property name="prefix" value="/WEB-INF/pages/"/>
    <property name="suffix" value=".html"/>
</bean>

<!--  配置控制器  -->
<bean id="/hello" class="com.controller.HelloController"></bean>

3.注解实现

3-1 配置文件

<!-- 开启注解版的springmvc,帮我们配置了SpringMVC的基本固定配置项,包含HandlerMapping与HandlerAdapter -->
<mvc:annotation-driven/>

<!-- 扫包,扫描指定包下的注解 -->
<context:component-scan base-package="controller"/>

<!-- 配置viewResolver -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <!-- 配置前缀 -->
    <property name="prefix" value="/WEB-INF/pages/"/>
    <!-- 配置后缀 -->
    <property name="suffix" value=".html"/>
</bean>

3-2 常用注解

  • @Controller

    • 标注在类上
    • 表示当前类是一个由Spring所管理的bean
    • 该注解表示当前bean是一个Controller
    • 其用法类似于普通bean@Component
  • @RequestMapping

    • 该注解可以标注在方法上,也可以标注在类上,也可以都标注
    • 表示访问对应的资源所需要的命令,其value属性用法类似于Servlet的url-pattern
    • 如果标注在类上,表示访问该类所需要的的命令

      • 如果只在类上进行标注,此时无法直接访问
      • 但是可以对类中的方法的请求进行限定
      • 每一个请求只能对应一个方法,如果超过一个则报错
    • 如果标注在方法上,表示访问该方法所需要的命令

      • 如果只在方法上进行标注,则可以直接访问
    • 如果类上与方法上同时存在,必须先访问类,后访问方法
    • value属性

      • 配置访问资源所需要的命令
      • 其值可以是字符串,也可以是字符串数组
      • 如果是一个字符串数组,数组中每一个元素都是该资源的访问命令
      • 如果值是一个字符串,且当前注解中有且仅有value属性

        • 此时关键字value=可以省略
@Controller
//@RequestMapping(value="/hello")
@RequestMapping("/hello")
public class HelloAnnotation {

    @RequestMapping(value={"/f1"})
    public ModelAndView f1(){
        ModelAndView mav = new ModelAndView();
        mav.setViewName("hello");
        mav.addObject("msg","Hello Annotation");
        return mav;
    }
}

三.响应配置

1.直接访问视图

<!--
    配置直接访问视图
    path属性:访问命令
    view-name属性:访问的视图名
            该视图名同样经过viewResolver处理
            最终访问的视图为:prefix+view-name+suffix
-->
<mvc:view-controller path="/showLogin" view-name="login"/>

2.Handler方法返回值类型

  • ModelAndView

    • 返回模型与视图
    • 可以两者均返回,也可以只返回其中任意一种
  • String

    • 使用字符串作为返回值的时候,其返回值的格式有三种情况
    • 直接写字符串

      • 返回的是视图名
      • 将当前的方法的返回值作为视图名来处理
      • 会经过viewResolver处理
    • redirect:命令

      • 使用重定向的方式访问指定的命令
      • 不和视图解析器一起工作。
      • 框架对重定向的操作:框架会把modelAndView中的简单类型的数据转为字符串,作为目标页面的get请求参数使用。目的是在两次请求中传递参数。重定向不能访问web-inf资源。
    • forward:命令

      • 使用转发的方式访问指定的命令
      • 不和视图解析器一起工作。

      @RequestMapping(value = "/forwardTest")

      public ModelAndView handle4(@RequestParam("name") String name){

      ModelAndView mv = new ModelAndView();

      mv.addObject("msg",name);

      mv.setViewName("forward:/WEB-INF/pages/success.html");

      return mv;}

  • void(少见)

    如果方法中有响应对象参数,返回响应可以用这个参数处理

    如果方法中没有相应参数,则会根据请求url作为viewName通过视图解析器处理响应。

  • @ResponseBody Object

    • 当方法或者方法的返回值类型被@ResponseBody进行标注的时候
    • 表示当前方法的返回值是作为数据模型返回
    • 不会返回视图,只返回数据模型
    • 一般用于Ajax操作
    • 如果当前Controller中所有的方法都是返回数据模型的时候

      • 可以在当前类上使用@RestController代替原有的@Controller
      • 当类使用@RestController进行标注之后
      • 其效果相当于为当前类中的每一个方法都自动添加@ResponseBody注解
//@Controller
@RestController
@RequestMapping("/resp")
public class ResponseController {

    @RequestMapping("/f1")
    public String f1(){
        return "login";
    }

    @RequestMapping("/f2")
    public ModelAndView f2(){
        ModelAndView mav = new ModelAndView();
        mav.setViewName("login");
        return mav;
    }

    @RequestMapping("/f3")
    public void f3(){
        System.out.println("ResponseController.f3");
        String s = "/WEB-INF/pages/"+"resp/f3"+".html";
    }

    @RequestMapping("/f4")
    public void f4(HttpServletResponse response) throws IOException {
        System.out.println("ResponseController.f4");
        PrintWriter out = response.getWriter();
        out.print("<h1 style='color:green;'>Hello World</h1>");
    }

    @ResponseBody
    @RequestMapping("/f5")
    public String f5(){
        return "<h1>hello</h1>";
    }

}

3.POST请求字符编码过滤器

<filter>
    <filter-name>characterEncodingFilter</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>
    
    <init-param>
         <param-name>forceEncoding</param-name>
         <param-value>true</param-value>
    </init-param>
</filter>

<filter-mapping>
    <filter-name>characterEncodingFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

4.访问静态资源

默认情况下,如果*处理器设置拦截的url-pattern是/;所有的SpringMVC工程均无法直接访问静态资源

静态资源:HTML、CSS、JavaScript、image...

必须通过配置实现访问静态资源的目的

有两种方式进行配置

  • 方式

    • 配置访问非WEB-INF下的资源
    <mvc:default-servlet-handler/>

练习:pages目录下添加login.html controller中添加控制器类,处理登陆请求

四.请求配置

1.路径的配置

1.1.基本配置
使用@RequestMapping(value="url")进行直接配置

该注解表示配置访问命令,可以配置在类上,也可以配置在方法上

1.2.ANT风格配置(使用通配符)
通配符配置
  • *

    • 匹配一层路径
  • **

    • 匹配0或多层路径
  • ?

    • 只匹配一个字符
    • 可以与其他字符联合使用
@RequestMapping("/f1/*")
public void f1(){
    System.out.println("RequestController.f1");
}

@RequestMapping("/f2/**")
public void f2(){
    System.out.println("RequestController.f2");
}

@RequestMapping("/f3/a?")
public void f3(){
    System.out.println("RequestController.f3");
}
1.3.REST风格配置
查询:   /user   GET
删除:   /user   DELETE
新建:   /user   POST
更新:   /user    PUT
  • {xxx}

    • 表示一层路径
    • 该方式必须与@PathVariable("xxx")联合使用
    • 该注解标注在方法的参数前,表示将路径中的xxx与方法的参数进行绑定
    • 当用户输入访问命令的时候,xxx部分的值即为当前参数的值
    • 当注解中xxx的值与变量名一致的时候,注解的参数可以省略
    • REST配置在一个请求中可以配置无数个
@RequestMapping("/f4/{name}")
public void f4(@PathVariable String name){
    System.out.println("RequestController.f4,name:"+name);
}

@RequestMapping("/f5/{aaa}/{password}")
public void f5(@PathVariable("aaa") String username,@PathVariable String password){
    System.out.println("username:"+username+",password:"+password);
}

@RequestMapping("/f6/{id}")
public void f6(@PathVariable Integer id){
    System.out.println("id:"+id);
}

2.其他配置

2.2 可以限制请求提交的方式
  • 可以通过两种方式进行设置
  • 方式一

    • 通过@RequestMapping的method属性指定当前请求的类型
    • 其值是一个枚举类型,通过RequestMethod进行获取
    • 请求方式一共八种

      • GET
      • POST
      • PUT
      • DELETE
      • HEAD
      • PTACH
      • OPTIONS
      • TRACE
  • 方式二

    • 直接通过使用@XXXMapping代替原有的@RequestMapping注解
    • XXX的值即为请求方式
    • 例如:@PostMapping表示使用post请求进行访问
@RequestMapping(value = "/f9",method = RequestMethod.POST)
public void f9(){
    System.out.println("RequestController.f9");
}

@PostMapping("/f10")
public void f10(){
    System.out.println("RequestController.f10");
}

五.参数配置

1.参数种类

1.1原生servlet对象
request/response/session
  • 这些都可以直接作为方法的参数使用
  • application不能直接作为方法的参数

InputStream/OutputStream

Reader/Writer/PrintWriter

1.2简单类型

@RequestParam(key)

  • 该注解标注在方法的参数前
  • 表示自动将请求中的参数的数据装配到对应的方法的参数中
  • 通过注解的key与请求中数据的key进行匹配
  • 当两者一致时,进行装配
  • 当方法的参数名与注解的key相同的时候,key可以省略
  • 当方法的参数不是一个对象的时候,且方法的参数名与注解的key相同

    • 此时注解可以省略
    • 当注解被省略之后,如果请求中没有传递指定的参数,则参数的值为null
    • 如果注解没有被省略,请求中没有传递指定的参数,则报错,400
  • 默认情况下

    • 当Handler方法的参数没有被任何注解进行标注
    • 且方法的参数是一个普通类型(非对象)
    • 且方法的参数有值的时候
    • 默认该参数自动被@RequestParam("参数名")进行标注
1.3pojo对象类型 Plain Ordinary java object

参数可以是对象类型

  • 在获取请求的时候
  • 会自动解析请求中所有的数据以及方法对象中所有的属性
  • 将请求中数据的key与对象中属性的名字进行匹配
  • 如果请求中数据的key与对象属性的名字相同,则进行数据的装配
  • 将请求中的value注入给对象的属性值中
1.5Model/Map/ModelMap

都会被封装成BindingAwareModelMap类型

  • 这三个参数用法一致,其中ModelMap就是ModelAndView中的模型部分
  • 这三个参数的用法类似于request作用域
  • 用于存储数据模型

@PathVariable("xxx")

  • 详情请参考REST风格的请求配置
@Controller
@RequestMapping("/param")
public class ParamController {

    @RequestMapping("/f1")
    public void f1(HttpServletRequest request, HttpServletResponse response, HttpSession session){
        System.out.println("ParamController.f1");
        ServletContext application = session.getServletContext();
        System.out.println("application:"+application);
    }

    @RequestMapping("/f2")
    public void f2(ServletContext application){
        System.out.println("ParamController.f2");
    }

    @RequestMapping("/f3")
    public void f3(InputStream in, OutputStream out){
        System.out.println("ParamController.f3");
    }

    @RequestMapping("/f4")
    public void f4(Reader reader, Writer writer, PrintWriter out){
        System.out.println("ParamController.f4");
    }

    @RequestMapping("/f5")
    public String f5(Model model, Map map, ModelMap modelMap){
        model.addAttribute("model","Hello Model");
        map.put("map","Hello Map");
        // ModelMap即可以使用model的方法,也可以使用map的方法
        // 相当于两者的结合
        modelMap.addAttribute("modelMap1","Hello ModelMap-Model");
        modelMap.put("modelMap2","Hello ModelMap-Map");
//        return "model";
        return "redirect:/showModel";
    }

    @RequestMapping("/f6")
    public String f6(Model model){
        model.addAttribute("msg","Hello");
//        return "model";
        return "redirect:/showModel";
    }

    @RequestMapping("/f7")
    public void f7(String username){
        System.out.println("username:"+username);
    }

    @RequestMapping("/f8")
    public void f8(@RequestParam("username") String username){
        System.out.println("username:"+username);
    }

    @RequestMapping("/f9")
    public void f9(@Value("admin") String name){
        System.out.println("name:"+name);
    }
}

七.异常处理

1.全局异常处理

2-1 通过@ControllerAdvice来处理

定义一个Java类,该类用于处理全局异常

该类使用@ControllerAdvice进行标注

表示当前类是一个全局异常处理类

<context:component-scan base-package="global"/>
@ControllerAdvice
public class GlobalException {
    @ExceptionHandler
    public ModelAndView exceptionHandler(Exception e){
        return new ModelAndView("exception","msg","服务器内部异常");
    }
}

八.拦截器

在处理Handler业务方法之前或者之后以及响应到达之前做一些额外的处理

可以将一些相关的操作封装成拦截器,进行额外的处理

1.开发步骤

1-1 创建一个Java类

该类实现HandlerInterceptor接口

不同Spring版本需求也不一样

  • 在Spring5中已经对这些方法做了默认的实现

    • 因此,可以根据需求选择性的重写部分的方法
  • 在Spring4中并没有对这些方法做任何的实现

    • 因此,必须重写所有的方法
public class LogHandlerInterceptor implements HandlerInterceptor {


    /**
     * 在处理核心业务逻辑之前执行
     * @param request       请求对象
     * @param response      响应对象
     * @param handler       Handler对象
     * @return              是否放行,true-放行,false-不放行
     * @throws Exception
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("LogHandlerInterceptor.preHandle");
        return false;
    }

    /**
     * 在处理核心业务逻辑之后执行
     * @param request
     * @param response
     * @param handler
     * @param modelAndView  目标Handler的视图与模型
     * @throws Exception
     */
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("LogHandlerInterceptor.postHandle");
    }

    /**
     * 在响应到达之前执行
     * @param request
     * @param response
     * @param handler
     * @param e     遇到的异常
     * @throws Exception
     */
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception e) throws Exception {
        System.out.println("LogHandlerInterceptor.afterCompletion");
    }
}

1-2 配置拦截器

在SpringMVC配置文件中进行配置
<!-- 配置拦截器 -->
<mvc:interceptors>
    <mvc:interceptor>
        <!-- 拦截谁,哪些请求会进入拦截器进行处理,支持通配符 -->
        <mvc:mapping path="/log/**"/>
        <!--
            不拦截谁,哪些请求不仅如此拦截器进行处理
            此处配置的请求一般是属于存在mapping配置的请求中的
            可以配置多个
         -->
        <mvc:exclude-mapping path="/log/add"/>
        <mvc:exclude-mapping path="/log/find"/>
        <!-- 拦截器是谁 -->
        <bean class="interceptor.LogHandlerInterceptor"/>
    </mvc:interceptor>
    
    
</mvc:interceptors>

1-3 过滤器和拦截器的区别

过滤器是j2EE的规范,需要在web.xml中配置(或者注解实现),依赖于servlet-api,在进行servlet前后进行过滤。

拦截器需要在spring配置文件中中配置,依赖于spring框架,针对handler的拦截。

2.练习

实现登录检查拦截器

当用户没有登录的时候,无法访问当前工程中的除登录相关之外的任意资源

此时不管访问任意资源,页面均会跳转到登录页面,提示:请先登录

只有用户登录之后,才能正常访问

public class CheckLoginInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws ServletException, IOException {
        User user = (User) request.getSession().getAttribute("user");
        if(user == null){
            request.setAttribute("loginMsg","请先登录");
            request.getRequestDispatcher("/showLogin").forward(request,response);
            return false;
        }
        return true;
    }
}
<mvc:interceptor>
    <mvc:mapping path="/**"/>
    <mvc:exclude-mapping path="/showLogin"/>
    <mvc:exclude-mapping path="/resp/f8"/>
    <bean class="interceptor.CheckLoginInterceptor"/>
</mvc:interceptor>

九.Ajax处理

1.返回普通字符串

在处理Ajax返回普通字符串的时候

如果字符串的值是一个中文,则可能会出现乱码问题

此时可以通过@RequestMapping注解提供的produces属性设置当前响应数据的格式

其效果相当于response.setContentType("属性值")

$(function(){
    $("#username").blur(function(){
       $.ajax({
          type:"post",
          url:"${pageContext.request.contextPath}/ajax/checkUsername",
          data:{"username":$(this).val()},
          success:function(result){
              $("#s1").html(result);
          }
       });
    });
});
@ResponseBody
@RequestMapping(value = "/checkUsername",produces = "text/html;charset=utf-8")
public String checkUsername(String username){
    if("admin".equals(username)){
        return "该用户已经被注册";
    }
    return "用户名可用";
}

2.返回JSON

2-1 POM依赖

<jackson.version>2.9.8</jackson.version>

<dependency>
  <groupId>com.fasterxml.jackson.core</groupId>
  <artifactId>jackson-core</artifactId>
  <version>${jackson.version}</version>
</dependency>


//导入这一个jar包
<dependency>
  <groupId>com.fasterxml.jackson.core</groupId>
  <artifactId>jackson-databind</artifactId>
  <version>${jackson.version}</version>
</dependency>

<dependency>
  <groupId>com.fasterxml.jackson.core</groupId>
  <artifactId>jackson-annotations</artifactId>
  <version>${jackson.version}</version>
</dependency>

2-2 Controller

@RequestMapping("/findAll")
public ModelAndView findAll(){
    ModelAndView mav = new ModelAndView();
    Connection conn = null;
    PreparedStatement ps = null;
    ResultSet rs = null;
    String url = "jdbc:mysql://127.0.0.1:3306/ums?useUnicode=true&characterEncoding=utf-8";
    List<User> users = new ArrayList<>();
    try {
        Class.forName("com.mysql.jdbc.Driver");
        conn = DriverManager.getConnection(url,"root","");

        String sql = new StringBuffer()
                .append(" select id,username,password,phone,address ")
                .append(" from t_user ")
                .toString();
        ps = conn.prepareStatement(sql);
        rs = ps.executeQuery();
        while(rs.next()){
            User user = new User();
            user.setId(rs.getInt("id"));
            user.setUsername(rs.getString("username"));
            user.setPassword(rs.getString("password"));
            user.setPhone(rs.getString("phone"));
            user.setAddress(rs.getString("address"));
            users.add(user);
        }
        mav.setViewName("list");
        mav.addObject("users",users);
    } catch (Exception e) {
        e.printStackTrace();
    }
    return mav;
}

@ResponseBody
@RequestMapping("/findById")
public User findById(Integer id){
    Connection conn = null;
    PreparedStatement ps = null;
    ResultSet rs = null;
    String url = "jdbc:mysql://127.0.0.1:3306/ums?useUnicode=true&characterEncoding=utf-8";
    User user = null;
    try {
        Class.forName("com.mysql.jdbc.Driver");
        conn = DriverManager.getConnection(url,"root","");

        String sql = new StringBuffer()
                .append(" select id,username,password,phone,address ")
                .append(" from t_user ")
                .append(" where id = ? ")
                .toString();
        ps = conn.prepareStatement(sql);
        ps.setInt(1,id);
        rs = ps.executeQuery();
        while(rs.next()){
            user = new User();
            user.setId(rs.getInt("id"));
            user.setUsername(rs.getString("username"));
            user.setPassword(rs.getString("password"));
            user.setPhone(rs.getString("phone"));
            user.setAddress(rs.getString("address"));
        }

    } catch (Exception e) {
        e.printStackTrace();
    }

    return user;
}

@ResponseBody
@RequestMapping("/findUser")
public User findUser(){
    User user = new User();
    user.setId(1);
    user.setUsername("admin");
    user.setPassword("123456");
    user.setPhone("13812345678");
    user.setAddress("江苏-南京");
    user.setDate(new Date());
    return user;
}

2-3 html

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
    <title>用户列表</title>
    <script type="text/javascript" src="${pageContext.request.contextPath}/js/jquery-1.9.1.min.js"></script>
    <script>
        $(function(){
           $("li").mouseover(function(){
                $("#d").show();
                $.ajax({
                   type:"post",
                   url:"findById",
                   data:{"id":$(this).attr("data-id")},
                   dataType:"json",
                   success:function(user){
                       $("#s1").html(user.id);
                       $("#s2").html(user.username);
                       $("#s3").html(user.password);
                       $("#s4").html(user.phone);
                       $("#s5").html(user.address);
                   }

                });
           });

           $("li").mouseout(function(){
              $("#d").hide();
           });
        });
    </script>
</head>
<body>
<ul>
    
</ul>
<hr/>
<div id="d" style="background-color: #dddddd;width: 20%;padding: 10px;display: none;">
    编号:<span id="s1"></span><br/>
    用户名:<span id="s2"></span><br/>
    密码:<span id="s3"></span><br/>
    电话:<span id="s4"></span><br/>
    地址:<span id="s5"></span><br/>
</div>
</body>
</html>

2-4 User

private Integer id;

private String username;
// 处理的时候忽略指定的属性
@JsonIgnore
private String password;
private String phone;
private String address;

// JsonFormat用于格式化属性
// 一般用于处理日期格式
// 日期的值是以标准时间为基准
// 我们属于东八区
// 比标准时间快了8个小时

//注意:如果接收数据是json,两个都配上
@JsonFormat(pattern = "yyyy年MM月dd日 HH:mm:ss",timezone = "GMT+8")

//接收前台输入的日期格式的字符串转为日期对象
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date date;

十.文件处理

1.文件上传

使用servlet3上传的新功能,不需要导入第三方jar包

2.1web.xml配置:
<servlet>
    <servlet-name>dispatchServlet</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:application.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
    <!--在servlet上使用multipart-config 支持上传-->
    <multipart-config/>
</servlet>
2.2 spring配置
<!--
    文件上传
    SpringMVC帮我们使用fileupload进行了解析
    在它解析的过程中肯定会用到配置文件中bean的id
    在底层代码中,对该bean的id做了限制
    其id的值必须是multipartResolver
 -->
<bean id="multipartResolver" class="org.springframework.web.multipart.support.StandardServletMultipartResolver"></bean>
2.3html
<form action="${pageContext.request.contextPath}/upload" method="post" enctype="multipart/form-data">
    <input type="file" name="file"/>
    <input type="submit" value="上传"/>
</form>

<img src="${url}" style="width: 200px;height: 200px">



ajax的方式:
 <script>
        $(function () {
            $("#btn").click(function () {
                var file = $("#file")[0].files[0];
                var fdata = new FormData();
                fdata.append("file",file);
                console.log(fdata);
                $.ajax({
                    url:"${pageContext.request.contextPath}/upload",
                    data:fdata,
                    type:"post",
                    processData:false,
                    contentType:false,
                    success:function (data) {
                        console.log(data);
                        // location.reload();
                        img.src = URL.createObjectURL(file);
                    }
                });
            });
        });
    </script>
2.4 java代码
@Controller
public class UploadController {

    @RequestMapping("/upload")
    public String upload(@RequestParam("file")MultipartFile file, HttpSession session) throws IOException {
        String dir = new SimpleDateFormat("yyyyMMdd").format(new Date());
        String path = session.getServletContext().getRealPath("/file/"+dir);
        System.out.println(path);
        File file1 = new File(path);
        file1.mkdirs();
        String fileName = file.getOriginalFilename();
        file.transferTo(new File(file1,fileName));
        session.setAttribute("url",session.getServletContext().getContextPath()+"/file/"+dir+"/"+fileName);
        return "upload";
    }
}

2.断点续传

  • 在开始上传时,都去后台查询该文件是否已经传递过,返回已经完成传递的字节数,如果没有传递过,就返回0,开始上传时,就从返回的数字开始上传。
  • 浏览器js端,调用自定义方法upload,每次只上传文件的固定大小数据,如1M,结束后再递归调用upload方法传下一个1M数据。
  • 在后台用FileOutputStream的append继续到要写数据的位置。

前端代码


    <input id="file" type="file" onchange="init()"/><br />
    <span id="fileSize">显示已上传的大小</span>
    <div id="process_border" style="width: 200px;height: 20px;background: gray;display: none">
        <div id="process" style="width: 0px;height: 20px;background: green"></div>
    </div>


const fileSize = 1024*1024;

function upload(start) {
    let fileObj = $("#file")[0].files[0];
    console.log("=======")
    console.log(fileObj);
    //
    if(start >= fileObj.size){
        $("#fileSize").html("上传成功!");
        $("#file").val(null);
        return;
    }
    let end = (start+fileSize) > fileObj.size ? fileObj.size : (start+fileSize);
    let fd = new FormData();
    fd.append("start",start);
    //将文件切块上传
    fd.append("f",fileObj.slice(start,end));
    fd.append("filename",fileObj.name);
    $.ajax({
        url:"/ssm/upload2",
        data:fd,
        type:"post",
        processData:false,
        contentType:false,
        success:function(){
            $("#fileSize").html(start+"/"+fileObj.size);
            $("#process").css("width",start*200/fileObj.size+"px");
            upload(end);
        }
    });
}

function init() {

    var fileObj = $("#file")[0].files[0];
    //显示进度条
    $("#process_border").css("display","block");

    //获取文件已经上传的大小
    $.ajax({
        url:"/ssm/getSize",
        data:{filename:fileObj.name},
        success:function (data) {
            console.log(data);

            $("#fileSize").text(data+"/"+fileObj.size).css("color","blue");
            upload(data);
        }
    });
}

java代码

@RequestMapping("/getSize")
public Long getSize(String filename, HttpSession session){
    String dir = new SimpleDateFormat("yyyyMMdd").format(new Date());
    String path = session.getServletContext().getRealPath("/upload/"+dir+"/"+filename);
    File file = new File(path);
    return file.exists() ? file.length() : 0;

}

@RequestMapping(value = "/upload2",method = RequestMethod.POST)
public void upload(MultipartFile f, String filename,HttpSession session,Long start) throws Exception {
    String dir = new SimpleDateFormat("yyyyMMdd").format(new Date());
    String path = session.getServletContext().getRealPath("/upload/"+dir);
    System.out.println("===="+path);
    File file = new File(path);
    //创建子目录
    file.mkdirs();
    System.out.println("文件名:"+filename+"起始位置:"+start);
    file = new File(path,filename);
    FileOutputStream os = new FileOutputStream(file,true);
    os.write(f.getBytes());
    os.flush();
    os.close();
}

3.文件下载

使用ResponseEntity实现

页面:
<a href="download?filename=jquery-3.5.1.min.js">下载jquery</a>

//ResponseEntity是一种泛型类型。因此,我们可以使用任何类型作为响应主体:
@RequestMapping("/download")
    public ResponseEntity<byte[]> download(HttpSession session,String filename) throws IOException {
        String path = session.getServletContext().getRealPath("jquery/"+filename);
        InputStream in=new FileInputStream(new File(path));
        byte[] body=null;
        body=new byte[in.available()];// 返回下一次对此输入流调用的方法可以不受阻塞地从此输入流读取(或跳过)的估计剩余字节数
        in.read(body);//读入到输入流里面
        // 设置头信息
        // 使用MultiValueMap接口的实现类HttpHeaders设置响应头信息
        HttpHeaders headers = new HttpHeaders();
        // 参数一:表示当前所使用的MIME协议方式,即下载的方式
        // 其值有两种
        //  1.attachment:以附件的方式进行下载
        //  2.inline:在线打开
        // 参数二:下载时所显示的文件名
        headers.setContentDispositionFormData("attachment",filename);
        return new ResponseEntity<byte[]>(body,headers, HttpStatus.OK);
    }

十一.SSM整合

步骤:

第一步:基础环境搭建(maven工程)

第二步:引入依赖

  • Spring
  • SpringMVC
  • MyBatis
  • mybatis-spring mybatis和spring的整合包
  • MyBatis-Generator
  • 数据库连接池 数据库驱动

第三步:编写整合配置文件

web.xml: *控制器,过滤器(乱码)

spring配置文件 :跟业务相关的配置,数据源,事务,mybatis的整合配置

springMVC配置文件:注解驱动,视图解析器,扫包,处理静态资源

mybatis配置文件

1.开发环境

  • 开发工具

    • IntelliJ IDEA 2018.2 x64
    • MySQL Server
  • 涉及框架

    • Spring
    • SpringMVC
    • MyBatis
    • Maven
    • MyBatis-Generator

3.配置文件

3-1 spring的配置文件

application.xml

<?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:tx="http://www.springframework.org/schema/tx"
       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/tx http://www.springframework.org/schema/tx/spring-tx.xsd">

    <!--  Spring配置文件  这里主要配置业务逻辑有关的 数据源 事务配置等等 -->

    <!--  扫描组件  不扫描controller注解  -->
    <context:component-scan base-package="com.gxh">
        <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
    </context:component-scan>

    <!-- 引入外部文件   -->
    <context:property-placeholder location="classpath:dataSource.properties"/>

    <!--数据源配置    -->
    <bean id="ds" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="${mysql.driver}"/>
        <property name="url" value="${mysql.url}"/>
        <property name="username" value="${mysql.username}"/>
        <property name="password" value="${mysql.password}"/>
    </bean>

    <!-- mybatis整合配置   -->
    <bean id="sqlSessionFactoryBean" class="org.mybatis.spring.SqlSessionFactoryBean">
        <!-- mybatis的全局配置文件位置       -->
        <property name="configLocation" value="classpath:mybatis-config.xml"/>
        <property name="dataSource" ref="ds"/>
        <!--  指定mybatis的mapper文件位置      -->
        <property name="mapperLocations" value="classpath:mapper/*.xml"/>
    </bean>

    <!-- 配置扫描器,将mybatis接口的代理实现加入到ioc容器中   -->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="com.gxh.dao"/>
    </bean>

    <!-- 事务控制   -->
    <!-- 事务管理器的配置   -->
    <bean id="tx" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="ds"/>
    </bean>
    <!-- 开启注解式事务   -->
    <tx:annotation-driven transaction-manager="tx"/>


</beans>

3-2 web.xml

<?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">

    <!--  1.配置spring监听器   服务一启动即加载spring配置文件  -->
    <!--   contextConfigLocation:spring文件位置 -->
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:application.xml</param-value>
    </context-param>
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
    
    <!--2.springMVC的前端控制器  拦截所有请求   -->
    <servlet>
        <servlet-name>dispatcherServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <!--  springmvc配置文件位置      -->
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:applicationContext.xml</param-value>
        </init-param>
    </servlet>
    <servlet-mapping>
        <servlet-name>dispatcherServlet</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

    <!--3.字符编码过滤器  如果有多个过滤器,字符编码过滤器配置在最前面  -->
    <filter>
        <filter-name>CharacterEncodingFilter</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>
        <init-param>
            <param-name>forceRequestEncoding</param-name>
            <param-value>true</param-value>
        </init-param>
        <init-param>
            <param-name>forceResponseEncoding</param-name>
            <param-value>true</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>CharacterEncodingFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

</web-app>

3-3 springMVC的配置文件

applicationContext.xml

<?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">

    <!-- 1.springMVC配置文件,包含网站跳转逻辑 -->
    <!-- 扫描   -->
    <context:component-scan base-package="com.gxh" use-default-filters="false">
        <!-- 只扫描控制器-->
        <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
    </context:component-scan>

    <!--2.配置视图解析器-->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/pages/"></property>
        <property name="suffix" value=".jsp"></property>
    </bean>

    <!-- 3.两个标准配置   -->
    <!--  处理静态资源  -->
    <mvc:default-servlet-handler/>
    <!-- 注解驱动   -->
    <mvc:annotation-driven/>
</beans>

使用mybatis的逆向工程生成部分代码

配置文件

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
        PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
        "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
  PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
  "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">

<generatorConfiguration>
    <!--数据库驱动的位置    -->
<!--    <classPathEntry location="D:\tools\.m2\repository\mysql\mysql-connector-java\8.0.21\mysql-connector-java-8.0.21.jar" />-->

    <!-- 配置数据库连接池   -->
    <context id="DB2Tables" targetRuntime="MyBatis3">
        
         <commentGenerator>
            <property name="suppressAllComments" value="true"/>
        </commentGenerator>
        <jdbcConnection driverClass="com.mysql.cj.jdbc.Driver"
                        connectionURL="jdbc:mysql://localhost:3306/mysql?serverTimezone=UTC"
                        userId="gxh"
                        password="123">
        </jdbcConnection>

        <javaTypeResolver >
            <property name="forceBigDecimals" value="false" />
        </javaTypeResolver>

        <!-- 指定java bean的生成路径       -->
        <javaModelGenerator targetPackage="com.gxh.bean" targetProject=".\src\main\java">
            <property name="enableSubPackages" value="true" />
            <property name="trimStrings" value="true" />
        </javaModelGenerator>

        <!--指定mapper文件的路径        -->
        <sqlMapGenerator targetPackage="mapper"  targetProject=".\src\main\resources">
            <property name="enableSubPackages" value="true" />
        </sqlMapGenerator>

        <!--  指定dao接口的路径     -->
        <javaClientGenerator type="XMLMAPPER" targetPackage="com.gxh.dao"  targetProject=".\src\main\java">
            <property name="enableSubPackages" value="true" />
        </javaClientGenerator>

        <!--  指定每个表的生成策略      -->
        <table tableName="t_emp" domainObjectName="Emp" >
        </table>
        <table tableName="t_dept" domainObjectName="Dept" >
        </table>

    </context>
</generatorConfiguration>



    //生成代码
     @Test
    public void main1() throws Exception{
        List<String> warnings = new ArrayList<String>();
        boolean overwrite = true;
        File configFile = new File("D:\\ideaWork\\springmvc-teach\\ssm\\mbg.xml");
        ConfigurationParser cp = new ConfigurationParser(warnings);
        Configuration config = cp.parseConfiguration(configFile);
        DefaultShellCallback callback = new DefaultShellCallback(overwrite);
        MyBatisGenerator myBatisGenerator = new MyBatisGenerator(config, callback, warnings);
        myBatisGenerator.generate(null);
    }
//为当前请求决定使用哪一个处理器
//通过这个映射器去找到对应的处理器RequestMappingHandlerMapping mapping;
mappedHandler = getHandler(processedRequest);   


//找适配器 RequestMappingHandlerAdapter adapter  为了去调用处理器
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

// Actually invoke the handler.
//使用适配去去调用处理器的方法
ModelAndView mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

//处理转发结果 根据视图名 能解析出转发地址  然后使用内部转发
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);

//渲染页面
render(mv, request, response);
if (this.handlerMappings != null) {
   for (HandlerMapping mapping : this.handlerMappings) {
      HandlerExecutionChain handler = mapping.getHandler(request);
      if (handler != null) {
         return handler;
      }
   }
}
return null;

十二. thymeleaf+springMVC

1.依赖

<dependency>
  <groupId>org.thymeleaf</groupId>
  <artifactId>thymeleaf</artifactId>
  <version>3.0.11.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.thymeleaf/thymeleaf-spring4 -->
<dependency>
  <groupId>org.thymeleaf</groupId>
  <artifactId>thymeleaf-spring4</artifactId>
  <version>3.0.11.RELEASE</version>
</dependency>

2.application.xml的配置

<bean id="viewResolver" class="org.thymeleaf.spring4.view.ThymeleafViewResolver">
    <property name="order" value="1"/>
    <property name="characterEncoding" value="UTF-8"/>
    <property name="templateEngine" ref="templateEngine"/>
</bean>

<bean id="templateEngine" class="org.thymeleaf.spring4.SpringTemplateEngine">
    <property name="templateResolver" ref="templateResolver" />
</bean>

<bean id="templateResolver" class="org.thymeleaf.spring4.templateresolver.SpringResourceTemplateResolver">
    <property name="prefix" value="/WEB-INF/pages/"/>
    <property name="suffix" value=".html"/>
    <property name="templateMode" value="HTML5"/>
    <property name="characterEncoding"  value="UTF-8" />
</bean>

3.html

<!DOCTYPE html>
<html  xmlns:th="http://www.thymeleaf.org" >
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <h1>成功!</h1>

    <div class="col-xs-6">
        <span th:text="${msg}"></span>
    </div>

</body>
</html>
上一篇:spring boot 2.X mybites 实战 自动生成+分页+查询使用


下一篇:Angular 2 中的编译器与预编译(AoT)优化