MapStruct 的进阶用法!!

前言

MapStruct 是一个 Java 编译时注解处理框架,用来自动化将一种 Java Bean 对象映射成另一种类型的对象。

该框架的主要目标是使开发人员在尽可能少的代码和最低的运行时间成本下实现属性映射。MapStruct 通过在编译时生成代码来实现这点,这与大多数其他 Java Bean 映射框架在运行时通过反射进行映射形成了鲜明对比。

MapStruct 具有以下主要特性:

  • 简洁: 简化了 Java Beans 之间转换的代码,自动生成使用简单的赋值语句完成的映射实现。

  • 性能优秀: 由于 MapStruct 是在编译时生成代码,不涉及任何反射,因此执行映射的性能优越。

  • 安全: 通过在编译时生成映射代码,MapStruct 提供了类型安全的映射,并能在编译时就发现潜在的错误。

  • 灵活: 可通过自定义转换方法、类型转换和映射策略等来满足复杂的映射需求。

  • 良好的 IDE 支持: 由于 MapStruct 是编译时工具,所以拥有良好的 IDE 集成,如代码自动完成、错误高亮等。

总的来说, MapStruct 是一个强大且灵活的映射框架,很好的解决有关对象转换的问题,实现了代码的简洁和性能的兼顾。MapStruct的常规用法,网上有很多教程了,本文将列举一些进阶用法,方便日常开发使用。

expression

在转化的时候,执行 java 表达式,直接看例子:

@Mapper(componentModel = "spring")
public interface MyMapper {
    @Mapping(target = "createTime", expression = "java(System.currentTimeMillis())")
    Target toTarget(Source source);
}

转化成 target 对象时,createTime字段的值,会设置为System.currentTimeMillis(),生成的代码如下:

@Component
public class MyMapperImpl implements MyMapper {

    @Override
    public Target toTarget(Source source) {
        Target target = new Target();
        target.setCreateTime( System.currentTimeMillis() );
        return target;
    }
}

qualifiedByName

做映射时,默认情况下,从source 字段到target 字段是直接使用 get/set,如下:

@Data
public class Source {
    private String name;
}

@Data
public class Target {
    private String name;
}
    

@Mapper(componentModel = "spring")
public interface MyMapper {
    Target toTarget(Source source);
}

生成的转化代码类如下:

@Component
public class MyMapperImpl implements MyMapper {

    @Override
    public Target toTarget(Source source) {
        if ( source == null ) {
            return null;
        }

        Target target = new Target();

        // 无脑 set/get
        target.setName( source.getName() );

        return target;
    }
}

如果这种直接的 set/get 无法满足需求,比如需要把 name 转化成大写格式,那么可以使用qualifiedByName:

@Mapper(componentModel = "spring")
public interface MyMapper {
    @Mapping(target = "name", source = "name", qualifiedByName = "toUpperCase")
    Target toTarget(Source source);

    @Named("toUpperCase")
    default String toUpperCase(String value) {
        // 这里写转换大写的逻辑
        return value == null ? null : value.toUpperCase();
    }
}

生成的代码如下:

@Component
public class MyMapperImpl implements MyMapper {

    @Override
    public Target toTarget(Source source) {
        if ( source == null ) {
            return null;
        }

        Target target = new Target();

        target.setName( toUpperCase( source.getName() ) );

        return target;
    }
}

nullValueMappingStrategy

如果 source 为 null 时,对应的 target 的处理策略,默认是 NullValueMappingStrategy.RETURN_NULL,即 target 中对应的字段也设置为 null。

但有时候设置为 null 可能不符合我们的需求,比如 target 中有个 List ids,我们希望如果 source 中ids 为 null 时,target 的 ids 设置为空 list。这时候可以使用nullValueMappingStrategy策略中的NullValueMappingStrategy.RETURN_DEFAULT

nullValueMappingStrategy 可以使用在某个方法上(只对该方法生效),也可以使用在类上(对类中的所有方法都生效),如下:

@Component
public class MyMapperImpl implements MyMapper {

