“贫血对象模型”(Anemic Model)的实现风格,即:对象仅仅对简单的数据进行封装,而关联关系和业务计算都散落在对象的范围之外。这种方式实际上是在沿用过程式的风格组织逻辑,而没有发挥面向对象技术的优势。 与之相对的则是“充血模型”(Rich Domain Model),也就是与某个概念相关的主要行为与逻辑,都被封装到了对应的领域对象中。 “充血模型”也就是 DDD 中强调的“富含知识的模型"。
当Spring的@Value+充血模型的Bean,不小心踩了一个坑,分享一下,让后来人走的更顺一些:
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);
}
}
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);
}
}
}
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;
}
}
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: 输入参数:
{
"note": "1234567890超过10们了",
"name": "一棵大树"
}
期望的处理结果:
{
"note": "1234567890",
"name": "一棵大树"
}
实际的处理结果:
{
"lengthLimit": 0,
"note": "",
"name": "一棵大树"
}
处理后,note的值被全部清空了
原因分析: 发现是下面这行代码有问题: com.tree.thrive.business.input.domain.req.InputCheckReq.subStringNote 上图
if (note.length() > lengthLimit) {
lengthLimit的值是0.并不是期望的值10。
InputCheckReq是请求参数,每次请求都会new一个,并不会使用到Spring容器中的那个单例InputCheckReq中的lengthLimit值 由于请求时lengthLimit参数没有传,那么就使用了基本类型的默认值0了。
解决办法:
@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;
}
}
/**
* 处理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);
}
}
问题解决。
小结:作用域问题。
DDD虽好,可不要贪杯哦