SpringMVC

Author Avatar
kevin
发表:2020-08-15 00:19:00
修改:2024-10-13 20:36:03

SpringMVC常用配置

搭建环境

<dependencies>
    <!-- SpringMVC -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-webmvc</artifactId>
        <version>5.3.1</version>
    </dependency>
    
    <!-- 日志 -->
    <dependency>
        <groupId>ch.qos.logback</groupId>
        <artifactId>logback-classic</artifactId>
        <version>1.2.3</version>
    </dependency>
    
    <!-- ServletAPI -->
    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>javax.servlet-api</artifactId>
        <version>3.1.0</version>
        <scope>provided</scope>
    </dependency>
    
    <!-- Spring5和Thymeleaf整合包 -->
    <dependency>
        <groupId>org.thymeleaf</groupId>
        <artifactId>thymeleaf-spring5</artifactId>
        <version>3.0.12.RELEASE</version>
    </dependency>
</dependencies>

日志配置文件

logback.xml

<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="true">
    <!-- 指定日志输出的位置 -->
    <appender name="STDOUT"
              class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <!-- 日志输出的格式 -->
            <!-- 按照顺序分别是:时间、日志级别、线程名称、打印日志的类、日志主体内容、换行 -->
            <pattern>[%d{HH:mm:ss.SSS}] [%-5level] [%thread] [%logger] [%msg]%n</pattern>
        </encoder>
    </appender>

    <!-- 设置全局日志级别。日志级别按顺序分别是:DEBUG、INFO、WARN、ERROR -->
    <!-- 指定任何一个日志级别都只打印当前级别和后面级别的日志。 -->
    <root level="DEBUG">
        <!-- 指定打印日志的appender,这里通过“STDOUT”引用了前面配置的appender -->
        <appender-ref ref="STDOUT" />
    </root>

    <!-- 根据特殊需求指定局部日志级别 -->
    <logger name="org.springframework.web.servlet.DispatcherServlet" level="DEBUG" />

</configuration>

web.xml配置

<!-- 配置SpringMVC中负责处理请求的核心Servlet,也被称为SpringMVC的前端控制器 -->
<servlet>
    <servlet-name>DispatcherServlet</servlet-name>
    
    <!-- DispatcherServlet的全类名 -->
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    
    <!-- 通过初始化参数指定SpringMVC配置文件位置 -->
    <init-param>
    
        <!-- 如果不记得contextConfigLocation配置项的名称,可以到DispatcherServlet的父类FrameworkServlet中查找 -->
        <param-name>contextConfigLocation</param-name>
    
        <!-- 使用classpath:说明这个路径从类路径的根目录开始才查找 -->
        <param-value>classpath:spring-mvc.xml</param-value>
    </init-param>
    
    <!-- 作为框架的核心组件,在启动过程中有大量的初始化操作要做,这些操作放在第一次请求时才执行非常不恰当 -->
    <!-- 我们应该将DispatcherServlet设置为随Web应用一起启动 -->
    <load-on-startup>1</load-on-startup>
    
</servlet>
    
<servlet-mapping>
    <servlet-name>DispatcherServlet</servlet-name>
    
    <!-- 对DispatcherServlet来说,url-pattern有两种方式配置 -->
    <!-- 方式一:配置“/”,表示匹配整个Web应用范围内所有请求。 -->
    <!-- 方式二:配置“*.扩展名”,表示匹配整个Web应用范围内部分请求 -->
    <url-pattern>/</url-pattern>
</servlet-mapping>

Spring配置文件

复制即可

<!-- 自动扫描包 -->
<context:component-scan base-package="com.kevin.mvc.handler"/>
    
<!-- Thymeleaf视图解析器 -->
<bean id="viewResolver" class="org.thymeleaf.spring5.view.ThymeleafViewResolver">
    <!--        视图解析器优先级-->
    <property name="order" value="1"/>
    <!--        解析视图编码-->
    <property name="characterEncoding" value="UTF-8"/>
    <!--        设置模板-->
    <property name="templateEngine">
        <bean class="org.thymeleaf.spring5.SpringTemplateEngine">
            <property name="templateResolver">
                <bean class="org.thymeleaf.spring5.templateresolver.SpringResourceTemplateResolver">
                    <!-- 视图前缀 -->
                    <property name="prefix" value="/WEB-INF/templates/"/>    
                    <!-- 视图后缀 -->
                    <property name="suffix" value=".html"/>
                    <property name="templateMode" value="HTML5"/>
                    <property name="characterEncoding" value="UTF-8" />
                </bean>
            </property>
        </bean>
    </property>
