前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >充血模型的一个坑

充血模型的一个坑

作者头像
烟雨平生
发布2023-03-07 13:55:21
3240
发布2023-03-07 13:55:21
举报
文章被收录于专栏:数字化之路

“贫血对象模型”(Anemic Model)的实现风格,即:对象仅仅对简单的数据进行封装,而关联关系和业务计算都散落在对象的范围之外。这种方式实际上是在沿用过程式的风格组织逻辑,而没有发挥面向对象技术的优势。 与之相对的则是“充血模型”(Rich Domain Model),也就是与某个概念相关的主要行为与逻辑,都被封装到了对应的领域对象中。 “充血模型”也就是 DDD 中强调的“富含知识的模型"。

当Spring的@Value+充血模型的Bean,不小心踩了一个坑,分享一下,让后来人走的更顺一些:

代码语言:javascript
复制
package com.tree.thrive.adapter.controller;

import com.alibaba.fastjson.JSON;
import com.tree.thrive.business.input.domain.dto.InputCheckResultDTO;
import com.tree.thrive.business.input.domain.req.InputCheckReq;
import com.tree.thrive.business.input.service.InputCheckService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

/**
 * learning
 *
 * @Auther: cheng.tang
 * @Date: 2022/4/27 5:43 AM
 * @Description:
 */
@Tag(name = "用户信息校验")
@RestController
@Slf4j
public class InputCheckController {

    @Autowired
    private InputCheckService inputCheckService;

    @Operation(summary = "用户信息处理", description = "有个长度限制")
    @PostMapping("/users")
    public InputCheckResultDTO inputCheck(@RequestBody InputCheckReq req) {
        log.info("请求参数 {}", JSON.toJSONString(req));
        return inputCheckService.handle(req);
    }


}

代码语言:javascript
复制
package com.tree.thrive.business.input.domain.req;

import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

/**
 * learning
 *
 * @Auther: cheng.tang
 * @Date: 2022/4/27 5:48 AM
 * @Description:
 */
@Slf4j
@Schema(description = "输入的参数。note的长度不能大于10", title = "请求参数")
@Component
@Data
public class InputCheckReq {

    @Value("${length.limit:10}")
    @Schema(accessMode = Schema.AccessMode.READ_ONLY)
    private int lengthLimit;

    @Schema(title = "备注", description = "如果超过10字符,后面的不要的", example = "1234567890超过10们了")
    private String note;

    @Schema(title = "name", example = "一棵大树")
    private String name;

    /**
     * 处理note的长度限制
     */
    public void subStringNote() {
        if (StringUtils.isBlank(note)) {
            setNote("");
            return;
        }
        if (note.length() > lengthLimit) {
            String newNote = note.substring(0, lengthLimit);
            log.warn("长度超了 name {} old {} newNote {}", name, note, newNote);
            setNote(newNote);
        }
    }

}

代码语言:javascript
复制
package com.tree.thrive.business.input.service;

import com.alibaba.fastjson.JSON;
import com.tree.thrive.business.input.domain.dto.InputCheckResultDTO;
import com.tree.thrive.business.input.domain.req.InputCheckReq;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;

/**
 * learning
 *
 * @Auther: cheng.tang
 * @Date: 2022/4/27 6:10 AM
 * @Description:
 */
@Service
@Slf4j
public class InputCheckServiceImpl implements InputCheckService {


    @Override
    public InputCheckResultDTO handle(InputCheckReq req) {
        req.subStringNote(lengthLimit);
        log.info("result {} ", JSON.toJSONString(req));
        InputCheckResultDTO resultDTO = new InputCheckResultDTO();
        BeanUtils.copyProperties(req, resultDTO);
        return resultDTO;
    }

}


代码语言:javascript
复制
package com.tree.thrive.business.input.domain.dto;

import com.tree.thrive.business.input.domain.req.InputCheckReq;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;

/**
 * top-tree-out-of-window
 *
 * @Auther: cheng.tang
 * @Date: 2022/4/27 7:30 AM
 * @Description:
 */
@Data
@EqualsAndHashCode(callSuper = true)
@Schema(title = "处理结果")
public class InputCheckResultDTO extends InputCheckReq {

}

按正常的业务case: 输入参数:

代码语言:javascript
复制
{
  "note": "1234567890超过10们了",
  "name": "一棵大树"
}

期望的处理结果:

代码语言:javascript
复制
{
  "note": "1234567890",
  "name": "一棵大树"
}

实际的处理结果:

代码语言:javascript
复制
{
  "lengthLimit": 0,
  "note": "",
  "name": "一棵大树"
}

处理后,note的值被全部清空了

原因分析: 发现是下面这行代码有问题: com.tree.thrive.business.input.domain.req.InputCheckReq.subStringNote 上图

代码语言:javascript
复制
if (note.length() > lengthLimit) {

lengthLimit的值是0.并不是期望的值10。

InputCheckReq是请求参数,每次请求都会new一个,并不会使用到Spring容器中的那个单例InputCheckReq中的lengthLimit值 由于请求时lengthLimit参数没有传,那么就使用了基本类型的默认值0了。

解决办法:

代码语言:javascript
复制
@Service
@Slf4j
public class InputCheckServiceImpl implements InputCheckService {


    @Value("${length.limit:10}")
    private int lengthLimit;

    @Override
    public InputCheckResultDTO handle(InputCheckReq req) {
        req.subStringNote(lengthLimit);
        log.info("result {} ", JSON.toJSONString(req));
        InputCheckResultDTO resultDTO = new InputCheckResultDTO();
        BeanUtils.copyProperties(req, resultDTO);
        return resultDTO;
    }

}
代码语言:javascript
复制
    /**
     * 处理note的长度限制
     * @param lengthLimit
     */
    public void subStringNote(int lengthLimit) {
        if (StringUtils.isBlank(note)) {
            setNote("");
            return;
        }
        if (note.length() > lengthLimit) {
            String newNote = note.substring(0, lengthLimit);
            log.warn("长度超了 name {} old {} newNote {}", name, note, newNote);
            setNote(newNote);
        }
    }

问题解决。

小结:作用域问题。

  • Spring @Value的作用域是放在Spring容器中的单例;
  • Rest接口请求参数的包装类,每次都会新建。Spring无法将@Value的值注入到这个没有纳入Spring管理的新Bean。

DDD虽好,可不要贪杯哦

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022-04-27,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 的数字化之路 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档