mybatis 使用 pagehelper

Mybatis 使用 pagehelp 分页插件

如何使用 page help

使用 page help

一. 导入 maven 依赖

 <!--   mybatis 依赖      -->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.4.5</version>
        </dependency>
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis-spring</artifactId>
            <version>2.0.2</version>
        </dependency>
        <!-- 分页 -->
        <dependency>
            <groupId>com.github.pagehelper</groupId>
            <artifactId>pagehelper</artifactId>
            <version>5.1.10</version>
        </dependency>

二、配置 xml文件

1. 有 mybatis-config.xml 文件

1.1 Spring-dao.xml 配置SqlSessionFactory对象

 <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <!-- 注入数据库连接池 -->
        <property name="dataSource" ref="dataSource"/>
        <!-- 配置MyBaties全局配置文件:mybatis-config.xml -->
        <property name="configLocation" value="classpath:mybatis-config.xml"/>
    </bean>

1.2 mybatis-config.xml 导入插件,设置属性

<!-- 
    plugins在配置文件中的位置必须符合要求,否则会报错,顺序如下:
    properties?, settings?, 
    typeAliases?, typeHandlers?, 
    objectFactory?,objectWrapperFactory?, 
    plugins?, 
    environments?, databaseIdProvider?, mappers?
-->
<?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>
        <package name="com.zhiyou100.entity"/>
        <!--给实体类起别名 -->
    </typeAliases>

    <plugins>
        <!-- com.github.pagehelper为PageHelper类所在包名 -->
        <plugin interceptor="com.github.pagehelper.PageInterceptor">
            <!-- pageNum<=0 时会查询第一页   reasonable=true -->
            <!-- 指定数据库方言   helperDialect=mysql --> 
            <property name="params" value="reasonable=true;helperDialect=mysql"/>
        </plugin>
    </plugins>
    <mappers>
        <package name="com.zhiyou100.mapper"/>
        <!-- 引入所有的sql映射文件 -->
    </mappers>

</configuration>

2. 没有 mybatis-config.xml 文件

2.1 Spring-dao.xml 配置SqlSessionFactory对象

<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <!-- 导入插件       -->
        <property name="plugins">
            <array>
                <bean class="com.github.pagehelper.PageInterceptor"/>
            </array>
        </property>
        <!-- 注入数据库连接池 -->
        <property name="dataSource" ref="dataSource"/>
        <!-- 配置MyBaties全局配置文件:mybatis-config.xml -->
        <property name="configLocation" value="classpath:mybatis-config.xml"/>
    </bean>

三、 配置 java 文件

0. 项目结构

