整合 Spring、SpringMVC、Mybatis 三个框架

概述

目前主流一种搭配方案,SSM,而不是 SSH,并且其中的 SS 都是 Spring 产物,其中不再为 Struct 保留位置。

这种方案除了其中 Spring 本身位置没有变动,其余两个更改了霸主地位。

不说历史,毕竟没有经历过。

Spring 与 SpringMVC 关系

可以看到 SSM 中,包含了这两个部分,它们之间是可以用父子关系来形容,SpringMVC 是专用于 Web 相关的开发所用,而 Spring 更专注于业务处理。

Spring 包含了所有核心技术,其它的框架都是依赖 Spring 框架的核心技术。

SpringMVC 在 Java 原生的 Servlet API 基础上进行扩展。

也就是说,Spring 与 SpringMVC 很多功能都是共同的,SpringMVC 包含 Spring 的功能,那么有必要在使用 Spring 框架吗?

简单说下,如果单独使用 SpringMVC 框架,那么就代表着将所有的业务逻辑,第三方框架的加载,还有数据库配置等等都需要在 SpringMVC 配置文件中定义,这样就会出现繁杂的配置,所有配置都放在 SpringMVC 层框架配置。要知道这个框架就是用来开发 web 的,按照这样配置的话,就不仅仅的 web 业务,而是将各种技术都放在了一起。

这样加上 Spring 框架,来负责:

  • 第三方框架的整合
  • 数据源配置
  • DAO
  • Serivce
  • 事务
  • Spring 配置

而 Spring MVC 只要负责:

  • 静态资源
  • 视图解析器
  • 所有与 mvc 相关的配置

既然将两者的负责范围进行了区分,那么在技术层面如何进行?

  • 如何初始化容器?
  • 如何各自负责范围?

关于第一个问题:可以使用监听器来解决,在 Servlet 容器启动的时候,就加载 Spring 容器。

第二个问题:还记得 xml 配置方式的扫描包的属性,其中有个 use-default-filter 和两个子标签来进行排斥处理。

Spring 配置

按照上面所描述的,只需要加载负责范围的。

使用 listener 来初始化容器,首先可以自定义的方式,创建一个监听类,实现 ServletContextListener 接口,并且在 contextInitialized 方法中实例化 ClassPathXmlApplicationContext 类。

public void contextInitialized(ServletContextEvent sce) {
	ApplicationContext ioc = new ClassPathXmlApplicationContext("applicationContext.xml");
	ServletContext servletContext = sce.getServletContext();
	servletContext.setAttribute("ioc", ioc);
}

