第三十章:SpringBoot使用MapStruct自動(dòng)映射DTO

MapStruct是一種類(lèi)型安全的bean映射類(lèi)生成java注釋處理器。
我們要做的就是定義一個(gè)映射器接口,聲明任何必需的映射方法。在編譯的過(guò)程中,MapStruct會(huì)生成此接口的實(shí)現(xiàn)。該實(shí)現(xiàn)使用純java方法調(diào)用的源和目標(biāo)對(duì)象之間的映射,MapStruct節(jié)省了時(shí)間,通過(guò)生成代碼完成繁瑣和容易出錯(cuò)的代碼邏輯。下面我們來(lái)揭開(kāi)它的神秘面紗

免費(fèi)教程專(zhuān)題

恒宇少年在博客整理三套免費(fèi)學(xué)習(xí)教程專(zhuān)題,由于文章偏多特意添加了閱讀指南,新文章以及之前的文章都會(huì)在專(zhuān)題內(nèi)陸續(xù)填充,希望可以幫助大家解惑更多知識(shí)點(diǎn)。

本章目標(biāo)

基于SpringBoot平臺(tái)完成MapStruct映射框架的集成。

SpringBoot 企業(yè)級(jí)核心技術(shù)學(xué)習(xí)專(zhuān)題


專(zhuān)題 專(zhuān)題名稱(chēng) 專(zhuān)題描述
001 Spring Boot 核心技術(shù) 講解SpringBoot一些企業(yè)級(jí)層面的核心組件
002 Spring Boot 核心技術(shù)章節(jié)源碼 Spring Boot 核心技術(shù)簡(jiǎn)書(shū)每一篇文章碼云對(duì)應(yīng)源碼
003 Spring Cloud 核心技術(shù) 對(duì)Spring Cloud核心技術(shù)全面講解
004 Spring Cloud 核心技術(shù)章節(jié)源碼 Spring Cloud 核心技術(shù)簡(jiǎn)書(shū)每一篇文章對(duì)應(yīng)源碼
005 QueryDSL 核心技術(shù) 全面講解QueryDSL核心技術(shù)以及基于SpringBoot整合SpringDataJPA
006 SpringDataJPA 核心技術(shù) 全面講解SpringDataJPA核心技術(shù)
007 SpringBoot核心技術(shù)學(xué)習(xí)目錄 SpringBoot系統(tǒng)的學(xué)習(xí)目錄,敬請(qǐng)關(guān)注點(diǎn)贊?。?

構(gòu)建項(xiàng)目


我們使用idea開(kāi)發(fā)工具創(chuàng)建一個(gè)SpringBoot項(xiàng)目,添加相應(yīng)的依賴(lài),pom.xml配置文件如下所示:

...省略部分代碼
<parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.6.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
        <org.mapstruct.version>1.2.0.CR1</org.mapstruct.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
            <!--<scope>provided</scope>-->
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <!--mapStruct依賴(lài)-->
        <dependency>
            <groupId>org.mapstruct</groupId>
            <artifactId>mapstruct-jdk8</artifactId>
            <version>${org.mapstruct.version}</version>
        </dependency>
        <dependency>
            <groupId>org.mapstruct</groupId>
            <artifactId>mapstruct-processor</artifactId>
            <version>${org.mapstruct.version}</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>javax.inject</groupId>
            <artifactId>javax.inject</artifactId>
            <version>1</version>
        </dependency>
        
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.0.31</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
    </dependencies>
....省略部分代碼

集成MapStruct官方提供了兩種方式,上面配置文件內(nèi)我們采用的是直接添加Maven依賴(lài),而官方文檔還提供了另外一種方式,采用Maven插件形式配置,配置如下所示:

...引用官方文檔
...
<properties>
    <org.mapstruct.version>1.2.0.CR1</org.mapstruct.version>
</properties>
...
<dependencies>
    <dependency>
        <groupId>org.mapstruct</groupId>
        <artifactId>mapstruct-jdk8</artifactId>
        <version>${org.mapstruct.version}</version>
    </dependency>
</dependencies>
...
<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.5.1</version>
            <configuration>
                <source>1.8</source>
                <target>1.8</target>
                <annotationProcessorPaths>
                    <path>
                        <groupId>org.mapstruct</groupId>
                        <artifactId>mapstruct-processor</artifactId>
                        <version>${org.mapstruct.version}</version>
                    </path>
                </annotationProcessorPaths>
            </configuration>
        </plugin>
    </plugins>
</build>
...