    @Override
    public Target toTarget(Source source) {
        if ( source == null ) {
            return null;
        }

        Target target = new Target();

        target.setName( source.getName() );
        List<Integer> list = source.getIds();
        if ( list != null ) {
            target.setIds( new ArrayList<Integer>( list ) );
        }
        else {
            target.setIds( null );
        }

        return target;
    }
}

指定NullValueMappingStrategy.RETURN_DEFAULT策略后:

@Mapper(componentModel = "spring",
        nullValueMappingStrategy = org.mapstruct.NullValueMappingStrategy.RETURN_DEFAULT)
public interface MyMapper {

    Target toTarget(Source source);
}

@Component
public class MyMapperImpl implements MyMapper {
    @Override
    public Target toTarget(Source source) {

        Target target = new Target();

        if ( source != null ) {
            target.setName( toUpperCase( source.getName() ) );
            List<Integer> list = source.getIds();
            if ( list != null ) {
                target.setIds( new ArrayList<Integer>( list ) );
            }
            else {
                target.setIds( new ArrayList<Integer>() );
            }
        }

        return target;
    }
}

可以看到,当 source 或者 source.ids 为 null 时,返回的 target 和 target.ids 都是默认的空值(空对象+空 list)。

Decorator

你可以通过创建一个 Decorator 类来对你的方法进行修饰并实现全局处理。

以下是一个例子:

public abstract class YourMapperDecorator implements YourMapper {

    private final YourMapper delegate;

    public YourMapperDecorator(YourMapper delegate) {
        this.delegate = delegate;
    }

    @Override
    public Target toTarget(Source source) {
        Target result = delegate.toTarget(source);
        if (result != null) {
            if (result.getField() == null) {
                result.setField("");
            }
            // 你可以在这里对其他字段进行同样的处理...
        }
        return result;
    }
}

然后你只需在你的 Mapper 接口上添加 @DecoratedWith 注解:

@Mapper
@DecoratedWith(YourMapperDecorator.class)
public interface YourMapper {
    Target toTarget(Source source);
}

这样,每次调用 toTarget 方法时,YourMapperDecorator 中的实现会被调用。在这里,你可以实现任何你想要的逻辑,例如对空字段赋予特定的默认值。