.
├── ApartmentInformationManagementSystem.iml
├── README.md
├── out
│   └── artifacts
│       ├── ApartmentInformationManagementSystem_Web_exploded
│       │   ├── META-INF
│       │   │   └── MANIFEST.MF
│       │   ├── WEB-INF
│       │   │   ├── classes
│       │   │   │   ├── applicationContext.xml
│       │   │   │   ├── com
│       │   │   │   │   └── zhiyou100
│       │   │   │   │       ├── action
│       │   │   │   │       │   ├── SourceAction.class
│       │   │   │   │       │   └── UserAction.class
│       │   │   │   │       ├── entity
│       │   │   │   │       │   ├── Contract.class
│       │   │   │   │       │   ├── Event.class
│       │   │   │   │       │   ├── House.class
│       │   │   │   │       │   ├── Lessee.class
│       │   │   │   │       │   ├── Rect.class
│       │   │   │   │       │   ├── Repairlog.class
│       │   │   │   │       │   ├── Rose.class
│       │   │   │   │       │   ├── Source.class
│       │   │   │   │       │   └── Worker.class
│       │   │   │   │       ├── filter
│       │   │   │   │       │   ├── GenericEncodingFilter.class
│       │   │   │   │       │   └── MyRequest.class
│       │   │   │   │       ├── mapper
│       │   │   │   │       │   ├── ContractMapper.class
│       │   │   │   │       │   ├── ContractMapper.xml
│       │   │   │   │       │   ├── EventMapper.class
│       │   │   │   │       │   ├── EventMapper.xml
│       │   │   │   │       │   ├── HouseMapper.class
│       │   │   │   │       │   ├── HouseMapper.xml
│       │   │   │   │       │   ├── LesseeMapper.class
│       │   │   │   │       │   ├── LesseeMapper.xml
│       │   │   │   │       │   ├── RectMapper.class
│       │   │   │   │       │   ├── RectMapper.xml
│       │   │   │   │       │   ├── RepairlogMapper.class
│       │   │   │   │       │   ├── RepairlogMapper.xml
│       │   │   │   │       │   ├── RoseMapper.class
│       │   │   │   │       │   ├── RoseMapper.xml
│       │   │   │   │       │   ├── SourceMapper.class
│       │   │   │   │       │   ├── SourceMapper.xml
│       │   │   │   │       │   ├── WorkerMapper.class
│       │   │   │   │       │   └── WorkerMapper.xml
│       │   │   │   │       ├── service
│       │   │   │   │       │   ├── SourceService.class
│       │   │   │   │       │   ├── WorkerService.class
│       │   │   │   │       │   └── impl
│       │   │   │   │       │       ├── SourceImpl.class
│       │   │   │   │       │       └── WorkerImpl.class
│       │   │   │   │       └── util
│       │   │   │   │           └── md5
│       │   │   │   │               └── Md5Util.class
│       │   │   │   ├── database.properties
│       │   │   │   ├── mybatis-config.xml
│       │   │   │   └── spring
│       │   │   │       ├── spring-dao.xml
│       │   │   │       ├── spring-mvc.xml
│       │   │   │       └── spring-service.xml
│       │   │   ├── lib
│       │   │   │   ├── aopalliance-1.0.jar
│       │   │   │   ├── asm-3.3.1.jar
│       │   │   │   ├── c3p0-0.9.1.2.jar
│       │   │   │   ├── cglib-2.2.2.jar
│       │   │   │   ├── commons-fileupload-1.3.1.jar
│       │   │   │   ├── commons-io-2.2.jar
│       │   │   │   ├── commons-logging-1.1.3.jar
│       │   │   │   ├── dom4j-1.6.1.jar
│       │   │   │   ├── fastjson-1.2.68.jar
│       │   │   │   ├── hutool-all-4.5.15.jar
│       │   │   │   ├── javax.annotation-api-1.2.jar
│       │   │   │   ├── javax.transaction-api-1.2.jar
│       │   │   │   ├── javax.websocket-api-1.0.jar
│       │   │   │   ├── javax.ws.rs-api-2.0.jar
│       │   │   │   ├── jsqlparser-2.0.jar
│       │   │   │   ├── jstl-1.2.jar
│       │   │   │   ├── mybatis-3.4.5.jar
│       │   │   │   ├── mybatis-spring-2.0.2.jar
│       │   │   │   ├── mysql-connector-java-8.0.20.jar
│       │   │   │   ├── pagehelper-5.1.10.jar
│       │   │   │   ├── protobuf-java-3.6.1.jar
│       │   │   │   ├── spring-aop-4.1.2.RELEASE.jar
│       │   │   │   ├── spring-beans-4.1.2.RELEASE.jar
│       │   │   │   ├── spring-context-4.1.2.RELEASE.jar
│       │   │   │   ├── spring-core-4.1.2.RELEASE.jar
│       │   │   │   ├── spring-expression-4.1.2.RELEASE.jar
│       │   │   │   ├── spring-jdbc-4.1.2.RELEASE.jar
│       │   │   │   ├── spring-orm-4.1.2.RELEASE.jar
│       │   │   │   ├── spring-oxm-4.1.2.RELEASE.jar
│       │   │   │   ├── spring-test-4.1.2.RELEASE.jar
│       │   │   │   ├── spring-tx-4.1.2.RELEASE.jar
│       │   │   │   ├── spring-web-4.1.2.RELEASE.jar
│       │   │   │   ├── spring-webmvc-4.1.2.RELEASE.jar
│       │   │   │   └── xml-apis-1.0.b2.jar
│       │   │   └── web.xml
│       │   ├── admin.jsp
│       │   ├── css
│       │   │   └── main.css
│       │   ├── image
│       │   │   ├── image.png
│       │   │   └── logo.png
│       │   ├── index.jsp
│       │   ├── js
│       │   │   └── jquery-1.6.4.min.js
│       │   ├── login.jsp
│       │   └── view
│       │       ├── contract
│       │       │   ├── add.jsp
│       │       │   ├── detail.jsp
│       │       │   ├── list.jsp
│       │       │   └── update.jsp
│       │       ├── house
│       │       │   ├── add.jsp
│       │       │   ├── detail.jsp
│       │       │   ├── list.jsp
│       │       │   └── update.jsp
│       │       └── source
│       │           └── list.jsp
│       └── ApartmentInformationManagementSystem_war_exploded
│           ├── META-INF
│           │   └── MANIFEST.MF
│           ├── WEB-INF
│           │   ├── classes
│           │   │   ├── applicationContext.xml
│           │   │   ├── com
│           │   │   │   └── zhiyou100
│           │   │   │       ├── action
│           │   │   │       │   ├── SourceAction.class
│           │   │   │       │   └── UserAction.class
│           │   │   │       ├── entity
│           │   │   │       │   ├── Contract.class
│           │   │   │       │   ├── Event.class
│           │   │   │       │   ├── House.class
│           │   │   │       │   ├── Lessee.class
│           │   │   │       │   ├── Rect.class
│           │   │   │       │   ├── Repairlog.class
│           │   │   │       │   ├── Rose.class
│           │   │   │       │   ├── Source.class
│           │   │   │       │   └── Worker.class
│           │   │   │       ├── filter
│           │   │   │       │   ├── GenericEncodingFilter.class
│           │   │   │       │   └── MyRequest.class
│           │   │   │       ├── mapper
│           │   │   │       │   ├── ContractMapper.class
│           │   │   │       │   ├── ContractMapper.xml
│           │   │   │       │   ├── EventMapper.class
│           │   │   │       │   ├── EventMapper.xml
│           │   │   │       │   ├── HouseMapper.class
│           │   │   │       │   ├── HouseMapper.xml
│           │   │   │       │   ├── LesseeMapper.class
│           │   │   │       │   ├── LesseeMapper.xml
│           │   │   │       │   ├── RectMapper.class
│           │   │   │       │   ├── RectMapper.xml
│           │   │   │       │   ├── RepairlogMapper.class
│           │   │   │       │   ├── RepairlogMapper.xml
│           │   │   │       │   ├── RoseMapper.class
│           │   │   │       │   ├── RoseMapper.xml
│           │   │   │       │   ├── SourceMapper.class
│           │   │   │       │   ├── SourceMapper.xml
│           │   │   │       │   ├── WorkerMapper.class
│           │   │   │       │   └── WorkerMapper.xml
│           │   │   │       ├── service
│           │   │   │       │   ├── SourceService.class
│           │   │   │       │   ├── WorkerService.class
│           │   │   │       │   └── impl
│           │   │   │       │       ├── SourceImpl.class
│           │   │   │       │       └── WorkerImpl.class
│           │   │   │       └── util
│           │   │   │           └── md5
│           │   │   │               └── Md5Util.class
│           │   │   ├── database.properties
│           │   │   ├── mybatis-config.xml
│           │   │   └── spring
│           │   │       ├── spring-dao.xml
│           │   │       ├── spring-mvc.xml
│           │   │       └── spring-service.xml
│           │   ├── lib
│           │   │   ├── asm-3.3.1.jar
│           │   │   ├── c3p0-0.9.1.2.jar
│           │   │   ├── cglib-2.2.2.jar
│           │   │   ├── commons-fileupload-1.3.1.jar
│           │   │   ├── commons-io-2.2.jar
│           │   │   ├── dom4j-1.6.1.jar
│           │   │   ├── fastjson-1.2.68.jar
│           │   │   ├── hutool-all-4.5.15.jar
│           │   │   ├── javax.annotation-api-1.2.jar
│           │   │   ├── javax.transaction-api-1.2.jar
│           │   │   ├── javax.websocket-api-1.0.jar
│           │   │   ├── javax.ws.rs-api-2.0.jar
│           │   │   ├── jstl-1.2.jar
│           │   │   ├── mybatis-3.4.5.jar
│           │   │   ├── mybatis-spring-2.0.2.jar
│           │   │   ├── mysql-connector-java-8.0.20.jar
│           │   │   ├── protobuf-java-3.6.1.jar
│           │   │   └── xml-apis-1.0.b2.jar
│           │   └── web.xml
│           ├── admin.jsp
│           ├── css
│           │   └── main.css
│           ├── image
│           │   ├── image.png
│           │   └── logo.png
│           ├── index.jsp
│           ├── js
│           │   └── jquery-1.6.4.min.js
│           ├── login.jsp
│           └── view
│               ├── contract
│               │   ├── add.jsp
│               │   ├── detail.jsp
│               │   ├── list.jsp
│               │   └── update.jsp
│               ├── house
│               │   ├── add.jsp
│               │   ├── detail.jsp
│               │   ├── list.jsp
│               │   └── update.jsp
│               └── source
│                   └── list.jsp
├── pom.xml
├── src
│   ├── main
│   │   ├── java
│   │   │   └── com
│   │   │       └── zhiyou100
│   │   │           ├── action
│   │   │           │   ├── SourceAction.java
│   │   │           │   └── UserAction.java
│   │   │           ├── entity
│   │   │           │   ├── Contract.java
│   │   │           │   ├── Event.java
│   │   │           │   ├── House.java
│   │   │           │   ├── Lessee.java
│   │   │           │   ├── Rect.java
│   │   │           │   ├── Repairlog.java
│   │   │           │   ├── Rose.java
│   │   │           │   ├── Source.java
│   │   │           │   └── Worker.java
│   │   │           ├── filter
│   │   │           │   └── GenericEncodingFilter.java
│   │   │           ├── mapper
│   │   │           │   ├── ContractMapper.java
│   │   │           │   ├── EventMapper.java
│   │   │           │   ├── HouseMapper.java
│   │   │           │   ├── LesseeMapper.java
│   │   │           │   ├── RectMapper.java
│   │   │           │   ├── RepairlogMapper.java
│   │   │           │   ├── RoseMapper.java
│   │   │           │   ├── SourceMapper.java
│   │   │           │   └── WorkerMapper.java
│   │   │           ├── service
│   │   │           │   ├── SourceService.java
│   │   │           │   ├── WorkerService.java
│   │   │           │   └── impl
│   │   │           │       ├── SourceImpl.java
│   │   │           │       └── WorkerImpl.java
│   │   │           └── util
│   │   │               └── md5
│   │   │                   └── Md5Util.java
│   │   └── resources
│   │       ├── applicationContext.xml
│   │       ├── com
│   │       │   └── zhiyou100
│   │       │       └── mapper
│   │       │           ├── ContractMapper.xml
│   │       │           ├── EventMapper.xml
│   │       │           ├── HouseMapper.xml
│   │       │           ├── LesseeMapper.xml
│   │       │           ├── RectMapper.xml
│   │       │           ├── RepairlogMapper.xml
│   │       │           ├── RoseMapper.xml
│   │       │           ├── SourceMapper.xml
│   │       │           └── WorkerMapper.xml
│   │       ├── database.properties
│   │       ├── mybatis-config.xml
│   │       └── spring
│   │           ├── spring-dao.xml
│   │           ├── spring-mvc.xml
│   │           └── spring-service.xml
│   └── test
│       └── java
│           └── com
│               └── zhiyou100
│                   └── util
│                       └── md5
│                           └── Md5UtilTest.java
├── target
│   ├── classes
│   │   ├── applicationContext.xml
│   │   ├── com
│   │   │   └── zhiyou100
│   │   │       ├── action
│   │   │       │   ├── SourceAction.class
│   │   │       │   └── UserAction.class
│   │   │       ├── entity
│   │   │       │   ├── Contract.class
│   │   │       │   ├── Event.class
│   │   │       │   ├── House.class
│   │   │       │   ├── Lessee.class
│   │   │       │   ├── Rect.class
│   │   │       │   ├── Repairlog.class
│   │   │       │   ├── Rose.class
│   │   │       │   ├── Source.class
│   │   │       │   └── Worker.class
│   │   │       ├── filter
│   │   │       │   ├── GenericEncodingFilter.class
│   │   │       │   └── MyRequest.class
│   │   │       ├── mapper
│   │   │       │   ├── ContractMapper.class
│   │   │       │   ├── ContractMapper.xml
│   │   │       │   ├── EventMapper.class
│   │   │       │   ├── EventMapper.xml
│   │   │       │   ├── HouseMapper.class
│   │   │       │   ├── HouseMapper.xml
│   │   │       │   ├── LesseeMapper.class
│   │   │       │   ├── LesseeMapper.xml
│   │   │       │   ├── RectMapper.class
│   │   │       │   ├── RectMapper.xml
│   │   │       │   ├── RepairlogMapper.class
│   │   │       │   ├── RepairlogMapper.xml
│   │   │       │   ├── RoseMapper.class
│   │   │       │   ├── RoseMapper.xml
│   │   │       │   ├── SourceMapper.class
│   │   │       │   ├── SourceMapper.xml
│   │   │       │   ├── WorkerMapper.class
│   │   │       │   └── WorkerMapper.xml
│   │   │       ├── service
│   │   │       │   ├── SourceService.class
│   │   │       │   ├── WorkerService.class
│   │   │       │   └── impl
│   │   │       │       ├── SourceImpl.class
│   │   │       │       └── WorkerImpl.class
│   │   │       └── util
│   │   │           └── md5
│   │   │               └── Md5Util.class
│   │   ├── database.properties
│   │   ├── mybatis-config.xml
│   │   └── spring
│   │       ├── spring-dao.xml
│   │       ├── spring-mvc.xml
│   │       └── spring-service.xml
│   ├── generated-sources
│   │   └── annotations
│   ├── generated-test-sources
│   │   └── test-annotations
│   └── test-classes
│       └── com
│           └── zhiyou100
│               └── util
│                   └── md5
│                       └── Md5UtilTest.class
└── web
    ├── WEB-INF
    │   └── web.xml
    ├── admin.jsp
    ├── css
    │   └── main.css
    ├── image
    │   ├── image.png
    │   └── logo.png
    ├── index.jsp
    ├── js
    │   └── jquery-1.6.4.min.js
    ├── login.jsp
    └── view
        ├── contract
        │   ├── add.jsp
        │   ├── detail.jsp
        │   ├── list.jsp
        │   └── update.jsp
        ├── event
        ├── house
        │   ├── add.jsp
        │   ├── detail.jsp
        │   ├── list.jsp
        │   └── update.jsp
        ├── lessee
        ├── repairlog
        ├── rose
        ├── source
        │   └── list.jsp
        └── worker

