整合Spring+Struts2+Mybatis加spring单元测试等

前言

自己是在CentOS7的IntelliJ IDEA里开发的,里面中文输入法有问题经常用不了,所以这里用了很多chinglish,希望不要介意;

一:pom依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>me.silentdoer</groupId>
    <artifactId>combine-spring-struts2-mybatis</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <spring.group>org.springframework</spring.group>
        <spring.version>4.3.4.RELEASE</spring.version>

        <logback.group>ch.qos.logback</logback.group>
        <logback.version>1.2.3</logback.version>

        <slf4j.group>org.slf4j</slf4j.group>
        <slf4j.version>1.7.25</slf4j.version>

        <mybatis.group>org.mybatis</mybatis.group>
        <mybatis.version>3.4.4</mybatis.version>
        <mybatis-spring.version>1.3.1</mybatis-spring.version>

        <mysql.group>mysql</mysql.group>
        <mysql-connector.version>5.1.41</mysql-connector.version>

        <alibaba.group>com.alibaba</alibaba.group>
        <druid.version>1.1.6</druid.version>
        <fastjson.version>1.2.41</fastjson.version>

        <dom4j.group>dom4j</dom4j.group>
        <dom4j.version>1.6.1</dom4j.version>

        <junit.group>junit</junit.group>
        <junit.version>4.12</junit.version>
    </properties>

    <dependencies>
        <!-- spring -->
        <dependency>
            <groupId>${spring.group}</groupId>
            <artifactId>spring-core</artifactId>
            <version>${spring.version}</version>
            <!-- scope 默认是compile的 -->
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>${spring.group}</groupId>
            <artifactId>spring-beans</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>${spring.group}</groupId>
            <artifactId>spring-context</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>${spring.group}</groupId>
            <artifactId>spring-context-support</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>${spring.group}</groupId>
            <artifactId>spring-expression</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>${spring.group}</groupId>
            <artifactId>spring-web</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>${spring.group}</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.8.10</version>
        </dependency>

        <!-- struts2 -->
        <dependency>
            <groupId>org.apache.struts</groupId>
            <artifactId>struts2-core</artifactId>
            <version>2.3.20</version>
        </dependency>
        <dependency>
            <groupId>org.apache.struts</groupId>
            <artifactId>struts2-convention-plugin</artifactId>
            <version>2.3.20</version>
        </dependency>
        <dependency>
            <groupId>org.apache.struts</groupId>
            <artifactId>struts2-spring-plugin</artifactId>
            <version>2.3.20</version>
        </dependency>

        <!-- database begin -->
        <dependency>
            <groupId>${mybatis.group}</groupId>
            <artifactId>mybatis</artifactId>
            <version>${mybatis.version}</version>
        </dependency>
        <dependency>
            <groupId>${mybatis.group}</groupId>
            <artifactId>mybatis-spring</artifactId>
            <version>${mybatis-spring.version}</version>
        </dependency>
        <dependency>
            <groupId>${mysql.group}</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>${mysql-connector.version}</version>
        </dependency>
        <dependency>
            <groupId>${alibaba.group}</groupId>
            <artifactId>druid</artifactId>
            <version>${druid.version}</version>
        </dependency>
        <!-- database end -->

        <!-- tool begin -->
        <dependency>
            <groupId>${alibaba.group}</groupId>
            <artifactId>fastjson</artifactId>
            <version>${fastjson.version}</version>
        </dependency>
        <dependency>
            <groupId>${dom4j.group}</groupId>
            <artifactId>dom4j</artifactId>
            <version>${dom4j.version}</version>
        </dependency>
        <dependency>
            <groupId>com.thoughtworks.xstream</groupId>
            <artifactId>xstream</artifactId>
            <version>1.4.9</version>
        </dependency>

        <!-- log begin -->
        <dependency>
            <groupId>${slf4j.group}</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>${slf4j.version}</version>
        </dependency>
        <dependency>
            <groupId>${logback.group}</groupId>
            <artifactId>logback-core</artifactId>
            <version>${logback.version}</version>
        </dependency>
        <dependency>
            <groupId>${logback.group}</groupId>
            <artifactId>logback-classic</artifactId>
            <version>${logback.version}</version>
        </dependency>
        <!-- log end -->

        <dependency>
            <groupId>cglib</groupId>
            <artifactId>cglib-nodep</artifactId>
            <version>3.1</version>
        </dependency>

        <dependency>
            <groupId>commons-fileupload</groupId>
            <artifactId>commons-fileupload</artifactId>
            <version>1.3.2</version>
        </dependency>

        <dependency>
            <groupId>commons-io</groupId>
            <artifactId>commons-io</artifactId>
            <version>2.5</version>
        </dependency>

        <!-- 用于测试 begin -->
        <dependency>
            <groupId>${spring.group}</groupId>
            <artifactId>spring-test</artifactId>
            <version>${spring.version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>${junit.group}</groupId>
            <artifactId>junit</artifactId>
            <!-- version据说还可以是一个范围值 -->
            <version>${junit.version}</version>
            <!-- 表示这个jar包是用于测试,打包时不会将此jar包打包进去 -->
            <scope>test</scope>
        </dependency>
        <!-- 用于测试 end -->

        <!-- 依赖jar包依赖的组件 begin-->
        <dependency>
            <groupId>commons-logging</groupId>
            <artifactId>commons-logging</artifactId>
            <version>1.2</version>
            <!-- compile 表示编译时和运行时都需要(但IDE可能有bug,不行的话还是自己主动将jar包放到lib目录 -->
            <scope>compile</scope>
        </dependency>

        <!-- mq and cache -->
        <!--<dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
            <version>2.9.0</version>
        </dependency>
        <dependency>
            <groupId>com.rabbitmq</groupId>
            <artifactId>amqp-client</artifactId>
            <version>4.1.0</version>
        </dependency>-->
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

二:配置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_3_1.xsd"
         version="3.1">

    <!--spring-->
    <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>

    <!-- other filters -->

    <!--struts2, use with last filter-->
    <filter>
        <filter-name>struts2</filter-name>
        <filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>struts2</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
</web-app>

三:配置struts2/mybatis/spring/mapper配置文件

注:均放在resources目录里

1.struts.xml

(struts1和2都可以用这个名字,struts2会自动加载就像logback.xml一样)

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"
                        "http://struts.apache.org/dtds/struts-2.3.dtd">

<struts>
    <constant name="struts.enable.DynamicMethodInvocation" value="false" />

    <!-- use development mode, when exception occur will show in the response body -->
    <constant name="struts.devMode" value="true" />

    <!-- ke yi bu yao -->
    <!--<constant name="struts.objectFactory" value="spring" />-->
    <constant name="struts.i18n.encoding" value="utf-8" />

    <!-- use to separate diff func action, extends is important for link struts2-spring -->
    <!-- namespace like RequestMapping on controller -->
    <!-- if namespace is /, then http://localhost:8090/getStudent?uid=1, if namespace is /entity, ..8090/entity/getStudent?uid=1 -->
    <package name="entity-resolver" extends="struts-default" namespace="/entity">
        <!-- name is in Struts registry key&context/student, class is the action class or spring beanName -->
        <!-- method attr is for special method to be handlerMethod -->
        <action name="getStudent" class="studentAction">
            <result name="success">/WEB-INF/content/vo/student.jsp</result>
            <result name="error">/WEB-INF/content/common/error.jsp</result>
        </action>
    </package>

    <package name="index" extends="struts-default" namespace="/">
        <!-- Struts2 process url also bu xu yao suffix -->
        <action name="index" class="indexAction">
            <result name="success">/index.html</result>
        </action>
    </package>

    <!--<package name="about-permission" extends="struts-default"></package>-->

</struts>

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>
    <typeAliases>
        <typeAlias type="me.silentdoer.ssmdemo.pojo.Student" alias="User"/>
    </typeAliases>

    <!--<environments default="development">
              <environment id="development">
                      <transactionManager type="JDBC"/>
                      <dataSource type="POOLED">
                              <property name="driver" value="com.mysql.jdbc.Driver"/>
                              <property name="url" value="jdbc:mysql://localhost:3306/db_test"/>
                              <property name="username" value="test"/>
                              <property name="password" value="test"/>
                      </dataSource>
              </environment>
      </environments>-->

    <mappers>
        <!-- must absolute path -->
        <mapper resource="config/mybatis/mapper/StudentMapper.xml"/>
    </mappers>
</configuration>

3.applicationContext.xml

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

    <!--<tx:annotation-driven transaction-manager="txManager" />-->

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

    <!-- transaction -->
    <tx:advice id="txAdvice" transaction-manager="txManager">
        <tx:attributes>
            <tx:method name="select*" read-only="true"/>
            <tx:method name="insert*" propagation="REQUIRED"/>
            <tx:method name="update*" propagation="REQUIRED"/>
            <tx:method name="delete*" propagation="REQUIRED"/>
        </tx:attributes>
    </tx:advice>

    <!-- first * is all visit qualifier -->
    <!-- aop -->
    <aop:config>
        <aop:pointcut id="pointcut" expression="execution(* me.silentdoer.ssmdemo.dao.impl.StudentDaoImpl.*(..))"/>
        <aop:advisor advice-ref="txAdvice" pointcut-ref="pointcut"/>
    </aop:config>

    <!-- datasource, can be replace with Druid -->
    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3306/db_test"/>
        <property name="username" value="test"/>
        <property name="password" value="test"/>
    </bean>

    <!-- 配置sessionFacfory -->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <!-- 加载dataSource -->
        <property name="dataSource" ref="dataSource"/>
        <!--加载usermapper.xml-->
        <property name="configLocation" value="classpath:mybatis-config.xml"/>
    </bean>

    <context:component-scan base-package="me.silentdoer.ssmdemo">
        <!-- exclude by regex, also can use aspectj ex to exclude -->
        <context:exclude-filter type="regex" expression="me.silentdoer.ssmdemo.pojo..*"/>
    </context:component-scan>

    <!-- userDao, it's extends SqlSessionDapSupport -->
    <!--<bean id="studentDao" class="me.silentdoer.ssmdemo.dao.impl.StudentDaoImpl">
        &lt;!&ndash; in setSqlsessionFactory method: this.sqlSession = new SqlSessionTemplate(sqlSessionFactory) &ndash;&gt;
        <property name="sqlSessionFactory" ref="sqlSessionFactory"/>
    </bean>-->

    <!--userService-->
    <!--<bean id="studentService" class="me.silentdoer.ssmdemo.service.impl.StudentServiceImpl">
        <property name="studentDao" ref="studentDao"/>
    </bean>-->

    <!--userAction-->
    <!--<bean id="userAction" class="me.silentdoer.ssmdemo.action.student.StudentAction">
        <property name="studentService" ref="studentService"/>
    </bean>-->
</beans>

4.StudentMapper.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="me.silentdoer.ssmdemo.pojo.StudentMapper">
    <resultMap id="studentResultMap" type="me.silentdoer.ssmdemo.pojo.Student">
        <id property="uid" column="uid"/>
        <result property="name" column="name"/>
        <result property="gender" column="gender"/>
        <result property="createTime" column="create_time"/>
        <result property="updateTime" column="update_time"/>
    </resultMap>

    <!--<insert id="insertOne">
        insert into student(name values (#{name})
    </insert>-->

    <select id="selectOne" resultMap="studentResultMap">
        select uid, name, gender, create_time, update_time from student where uid=#{uid}
    </select>
</mapper>

四:代码实现

1.pojo层的编码

package me.silentdoer.ssmdemo.pojo;

import java.io.Serializable;
import java.sql.Timestamp;

/**
 * @author silentdoer
 * @version 1.0
 * @description the description
 * @date 4/21/18 2:11 PM
 */
public class Student implements Serializable {
    private long uid;
    private String name;
    private int gender;
    private Timestamp createTime;  // Timestamp is implements java.util.Date
    private Timestamp updateTime;

    public long getUid() {
        return uid;
    }

    public void setUid(long uid) {
        this.uid = uid;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getGender() {
        return gender;
    }

    public void setGender(int gender) {
        this.gender = gender;
    }

    public Timestamp getCreateTime() {
        return createTime;
    }

    public void setCreateTime(Timestamp createTime) {
        this.createTime = createTime;
    }

    public Timestamp getUpdateTime() {
        return updateTime;
    }

    public void setUpdateTime(Timestamp updateTime) {
        this.updateTime = updateTime;
    }

    @Override
    public String toString(){
        return String.format("[uid=%s, name=%s, gender=%s, createTime=%s, updateTime=%s]", this.uid, this.name, this.gender, this.createTime, this.updateTime);
    }
}

2.dao层的编码

package me.silentdoer.ssmdemo.dao;

import me.silentdoer.ssmdemo.pojo.Student;
import org.apache.ibatis.annotations.Param;

/**
 * @author silentdoer
 * @version 1.0
 * @description the description
 * @date 4/21/18 1:40 PM
 */
public interface StudentDao {
    Student selectOne(@Param("uid") long uid);
}
package me.silentdoer.ssmdemo.dao.impl;

import me.silentdoer.ssmdemo.dao.StudentDao;
import me.silentdoer.ssmdemo.pojo.Student;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.support.SqlSessionDaoSupport;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Repository;

import javax.annotation.Resource;

/**
 * @author silentdoer
 * @version 1.0
 * @description the description
 * @date 4/21/18 2:31 PM
 */
// this.sqlSessionProxy.selectOne(statement), proxy will use invoke method to call sqlSessionFactory.openSession();
    // this session is sqlSession = new SqlSessionTemplate(sqlSessionFactory); not real SqlSession, in template use proxy to exec
@Repository("studentDao")  // default singleton, but in the inside it's will use SqlSessionFactory to openSession(), so can be singleton.
public class StudentDaoImpl extends SqlSessionDaoSupport implements StudentDao {

    public Student selectOne(long uid) {
        final SqlSession session = this.getSqlSession();
        String statement = "me.silentdoer.ssmdemo.pojo.StudentMapper.selectOne";
        return session.selectOne(statement, uid);
    }

    /*@Autowired  // by type
    @Qualifier("sqlSessionFactory")*/
    @Resource  // by name and default is setter rm set and lower first char
    @Override
    public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {
        super.setSqlSessionFactory(sqlSessionFactory);
    }
}

3.service层的编码

package me.silentdoer.ssmdemo.service;

import me.silentdoer.ssmdemo.pojo.Student;

/**
 * @author silentdoer
 * @version 1.0
 * @description the description
 * @date 4/21/18 2:43 PM
 */
public interface StudentService {
    // ERP Logic layer, like login process etc.
    Student getOneUserWithLogic(Long uid);
}
package me.silentdoer.ssmdemo.service.impl;

import me.silentdoer.ssmdemo.dao.StudentDao;
import me.silentdoer.ssmdemo.pojo.Student;
import me.silentdoer.ssmdemo.service.StudentService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;

/**
 * @author silentdoer
 * @version 1.0
 * @description the description
 * @date 4/21/18 2:43 PM
 */
@Service("studentService")  // need trans to studentService, otherwise will be studentServiceImpl will occur can not get bean by name
public class StudentServiceImpl implements StudentService {
    // field name can diff with it's getter&setter; in structure is use getter&setter name.
    private StudentDao studentDao;

    @Autowired
    @Qualifier("studentDao")
    public void setStudentDao(StudentDao studentDao) {
        this.studentDao = studentDao;
    }

    public Student getOneUserWithLogic(Long uid) {
        // some logic impl
        if(uid == null){
            throw new IllegalArgumentException("uid is not null, please check.");
        }
        System.out.println(String.format("uid is:%s", uid));
        Student student = this.studentDao.selectOne(uid);
        return student;
    }
}

4.action层的编码(controller)

package me.silentdoer.ssmdemo.action.index;

import com.opensymphony.xwork2.ActionSupport;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Controller;

/**
 * @author silentdoer
 * @version 1.0
 * @description the description
 * @date 4/21/18 10:53 PM
 */
@Controller
public class IndexAction extends ActionSupport {
}
package me.silentdoer.ssmdemo.action.student;

import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionSupport;
import me.silentdoer.ssmdemo.pojo.Student;
import me.silentdoer.ssmdemo.service.StudentService;
import org.apache.struts2.interceptor.RequestAware;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Controller;

import javax.annotation.Resource;
import java.util.Map;

/**
 * @author silentdoer
 * @version 1.0
 * @description the description
 * @date 4/21/18 2:45 PM
 */
@Scope("prototype")  // must be set prototype
@Controller("studentAction")  // Or @Component for component-scan by annotation-filter
public class StudentAction extends ActionSupport /*implements RequestAware*/ {
    private static final long serialVersionUID = 1L;  // ActionSupport implements Serializable
    private Long uid;
    private Student student;
    @Resource
    private StudentService studentService;

    // like SpringMVC InternalResourceViewResolver to search success mapping file
    @Override
    public String execute(){
        // setter for jsp usage, struts2 will purge the map to request attr when after execute().
        Map request = (Map) ActionContext.getContext().get("request");
        // request obj is generate&store by struts2, so request.put is also struts2 to put key-value to request attr.
        request.put("requestAttrKey", "value8888888");
        try {
            Student student = this.studentService.getOneUserWithLogic(this.uid);
            System.out.println(String.format("The student is: %s", student));
            this.student = student;
        }catch (Exception ex){
            // to log exception
            ex.printStackTrace();
            request.put("error", ex.getMessage());
            return ERROR;  // error page
        }
        return SUCCESS;  // normal page
    }

    public void setStudentService(StudentService studentService) {
        this.studentService = studentService;
    }

    public void setUid(Long uid) {
        this.uid = uid;
    }

    // student will put to request attr, key is student
    public Student getStudent() {
        return student;
    }
}

五:数据库和表的建立

数据库名为db_test,表名为student,里面就五个字段,可以参考pojo建立;

六:源码地址

github地址:https://github.com/Silentdoer/demos.git的Combine-Spring-Struts2-Mybatis项目

原文地址:https://www.cnblogs.com/silentdoer/p/8901407.html