</bean>

常见注解

RequestMapping注解(重点)

可以标记在类上和方法上

注解标记在类上

网页访问时需要添加类的注解和方法上的注解路径

<h3>测试@RequestMapping注解标记在类上</h3>
<a th:href="@{/user/login}">用户登录</a><br/>
<a th:href="@{/user/register}">用户注册</a><br/>
<a th:href="@{/user/logout}">用户退出</a><br/>

指定请求方式
//如果不加请求方式,post和get都可以进入该方法,设置了则不行

原版进阶版
@RequestMapping(value = "/emp", method = RequestMethod.GET)指定为GET请求@GetMapping("/emp")
@RequestMapping(value = "/emp", method = RequestMethod.POST)指定为POST请求@PostMapping("/emp")
@RequestMapping(value = "/emp", method = RequestMethod.PUT)指定为PUT请求@PutMapping("/emp")
@RequestMapping(value = "/emp", method = RequestMethod.DELETE)指定为DELETE请求@DeleteMapping("/emp")

获取请求参数(重点)

根据一个参数名获取一个参数值

最简单的方式获取单个请求参数:就是在handler方法中添加一个和请求参数名同名的参数,来接收请求参数

使用RequestParam注解

@RequestMapping("/oneParameter")
public String oneParameter(@RequestParam("username") String username){
    //最简单的方式获取单个请求参数:就是在handler方法中添加一个和请求参数名同名的参数,来接收请求参数
    //其实这个地方String username此处省略了一个注解 @RequestParam
    //@RequestParam("username")就是通过参数名获取请求参数
    logger.debug(username);
    return "target";
}

注意

如果这个值没有传输将会报错,默认属性是required=true自动装配

@RequestParam 注解的 required 属性:默认值为true,表示请求参数默认必须提供

可以通过将required 属性设置为 false 表示这个请求参数可有可无:

@RequestParam(value = "userName", required = false)可以设置为false

也可以通过设置请求参数的默认值来解决上述400错误-貌似常用

@RequestParam(value = "userName", defaultValue = "missing")

根据一个参数名获取多个参数值

如果是多选框,同名参数,会把他们的值用逗号隔开

可以使用数组或者list或者map或者pojo装配

参数中使用对应的装配对象

可以设置@RequestParam的属性value设置映射值

@RequestParam("team")List<String> teamList
Employee employee
@RequestParam Map map

访问静态资源(重点)和标配配置

SpringMVC 配置文件中增加配置:

<!-- 加入这个配置,SpringMVC 就会在遇到没有 @RequestMapping 的请求时放它过去 -->
<!-- 所谓放它过去就是让这个请求去找它原本要访问的资源 -->
<mvc:default-servlet-handler/>

<!-- 开启 SpringMVC 的注解驱动功能。这个配置也被称为 SpringMVC 的标配。 -->
<!-- 标配:因为 SpringMVC 环境下非常多的功能都要求必须打开注解驱动才能正常工作。 -->
<mvc:annotation-driven/>

请求转发的指令

@RequestMapping("/forwardCommand")
public String forwardCommand(){
    //使用转发指令:在handler方法中,访问Thymeleaf前后缀控制范围外的页面
    //"forward:要转发到的资源路径",相当于使用的是请求转发跳转
    //请求转发的绝对路径是:在uri的基础上省略"/项目名"
   // return "forward:/outter.html";
    return "forward:/forwardCommand2"
}

重定向的指令

@RequestMapping("/redirectCommand")
public String redirectCommand(){
    //使用重定向指令:在handler方法中,访问Thymeleaf前后缀控制范围外的页面
    //"redirect:要转发到的资源路径",相当于使用的是请求转发跳转
    //重定向的绝对路径:就是uri   "/项目名/资源路径"
    //因为SpringMVC框架在接收到redirect指令之后,会自动给访问路径加上"/项目名"
    //return "redirect:/outter.html";
    return "redirect:www.baidu.com";
}

mvc:view-controller访问页面(重点)

如果只有跳转某个页面而没有方法体,就可以不用写方法,可以使用

mvc配置文件配置标签即可替代

跳转到首页

  <mvc:view-controller path="/" view-name="index"/>

SpringMVC使用域对象

ModelAndView -----推荐使用

有model和view功能
model主要用于请求域共享数据
view主要用于设置视图,实现页面跳转

@RequestMapping("/attr/request/mav")
public ModelAndView testAttrByModelAndView(ModelAndView modelAndView) {
    
    // 2.存入模型数据
    modelAndView.addObject("key", "value");
    
    // 3.设置视图名称
    modelAndView.setViewName("target");
    
    return modelAndView;
}

