听风辄碎的孤魂。

0%

SpringMVC

常见开发架构:C/S客户端服务器;B/S浏览器服务器;在B/S架构中,通常使用三层架构:表现层、业务层、持久层。

  • 表现层:即web层,分为展示层和控制层,表现层依赖业务层。
  • 业务层:即service层,负责业务逻辑处理
  • 持久层:即dao层,负责数据持久化,作用也就是和数据库进行交互

==MVC模型==:M指Model,通常指数据模型,用于封装数据(java bean);V指View,通常指JSP或HTML,用来展示数据;C值Controller,处理程序逻辑(servlet)

aswpfs.png

SpringMVC简介

  • SpringMVC是一种基于Java的实现MVC设计模型的请求驱动类型的轻量级Web框架,属于 Spring FrameWork 的后续产品。
  • asw3nK.png

SpringMVC入门案例

需求分析

  • graph LR
    A(inidex.jsp  超链接标签)--发送请求-->B(编写类和方法,并转到成功页面)
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20

    * 思路:1、搭建开发环境;2、编写入门程序;

    ### 开发环境搭建

    * 创建Maven工程,添加文件夹并设置文件夹属性

    * 在web.xml中添加Servlet基本配置

    其中url为/代表拦截所有请求

    ```xml
    <servlet>
    <servlet-name>dispatcherServlet</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    </servlet>
    <servlet-mapping>
    <servlet-name>dispatcherServlet</servlet-name>
    <url-pattern>/</url-pattern>
    </servlet-mapping>
  • 创建SpringMVC配置文件springmvc.xml

  • 配置Tomcat服务器,选择浏览器,Deployment中添加项目,设置路径名

入门初始代码编写

  • 在html中创建一个超链接,在java文件下创建控制器,并编写方法

    1
    2
    3
    4
    5
    <body>
    <h3>入门程序</h3>
    <--这里的href为对应控制器方法上的path--/>
    <a href="/hello">点击跳转</a>
    </body>
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    @Controller
    public class HelloController {
    //代表请求映射,这里/hello代表映射这个方法,/不要忘记
    @RequestMapping(path = "/hello")
    public String helloController(){
    System.out.println("hello controller!");
    //这里返回值是视图解析器要访问的网页名,具体实现SpringMvc框架默认规则
    return "success";
    }
    }
  • 在springmvc.xml中添加组件扫描,将控制器类交给Spring管理

    1
    <context:component-scan base-package="com.zjut.controller"/>
  • 在控制器的类和方法上添加注解,并将方法上的path添加到html中的href

  • 将配置文件的加载交给web.xml中的servlet加载并使其生效,在servlet-class标签下添加

    1
    2
    3
    4
    5
    6
    7
    <!--将springmvc配置文件交给servlet加载并生效-->
    <init-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:springmvc.xml</param-value>
    </init-param>
    <!--代表启动时便创建servlet,并加载springmvc.xml配置文件-->
    <load-on-startup>1</load-on-startup>
  • 创建将要跳转的页面,在控制器中的方法执行后要打开这个页面

  • 在spring.mvc配置文件中配置视图解析器对象,类名固定,在内部添加参数,配置完之后才能解析控制器中方法返回的页面

    1
    2
    3
    4
    5
    6
    7
    <!--配置视图解析对象,这里由这个视图解析器负责解析返回值并跳转页面-->
    <bean id="internalResourceViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <!--配置文件路径,到该路径下去搜索-->
    <property name="prefix" value="/WEB-INF/pages/"/>
    <!--目标文件的后缀名为.html-->
    <property name="suffix" value=".html"/>
    </bean>
  • 开启SpringMVC框架注解的支持

    1
    2
    <!--开启SpringMVC框架注解的支持-->
    <mvc:annotation-driven/>

    入门案例的执行流程

    1. 当启动Tomcat服务器的时候,因为配置了load-on-startup标签,所以会创建DispatcherServlet对象,就会加载springmvc.xml配置文件
    2. 开启了注解扫描,那么HelloController对象就会被创建
    3. 从index.html点击超链接发送请求,请求会先到达DispatcherServlet核心控制器,根据配置@RequestMapping注解找到执行的具体方法
    4. 根据执行方法的返回值,再根据配置的视图解析器,去指定的目录下查找指定名称的html文件
    5. Tomcat服务器渲染页面,做出响应
    6. 流程图

    aswDnf.png

SpringMVC基于组件的运行流程

  • aswHN4.jpg
  • 处理器映射器:开发中要编写的具体业务控制器,由DispatcherServlet把用户请求转发到Handler,由Handler对具体的用户请求进行处理

  • 处理器适配器:通过HandlerAdapter对处理器进行执行,这是适配器模式的应用,通过扩展适配器可以对更多类型的处理器进行执行

  • 视图解析器:负责将处理结果生成View视图,View Resolver首先根据逻辑视图名解析成物理视图名即具体的页面地址,再生成View视图对象,最后对View进行渲染将处理结果通过页面展示给用户。

  • <mvc:annotation-driven>说明:配置文件中声明该语句,能够默认进行处理器映射器和处理器适配器的配置。

  • RequesMapping

    • 作用:用于建立请求URL和处理请求方法之间的对应关系。

    • 使用:可添加在类上和方法上,类上的话在访问方法要多级路径,目的是模块化管理

    • 属性:

      value/path:用于指定请求的URL
      method:用于指定请求的方式,如图当值设为post时,get请求无法访问

      1
      @RequestMapping(value="/saveAccount",method={RequestMethod.POST})

      params:用于指定限制请求参数的条件

问题

==tomcat无法访问html后缀的文件==

  • 原因是默认的配置DispatcherServlet屏蔽了html页面的访问,需要加上如下web.xml中后面多加这样一个mapping

    1
    2
    3
    4
    <servlet-mapping>
    <servlet-name>default</servlet-name>
    <url-pattern>*.html</url-pattern>
    </servlet-mapping>

请求参数绑定

请求参数绑定入门操作

  • mvc框架会自动将传过来的参数绑定入控制器的方法中

    1
    <a href="/param/paramTest?user=wdc&password=12345" >请求参数绑定入门</a>
    1
    2
    3
    4
    5
    6
    7
    8
    9
    @Controller
    @RequestMapping(path = "/param")
    public class ParamController {
    @RequestMapping(path = "/paramTest")
    public String paramTest(String user, String password){
    System.out.println("user:" + user + " password:" + password);
    return "success";
    }
    }

请求参数为bean类型

  • 此时html中提交一个表单,这里提交的是一个对象,有user,money,account三个属性,其中user为自定义类

    1
    2
    3
    4
    5
    6
    7
    8
    9
    <!--这里action为对应RequestMapper的url,metho为post类型-->
    <form action="/param/paramBean" method="post">
    <!--这里的name与后面的实体类对象的属性要相同-->
    姓名:<input type="text" name="user.name"/><br/> <!--因此用这种形式来绑定引用类型user内部的属性,属性名也要求相同-->
    年龄:<input type="text" name="user.age"/><br/>
    金额:<input type="text" name="money"/><br>
    账户:<input type="text" name="account"/><br>
    <input type="submit" name=""/><br>
    </form>
  • 创建Java实体对象UserAccount与User,并编写表单提交所对应的方法

    1
    2
    3
    4
    5
    @RequestMapping(path = "paramBean")
    public String paramBean(UserAccount userAccount){
    System.out.println(userAccount);
    return "success";
    }
  • 在web.xml中配置过滤器解决post请求中文乱码问题

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    <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>
    </filter>
    <filter-mapping>
    <filter-name>characterEncodingFilter</filter-name>
    <url-pattern>/*</url-pattern>
    </filter-mapping>

请求类型为List或Map

  • 在实体类中存在List<User> userList, Map<String, User> userMap 时,表单中如下填写

    1
    2
    3
    4
    <input type="text" name="userList[0].name" ><br/> 
    <input type="text" name="userList[0].age" ><br/>
    <input type="text" name="userMap['用户1'].name" ><br/> <!--这里前者填写key,后者填写value-->
    <input type="text" name="userMap['用户1'].money" ><br/>
  • SpringMVC会自动帮你转换数据类型,如前端界面是String,后端实体类为Integer,会帮你自动完成转换,当无法自动转换时,可以使用自定义类型转换器来解决

问题

  • 页面跳转再返回,Chrome会从缓存中读取页面,而不访问服务器,导致无法查看参数信息

    • 解决办法,在Chrome开发者工具中Prefrence-Network-disable chache
  • 在请求参数为Bean类型时,跳转时会出现500异常,并提示org.springframework.beans.InvalidPropertyException:Invalid property 'users'

    • 解决办法,在创建实体对象时,==必须要有无参构造方法==
  • 在web.xml中添加了filter之后,webapp标签标红

    • 解决办法:web.xml中有标签顺序的格式要求,错误就会标红,将filter写在servlet前面即可

常用注解

RequestParam注解

  • 作用:把请求中的指定名称的参数传递给控制器中的形参赋值(当请求的变量与控制器的变量名不同时使用)

  • 属性:value:请求参数中的名称;required:请求参数中是否必须提供此参数,默认值是true,必须提供

    1
    2
    3
    4
    5
    @RequestMapping(path="/hello")
    public String sayHello(@RequestParam(value="username",required=false)String name) {
    System.out.println(name);
    return "success";
    }

ResponseBody

  • 用来响应json数据,详见下一节

RequestBody注解

  • 作用:用于获取请求体的内容(注意:get方法不行,get方法不带请求体,而是将内容放在地址栏后面),打印的是请求体的内容

    1
    2
    3
    4
    5
    @RequestMapping(path="/hello")
    public String sayHello(@RequestBody String body) {
    System.out.println(body);
    return "success";
    }

PathVariable注解

  • 作用:拥有绑定url中的占位符的。例如:url中有/delete/{id},{id}就是占位符

  • 属性:value:指定url中的占位符名称

  • Restful风格(REST,Representational State Transfer):特点:1. 结构清晰,2.符合标准,3.易于理解,4.扩展方便

  • 使用:

    • html中的url直接“/”后跟内容,==url中占位符来替代,注解能帮助解析该内容==
    1
    <a href="user/hello/1">入门案例</a>
    1
    2
    3
    4
    5
    6
    @RequestMapping(path="/hello/{uid}")
    //注解解析{id}里的内容,并将uid赋值给方法入参的id
    public String sayHello(@PathVariable(value="uid") String id) {
    System.out.println(id);
    return "success";
    }

ModelAttribute注解

  • 出现在方法上,表示当前方法会在控制器的方法执行之前,先执行(可修饰没有返回值与有具体返回值的方法);出现在参数上,获取指定的数据给参数赋值。
  • 属性:value:用于获取数据的key,key可以是POJO的属性名称,也可以是map结构的key

SessionAttribute

  • 作用:用于多次执行控制器方法间的参数共享

  • 属性:value:用于指定存入的属性名称;type:用于指定存入的数据类型。

  • 使用:1、在控制器的方法中用Model接口设置request域,5-10行;2、接下来在jsp文件中打印request域的信息,能够发现已经写入;3、在类上添加@SessionAttributes注解,value为设置的name,在jsp中打印session域的信息;4、添加两个方法,验证参数共享的实现,第二个方法从中取name并打印在控制台,第三个方法删除session域。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    @Controller
    @RequestMapping(path = "/annotation")
    @SessionAttributes(value = "name") //添加注解后会将name小高放入Session域中,以实现控制器之间的参数共享
    public class AnnotationController {
    @RequestMapping(path = "/save_SessionAttributes")
    public String save_SessionAttribute(Model model){
    //底层会存储到request域对象中
    model.addAttribute("name", "小高");
    return "success";
    }
    @RequestMapping(path = "/get_SessionAttributes")
    public String get_SessionAttribute(ModelMap modelmap){
    //从Session域中取值
    System.out.println(modelmap.get("name"));
    return "success";
    }
    @RequestMapping(path = "/delete_SessionAttributes")
    public String delete_SessionAttribute(SessionStatus status){
    System.out.println("清除Session域...");
    status.setComplete();//调用方法删除Session域,结束参数共享
    return "success";
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    <!DOCTYPE html>
    <%@ page contentType="text/html; charset=UTF-8" language="java" isELIgnored="false" %>
    <html lang="en">
    <head>
    <meta charset="UTF-8">
    <title>已跳转</title>
    </head>
    <body>
    <h3>成功访问控制器!</h3>
    ${requestScope.name}<br>
    ${sessionScope}
    </body>
    </html>

响应数据和返回视图

返回值是字符串

  • 返回普通的视图,上面讲的跳转的都是普通的视图

  • 从数据库中调用一个对象,并写入responseScope,以供前端界面读取

    1
    2
    3
    4
    5
    6
    7
    8
    @RequestMapping(path = "/testString")
    public String testString(ModelMap modelMap){
    System.out.println("成功执行testString...");
    //模拟从数据库中读取一个User,并添加到responseScope
    User user = new User("小高", "25");
    modelMap.addAttribute("user", user);
    return "success";
    }

返回值是void

  • 返回值是void类型,视图解析器首先有一个默认的url,会报错找不到/WEB-INF/pages/response/testVoid.jsp,有三种方式来处理void响应。==使用转发或重定向时,不使用视图解析器,而要保证路径名不错==

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    @RequestMapping(path = "/testVoid")
    public void testVoid(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    //1、请求转发,发送一个请求
    request.getRequestDispatcher("/WEB-INF/pages/success.jsp").forward(request, response);

    //2、重定向,发送两个请求,无法访问/WEB-INF/pages/内的文件
    response.sendRedirect(request.getContextPath() + "/index.jsp");

    //3、直接响应数据
    response.setCharacterEncoding("UTF-8");
    response.setContentType("text/html;charset=UTF-8");
    response.getWriter().println("你好");
    return;
    }

返回值是ModelAndView

  • 和String没有区别,事实上返回值是String时,底层就是调用ModelAndView对象来实现

用ResponseBody响应json数据

  • 事实上,web.xml中配置的DispatcherServlet会将所用静态资源也全部拦截,因此界面上的js、css、images都会被拦截而无法响应,因此先在springmvc.xml中配置

    1
    2
    <!--配置前端控制器不拦截静态资源-->
    <mvc:resources mapping="/js/**" location="/js/"/>
  • ajax请求的发送

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    $('#button1').click(function () {
    $.ajax({
    url: "/response/testJson",
    contentType: "application/json; charset=utf-8;",
    data_type: "json",
    data: JSON.stringify({"name": "xiaogao", "age" : "24"}),
    type: 'post',
    success:function (data) {
    console.log(data);
    }
    });
    });
  • 控制器接收post请求并作出响应

    • 这里需要在maven中添加com.fasterxml.jackson的三个相关依赖,这样以后,@RequestBody User user就能直接将传过来的数据解析成User对象(==要求穿过来的json中的参数名和User中的属性相同==);在接收完成后,在方法上加@ResponseBody就能直接将数据以JSON格式返回给ajax。
    1
    2
    3
    4
    5
    6
    7
    @RequestMapping(path = "/testJson")
    public @ResponseBody User testJson(@RequestBody User user){
    System.out.println(user);
    user.setName("阿高");
    user.setAge("1000");
    return user;
    }

问题

  • ajax请求post时出现415错误

    • 问题原因,在ajax中的contentType: "application/x-www-form-urlencoded; charset=utf-8;"不行,应改成application/json; charset=utf-8;
  • 在ajax的data的json中存在中文时,若控制器中传入String,则会出现中文乱码

    • 问题原因,直接传json会解析出错,因为@ResponseBody注解接收的是字符串而不是JSON对象,解决办法是ajax发送时用data:JSON.stringify(data)将数据转换成字符串
  • Tomcat在使用时,若是web目录下的内容发生变动,只需更新resource和class,若java目录下的内容发生变动,则需要重新部署才会生效

文件上传

  • 分服务器目的:在实际开发中,我们会有很多处理不同功能的服务器。例如:
    • 应用服务器:负责部署我们的应用
    • 数据库服务器:运行我们的数据库 (mysql oracle)
    • 缓存和消息服务器:负责处理大并发访问的缓存和消息(redis)
    • 文件服务器:负责存储用户上传文件的服务器

异常处理

SpringMVC异常处理流程

asByWj.png

解决思路

  • 编写一个自定义异常类

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    public class MyException {
    private String message;
    public MyException(String message) {
    this.message = message;
    }
    public String getMessage() {
    return message;
    }
    public void setMessage(String message) {
    this.message = message;
    }
    }
  • 模拟Comtroller中出现了异常

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    @Controller
    @RequestMapping(path = "/exceptionTest")
    public class exceptionController {

    @RequestMapping(path = "myExceptionTest")
    public String myExceptionTest() throws Exception{
    try {
    int num = 10 / 0; //模拟异常
    } catch (Exception e) {
    e.printStackTrace(); //打印异常信息
    //抛出自定义异常信息
    throw new MyException("页面发生了错误...");
    }
    return "error";
    }
    }
  • 编写异常处理器(需要继承HandlerExceptionResolver接口),编写完后将其交给Spring处理

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    @Controller
    public class MyExceptionHandler implements HandlerExceptionResolver {
    @Override
    public ModelAndView resolveException(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) {
    //获取异常对象
    MyException myException;
    if(e instanceof MyException){
    myException = (MyException) e;
    }else {
    e = new MyException("页面出错了...");
    }
    //床架ModelAndView对象并从中添加内容
    ModelAndView modelAndView = new ModelAndView();
    modelAndView.addObject("errorMessage", e.getMessage());
    modelAndView.setViewName("error");
    return modelAndView;
    }
    }
  • 编写前端友好提示的页面,打印ModelAndView中添加的内容${errorMessage}

拦截器

概述

  • SpringMVC框架中的拦截器用于对处理器进行预处理和后处理的技术
  • 可以定义拦截器链,在访问被拦截的方法时,拦截器链中的拦截器会按着定义的顺序执行
  • 拦截器和过滤器的功能比较类似,有区别
    • 过滤器是Servlet规范的一部分,任何框架都可以使用过滤器技术
    • 拦截器是SpringMVC框架独有的
    • 过滤器配置了/*,可以拦截任何资源
    • 拦截器只会对控制器中的方法进行拦截
  • 拦截器也是AOP思想的一种实现方式
  • 想要自定义拦截器,需要实现HandlerInterceptor接口
asBzkD.png

自定义拦截器编写

  • 编写拦截器类,实现HandlerIntercepter接口

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    public class MyInterceptor implements HandlerInterceptor {

    //预处理,在controller方法执行前执行
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    System.out.println("MyInterceptor的预处理方法执行了...pre");
    return true;
    }

    //后处理,在controller方法执行后,在success.jsp执行前
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
    System.out.println("MyInterceptor的后处理方法执行了...post");
    }

    //完成后处理,在success.jsp执行后
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
    System.out.println("MyInterceptor的完成处理方法执行了...after");
    }
    }
  • springmvc.xml中配置拦截器,==若有多个拦截器,需要配置多个==<mvc:interceptor>标签

    1
    2
    3
    4
    5
    6
    7
    8
    <!--配置拦截器-->
    <mvc:interceptors>
    <mvc:interceptor>
    <!--配置要拦截的方法,也可用mvc:exclude-mapping配置不用拦截的方法-->
    <mvc:mapping path="/interceptor/*"/>
    <bean class="com.zjut.domain.MyInterceptor"/>
    </mvc:interceptor>
    </mvc:interceptors>
  • 效果

    1
    2
    3
    4
    5
    MyInterceptor的预处理方法执行了...pre
    拦截器控制器执行了...
    MyInterceptor的后处理方法执行了...post
    success.jsp执行了...
    MyInterceptor的完成处理方法执行了...after

SSM整合

流程

  • 由Spring去整合另外两个框架
1
2
graph LR
A(表现层SpringMVC)--整合---B(业务层Spring)--整合---c(DAO层Mybatis)

环境搭建

  • Navicat中创建数据库与表

    1
    2
    3
    4
    5
    6
    7
    create database ssm;
    use ssm;
    create table account(
    id int primary key auto_increment,
    name varchar(20),
    money double
    );
  • 创建Maven工程,并添加所需依赖

  • 创建domain文件夹存放javabean,controller文件夹存放控制器,dao文件夹存放dao接口,service文件夹存放业务实现类,目录结构如下

    asrJKI.png

编写Spring框架

  • 创建配置文件,开启注解扫描

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    <?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.zjut.service"/>
    <context:component-scan base-package="com.zjut.dao"/>
    <context:component-scan base-package="com.zjut.domain"/>
    </beans>
  • 在业务层实现类上添加注解

    1
    2
    @Service("accountService")
    public class AccountServiceImpl implements AccountService {
  • 创建测试类,测试Spring框架运行情况

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    @RunWith(value = SpringJUnit4ClassRunner.class)
    @ContextConfiguration("classpath:applicationContext.xml")
    public class SpringTest {
    @Autowired
    @Qualifier(value = "accountService")
    private AccountServiceImpl accountService;

    @Test
    public void springTest(){
    accountService.findAll();
    }
    }

编写SpringMVC框架

  • web.xml中添加前端控制器,配置读取springmvc.xml配置文件,并添加解决中文乱码的过滤器
  • 创建springmvc.xmlpei配置文件,并开启controller文件夹的组件扫描,配置视图解析器、静态资源扫描、开启springmvc注解支持
  • 在index.jsp中创建超链接,并创建点击后将要跳转的页面
  • 在controller文件夹中的类添加控制器方法
  • 配置Tomcat服务器,并运行

Spring整合SpringMVC

  • 目的:控制器中能共访问业务层的对象,并调用业务层的方法

  • 思路

    asrrxs.png
  • 这个监听器spring框架提供了,可以直接在web.xml中配置监听器即可使用

  • 该间监听器只能加载WEB-INF目录中名称为applicationContext.xml的配置文件,因此要另外指定spring配置文件的位置

    1
    2
    3
    4
    5
    6
    7
    8
    9
    <!--配置监听器,配置spring提供的监听器,用于启动服务时加载容器 -->
    <listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
    <!--手动指定配置文件位置-->
    <context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:applicationContext.xml</param-value>
    </context-param>

编写Mybatis框架

  • 创建查询映射文件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    <mapper namespace="com.zjut.dao.AccountDao">
    <!--查找所有账户-->
    <select id="findAll" resultType="com.zjut.domain.Account">
    SELECT
    *
    FROM `account`
    </select>

    <!--插入用户-->
    <insert id="save" parameterType="com.zjut.domain.Account">
    INSERT INTO `account` ( `name`, `money` )
    VALUES
    ( #{name},
    #{money});
    </insert>
    </mapper>
  • 创建Mybatis配置文件SQLMapConfig.xml,配置数据库内容,并添加映射

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    <configuration>
    <!-- 和spring整合后 environments配置将废除 -->
    <environments default="development">
    <environment id="development">
    <!-- 使用jdbc事务管理 -->
    <transactionManager type="JDBC" />
    <!-- 数据库连接池 -->
    <dataSource type="POOLED">
    <property name="driver" value="com.mysql.cj.jdbc.Driver" />
    <property name="url"
    value="jdbc:mysql://localhost:3306/ssm?characterEncoding=utf-8&amp;serverTimezone=UTC" />
    <property name="username" value="root" />
    <property name="password" value="Wdc82563815" />
    </dataSource>
    </environment>
    </environments>

    <!--加载映射文件-->
    <mappers>
    <!--<package name="com.zjut.dao.mapper"/>-->
    <mapper resource="\mapper\accountMapper.xml"></mapper>
    </mappers>
    </configuration>
  • 测试运行结果

Spring整合Mybatis

  • 目的:service能成功调用dao对象并完成查询等dao对象中的操作

  • 思想:

    • 1、SqlSessionFactory对象应该放到spring容器中作为单例存在。
    • 2、传统dao的开发方式中,应该从spring容器中获得sqlsession对象。
    • 3、Mapper代理形式中,应该从spring容器中直接获得mapper的代理对象。
    • 4、数据库的连接以及数据库连接池事务管理都交给spring容器来完成。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    <!--加载JDBC配置文件-->
    <context:property-placeholder location="jdbc.properties"/>
    <!--配置数据库连接池-->
    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
    <property name="url" value="${jdbc.url}"/>
    <property name="driverClassName" value="${jdbc.driver}"/>
    <property name="username" value="${jdbc.username}"/>
    <property name="password" value="${jdbc.password}"/>
    <!--连接池最大连接数与最大空闲数-->
    <property name="maxActive" value="10"/>
    <property name="maxIdle" value="5"/>
    </bean>

    <!--配置SqlSessionFactory-->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
    <!--数据源,ref即数据库连接池的id-->
    <property name="dataSource" ref="dataSource"/>
    <!-- mybatis核心配置文件 -->
    <!--<property name="configLocation" value="classpath:com/zjut/controller/SQLMapConfig.xml"/>-->
    </bean>

    <!--动态dao开发方式-->
    <bean id="mapperScannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
    <property name="basePackage" value="com.zjut.dao"/>
    <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
    </bean>

声明事务管理

  • 以上已经基本完成了SSM整合,但是保存还没有实现,因此要配置声明式事务管理

    • 配置事务管理器

    • 配置事务通知

    • 配置AOP增强

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      <!--配置事务管理器-->
      <bean id="dataSourceTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
      <property name="dataSource" ref="dataSource"/>
      </bean>
      <!--配置事务通知-->
      <tx:advice id="txAdvice" transaction-manager="dataSourceTransactionManager">
      <tx:attributes>
      <!--表示find开头的方法都是只读的-->
      <tx:method name="find*" read-only="true"/>
      <tx:method name="*" isolation="DEFAULT"/>
      </tx:attributes>
      </tx:advice>

      <aop:config>
      <!--对service层接口中的所有方法都增强-->
      <aop:advisor advice-ref="txAdvice" pointcut="execution(* com.zjut.service.impl.*AccountServiceImpl.*(..))"/>
      </aop:config>

问题

  • 在springMVC和spring整合时,在控制器中注入业务层类时,使用@Resource注解会报错

  • 另外若在controller中直接使用@AutoWired注入service对象会警告

    • 原因:通常依赖注入方式有三种

      • constructor
        为了强制依赖,或者为了易变性,使用构造方法注入
      • getter & setter
        为了可选的或者可变的依赖,使用setter注入
      • 通过反射直接注入到fields@Autowired就是通过这种方式
        尽量避免使用直接在属性上注入
    • 属性注入的坏处

      1、你不能使用属性注入的方式构建不可变对象。
      2、你的类和依赖容器强耦合,不能再容器外使用。
      3、你的类不能绕过反射(例如单元测试的时候)进行实例化,必须通过依赖容器才能实例化。
      4、实际的依赖被隐藏在外面,不是在构造方法或者其它方法里面反射的。
      5、一个类经常会有超过10个的依赖。如果使用构造方法的方式注入的话,构造方法会有10个参数,明显有点蠢。但是如果使用属性注入的话就没有这样的限制。但是一个类有很多的依赖,是一个危险的标志,因为很有可能这个类完成了超过一件事,违背了单一职责原则。

    • 解决:使用构造器注入该属性,IDEA里直接alt+enter即可

  • 在最终测试能否跑通时,发现在controller中注入service接口属性时报错org.springframework.beans.factory.BeanNotOfRequiredTypeException: Bean named 'accountService' is expected to be of type 'com.zjut.service.impl.AccountServiceImpl'

    • 解决办法:在注入serivce时,类型应写成接口类型而不是实现类的类型,否则就会报错