108 directories, 277 files

1. SourceAction.java 控制层

package com.zhiyou100.action;

import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.zhiyou100.entity.Source;
import com.zhiyou100.service.SourceService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;

import java.util.List;

/**
 * 资源 分页测试
 *
 * @author yang
 * @version 1.0.0
 * @date 2020-08-28 09:56
 */
@Controller
@RequestMapping("/source")
public class SourceAction {
    private SourceService sourceService;

    @Autowired
    public void setSourceService(SourceService sourceService) {
        this.sourceService = sourceService;
    }

    @RequestMapping("/list.action")
    public String listSource(Model model,
                             @RequestParam(defaultValue = "1", required = true, value = "pageNo") Integer pageNo
    ) {
        // 每页显示的记录数
        int pageSize = 10;

        // 分页查询
        PageHelper.startPage(pageNo, pageSize);

        // 获取所有的信息
        final List<Source> list = sourceService.list();

        // 使用 PageInfo<Source> 结果进行封装
        final PageInfo<Source> pageInfo = new PageInfo<>(list);
        model.addAttribute("pageInfo", pageInfo);
        return "view/source/list";
    }


}

2. SourceService.java 服务层

package com.zhiyou100.service;

import com.zhiyou100.entity.Source;

import java.util.List;

/**
 * 资源 服务层
 *
 * @author yang
 * @version 1.0.0
 * @date 2020-08-28 10:01
 */