使用 Model 类型的形参操作

@RequestMapping("/testAttrRequestModel")
public String testAttrRequestModel(Model model){
    //目标:目标:将数据存储到请求域对象,然后跳转到target页面
    //方式二:使用Model对象往请求域中存储值
    model.addAttribute("username","kevin");
    return "target";
}

模型的本质

不管是model还是modelMap,map等都会返回成BindingAwareModelMap
最后都会调用控制器,控制器封装成ModelAndView

会话域对象session

使用会话域最简单直接的办法就是使用原生的 HttpSession 对象

@RequestMapping("/attrSession")
public String attrSession(HttpSession httpSession){
    //目标:往会话域中存储数据
    httpSession.setAttribute("address","召唤师峡谷");
    return "target";
}

应用域

可以使用使用IOC注入的方式来操作:

@Autowired
private ServletContext servletContext;
@RequestMapping("/attr/application")
public String attrApplication() {
    servletContext.setAttribute("appScopeMsg", "i am hungry...");
    return "target";
}

第二种方式

 //获得context第二种方法
 public String attrSession(HttpSession httpSession){

    ServletContext application = httpSession.getServletContext();
    application.setAttribute(key,value);
    return "target";
}

解决乱码问题

解决POST请求的中文字符乱码问题
放在servlet之前
mvc自带过滤器

webxml配置文件

  <!-- 配置过滤器解决 POST 请求的字符乱码问题 -->
    <filter>
        <filter-name>CharacterEncodingFilter</filter-name>
        <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>

        <!-- encoding参数指定要使用的字符集名称 -->
        <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>

RESTFul风格交互方式

操作请求方式
查询操作GET
保存操作POST
删除操作DELETE
更新操作PUT

不是通过方法名增删改查,而是通过请求方式不同做不同的反应

从前到后各个单词使用斜杠分开,不使用问号键值对方式携带请求参数

请求方式的映射

DELETE 和 PUT 请求无法直接做到,所以需要映射

修改web.xml文件

<!--一定要配置在解决乱码的Filter之后-->
<filter>
    <filter-name>hiddenHttpMethodFilter</filter-name>
    <filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>hiddenHttpMethodFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

注意

  • 要点1:原请求方式必须是 post
  • 要点2:新的请求方式名称通过请求参数发送
  • 要点3:请求参数名称必须是 _method
  • 要点4:请求参数的值就是要改成的请求方式
<form th:action="@{/rest/movie}" method="post">
    <input type="hidden" name="_method" value="put"/>
    <button>发送请求</button>
</form>

由于删除一般是超链接,所以可以用vue或者原生做一个点击响应

因为超链接默认 是get请求,post只能通过form表单发送

所以可以在点击事件里取消默认行为,绑定表单发送post请求

<div id="app">
    <a @click="deleteUser"  th:href="@{/user/1}">删除用户信息</a>
    <form method="post" id="deleteForm">
        <input type="hidden"  name="_method" value="delete">
    </form>
</div>
<script type="text/javascript">
    let vm = new Vue({
        el:"#app",
        methods:{
            deleteUser(){
                //根据id获取表单元素
                let formId = document.getElementById("deleteForm");
               //触发点击事件的超连接属性赋值给表单action
                formId.action = event.target.href;
                //提交表单
                formId.submit();
                // 取消超链接默认行为
                event.preventDefault();
            }
        }
    })
</script>

获取路径参数

handler里的{}表示占位符

表示获取该位置的值,@PathVariable("id")表示获取占位符为"id"的值

此值必须传送,否则报错

单个路径参数

<a th:href="@{/user/1}">根据id查询用户信息</a><br>

handler

    @GetMapping("/user/{id}")
    public String getUserById(@PathVariable("id") Integer id){
        System.out.println("根据id查询用户信息");
        System.out.println(id);
        return "target";
    }

多个路径参数

<a th:href="@{/rest/movie/2/22/123}">携带多个参数</a>

handler

@GetMapping("/movie/{categoryId}/{groupId}/{movieId}")
public String findMovieById(@PathVariable("categoryId") Integer categoryId,@PathVariable("groupId")Integer groupId,@PathVariable("movieId") Integer movieId){
    logger.debug("GET请求...."+categoryId+":"+groupId+":"+movieId);
    return "target";
}

Ajax交互

注解使用的是jackson转换,需要导入jar包

用来接受json数据才用这个参数

<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.12.1</version>
</dependency>