我個(gè)人比較喜歡采用第一種方式,不需要配置過(guò)多的插件,依賴(lài)方式比較方便。
接下來(lái)我們開(kāi)始配置下數(shù)據(jù)庫(kù)連接信息以及簡(jiǎn)單的兩張表的SpringDataJPA相關(guān)接口。

數(shù)據(jù)庫(kù)連接信息

在resource下新創(chuàng)建一個(gè)application.yml文件,并添加如下數(shù)據(jù)庫(kù)連接配置:

spring:
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://127.0.0.1:3306/test?characterEncoding=utf8
    username: root
    password: 123456
    #最大活躍數(shù)
    maxActive: 20
    #初始化數(shù)量
    initialSize: 1
    #最大連接等待超時(shí)時(shí)間
    maxWait: 60000
    #打開(kāi)PSCache,并且指定每個(gè)連接PSCache的大小
    poolPreparedStatements: true
    maxPoolPreparedStatementPerConnectionSize: 20
    #通過(guò)connectionProperties屬性來(lái)打開(kāi)mergeSql功能;慢SQL記錄
    #connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000
    minIdle: 1
    timeBetweenEvictionRunsMillis: 60000
    minEvictableIdleTimeMillis: 300000
    validationQuery: select 1 from dual
    testWhileIdle: true
    testOnBorrow: false
    testOnReturn: false
    #配置監(jiān)控統(tǒng)計(jì)攔截的filters,去掉后監(jiān)控界面sql將無(wú)法統(tǒng)計(jì),'wall'用于防火墻
    filters: stat, wall, log4j
  jpa:
    properties:
      hibernate:
        show_sql: true
        format_sql: true

有關(guān)SpringDataJPA相關(guān)的學(xué)習(xí)請(qǐng)?jiān)L問(wèn)第三章:SpringBoot使用SpringDataJPA完成CRUD,我們?cè)跀?shù)據(jù)庫(kù)內(nèi)創(chuàng)建兩張表信息分別是商品基本信息表、商品類(lèi)型表。
兩張表有相應(yīng)的關(guān)聯(lián),我們?cè)诓徊捎眠B接查詢(xún)的方式模擬使用MapStruct,表信息如下所示:

--商品類(lèi)型信息表
CREATE TABLE `good_types` (
  `tgt_id` int(11) NOT NULL AUTO_INCREMENT,
  `tgt_name` varchar(30) DEFAULT NULL,
  `tgt_is_show` int(1) DEFAULT NULL,
  `tgt_order` int(255) DEFAULT NULL,
  PRIMARY KEY (`tgt_id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;

--商品基本信息表
CREATE TABLE `good_infos` (
  `tg_id` int(11) NOT NULL AUTO_INCREMENT,
  `tg_type_id` int(11) DEFAULT NULL,
  `tg_title` varchar(30) DEFAULT NULL,
  `tg_price` decimal(8,2) DEFAULT NULL,
  `tg_order` int(2) DEFAULT NULL,
  PRIMARY KEY (`tg_id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;

INSERT INTO `good_types` VALUES ('1', '青菜', '1', '1');
INSERT INTO `good_infos` VALUES ('1', '1', '芹菜', '12.40', '1');

下面我們根據(jù)這兩張表創(chuàng)建對(duì)應(yīng)的實(shí)體類(lèi)。

商品類(lèi)型實(shí)體

package com.yuqiyu.chapter30.bean;

import lombok.Data;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;

/**
 * ========================
 * Created with IntelliJ IDEA.
 * User:恒宇少年
 * Date:2017/8/20
 * Time:11:17
 * 碼云:http://git.oschina.net/jnyqy
 * ========================
 */
@Entity
@Table(name = "good_types")
@Data
public class GoodTypeBean
{
    @Id
    @Column(name = "tgt_id")
    private Long id;

    @Column(name = "tgt_name")
    private String name;
    @Column(name = "tgt_is_show")
    private int show;
    @Column(name = "tgt_order")
    private int order;

}

商品基本信息實(shí)體

package com.yuqiyu.chapter30.bean;

import lombok.Data;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;

/**
 * ========================
 * Created with IntelliJ IDEA.
 * User:恒宇少年
 * Date:2017/8/20
 * Time:11:16
 * 碼云:http://git.oschina.net/jnyqy
 * ========================
 */
@Entity
@Table(name = "good_infos")
@Data
public class GoodInfoBean
{
    @Id
    @Column(name = "tg_id")
    private Long id;
    @Column(name = "tg_title")
    private String title;
    @Column(name = "tg_price")
    private double price;
    @Column(name = "tg_order")
    private int order;
    @Column(name = "tg_type_id")
    private Long typeId;
}

接下來(lái)我們繼續(xù)創(chuàng)建相關(guān)的JPA。

商品類(lèi)型JPA

package com.yuqiyu.chapter30.jpa;

import com.yuqiyu.chapter30.bean.GoodTypeBean;
import org.springframework.data.jpa.repository.JpaRepository;

/**
 * ========================
 * Created with IntelliJ IDEA.
 * User:恒宇少年
 * Date:2017/8/20
 * Time:11:24
 * 碼云:http://git.oschina.net/jnyqy
 * ========================
 */
public interface GoodTypeJPA
    extends JpaRepository<GoodTypeBean,Long>
{
}

商品信息JPA

package com.yuqiyu.chapter30.jpa;

import com.yuqiyu.chapter30.bean.GoodInfoBean;
import org.springframework.data.jpa.repository.JpaRepository;

/**
 * ========================
 * Created with IntelliJ IDEA.
 * User:恒宇少年
 * Date:2017/8/20
 * Time:11:23
 * 碼云:http://git.oschina.net/jnyqy
 * ========================
 */
public interface GoodInfoJPA
    extends JpaRepository<GoodInfoBean,Long>
{
    
}

配置MapStruct

到目前為止我們的準(zhǔn)備工作差不多完成了,下面我們開(kāi)始配置使用MapStruct。我們的最終目的是為了返回一個(gè)自定義的DTO實(shí)體,那么我們就先來(lái)創(chuàng)建這個(gè)DTO,DTO的代碼如下所示:

package com.yuqiyu.chapter30.dto;

import lombok.Data;

/**
 * 轉(zhuǎn)換Dto
 * ========================
 * Created with IntelliJ IDEA.
 * User:恒宇少年
 * Date:2017/8/20
 * Time:11:25
 * 碼云:http://git.oschina.net/jnyqy
 * ========================
 */
@Data
public class GoodInfoDTO
{
    //商品編號(hào)
    private String goodId;
    //商品名稱(chēng)
    private String goodName;
    //商品價(jià)格
    private double goodPrice;
    //類(lèi)型名稱(chēng)
    private String typeName;
}

可以看到GoodInfoDTO實(shí)體內(nèi)集成了商品信息、商品類(lèi)型兩張表內(nèi)的數(shù)據(jù),對(duì)應(yīng)查詢(xún)出信息后,我們需要使用MapStruct自動(dòng)映射到GoodInfoDTO。

創(chuàng)建Mapper

Mapper這個(gè)定義一般是被廣泛應(yīng)用到MyBatis半自動(dòng)化ORM框架上,而這里的Mapper跟Mybatis沒(méi)有關(guān)系。下面我們先來(lái)看下代碼,如下所示:

package com.yuqiyu.chapter30.mapper;

import com.yuqiyu.chapter30.bean.GoodInfoBean;
import com.yuqiyu.chapter30.bean.GoodTypeBean;
import com.yuqiyu.chapter30.dto.GoodInfoDTO;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.Mappings;

/**
 * 配置映射
 * ========================
 * Created with IntelliJ IDEA.
 * User:恒宇少年
 * Date:2017/8/20
 * Time:11:26
 * 碼云:http://git.oschina.net/jnyqy
 * ========================
 */
@Mapper(componentModel = "spring")
//@Mapper
public interface GoodInfoMapper
{
    //public static GoodInfoMapper MAPPER = Mappers.getMapper(GoodInfoMapper.class);

    @Mappings({
            @Mapping(source = "type.name",target = "typeName"),
            @Mapping(source = "good.id",target = "goodId"),
            @Mapping(source = "good.title",target = "goodName"),
            @Mapping(source = "good.price",target = "goodPrice")
    })
    public GoodInfoDTO from(GoodInfoBean good, GoodTypeBean type);
}

可以看到GoodInfoMapper是一個(gè)接口的形式存在的,當(dāng)然也可以是一個(gè)抽象類(lèi),如果你需要在轉(zhuǎn)換的時(shí)候才用個(gè)性化的定制的時(shí)候可以采用抽象類(lèi)的方式,相應(yīng)的代碼配置官方文檔已經(jīng)聲明。
@Mapper注解是用于標(biāo)注接口、抽象類(lèi)是被MapStruct自動(dòng)映射的標(biāo)識(shí),只有存在該注解才會(huì)將內(nèi)部的接口方法自動(dòng)實(shí)現(xiàn)。
MapStruct為我們提供了多種的獲取Mapper的方式,比較常用的兩種分別是

默認(rèn)配置

默認(rèn)配置,我們不需要做過(guò)多的配置內(nèi)容,獲取Mapper的方式就是采用Mappers通過(guò)動(dòng)態(tài)工廠內(nèi)部反射機(jī)制完成Mapper實(shí)現(xiàn)類(lèi)的獲取。
默認(rèn)方式獲取Mapper如下所示:

//Mapper接口內(nèi)部定義
public static GoodInfoMapper MAPPER = Mappers.getMapper(GoodInfoMapper.class);

//外部調(diào)用
GoodInfoMapper.MAPPER.from(goodBean,goodTypeBean);
Spring方式配置

Spring方式我們需要在@Mapper注解內(nèi)添加componentModel屬性值,配置后在外部可以采用@Autowired方式注入Mapper實(shí)現(xiàn)類(lèi)完成映射方法調(diào)用。
Spring方式獲取Mapper如下所示:

//注解配置
@Mapper(componentModel = "spring")

//注入Mapper實(shí)現(xiàn)類(lèi)
@Autowired
private GoodInfoMapper goodInfoMapper;

//調(diào)用
goodInfoMapper.from(goodBean,goodTypeBean);
@Mappings & @Mapping

Mapper接口定義方法上面聲明了一系列的注解映射@Mapping以及@Mappings,那么這兩個(gè)注解是用來(lái)干什么工作的呢?
@Mapping注解我們用到了兩個(gè)屬性,分別是sourcetarget

source代表的是映射接口方法內(nèi)的參數(shù)名稱(chēng),如果是基本類(lèi)型的參數(shù),參數(shù)名可以直接作為source的內(nèi)容,如果是實(shí)體類(lèi)型,則可以采用實(shí)體參數(shù)名.字段名的方式作為source的內(nèi)容,配置如上面GoodInfoMapper內(nèi)容所示。

target代表的是映射到方法方法值內(nèi)的字段名稱(chēng),配置如上面GoodInfoMapper所示。

查看Mapper實(shí)現(xiàn)

下面我們執(zhí)行maven compile命令,到target/generated-sources/annotations目錄下查看對(duì)應(yīng)Mapper實(shí)現(xiàn)類(lèi),實(shí)現(xiàn)類(lèi)代碼如下所示:

package com.yuqiyu.chapter30.mapper;

import com.yuqiyu.chapter30.bean.GoodInfoBean;
import com.yuqiyu.chapter30.bean.GoodTypeBean;
import com.yuqiyu.chapter30.dto.GoodInfoDTO;
import javax.annotation.Generated;
import org.springframework.stereotype.Component;

@Generated(
    value = "org.mapstruct.ap.MappingProcessor",
    date = "2017-08-20T12:52:52+0800",
    comments = "version: 1.2.0.CR1, compiler: javac, environment: Java 1.8.0_111 (Oracle Corporation)"
)
@Component
public class GoodInfoMapperImpl implements GoodInfoMapper {

    @Override
    public GoodInfoDTO from(GoodInfoBean good, GoodTypeBean type) {
        if ( good == null && type == null ) {
            return null;
        }

        GoodInfoDTO goodInfoDTO = new GoodInfoDTO();

        if ( good != null ) {
            if ( good.getId() != null ) {
                goodInfoDTO.setGoodId( String.valueOf( good.getId() ) );
            }
            goodInfoDTO.setGoodName( good.getTitle() );
            goodInfoDTO.setGoodPrice( good.getPrice() );
        }
        if ( type != null ) {
            goodInfoDTO.setTypeName( type.getName() );
        }

        return goodInfoDTO;
    }
}

MapStruct根據(jù)我們配置的@Mapping注解自動(dòng)將source實(shí)體內(nèi)的字段進(jìn)行了調(diào)用target實(shí)體內(nèi)字段的setXxx方法賦值,并且做出了一切參數(shù)驗(yàn)證。
我們采用了Spring方式獲取Mapper,在自動(dòng)生成的實(shí)現(xiàn)類(lèi)上MapStruct為我們自動(dòng)添加了@ComponentSpring聲明式注入注解配置。

運(yùn)行測(cè)試

下面我們來(lái)創(chuàng)建一個(gè)測(cè)試的Controller,用于訪問(wèn)具體請(qǐng)求地址時(shí)查詢(xún)出商品的基本信息以及商品的類(lèi)型后調(diào)用GoodInfoMapper.from(xxx,xxx)方法完成返回GoodInfoDTO實(shí)例。Controller代碼實(shí)現(xiàn)如下所示:

package com.yuqiyu.chapter30.controller;

import com.yuqiyu.chapter30.bean.GoodInfoBean;
import com.yuqiyu.chapter30.bean.GoodTypeBean;
import com.yuqiyu.chapter30.dto.GoodInfoDTO;
import com.yuqiyu.chapter30.jpa.GoodInfoJPA;
import com.yuqiyu.chapter30.jpa.GoodTypeJPA;
import com.yuqiyu.chapter30.mapper.GoodInfoMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * 測(cè)試控制器
 * ========================
 * Created with IntelliJ IDEA.
 * User:恒宇少年
 * Date:2017/8/20
 * Time:12:24
 * 碼云:http://git.oschina.net/jnyqy
 * ========================
 */
@RestController
public class GoodInfoController
{
    /**
     * 注入商品基本信息jpa
     */
    @Autowired
    private GoodInfoJPA goodInfoJPA;
    /**
     * 注入商品類(lèi)型jpa
     */
    @Autowired
    private GoodTypeJPA goodTypeJPA;
    /**
     * 注入mapStruct轉(zhuǎn)換Mapper
     */
    @Autowired
    private GoodInfoMapper goodInfoMapper;

    /**
     * 查詢(xún)商品詳情
     * @param id
     * @return
     */
    @RequestMapping(value = "/detail/{id}")
    public GoodInfoDTO detail(@PathVariable("id") Long id)
    {
        //查詢(xún)商品基本信息
        GoodInfoBean goodInfoBean = goodInfoJPA.findOne(id);
        //查詢(xún)商品類(lèi)型基本信息
        GoodTypeBean typeBean = goodTypeJPA.findOne(goodInfoBean.getTypeId());
        //返回轉(zhuǎn)換dto
        return goodInfoMapper.from(goodInfoBean,typeBean);
    }
}

在Controller內(nèi)我們注入了GoodInfoJPA、GoodTypeJPA以及GoodInfoMapper,在查詢(xún)商品詳情方法時(shí)做出了映射處理。接下來(lái)我們啟動(dòng)項(xiàng)目訪問(wèn)地址http://127.0.0.1:8080/detail/1查看界面輸出效果,如下所示:

{
goodId: "1",
goodName: "芹菜",
goodPrice: 12.4,
typeName: "青菜"
}

可以看到界面輸出了GoodInfoDTO內(nèi)的所有字段內(nèi)容,并且通過(guò)from方法將對(duì)應(yīng)配置的target字段賦值。

總結(jié)

本章主要講述了基于SpringBoot開(kāi)發(fā)框架上集成MapStruct自動(dòng)映射框架,完成模擬多表獲取數(shù)據(jù)后將某一些字段通過(guò)@Mapping配置自動(dòng)映射到DTO實(shí)體實(shí)例指定的字段內(nèi)。
MapStruct官方文檔地址:http://mapstruct.org/documentation/dev/reference/html/

本章代碼已經(jīng)上傳到碼云:
SpringBoot配套源碼地址:https://gitee.com/hengboy/spring-boot-chapter
SpringCloud配套源碼地址:https://gitee.com/hengboy/spring-cloud-chapter

作者個(gè)人 博客
使用開(kāi)源框架 ApiBoot 助你成為Api接口服務(wù)架構(gòu)師

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 1. 簡(jiǎn)介 1.1 什么是 MyBatis ? MyBatis 是支持定制化 SQL、存儲(chǔ)過(guò)程以及高級(jí)映射的優(yōu)秀的...
    笨鳥(niǎo)慢飛閱讀 6,220評(píng)論 0 4
  • Spring Cloud為開(kāi)發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見(jiàn)模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,525評(píng)論 19 139
  • 前兩章我們簡(jiǎn)單講解了SpringBoot的易用性,SpringBoot框架內(nèi)部提供了很多我們需要用到的組件,需要什...
    恒宇少年閱讀 78,489評(píng)論 150 119
  • 沒(méi)睡個(gè)昏天黑地,沒(méi)出去浪一圈,總感覺(jué)辭了個(gè)假職。 于是攤在床挑了部電影看,很慶幸看了這部久仰大名但一直沒(méi)堅(jiān)持看完的...
    木子肆閱讀 726評(píng)論 0 0
  • 0413晨讀淺悟 《非暴力溝通》 1.只觀察,不評(píng)判 在與別人溝通交流的時(shí)候,溝通內(nèi)容含有從不總是經(jīng)常這些詞的時(shí)候...
    houpanpan926閱讀 3,434評(píng)論 0 0

友情鏈接更多精彩內(nèi)容