mapStruct由懵逼到入门

工作当中用到了mapStruct, 刚刚使用不求甚解, 仿照同事的代码贴个标签就用起来了, 但其实一点都不懂。下面记录一下由傻逼到入门的知识接收记录。

(190527-0609)

一. mapStruct简单使用

mapStruct用在由DO向DTO转换,或者其他什么两个对象之间的属性拷贝。

过程

  1. 先写一个转换接口,定义一下转换的接口方法(入参是被转化对象,出参是转化成为的对象)。

  2. 把接口贴上个标签,引入maven包,就会在编译时候自动生成转换接口的实现类,把两个对象相同的属性copy的实现方法。

代码

1. pom文件中引入maven包

    <properties>
        <org.mapstruct.version>1.3.0.Finalorg.mapstruct.version>
    properties>
    <dependencies>
        
        <dependency>
            <groupId>org.mapstructgroupId>
            <artifactId>mapstructartifactId>
            <version>${org.mapstruct.version}version>
        dependency>
        
        <dependency>
            <groupId>org.mapstructgroupId>
            <artifactId>mapstruct-processorartifactId>
            <version>${org.mapstruct.version}version>
            <scope>providedscope>
        dependency>
        
        <dependency>
            <groupId>com.google.auto.servicegroupId>
            <artifactId>auto-serviceartifactId>
            <version>1.0-rc5version>
        dependency>
    dependencies>
    <build>
        <plugins>
            
            <plugin>
                <groupId>org.apache.maven.pluginsgroupId>
                <artifactId>maven-compiler-pluginartifactId>
                <version>3.5.1version> 
                <configuration>
                    <source>1.8source> 
                    <target>1.8target> 
                    <annotationProcessorPaths>
                    annotationProcessorPaths>
                configuration>
            plugin>
        plugins>
    build>

2. 定义转换接口

import cn.gasin.domain.Car;
import cn.gasin.dto.CarDto;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.factory.Mappers;
/**
* 转换接口
*/
@Mapper
public interface CarMapper {
	//1.定义的实例
	CarMapper INSTANCE = Mappers.getMapper(CarMapper.class);
	//2.贴上映射标签的转换接口方法,标签内的值是源属性名和目标属性名不同时候添加的
	@Mapping(source = "numberOfSeats", target = "seatCount")
	CarDto carToCarDto(Car car);
}

3. maven编译,生成实现类

在这里插入图片描述=
mapStruct由懵逼到入门_第1张图片

二、 mapStruct的工作原理

mapStruct利用jdk1.6的注解处理器,在编译时候拿到Mapper注解的接口,生成属性拷贝的实现类代码。

过程:

1. mapStruct继承abstructProcessor实现MappingProcessor编译时注解处理器,重写process方法。在其中处理编译时候生成代码的业务

mapStruct由懵逼到入门_第2张图片
mapStruct由懵逼到入门_第3张图片

2. 在META-INF文件夹下的services文件夹下创建一个Processer接口全限定名的文件,把实现类MappingProcessor的全限定名贴上去,这是SPI(Service Provider Interface)的用法, 它可以发现我们的注解处理器

mapStruct由懵逼到入门_第4张图片

3. 总结: 在编译时候,java发现Processor后,生成mappingProcessor对象,每个被编译的类都经过process方法,mapStruct在其中拦截处理,如果是mapper接口的话就按照规则生成转换接口的实现类。

名词介绍:

1. annotation 注解

​ 注解是在源文件中嵌入的Java元数据(附加信息),这些元数据不算是我们的代码,是一些传递给Java的信息(我们也可以拿到这些信息)。是一种类型,类似enum,就是告诉java这个类只能有这几个对象,annotation贴在哪里(类/方法/字段)就是告诉java这个类/方法/字段有啥不一样的标签。

​ 这里可以看一下注解的定义和怎么简单写&用一个注解

知道了注解是什么,也就可以想一两个用法。

​ 比方说mapStruct里面,它就是在编译时候拿到了自己的@Mapper注解(怎么拿到下面说),拿到了注解后就知道了源类和目标类。然后通过反射拿到属性,同名的属性互相拷贝。不同名的属性根据@Mapping注解的source和target进行拷贝。。。(还有很多高级的功能,我不太清楚)

​ Spring里面的注解配置,都是拿到注解信息后进行的。

2. processor 注解处理器

​ javax.annotation.processing.Processor 接口是JDK1.6提供的专门编译时处理注解的接口,继承它,就可以在编译时候在processing方法种拿到想要处理的注解。

​ 注解处理器的详解看这里

对于mapStruct,在编译时后就拿到@Mapper注解信息和注解贴在的接口,然后生成转换的实现类。

3. SPI 服务发现

​ java.util.ServiceLoader进行服务实现的发现与实例化,和反射类似,属于运行时的动态技术,在运行时候使用ServiceLoader拿到接口的实现类。

​ 使用对应库Jar包中需要在META-INF/services下放入名称为接口Api完整类名的文件,并填入对应实现类名(如Jar包内有多个实现,则用换行进行分隔)

Processor和SPI没什么关系,Javac进行注解处理的时候,也使用了SPI技术

SPI简单实用介绍

三、 不懂的地方

1. SPI和processor是怎么协调工作的?

2. SPI如果同一个服务下有多个实例,该怎么办?

TODO 计划下周(190610-16)了解上面两个问题, 并查看一下MyBatis的源码

你可能感兴趣的:(mapStruct)