常用注解

ResponseBody注解

拿当前方法的返回值转成JSON个数数据作为响应体,不要再找视图.

@ResponseBody
@RequestMapping("/jsonBodyParameter")
public Movie jsonBodyParameter(@RequestBody User user){
    logger.debug(user.toString());

    Movie movie = new Movie(1, "西游记", 40.0);
    //目标:将movie转成json字符串响应给客户端
    //1. handler方法的返回值就是你要转成json的那个对象
    //2. handler方法上必须添加ResponseBody注解
    //3. 你的项目中一定要引入了jackson的依赖
    return movie;
}

RequestBody注解

当前段用axios传输json数据的时候封装bean,需要在参数

上使用这个注解,表示是json请求体

主要用来接收前端传递给后端的json字符串中的数据的,注解只能加在方法的参数上

前端的请求方式必须为post,因为post请求方式才具有请求体数据。

RestController注解

如果该类每个方法都是返回的json数据就可以使用这个注解再类上

表示类上的ResponseBody 注解可以和Controller 注解合并为RestController 注解

@RestController
public class Movie{}

数据校验(重要)

导入依赖jar包

<dependency>
    <groupId>org.hibernate.validator</groupId>
    <artifactId>hibernate-validator</artifactId>
    <version>6.2.0.Final</version>
</dependency>

<dependency>
    <groupId>org.hibernate.validator</groupId>
    <artifactId>hibernate-validator-annotation-processor</artifactId>
    <version>6.2.0.Final</version>
</dependency>
注解规则
@Null标注值必须为 null
@NotNull标注值不可为 null
@AssertTrue标注值必须为 true
@AssertFalse标注值必须为 false
@Min(value)标注值必须大于或等于 value
@Max(value)标注值必须小于或等于 value
@DecimalMin(value必须是字符串)标注值必须大于或等于 value,用于校验double精度问题
@DecimalMax(value必须是字符串)标注值必须小于或等于 value,用于校验double精度问题
@Size(max,min)集合的长度集合长度,标注值大小必须在 max 和 min 限定的范围内
@Digits(integer,fratction)标注值值必须是一个数字,且必须在可接受的范围内,integer整数部位精度,fratction小数部位精度
@Past标注值只能用于日期型,且必须是过去的日期,或者当天
@Future标注值只能用于日期型,且必须是将来的日期,不能是当天
@Pattern(value)标注值必须符合指定的正则表达式
@Email标注值必须是格式正确的 Email 地址
@Length标注值字符串大小必须在指定的范围内
@NotEmpty标注值字符串不能是空字符串

给要进行校验的字段添加上校验规则注解

@Data
@AllArgsConstructor
@NoArgsConstructor
public class President {
    // 字符串长度:[3,6]
    @Size(min = 3, max = 6)
    private String username;
    // 字符串必须满足Email格式
    @Email
    private String email;

}

给handler方法的形参加上@Validated注解

@Validated : 对当前的参数进行数据校验

@RequestMapping("/savePresident")
public String savePresident(@Validated President president){
    logger.debug(president.toString());
    return "target";
}

校验错误显示友好提示信息

参数加上BindingResult bindingResult

接口: BindingResult 绑定是数据校验的结果

boolean boolean hasErrors() 判断是有校验的数据错误

FieldError getFieldError(); 返回验证错误的字段

FieldError类的方法getField() 返回出错的字段名字

@RequestMapping("/save/president")
public String savePresident(
        // 在实体类参数和 BindingResult 之间不能有任何其他参数
        @Validated President president, BindingResult bindingResult) {
 
    //判断是否有字段验证失败
    if (bindingResult.hasErrors()) {
          //验证结果的绑定对象,方法返回出错误的字段对象
         FieldError fieldError = bindingResult.getFieldError();
         //出错的字段对象的方法,获取出现的详细信息
         String defaultMessage = fieldError.getDefaultMessage();
        //字段对象,获取字段名,获得发生错误的字段
            String field = fieldError.getField();
        //跳转自定义错误的网页
        return "error";
    }
     
    logger.debug(president.getEmail());
 
    return "target";
}

文件上传(重要)

前段需要满足的要求:

  • 第一点:请求方式必须是 POST
  • 第二点:请求体的编码方式必须是 multipart/form-data(通过 form 标签的 enctype 属性设置)
    • 不然会默认传参,&&的方式,所以需要设置为enctype,设置为二进制传输
  • 第三点:使用 input 标签、type 属性设置为 file 来生成文件上传框
    • 文件域inputy标签,必须有name属性值