接着在 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">
    <listener>
    	<listener-name>MyListener</listener-name>
    	<listener-class>com.huasio.listener.MyListener</listener-class>
    </listener>

    <listener-mapping>
    	<listener-name>MyListener</listener-name>
    	<url-pattern>/*</url-pattern>
    </listener-mapping>
</web-app>

这样,在 Servlet 容器启动的时候,就会调用监听器的方法,并且在程序运行期间,都能使用 ioc 实例。

这种方式除了有些繁琐之外,还有一种方式,是 Spring 自身提供的。只需要在 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">
	<context-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>classpath:applicationContext.xml</param-value>
	</context-param>
	<listener>
		<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
	</listener>
</web-app>

那么要是在程序中需要用到容器怎么获取?

通过源码,找到这么一行:

String ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE = WebApplicationContext.class.getName() + ".ROOT";

通过 "org.springframework.web.context.WebApplicationContext.ROOT" 就可以从 "ServletContext" 里面获取到整个 ioc。

初始化问题解决之后,接下来就是如何区分职责配置了。目前整个方案,对基础的进行配置,那么在 Spring 就要配置 Mybatis。

用到的依赖包:

  • spring 本身
  • mybatis
  • spring-mybatis 整合工具
  • driud 也就是数据源
  • mysql 驱动
  • log4j
    • 这个只要将配置文件放在能被解析到类路径就行,spring 会自动寻找名为:log4j.xml 的配置文件。

要配置的东西有:

  • 排斥扫描组件
  • druid 也就是数据源
  • Mybatis 配置

<?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:mybatis-spring="http://mybatis.org/schema/mybatis-spring"
       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://mybatis.org/schema/mybatis-spring http://mybatis.org/schema/mybatis-spring.xsd">
	
	<!-- 排次扫描组件 -->
	<!-- 排除 @Controller 注解的组件 -->
	<context:component-scan base-package="***">
		<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller" />
	</context:component-scan>

	<!-- 配置 Druid 数据源 -->
	<!-- 引入外部 properties 配置文件 -->
	<context:property-placeholder location="classpath:applicationContext.xml" />
	<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
		<property name="username" value="${jdbc.username}" />
		<property name="password" value="${jdbc.password}" />
		<property name="url" value="${jdbc.url}" />
		<property name="maxActive" value="${jdbc.maxActive}" />
		<property name="initialSize" value="${jdbc.initialSize}" />
		<property name="maxWait" value="${jdbc.maxWait}" />
		<property name="minIdle" value="${jdbc.minIdle}" />
	</bean>

	<!-- 配置 Mybatis -->
	<bean class="org.mybatis.spring.SqlSessionFactoryBean">
		<property name="dataSource" value="dataSource" />
		<!-- mapper 配置文件 -->
		<property name="mapperLocations" value="classpath:mybatis/*.xml" />
		<!-- 加载 Mybatis 全局配置文件 -->
		<property name="configLocation" value="classpath:mybatis-config.xml" />
	</bean>
	<!-- 扫描 mapper 接口 -->
	<bean id="mapperScannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        	<property name="basePackage" value="com.huasio.crowdfunding.mapper"/>
    	</bean>
</beans>

与 Mybatis 整合,需要用到 Spring 提供的一个 spring-mybatis 依赖。

对于一些 Mybatis 的配置,仍然可以在 mybatis 的全局配置文件中配置,因为在 spring 的配置文件加载了。

目前来看,整合的必要,就不用写一个接口到 mybatis-config.xml 声明一个 mapper 接口,因为这里自动扫描 mapper 配置文件和 mapper 接口。

对于获取 mybatis 实例,仍然是通过 spring ioc 容器来获取,还记得上面说过得可以从 ServletContext 获取 ioc 容器吗,就是这样操作的。

当然,还是使用注解自动装配的 DAO 更简便。

log4j 配置文件:

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

SpringMVC

配置好了 Spring 的负责的访问,接下来 SpringMVC 就简单多了。

需要用到的依赖:

  • spring-mvc

目前需要配置:

  • 排斥组件加载
  • 加载注解驱动
  • 前端核心控制器
  • 视图解析器
  • 静态资源加载
  • 请求编码字符集
  • RESTful

挺多的,比 spring 还多,实际并不是。 SpringMVC 要配置东西没有那么多,一些用到的时候才会配置。

首先还是一样,在 web.xml 配置文件,需要将前端核心控制器初始化加载,然后配置请求编码字符集、RESTful等。

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">
	<!-- 前端核心控制器 -->
	<servlet>
		<servlet-name>dispatcherServlet</servlet-name>
		<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
		<!-- 定义 springMVC 配置文件 -->
		<!-- 默认位置在 WEB-INFO/<servlet-name>-servlet.xml -->
		<init-param>
			<param-name>contextConfigLocation</param-name>
			<param-value>classpath:springMVC.xml</param-value>
		</init-param>
		<!-- Servlet 容器启动就加载类 -->
		<load-on-startup>1</load-on-startup>
	</servlet>
	<servlet-mapping>
		<servlet-name>dispatcherServlet</servlet-name>
		<url-pattern>/</url-pattern>
	</servlet-mapping>

	<!-- POST 请求编码设置 -->
	<filter>
		<filter-name>characterEncodingFilter</filter-name>
		<filter-class>org.springframework.web.filter.CharacterEncoding</filter-class>
		<init-param>
			<!-- 强制设置编码 -->
			<!-- 在 5.0 的时候不是这个属性 -->
			<param-name>forceRequestEncoding</param-name>
			<param-value>true</param-value>
		</init-param>
		<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>

	<!-- RESTful 配置 -->
	<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>
</web-app>

初始化的配置就配置好了,接下来就是在 SpringMVC 配置文件。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       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 http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd">
	<!-- 排斥组件扫描 -->
	<context:component-scan base-package="***.controller">
		<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller" />
	</context:component-scan>

	<!-- 视图解析器 -->
	<bean id="" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
		<!-- 前缀 -->
		<property name="prefix" value="/WEB-INFO/views/" />
		<!-- 后缀 -->
		<property name="suffix" value=".jsp" />
	</bean>

	<!-- 加载注解驱动 -->
	<mvc:annotation-driven />

	<!-- 配置静态资源加载 -->
	<!-- mapping 是虚拟路径 -->
	<!-- location 是实际路径 -->
	<mvc:resources mapping="/static/**" location="/static/" />
</beans>

就这样,SSM 都配置好了。

Mybatis

这里没什么好配置的了,有什么 Mybatis 的配置就写 mybatis-config.xml 全局配置文件就行,Spring 会自动加载。