SSM框架整合篇

SSM(Spring+SpringMVC+MyBatis)框架集是由Spring、MyBatis两个开源框架整合而成(Spring整合Mybatis,SpringMVC是Spring中的部分内容),是继SSH(Spring+Struts+Hibernate)之后,目前比较主流的Java EE企业级框架,适用于搭建各种大型的企业级应用系统。

Spring

Spring是于2003年兴起的一个轻量级的Java开发框架,是为了解决企业级应用开发的复杂性而创建的,为了简化开发。它可以使用基本的JavaBean来完成以前只可能由EJB完成的事情。它是一个轻量级的控制反转(IoC)和面向切面(AOP)的容器框架。

SpringMVC

SpringMVC是一种基于Java,实现了Web MVC设计模式,请求驱动类型的轻量级Web框架。它属于Spring Framework的后续产品,是Spring的一个子项目。

Mybatis

MyBatis是一个可以自定义SQL、存储过程和高级映射的持久层框架。

一.项目创建

1.1 创建项目

创建Maven项目并选择webapp原型

图片[1]-SSM框架整合篇-墨初小屋

1.2 导入所需依赖

<properties>
    <spring.version>5.3.1</spring.version>
</properties>

<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-beans</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <!--springmvc-->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-web</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <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>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-aspects</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-test</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <!-- Mybatis核心 -->
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
        <version>3.5.7</version>
    </dependency>
    <!--mybatis和spring的整合包-->
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis-spring</artifactId>
        <version>2.0.6</version>
    </dependency>
    <!-- 连接池 -->
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid</artifactId>
        <version>1.0.9</version>
    </dependency>
    <!-- junit测试 -->
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
        <scope>test</scope>
    </dependency>
    <!-- MySQL驱动 -->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>8.0.16</version>
    </dependency>
    <!-- log4j日志 -->
    <dependency>
        <groupId>log4j</groupId>
        <artifactId>log4j</artifactId>
        <version>1.2.17</version>
    </dependency>
    <!--分页插件-->
    <dependency>
        <groupId>com.github.pagehelper</groupId>
        <artifactId>pagehelper</artifactId>
        <version>5.2.0</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>
    <!--jackson处理json数组-->
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-databind</artifactId>
        <version>2.12.1</version>
    </dependency>
    <!--文件上传依赖-->
    <dependency>
        <groupId>commons-fileupload</groupId>
        <artifactId>commons-fileupload</artifactId>
        <version>1.3.1</version>
    </dependency>
    <!-- Spring5和Thymeleaf整合包 -->
    <dependency>
        <groupId>org.thymeleaf</groupId>
        <artifactId>thymeleaf-spring5</artifactId>
        <version>3.0.12.RELEASE</version>
    </dependency>
</dependencies>

二.目录结构

图片[2]-SSM框架整合篇-墨初小屋

控制层controller、业务层service、持久层mapper(dao)

web应用程序配置文件:web.xml

web框架SpringMVC配置文件:springmvc.xml

Spring框架配置文件:spring.xml

MyBatis核心配置文件:mybatis-config.xml

数据库连接配置JDBC属性文件:jdbc.properties

日志配置文件:log4j.xml

三.配置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">

    <!--配置Spring的编码过滤器-->
    <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>

    <!--配置处理请求方式的过滤器-->
    <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>

    <!--配置SpringMVC的前端控制器-->
    <servlet>
        <servlet-name>SpringMVC</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:springmvc.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>SpringMVC</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

    <!--配置Spring的监听器,在服务器启动时加载Spring的配置文件-->
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

    <!--设置Spring配置文件自定义的位置和名称-->
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:spring.xml</param-value>
    </context-param>

</web-app>

3.1 编码过滤器

解决获取请求参数的乱码问题,可以使用 SpringMVC 提供的编码过滤器 CharacterEncodingFilter

<!--配置Spring的编码过滤器-->
<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>

encoding:编码类型

forceEncoding:同时为响应设置

3.2 处理请求方式的过滤器

SpringMVC 提供了 HiddenHttpMethodFilter 帮助我们将 POST 请求转换为 DELETE 或 PUT 请求(删除与更新)