表单

<form th:action="@{/kevin/upload}" method="post" enctype="multipart/form-data">
    
    <input type="file" name="picture" />
    <button type="submit">上传头像</button>
     
</form>

MultipartFile接口-文件上传解析器

引入依赖--胡

<!-- https://mvnrepository.com/artifact/commons-fileupload/commons-fileupload -->
<dependency>
    <groupId>commons-fileupload</groupId>
    <artifactId>commons-fileupload</artifactId>
    <version>1.3.1</version>
</dependency>

在 SpringMVC 的配置文件中加入 multipart 类型数据的解析器: --胡

id名必须叫这个!!!!!!!!

<bean id="multipartResolver" 
      class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
    
    <!-- 由于上传文件的表单请求体编码方式是 multipart/form-data 格式,所以要在解析器中指定字符集 -->
    <property name="defaultEncoding" value="UTF-8"/>
    
</bean>

具体代码

  Logger logger = LoggerFactory.getLogger(this.getClass());
    //文件的上传
    @RequestMapping("testUp")
    //多文件上传,改为数组MultipartFile[] photo,然后遍历即可
    public String testUp(MultipartFile photo) throws IOException {
        logger.debug(photo.getName());
        //获得上传文件名,防止重名覆盖,需要uuid
        String filename = photo.getOriginalFilename();
        //获得文件后缀,防止重名覆盖,通过最后一个点截取
        String suffixName = filename.substring(filename.lastIndexOf("."));
        //将其文件名改成一个唯一的名字,这样就能保证不会出现上传文件同名的情况
        //唯一的文件名就是UUIDName拼接后缀
        filename = UUID.randomUUID().toString()+suffixName;
        //1. 指定转存的目录路径:动态获取部署的目录路径
        String phptoPath = servletContext.getRealPath("photo");
        //如果文件路径不存在则创建
        File file = new File(phptoPath);
        if (!file.exists()){
//            不存在则创建目录
            file.mkdirs();
        }
//        父路径和文件名
        File finalPath = new File(phptoPath,filename);
        //要将该文件转存到目录中
        photo.transferTo(finalPath);
        return "success";
    }

拦截器(重点)

拦截器与过滤器的对比

相同点

  • 拦截(配置拦截路径):必须先把请求拦住,才能执行后续操作
  • 过滤(根据某种规则/业务逻辑进行筛选):拦截器或过滤器存在的意义就是对请求进行统一处理
  • 放行(满足规则/筛选条件,就让你访问你想访问的资源):对请求执行了必要操作后,放请求过去,让它访问原本想要访问的资源

不同点

  • 工作平台不同
    • 过滤器工作在 Servlet 容器中
    • 拦截器工作在 SpringMVC 的基础上
  • 拦截的范围
    • 过滤器:能够拦截到的最大范围是整个 Web 应用
    • 拦截器:能够拦截到的最大范围是整个 SpringMVC 负责的请求(handler方法、view-controller跳转页面)
  • IOC 容器支持
    • 过滤器:想得到 IOC 容器需要调用专门的工具方法,是间接的
    • 拦截器:它自己就在 IOC 容器中,所以可以直接从 IOC 容器中装配组件,也就是可以直接得到 IOC 容器的支持

自定义拦截器

  • 实现接口HandlerInterceptor
    • 接口有默认方法,手动实现
  • 重写方法:
    • preHandle:handler之前执行,返回true表示放行
    • postHandle:handler逻辑真正执行完成但尚未返回页面
      • 性能测试
    • afterCompletion:返回页面之后----渲染完之后
      • 性能测试

springmvc配置拦截器

默认拦截全部请求

//配置多个拦截器
<mvc:interceptors>
    //配置单个拦截器
    <mvc:interceptor>
        //要拦截的handler,/*拦截一层目录,/**拦截多层目录(web应用程序下的所有handler)
    <mvc:mapping path="/**"/>
        //不拦截的handler
            <mvc:exclude-mapping path="/user/**"/>
    	<bean class="com.kevin.interceptor.MyInterceptor" id="interceptor"/>
    </mvc:interceptor>
</mvc:interceptors>

多个拦截器执行顺序

  • 拦截器链执行时,拦截器链正常流程测试
    • preHandle顺序执行 i++
    • postHandle倒序执行 i--
      • if判断拦截,true走postHandle,false调用afterCompletion
    • afterCompletion倒序执行 i--
  • 拦截器链中断流程测试
    • 拦截器链中有中断时,整个链中的拦截器的postHandle都不会执行

SSM整合(重点)