public interface SourceService {
    /**
     * 插入一条 资源
     *
     * @param source 资源
     */
    void inertOne(Source source);

    /**
     * 获取所有的 source
     * @return 所有的 source
     */
    List<Source> list();
}

3. SourceImpl.java 服务层实现类

package com.zhiyou100.service.impl;

import com.zhiyou100.entity.Source;
import com.zhiyou100.mapper.SourceMapper;
import com.zhiyou100.service.SourceService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

/**
 * 资源实现类
 *
 * @author yang
 * @version 1.0.0
 * @date 2020-08-28 10:02
 */
@Service
public class SourceImpl implements SourceService {
    private SourceMapper sourceMapper;

    @Autowired
    public void setSourceMapper(SourceMapper sourceMapper) {
        this.sourceMapper = sourceMapper;
    }

    @Override
    public void inertOne(Source source) {
        sourceMapper.insertOne(source);
    }

    @Override
    public List<Source> list() {
        return sourceMapper.list();
    }
}

4. SourceMapper.java dao 接口

package com.zhiyou100.mapper;

import com.zhiyou100.entity.Source;

import java.util.List;

/**
 * 资源 mapper
 *
 * @author yang
 */
public interface SourceMapper {
    /**
     * 插入一条
     * @param source 资源
     */
    void insertOne(Source source);