<!--配置处理请求方式的过滤器-->
<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.当前请求必须传输请求参数method,method的值才是最终的请求方式
<form th:action="@{/user}" method="post">
    <input type="hidden" name="_method" value="put">
    <input type="submit" value="修改用户信息">
</form> <br>
<form th:action="@{/user/1}" method="post">
    <input type="hidden" name="_method" value="delete">
    <input type="submit" value="删除用户信息">
</form> <br>
@RequestMapping(value = "/user", method = RequestMethod.PUT)
public String updateUser(){
    System.out.println("修改用户信息-->    /user   -->put");
    return "success";
}

@RequestMapping(value = "/user/{id}", method = RequestMethod.DELETE)
public String deleteUser(@PathVariable("id") Integer id){
    System.out.println("删除用户信息-->    /user/" + id + " -->delete");
    return "success";
}

在web.xml中注册时,必须先注册CharacterEncodingFilter,再注册HiddenHttpMethodFilter

原因:

​ 在 CharacterEncodingFilter 中通过 request.setCharacterEncoding(encoding) 方法设置字符集的 request.setCharacterEncoding(encoding) 方法要求前面不能有任何获取请求参数的操作

​ 而 HiddenHttpMethodFilter 恰恰有一个获取请求方式的操作:String paramValue = request.getParameter(this.methodParam);

3.3 SpringMVC的前端控制器

对请求和响应进行统一处理

<!--配置SpringMVC的前端控制器-->
<servlet>
    <servlet-name>SpringMVC</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:springmvc.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
    <servlet-name>SpringMVC</servlet-name>
    <url-pattern>/</url-pattern>
</servlet-mapping>

contextConfigLocation:设置SpringMVC配置文件的位置和名称

load-on-startup:DispatcherServlet的初始化时间提前到服务器启动时,提高初次访问网页速度