总体 SSM 整合所需依赖

<dependencies>
<!--    日志依赖-->
    <dependency>
      <groupId>ch.qos.logback</groupId>
      <artifactId>logback-classic</artifactId>
      <version>1.2.3</version>
    </dependency>
<!--    spring依赖-->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-webmvc</artifactId>
      <version>${spring.version}</version>
    </dependency>
      <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-jdbc</artifactId>
          <version>${spring.version}</version>
      </dependency>
<!--      spring和thymeleaf整合包-->
      <dependency>
          <groupId>org.thymeleaf</groupId>
          <artifactId>thymeleaf-spring5</artifactId>
          <version>3.0.12.RELEASE</version>
      </dependency>
<!--        Mybatis核心-->
      <dependency>
          <groupId>org.mybatis</groupId>
          <artifactId>mybatis</artifactId>
          <version>3.5.5</version>
      </dependency>
<!--      mybatis和spring整合包-->
      <dependency>
          <groupId>org.mybatis</groupId>
          <artifactId>mybatis-spring</artifactId>
          <version>2.0.5</version>
      </dependency>
<!--      mysql驱动-->
      <dependency>
          <groupId>mysql</groupId>
          <artifactId>mysql-connector-java</artifactId>
          <version>5.1.46</version>
      </dependency>
<!--      德鲁伊连接池-->
      <dependency>
          <groupId>com.alibaba</groupId>
          <artifactId>druid</artifactId>
          <version>1.1.10</version>
      </dependency>
<!--      分页插件-->
      <dependency>
          <groupId>com.github.pagehelper</groupId>
          <artifactId>pagehelper</artifactId>
          <version>5.1.2</version>
      </dependency>
<!--      lombok依赖-->
      <dependency>
          <groupId>org.projectlombok</groupId>
          <artifactId>lombok</artifactId>
          <version>1.18.12</version>
          <scope>provided</scope>
      </dependency>
<!--      servlet依赖-->
      <dependency>
          <groupId>javax.servlet</groupId>
          <artifactId>javax.servlet-api</artifactId>
          <version>3.1.0</version>
          <scope>provided</scope>
      </dependency>
<!--      导入jackson依赖-->
      <dependency>
          <groupId>com.fasterxml.jackson.core</groupId>
          <artifactId>jackson-databind</artifactId>
          <version>2.9.0</version>
      </dependency>
<!--    junit依赖-->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-test</artifactId>
      <version>${spring.version}</version>
        <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.12</version>
      <scope>test</scope>
    </dependency>
  </dependencies>

业务层和持久层的配置分成2个文件,各自配置各自需要的内容,相互之间不干扰

配置数据源

db.properties

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mybatis?characterEncoding=UTF8
jdbc.username=root
jdbc.password=root

加入日志配置文件

logback.xml

<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="true">
    <!-- 指定日志输出的位置 -->
    <appender name="STDOUT"
              class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <!-- 日志输出的格式 -->
            <!-- 按照顺序分别是:时间、日志级别、线程名称、打印日志的类、日志主体内容、换行 -->
            <pattern>[%d{HH:mm:ss.SSS}] [%-5level] [%thread] [%logger] [%msg]%n</pattern>
        </encoder>
    </appender>

    <!-- 设置全局日志级别。日志级别按顺序分别是:DEBUG、INFO、WARN、ERROR -->
    <!-- 指定任何一个日志级别都只打印当前级别和后面级别的日志。 -->
    <root level="DEBUG">
        <!-- 指定打印日志的appender,这里通过“STDOUT”引用了前面配置的appender -->
        <appender-ref ref="STDOUT" />
    </root>

    <!-- 根据特殊需求指定局部日志级别 -->
    <logger name="com.stu.mapper" level="DEBUG"/>
    <logger name="org.springframework.web.servlet.DispatcherServlet" level="DEBUG" />

</configuration>

创建业务层配置文件

加入声明式事务

application-service.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:tx="http://www.springframework.org/schema/tx"
       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
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/tx
        http://www.springframework.org/schema/tx/spring-tx.xsd">

<!--    配置包扫描-->
    <context:component-scan base-package="com.stu.service"/>
<!--    配置事务管理-->
    <bean class="org.springframework.jdbc.datasource.DataSourceTransactionManager" id="transactionManager">
        <constructor-arg name="dataSource" ref="dataSource"/>
    </bean>
<!--    开启事务注解-->
    <tx:annotation-driven transaction-manager="transactionManager"/>
</beans>

创建 Mybatis 全局配置文件

mybatis-config.xml

