zoukankan      html  css  js  c++  java
  • 整合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项目

  • 相关阅读:
    C++ Builder XE2随意学习 (12) > DevExpress Dock拖动框架实现
    C++ Builder XE2随意学习 (14) > 窗口透明处理
    C++ Builder XE2随意学习(9) > 安装实录
    C++ Builder XE2随意学习 (13) > Gdiplus
    C++ Builder XE2随意学习 (10) > AlphaControls控件使用初探
    ASP.NET 4.0 视频教程
    C++ Builder XE2随意学习 (11) > DevExpress Ribbon界面效果
    C++ Builder XE2随意学习 (7) > RAD Studio XE2 – Setting it up for a Mac
    C++ Builder XE2随意学习(8) > 快速入门教程
    jQuery实现倒计时效果
  • 原文地址:https://www.cnblogs.com/silentdoer/p/8901407.html
Copyright © 2011-2022 走看看