    /**
     * 获取所有的 source
     * @return 所有的 source
     */
    List<Source> list();

}

5. SourceMapper.xml dao 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="com.zhiyou100.mapper.SourceMapper">
    <resultMap id="BaseResultMap" type="com.zhiyou100.entity.Source">
        <id column="sid" property="sid" jdbcType="INTEGER"/>
        <result column="sname" property="sname" jdbcType="VARCHAR"/>
        <result column="srul" property="srul" jdbcType="VARCHAR"/>
    </resultMap>
    <sql id="Base_Column_List">
        sid, sname, srul
    </sql>
    <insert id="insertOne" parameterType="Source">
        insert into db_zygy.source (sid, sname, srul)
        values (#{sid}, #{sname}, #{srul})
    </insert>

    <!-- 注意,不要加上 ' ; '    -->
    <select id="list" resultType="com.zhiyou100.entity.Source">
        select
        <include refid="Base_Column_List"/>
        from db_zygy.source
    </select>

</mapper>

四、 效果

jjSH

五、PageInfo 方法

//获取第1页,10条内容,默认查询总数count
PageHelper.startPage(1, 10);
List<User> list = userMapper.selectAll();
//用PageInfo对结果进行包装
PageInfo page = new PageInfo(list);
//测试PageInfo全部属性
//PageInfo包含了非常全面的分页属性
assertEquals(1, page.getPageNum());
// getPageNum 获取当前页数
assertEquals(10, page.getPageSize());
// getPageSize 获取每页显示的记录条数
assertEquals(1, page.getStartRow());
// getStartRow 获取开始行
assertEquals(10, page.getEndRow());
// getEndRow 获取结束行
assertEquals(183, page.getTotal());
// getTotal 获取总记录数
assertEquals(19, page.getPages());
// getPages 获取总页数
assertEquals(1, page.getFirstPage());
// getFirstPage 获取第一个页
assertEquals(8, page.getLastPage());
// getLastPage 获取最后一页
assertEquals(true, page.isFirstPage());
//  isFirstPage 是不是第一页
assertEquals(false, page.isLastPage());
// isLastPage 是不是最后一页
assertEquals(false, page.isHasPreviousPage());
// isHasPreviousPage 有没有上一页
assertEquals(true, page.isHasNextPage());
// isHasNextPage 有没有下一页
原文地址:https://www.cnblogs.com/javayanglei/p/13577597.html