<?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="mapUnderscoreToCamelCase" value="true"/>
        <!--        懒加载-->
        <setting name="lazyLoadingEnabled" value="true"/>
    </settings>
</configuration>

创建Mapper配置文件(存放路径在resources中要与对应的接口的路径一致)

resources下面包路径取名必须是/

com/stu/mapper 会自动用点代替

MovieMapper.xml

<?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.stu.mapper.MovieMapper">
<!--    查询所有用户-->
    <select id="movieList" resultType="movie">
        select movie_id,movie_name,movie_price from movie
    </select>
</mapper>

持久层的配置

application-mapper.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:aop="http://www.springframework.org/schema/aop"
       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
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/mvc
        http://www.springframework.org/schema/mvc/spring-mvc.xsd
        http://www.springframework.org/schema/tx
        http://www.springframework.org/schema/tx/spring-tx.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd">

<!--        引入外部文件-->
    <context:property-placeholder location="classpath:db.properties"/>
<!--    配置德鲁伊-->
    <bean class="com.alibaba.druid.pool.DruidDataSource" id="dataSource">
        <property name="driverClassName" value="${jdbc.driver}"/>
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>
    <!--
           配置Spring框架和MyBatis框架合体
           mybatis-spring.jar包中类
           SqlSessionFactoryBean 框架的整合类: 定义了SqlSessionFactoryBuilder
           SqlSessionFactoryBean类配置 插件 (MyBatis框架功能没有,插件是增强)
           字段 : plugins 插件
       -->
    <bean class="org.mybatis.spring.SqlSessionFactoryBean" id="sqlSessionFactoryBean">
<!--        注入数据源-->
        <property name="dataSource" ref="dataSource"/>
        <!--配置别名包扫描-->
        <property name="typeAliasesPackage" value="com.stu.pojo"/>
        <!--映射mybatis配置文件-->
        <property name="configLocation" value="classpath:mybatis-config.xml"/>
        <!--set方法注入,分页助手插件-->
        <property name="plugins">
            <array>
                <bean class="com.github.pagehelper.PageInterceptor" id="interceptor">
                    <!--
                                          对象需要注入 集合 Properties
                                          配置数据库的方言
                                          SQL语句:标准化,CRUD数据库都是一样,普通话
                                          SQL语句是有区别的 方法
                                      -->
                    <property name="properties">
                        <props>
                            <prop key="helperDialect">mysql</prop>
                        </props>
                    </property>
                </bean>
            </array>
        </property>
    </bean>
    <!--配置MyBatis框架的接口扫描,扫描mapper接口,动态代理-->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!--        扫描包名-->
        <property name="basePackage" value="com.stu.mapper"/>
        <!--set方法注入, 注入对象SqlSessionFactoryBean, 字段类型是字符串-->
        <property name="sqlSessionFactoryBeanName" value="sqlSessionFactoryBean"/>
    </bean>
</beans>

Spring 和 SpringMVC 整合

springmvc配置

springmvc.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:aop="http://www.springframework.org/schema/aop"
       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
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/mvc
        http://www.springframework.org/schema/mvc/spring-mvc.xsd
        http://www.springframework.org/schema/tx
        http://www.springframework.org/schema/tx/spring-tx.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd">


<!--    配置包扫描-->
    <context:component-scan base-package="com.stu.handler"/>
    <!--    开启注解驱动-->
    <mvc:annotation-driven/>
    <!--3. 静态资源处理-->
    <mvc:default-servlet-handler/>
<!--    配置视图转发器-->
    <!-- Thymeleaf视图解析器 -->
    <bean id="viewResolver" class="org.thymeleaf.spring5.view.ThymeleafViewResolver">
        <property name="order" value="1"/>
        <property name="characterEncoding" value="UTF-8"/>
        <property name="templateEngine">
            <bean class="org.thymeleaf.spring5.SpringTemplateEngine">
                <property name="templateResolver">
                    <bean class="org.thymeleaf.spring5.templateresolver.SpringResourceTemplateResolver">

                        <!-- 视图前缀 -->
                        <property name="prefix" value="/WEB-INF/templates/"/>

                        <!-- 视图后缀 -->
                        <property name="suffix" value=".html"/>

                        <!--模板类型-->
                        <property name="templateMode" value="HTML5"/>
                        <!--模板的字符编码-->
                        <property name="characterEncoding" value="UTF-8"/>
                    </bean>
                </property>
            </bean>
        </property>
    </bean>

    <!--配置view-controller-->
    <mvc:view-controller path="/" view-name="index"/>