url-pattern中 / 和 /* 的区别:
​ /: 匹配浏览器向服务器发送的所有请求(不包括. jsp)
​ /*: 匹配浏览器向服务器发送的所有请求(包括. jsp)
bispatcherServlet不能处理jsp的请求

3.4 Spring的监听器

在web服务器的启动时,读取Spring的配置文件,创建Spring的IOC容器

<!--配置Spring的监听器-->
<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!--设置Spring配置文件自定义的位置和名称-->
<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:spring.xml</param-value>
</context-param>

四.配置springmvc.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">

    <!-- 扫描控制层组件 -->
    <context:component-scan base-package="com.junn.ssm.controller"></context:component-scan>

    <!-- 配置视图解析器 -->
    <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>

    <!-- 配置默认的servlet处理静态资源 -->
    <mvc:default-servlet-handler />

    <!-- 开启MVC的注解驱动 -->
    <mvc:annotation-driven />

    <!-- 配置访问首页的视图控制 -->
    <mvc:view-controller path="/" view-name="index"></mvc:view-controller>

    <!-- 配置文件上传解析器 -->
    <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"></bean>

</beans>
<!--
	拦截器和异常解析器需要才配置
-->

<!-- 配置拦截器 -->
<mvc:interceptors>
    <mvc:interceptor>
        <mvc:mapping path="/**"/>
        <mvc:exclude-mapping path="/testRequestEntity"/>
        <ref bean="firstInterceptor"></ref>
    </mvc:interceptor>
</mvc:interceptors>

<!-- 配置异常解析器 -->
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
    <property name="exceptionMappings">
        <props>
            <prop key="java.lang.ArithmeticException">error</prop>
        </props>
    </property>
    <property name="exceptionAttribute" value="ex"></property>
</bean>

4.1 扫描组件

<!-- 扫描控制层组件 -->
<context:component-scan base-package="com.junn.ssm.controller"></context:component-scan>

① 注解

和 XML 配置文件一样,注解本身并不能执行,
注解本身仅仅只是做一个标记,具体的功能是框架检测 到注解标记的位置,然后针对这个位置按照注解标记的功能来执行具体操作。
本质上:所有一切的操作都是Java代码来完成的,XML和注解只是告诉框架中的Java代码如何执行。
@Service
public class UserServiceImpl implements UserService {
    
}
@Component:将类标识为普通组件
@Controller:将类标识为控制层组件
@Service:将类标 识为业务层组件
@Repository:将类标识为持久层组件

② 扫描

Spring 为了知道程序员在哪些地方标记了什么注解,就需要通过扫描的方式,来进行检测。然后根据注解进行后续操作

1. 最基本的扫描方式

<!--扫描组件-->
<context:component-scan base-package="com.junn.spring"></context:component-scan>
存在的问题:
​ SpringMVC 扫描控制层
​ Spring 扫描除控制层以外所有组件
所以需要排除组件

2. 指定要排除的组件

context:exclude-filter标签:指定排除规则
type:设置排除或包含的依据
​ type="annotation",根据注解排除(最常用),expression中设置要排除的注解的全类名
​ type="assignable",根据类型排除,expression中设置要排除的类型的全类名
<context:component-scan base-package="com.junn.spring">
    <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>

3. 仅扫描指定组件

context:include-filter标签:指定在原有扫描规则的基础上追加的规则
type:设置排除或包含的依据
​ type="annotation",根据注解排除,expression中设置要排除的注解的全类名
​ type="assignable",根据类型排除,expression中设置要排除的类型的全类名

use-default-filters属性:取值false表示关闭默认扫描规则,
此时必须设置use-default-filters="false",因为默认规则即扫描指定包下所有类
<context:component-scan base-package="com.junn.spring" use-default-filters="false">
	<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>

③ 组件所对应的bean的id

在我们使用XML方式管理bean的时候,每个bean都有一个唯一标识,便于在其他地方引用。

现在使用注解后,每个组件仍然应该有一个唯一标识

默认情况
​ 类名首字母小写就是bean的id。
​ 例如:UserController类对应的bean的id就是userController。

自定义bean的id
​ 可通过标识组件的注解的value属性设置自定义的bean的id
@Service("userService")//默认为userServiceImpl 
public class UserServiceImpl implements UserService {

}

④ 基于注解的自动装配

@Autowired注解能够标识的位置
a> 标识在成员变量上,此时不需要设置成员变量的set方法
b> 标识在set方法上
c> 标识在为当前成员变量赋值的有参构造上
@Controller
public class UserController {

    @Autowired
    private UserService userService;

    public void saveUser(){
        userService.saveUser();
    }

}

// 注意基于注解的自动装配不需要get和set方法

4.2 视图解析器

<!-- 配置视图解析器 -->
<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>
浏览器发送请求,若请求地址符合前端控制器的url-pattern,该请求就会被前端控制器 DispatcherServlet处理。
前端控制器会读取SpringMVC的核心配置文件,通过扫描组件找到控制器, 将请求地址和控制器中@RequestMapping注解的value属性值进行匹配,
若匹配成功,该注解所标识的 控制器方法就是处理请求的方法。处理请求的方法需要返回一个字符串类型的视图名称,该视图名称会 被视图解析器解析,
加上前缀和后缀组成视图的路径,通过Thymeleaf对视图进行渲染,最终转发到视 图所对应页面

4.3 默认的servlet与开启注解

当前工程的 web. xml 配置的前端控制器 DispatcherServlet 的 url-pattern 是 /,tomcat 的 web. xml 配置的 DefaultServlet 的 url -pattern 也是 /

此时,浏览器发送的请求会优先被Dispatcherservlet进行处理,但是Dispatcherservlet 无法处理静态资源

<!-- 配置默认的servlet处理静态资源 -->
<mvc:default-servlet-handler />

<!-- 开启MVC的注解驱动 -->
<mvc:annotation-driven />
配置 <mvc:default-servlet-handler /> ,此时浏览器发送的所有请求都会被DefaultServlet处理
配置 <mvc:annotation-driven /> 和 <mvc:annotation-driven />,浏览器发送的请求会先被DispatcherServlet处理,无法处理在交给Defaul tServlet处理

4.4 视图控制器

当控制器方法中,仅仅用来实现页面跳转,比如首页,即只需要设置视图名称时,可以将处理器方法使用viewcontroller标签进行表示

<!-- 配置访问首页的视图控制 -->
<mvc:view-controller path="/" view-name="index"></mvc:view-controller>
注:
当SpringMVC中设置任何一个view-controller时,其他控制器中的请求映射将全部失效,
此时需要在SpringMVC的核心配置文件中设置开启mvc注解驱动的标签:<mvc:annotation-driven />

4.5 文件上传解析器

必须通过文件解析器的解析才能将文件转换为MultipartFile对象

<!-- 配置文件上传解析器 -->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"></bean>

① 文件下载

使用ResponseEntity实现下载文件的功能

@RequestMapping("/test/down")
public ResponseEntity<byte[]> testResponseEntity(HttpSession session) throws IOException {
    //获取ServletContext对象
    ServletContext servletContext = session.getServletContext();
    //获取服务器中文件的真实路径
    String realPath = servletContext.getRealPath("/img/head_cat.jpg");
    //创建输入流
    InputStream is = new FileInputStream(realPath);
    //创建字节数组,获取字节数
    byte[] bytes = new byte[is.available()];
    //将流读到字节数组中
    is.read(bytes);
    //创建HttpHeaders对象设置响应头信息
    MultiValueMap<String, String> headers = new HttpHeaders();
    //设置要下载方式以及下载文件的名字
    headers.add("Content-Disposition", "attachment;filename=1.jpg");
    //设置响应状态码
    HttpStatus statusCode = HttpStatus.OK;
    //创建ResponseEntity对象
    ResponseEntity<byte[]> responseEntity = new ResponseEntity<>(bytes, headers, statusCode);
    //关闭输入流
    is.close();
    return responseEntity;
}

② 文件上传

要求form表单的请求方式必须为post,并且添加属性enctype=”multipart/form-data”

SpringMVC中将上传的文件封装到MultipartFile对象中,通过此对象可以获取文件相关信息

文件上传的要求:
form表单的请求方式必须为post
form表单必须设置属性enctype="multipart/form-data"以二进制形式传输
<form action="/SpringMVC/test/up" method="post" enctype="multipart/form-data">
    头像:<input type="file" name="photo"><br/>
    <input type="submit" value="上传">
</form>
@RequestMapping("/test/up")
public String testUp(MultipartFile photo, HttpSession session) throws IOException {
    //获取上传的文件的文件名
    String fileName = photo.getOriginalFilename();
    //获取上传的文件的后缀名
    String hzName = fileName.substring(fileName.lastIndexOf("."));
    //获取uuid
    String uuid = UUID.randomUUID().toString();
    //拼接一个新的文件名
    fileName = uuid + hzName;
    //获取ServletContext对象
    ServletContext servletContext = session.getServletContext();
    //获取当前工程下photo目录的真实路径
    String photoPath = servletContext.getRealPath("photo");
    //创建photoPath所对应的File对象
    File file = new File(photoPath);
    //判断file所对应目录是否存在
    if (!file.exists()) {
        file.mkdir();
    }
    String finalPath = photoPath + File.separator + fileName;
    //上传文件
    photo.transferTo(new File(finalPath));
    return "success";
}

注意:文件类型input的name值需要与MultipartFile参数值对应,否则会报空指针异常

4.6 拦截器

用于拦截控制器方法的执行,它需要实现HandlerInterceptor ,前端控制器DispatcherServlet本质上就是一个拦截器

① 拦截器的配置

SpringMVC的拦截器必须在SpringMVC的配置文件中进行配置

通过mvc:mapping设置需要拦截的请求,通过mvc:exclude-mapping设置需要排除的请求

<mvc:interceptors>
        <ref bean="firstInterceptor" />
        <ref bean="secondInterceptor" />
        <!--或者-->
        <mvc:interceptor>
            <mvc:mapping path="/**"/>
            <mvc:exclude-mapping path="/testRequestEntity"/>
            <ref bean="firstInterceptor"></ref>
        </mvc:interceptor>
        <mvc:interceptor>
            <mvc:mapping path="/**"/>
            <mvc:exclude-mapping path="/testRequestEntity"/>
            <ref bean="secondInterceptor"></ref>
        </mvc:interceptor>
    </mvc:interceptors>

<!-- /*匹配一层,/**匹配所有层 -->

② 拦截器的三个抽象方法

preHandle:控制器方法执行之前执行preHandle(),其boolean类型的返回值表示是否拦截或放行,返回true为放行,即调用控制器方法;返回false表示拦截,即不调用控制器方法

postHandle:控制器方法执行之后执行postHandle()

afterCompletion:处理完视图和模型数据,渲染视图完毕之后执行afterCompletion()

// 此时类名首字母小写就是bean的id
@Component
public class FirstInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("FirstInterceptor-->preHandle");
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("FirstInterceptor-->postHandle");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("FirstInterceptor-->afterCompletion");
    }
}

③ 多个拦截器的执行顺序

a. 若每个拦截器的preHandle()都返回true

此时多个拦截器的执行顺序和拦截器在SpringMVC的配置文件的配置顺序有关:

preHandle()会按照配置的顺序执行,而postHandle()和afterCompletion()会按照配置的反序执行

b. 若某个拦截器的preHandle()返回了false

preHandle()返回false和它之前的拦截器的preHandle()都会执行,postHandle()都不执行,返回false 的拦截器之前的拦截器的afterCompletion()会执行

4.7 异常处理器

① 基于配置的异常处理

SpringMVC提供了一个处理控制器方法执行过程中所出现的异常的接口:HandlerExceptionResolver
HandlerExceptionResolver接口的实现类有:DefaultHandlerExceptionResolver和 SimpleMappingExceptionResolver

SpringMVC提供了自定义的异常处理器SimpleMappingExceptionResolver,使用方式:

<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
    <property name="exceptionMappings">
        <props>
            <!--key设置要处理的异常,value设置出现该异常时要跳转的页面所对应的逻辑视图-->
            <prop key="java.lang.ArithmeticException">error</prop>
            <prop key="其他异常">其他页面</prop>
        </props>
    </property>
    <!--设置共享在请求域中的异常信息的属性名-->
    <property name="exceptionAttribute" value="ex"></property>
</bean>

② 基于注解的异常处理

@ControllerAdvice	//将当前类标识为异常处理的组件
public class ExceptionController {
    @ExceptionHandler(ArithmeticException.class)	//用于设置所标识方法处理的异常
    public String handleArithmeticException(Exception ex, Model model){
        model.addAttribute("ex", ex);
        return "error";
    }
}

// ex表示当前请求处理中出现的异常对象

五.搭建MyBatis环境

核心配置文件 — 如何连接数据库

映射文件 — 如何操作数据库 即SQL语句

5.1 创建属性文件

<!-- jdbc.properties -->

jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/ssm_learn?serveTimezone=UTC
jdbc.username=root
jdbc.password=root

5.2 创建核心配置文件

<!-- 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"/>
    </settings>

    <plugins>
        <!--配置分页插件-->
        <plugin interceptor="com.github.pagehelper.PageInterceptor"></plugin>
    </plugins>

</configuration>

数据源,类型别名所对应的包,以及映射文件位置在spring中设置

5.3 创建Mapper接口和映射文件

public interface EmployeeMapper {
    List<Employee> getEmployeePage();
}
<?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.atguigu.ssm.mapper.EmployeeMapper">
    <select id="getEmployeePage" resultType="Employee">
        select * from t_emp
    </select>
</mapper>

5.4 创建mybatis日志文件

<!-- log4j.xml -->

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
    <appender name="STDOUT" class="org.apache.log4j.ConsoleAppender">
        <param name="Encoding" value="UTF-8"/>
        <layout class="org.apache.log4j.PatternLayout">
            <param name="ConversionPattern" value="%-5p %d{MM-dd HH:mm:ss,SSS} %m (%F:%L) \n"/>
        </layout>
    </appender>
    <logger name="java.sql">
        <level value="debug"/>
    </logger>
    <logger name="org.apache.ibatis">
        <level value="info"/>
    </logger>
    <root>
        <level value="debug"/>
        <appender-ref ref="STDOUT"/>
    </root>
</log4j:configuration>

六.配置spring.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">

    <!-- 扫描组件(除控制层) -->
    <context:component-scan base-package="com.junn.ssm">
        <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
    </context:component-scan>

    <!-- 引入jdbc.properties -->
    <context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder>

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

    <!-- 配置事务管理器 -->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"></property>
    </bean>

    <!-- 开启事务的注解驱动 -->
    <tx:annotation-driven transaction-manager="transactionManager"/>

    <!-- 配置用于创建SqlSessionFactory的工厂bean -->
    <bean class="org.mybatis.spring.SqlSessionFactoryBean">
        <!-- 设置MyBatis配置文件的路径(可以不设置) -->
        <property name="configLocation" value="classpath:mybatis-config.xml"></property>
        <!-- 设置数据源 -->
        <property name="dataSource" ref="dataSource"></property>
        <!-- 设置类型别名所对应的包 -->
        <property name="typeAliasesPackage" value="com.junn.ssm.pojo"></property>
    </bean>

    <!-- 配置mapper接口的扫描配置 -->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="com.junn.ssm.mapper"></property>
    </bean>

</beans>

6.1 扫描组件

见4.1

6.2 引入jdbc.properties

数据库连接配置属性文件

6.3 Druid数据源

连接数据库

6.4 开启事务

① 添加事务配置

声明式事务的配置步骤:
1,在Spring的配置文件中配置事务管理器
2、开启事务的注解驱动
<!--配置事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="druidDataSource"></property>
</bean>

<!--
        开启事务的注解驱动
        将使用@Transactional注解所标识的方法或类中所有的方法使用事务进行管理
        transaction- manager属性设置事务管理器的id
        若事务管理器的bean的id默认为transactionManager,则该属性可以不写
-->
<tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>

② 添加事务注解

因为service层表示业务逻辑层,一个方法表示一个完成的功能,
因此处理事务一般在service层处理在BookServiceImpl的buybook()添加注解@Transactional

@Transactional注解标识的位置:
1, 标识在方法上
2, 标识在类上,则类中所有的方法都会被事务管理

在需要被事务管理的方法上,添加@Transactional注解, 该方法就会被事务管理
@Service
public class BookServiceImpl implements BookService {

    @Autowired
    private BookDao bookDao;

    @Override
    @Transactional
    public void buyBook(Integer userId, Integer bookId) {
        //查询图书价格
        Integer price = bookDao.getPriceByBookId(bookId);
        //更新图书库存
        bookDao.updateStock(bookId);
        //更新用户余额
        bookDao.updateBalance(userId, price);
    }

}

③ 测试

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:tx-annotation.xml")
public class TxByAnnotationTest {

    @Autowired
    private BookController bookController;

    @Test
    public void testBuyBook(){
        bookController.buyBook(1, 1);
    }

}

6.5 自动装配获取mapper对象

<!-- 配置用于创建SqlSessionFactory的工厂bean -->
<bean class="org.mybatis.spring.SqlSessionFactoryBean">
    <!-- 设置MyBatis配置文件的路径(可以不设置) -->
    <property name="configLocation" value="classpath:mybatis-config.xml">
    </property>
    <!-- 设置数据源 -->
    <property name="dataSource" ref="dataSource"></property>
    <!-- 设置类型别名所对应的包 -->
    <property name="typeAliasesPackage" value="com.atguigu.ssm.pojo">
    </property>
    <!--
        设置映射文件的路径
        若映射文件所在路径和mapper接口所在路径一致,则不需要设置
    -->
    <!--
    	<property name="mapperLocations" value="classpath:mapper/*.xml"></property>
    -->
</bean>

<!--
配置mapper接口的扫描配置
由mybatis-spring提供,可以将指定包下所有的mapper接口创建动态代理
并将这些动态代理作为IOC容器的bean管理
-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
    <property name="basePackage" value="com.atguigu.ssm.mapper"></property>
</bean>
@Service
public class EmployeeServiceImpl implements EmployeeService {

    @Autowired
    private EmployeeMapper employeeMapper;

    @Override
    public PageInfo<Employee> getEmployeePage(Integer pageNum) {
        PageHelper.startPage(pageNum, 8);
        List<Employee> list = employeeMapper.getEmployeePage();
        PageInfo<Employee> page = new PageInfo<>(list, 5);
        return page;
    }
}
© 版权声明
THE END
喜欢就支持一下吧
点赞2
分享
评论 抢沙发
墨初的头像-墨初小屋

昵称

取消
昵称表情代码图片