<think>嗯,用户想要了解C++的进阶教程,我得先理清楚他们可能已经掌握了哪些基础知识。通常来说,进阶教程适合那些已经了解C++基本语法、面向对象编程和STL的人。所以,我需要先确认用户的基础,但假设他们已经有这些知识了。 接下来,我得考虑C++进阶的核心内容。内存管理、模板、多线程、移动语义这些肯定是重点。内存管理方面,比如智能指针、RAII机制,这些是避免内存泄漏的关键。模板部分,包括模板元编程和SFINAE,可能需要详细解释,因为它们对于编写通用库很重要。 多线程编程在C++11之后变得更为重要,所以得涵盖线程、互斥锁、条件变量,还有async和future。移动语义和完美转发是C++11的新特性,能提升性能,这部分需要结合例子说明。 然后,标准库的深入使用,比如STL容器和算法的高效使用,还有C++17、20的新特性,比如概念、范围库、协程,这些也是进阶内容。设计模式方面,单例、工厂、观察者模式,这些在大型项目中常用,需要讲解如何用C++实现。 性能优化也是关键,包括内联、缓存优化、避免拷贝等技巧。可能用户对实际项目中的优化策略感兴趣,所以得提供一些实用建议。 还得考虑用户的学习路径,推荐一些经典书籍,比如《Effective C++》、《C++ Primer》第五版,以及在线资源如CppReference。实践项目建议也很重要,比如实现容器、解析器或小型服务器,帮助他们巩固知识。 用户可能没有明确说出来的需求,比如希望了解如何应对实际开发中的复杂问题,或者如何提升代码的性能和可维护性。因此,在回答中不仅要涵盖知识点,还要强调实践和资源推荐,帮助他们系统性地提升。 需要确保回答结构清晰,分点列出各个进阶主题,每个部分简明扼要,同时提供学习建议和资源,让用户有明确的学习方向。另外,避免使用过于专业的术语,或者在必要时给出解释,确保用户能够理解。</think>以下是一份针对具备C++基础的学习者的进阶学习指南,按核心知识点分层讲解: --- ### 一、C++进阶核心知识体系 #### 1. 内存管理深入 - **智能指针** - `unique_ptr`所有权模型:`std::unique_ptr<int> ptr = std::make_unique<int>(42);` - `shared_ptr`引用计数:`auto ptr = std::make_shared<MyClass>();` - `weak_ptr`解决循环引用问题 - **RAII原则**:通过构造函数获取资源,析构函数释放资源 - **自定义内存分配器**:实现`std::allocator`接口 #### 2. 模板与泛型编程 - **模板元编程** ```cpp template<int N> struct Factorial { static const int value = N * Factorial<N-1>::value; }; template<> struct Factorial<0> { static const int value = 1; }; ``` - **SFINAE**(替换失败非错误) ```cpp template<typename T> auto func(T t) -> decltype(t.serialize(), void()) { // 当T有serialize()方法时启用此重载 } ``` - **变参模板**:`template<typename... Args> void log(Args&&... args);` #### 3. 并发与多线程 - **线程管理** ```cpp std::thread t([](){ std::cout << "Running in thread" << std::endl; }); t.join(); ``` - **同步机制** - 互斥锁:`std::mutex`, `std::lock_guard` - 条件变量:`std::condition_variable` - **原子操作**:`std::atomic<int> counter{0};` - **异步编程**:`std::async`, `std::future` #### 4. 移动语义与完美转发 - **右值引用**:`T&&`标识可移动资源 - **移动构造函数** ```cpp class Vector { public: Vector(Vector&& other) noexcept : data_(other.data_), size_(other.size_) { other.data_ = nullptr; // 转移所有权 } }; ``` - **完美转发**:`template<typename T> void relay(T&& arg) { func(std::forward<T>(arg)); }` --- ### 二、标准库深入 1. **STL扩展使用** - 容器:`std::unordered_map`哈希冲突优化 - 算法:`std::transform`与lambda结合 ```cpp std::vector<int> vec{1,2,3}; std::transform(vec.begin(), vec.end(), vec.begin(), [](int x){ return x*2; }); ``` 2. **正则表达式**:`std::regex_match` 3. **文件系统**(C++17):`std::filesystem::path` --- ### 三、现代C++新特性 1. **C++11/14** - 类型推导:`auto`, `decltype` - Lambda表达式完整形式:`[capture](params) -> retType { ... }` 2. **C++17** - 结构化绑定:`auto [x,y] = std::make_pair(1,2);` - `std::optional`错误处理 3. **C++20** - 概念(Concepts):约束模板参数 - 协程:`co_await`异步编程 - 范围库:`std::ranges::sort` --- ### 四、高级编程技巧 1. **CRTP模式**(奇异递归模板模式) ```cpp template<typename T> class Base { public: void interface() { static_cast<T*>(this)->implementation(); } }; class Derived : public Base<Derived> { void implementation() { /*...*/ } }; ``` 2. **类型擦除**:通过`std::function`或自定义实现 3. **元编程库**:Boost.Hana(用于现代元编程) --- ### 五、性能优化策略 1. 内存对齐:`alignas(64) struct CacheLine { ... };` 2. 避免虚函数开销:使用CRTP替代动态多态 3. 分支预测优化:`[[likely]]`/`[[unlikely]]`属性(C++20) 4. SIMD指令:通过`#include <immintrin.h>`使用AVX指令集 --- ### 六、推荐学习路径 1. **经典书籍** - 《Effective Modern C++》(Scott Meyers) - 《C++ Concurrency in Action》(Anthony Williams) 2. **在线资源** - CppReference(权威文档) - LearnCpp(基础到进阶教程) 3. **实践项目** - 实现简化版STL容器 - 开发多线程网络服务器 - 编写模板元编程实现的数学库 --- 通过系统性学习以上内容,可逐步掌握C++在系统编程、高性能计算等领域的核心应用能力。建议通过实际项目结合理论学习,特别注意编译器的具体实现差异。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值