</beans>

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

    <!--使用ContextLoaderListener加载spring-persist.xml-->
    <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>
    <!-- 配置过滤器解决 POST 请求的字符乱码问题 -->
    <filter>
        <filter-name>CharacterEncodingFilter</filter-name>
        <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
        <!-- encoding参数指定要使用的字符集名称 -->
        <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>
    <!--
       在服务器启动的时候加载配置文件,创建ioc容器有两种方式:
       1. DispatcherServlet
       2. ContextLoaderListener
   -->
    <!--1. 使用DispatcherServlet加载spring-mvc.xml-->
    <servlet>
        <servlet-name>dispatcherServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:springmvc.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>dispatcherServlet</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
</web-app>

分页

浸入式,即插即用,热拔式

分页执行流程

  • 查询总记录数(用count()函数)
  • 查询当前页数据(使用limit查询)
  • 根据总记录数和每页条数计算总页数
  • 在1~总页数之间修正页码
    • 用户输入的页码 < 1:将页码设定为第一页
    • 用户输入的页码 > 总页数:将页码设定为最后一页
  • 封装上述所有数据,发送到页面显示

Mybatis的分页插件

依赖

<!-- https://mvnrepository.com/artifact/com.github.pagehelper/pagehelper -->
<dependency>
    <groupId>com.github.pagehelper</groupId>
    <artifactId>pagehelper</artifactId>
    <version>5.2.0</version>
</dependency>

配置

持久层的配置

application-mapper.xml

<!-- 配置 SqlSessionFactoryBean -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
 
    ……
 
    <!-- 在 plugins 属性中配置 Mybatis 插件 -->
    <property name="plugins">
        <array>
            <bean class="com.github.pagehelper.PageInterceptor">
                <property name="properties">
                    <props>
                        <!-- 设置 reasonable 为 true 表示将页码进行合理化修正。页码的有效范围:1~总页数 -->
                        <prop key="reasonable">true</prop>
                        
                        <!-- 数据库方言:同样都是 SQL 语句,拿到不同数据库中,在语法上会有差异 -->
                        <!-- 默认情况下,按照 MySQL 作为数据库方言来运行 -->
                        <prop key="helperDialect">mysql</prop>
                    </props>
                </property>
            </bean>
        </array>
    </property>
 
</bean>
  • PageHelper类:开启分页功能
    • 静态方法:startPage(int 当前页数,int 每页显示条数)

开启分页功能,就在 SQL 语句后面附加 LIMIT 子句并查询总记录数;不开启就还是按照原样查询。分页功能对原有的 Mapper 接口、SQL 语句没有任何影响。这个效果可以称之为是非侵入式。

  • PageInfo类:封装分页结果集数据
    • 方法:long getTotal() 查询到的总记录数
    • 方法:int getPages()//查询到的总页数
    • 方法:List getList() 查询到的数据表的数据

handler层

    //查询分页
    @RequestMapping("queryByPage")
    public  PageInfo<Movie> queryByPage(Integer currentPage){
        //每页查询数量
        Integer pageSize = 4;
        PageInfo<Movie> moviePageInfo = movieService.queryByPage(currentPage, pageSize);
        return moviePageInfo;
    }

service层

    //查询分页
    @Override
    public PageInfo<Movie> queryByPage(Integer currentPage, Integer pageSize) {
        //1 开启分页,传递当前的页数,每页显示的条数
        //返回分页对象
        PageHelper.startPage(currentPage,pageSize);
        List<Movie> movies = movieMapper.movieList();
        return new PageInfo<>(movies);
    }

持久层

MovieMapper.xml

    <select id="movieList" resultType="movie">
        select movie_id,movie_name,movie_price from movie
    </select>

/*,/,和*.do的区别

SpringMVC访问静态资源:/*,/,和*.do的区别

​ 第一种配置:.do,还可以写.action等等,表示以.do结尾的或者以.action结尾的URL都由前端控制器DispatcherServlet来解析
​ 第二种配置:/,所有访问的 URL 都由DispatcherServlet来解析,但是这里最好配置静态文件不由DispatcherServlet来解析,搭配下面使用

<mvc:annotation-driven/>
<mvc:default-servlet-handler/>

​ 错误配置:/*,注意这里是不能这样配置的,应为如果这样写,最后转发到 jsp 页面的时候,仍然会由DispatcherServlet进行解析,
而这时候会找不到对应的Handler,从而报错!!!

"/*"表示匹配所有请求(包含所有后缀)

/*的配置不适用于DispatcherServlet,一般用于过滤器来拦截所有资源

评论