refactor(model): 重构模型类包结构并优化序列化处理
重构模型类包结构,将模型类按功能模块划分到不同的子包中。优化序列化处理,为VO类添加serialVersionUID并实现Serializable接口。移除部分冗余的serialVersionUID字段,简化模型类代码。同时修复UITools中空值处理的问题,并更新pom版本至0.0.100-SNAPSHOT。 - 将模型类按功能模块划分到ds子包中 - 为VO类添加序列化支持 - 移除冗余的serialVersionUID字段 - 修复UITools空值处理问题 - 更新项目版本号
This commit is contained in:
@@ -0,0 +1,213 @@
|
||||
# WebSocket服务组件分析报告
|
||||
|
||||
## 1. 概述
|
||||
|
||||
本报告对Contract-Manager项目中的WebSocket服务组件进行详细分析,旨在识别泛型类型处理的潜在问题,并提供优化建议。根据任务T10的要求,分析重点包括WebSocket相关组件代码、接口交互方法、泛型关系影响部分,并基于分析结果提出修改方案。
|
||||
|
||||
## 2. WebSocket服务组件架构
|
||||
|
||||
### 2.1 核心组件
|
||||
|
||||
项目中的WebSocket服务主要由以下核心组件组成:
|
||||
|
||||
1. **WebSocketServerHandler**:负责WebSocket会话管理,处理客户端连接、消息接收和断开连接
|
||||
2. **WebSocketServerCallbackManager**:负责处理客户端消息并调用相应的Service方法
|
||||
3. **WebSocketServerTaskManager**:负责任务调度和处理
|
||||
4. **WebSocketService**:提供向客户端发送WebSocket消息的功能
|
||||
|
||||
### 2.2 组件交互流程图
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Client as 客户端
|
||||
participant Handler as WebSocketServerHandler
|
||||
participant CallbackManager as WebSocketServerCallbackManager
|
||||
participant TaskManager as WebSocketServerTaskManager
|
||||
participant Service as Service实现类
|
||||
participant Repository as 数据仓库
|
||||
|
||||
Client->>Handler: 建立WebSocket连接
|
||||
Handler->>Handler: 管理活跃会话
|
||||
Client->>Handler: 发送JSON消息
|
||||
Handler->>Handler: 解析JSON消息
|
||||
alt 消息包含messageId
|
||||
Handler->>CallbackManager: onMessage()
|
||||
CallbackManager->>Service: 调用相应Service方法
|
||||
Service->>Repository: 访问数据
|
||||
Repository-->>Service: 返回数据
|
||||
Service-->>CallbackManager: 返回处理结果
|
||||
CallbackManager-->>Handler: 返回结果
|
||||
else 消息包含sessionId
|
||||
Handler->>TaskManager: onMessage()
|
||||
TaskManager->>TaskManager: 处理任务
|
||||
TaskManager-->>Handler: 返回任务状态
|
||||
end
|
||||
Handler-->>Client: 发送响应
|
||||
```
|
||||
|
||||
## 3. 关键接口和类分析
|
||||
|
||||
### 3.1 IEntityService接口
|
||||
|
||||
```java
|
||||
public interface IEntityService<T> {
|
||||
T getById(Integer id);
|
||||
Page<T> findAll(Specification<T> spec, Pageable pageable);
|
||||
Specification<T> getSpecification(String searchText);
|
||||
default List<T> search(String searchText);
|
||||
void delete(T entity);
|
||||
T save(T entity);
|
||||
}
|
||||
```
|
||||
- **泛型参数T**: 代表实体类类型(Model)
|
||||
- **主要职责**: 提供对实体类的基本CRUD操作
|
||||
- **特别说明**: `getById`方法不能使用@Cacheable注解(如果实体类有关联实体)
|
||||
|
||||
### 3.2 QueryService接口
|
||||
|
||||
```java
|
||||
public interface QueryService<Vo> {
|
||||
Vo findById(Integer id);
|
||||
Page<Vo> findAll(JsonNode paramsNode, Pageable pageable);
|
||||
default long count(JsonNode paramsNode);
|
||||
}
|
||||
```
|
||||
- **泛型参数Vo**: 代表视图对象类型
|
||||
- **主要职责**: 提供基于JSON参数的查询功能,返回VO对象
|
||||
- **特点**: 专为WebSocket通信设计的查询接口
|
||||
|
||||
### 3.3 WebSocketServerCallbackManager类
|
||||
|
||||
**主要功能**: 处理来自WebSocket客户端的请求,并根据请求调用相应的Service方法。
|
||||
|
||||
**关键方法分析**:
|
||||
|
||||
#### createNewEntity方法
|
||||
|
||||
```java
|
||||
private <T> T createNewEntity(IEntityService<T> entityService) {
|
||||
try {
|
||||
// 通过分析接口的泛型参数来获取实体类型
|
||||
Class<?> serviceClass = entityService.getClass();
|
||||
|
||||
// 1. 直接检查接口
|
||||
Class<T> entityClass = findEntityTypeInInterfaces(serviceClass);
|
||||
if (entityClass != null) {
|
||||
return entityClass.getDeclaredConstructor().newInstance();
|
||||
}
|
||||
|
||||
// 2. 处理Spring代理类 - 获取原始类
|
||||
Class<?> targetClass = getTargetClass(serviceClass);
|
||||
if (targetClass != serviceClass) {
|
||||
entityClass = findEntityTypeInInterfaces(targetClass);
|
||||
if (entityClass != null) {
|
||||
return entityClass.getDeclaredConstructor().newInstance();
|
||||
}
|
||||
}
|
||||
|
||||
// 3. 尝试查找父类
|
||||
entityClass = findEntityTypeInSuperclass(serviceClass);
|
||||
if (entityClass != null) {
|
||||
return entityClass.getDeclaredConstructor().newInstance();
|
||||
}
|
||||
|
||||
// 4. 如果上述方法都失败,尝试从参数类型推断
|
||||
entityClass = findEntityTypeFromMethodParameters(serviceClass);
|
||||
if (entityClass != null) {
|
||||
return entityClass.getDeclaredConstructor().newInstance();
|
||||
}
|
||||
|
||||
// 如果所有方法都失败,抛出更具描述性的异常
|
||||
throw new UnsupportedOperationException("无法确定实体类型,请检查服务实现: " + serviceClass.getName());
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("无法创建Entity实例: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
该方法负责创建新的实体对象实例,但在处理泛型参数时存在一些潜在问题:
|
||||
1. 没有缓存已解析的实体类型,每次调用都需要重新解析
|
||||
2. 类型推断逻辑较为复杂,容易出错
|
||||
3. 缺乏类型安全检查
|
||||
|
||||
#### findEntityTypeInInterfaces方法
|
||||
|
||||
```java
|
||||
@SuppressWarnings("unchecked")
|
||||
private <T> Class<T> findEntityTypeInInterfaces(Class<?> serviceClass) {
|
||||
Type[] interfaces = serviceClass.getGenericInterfaces();
|
||||
|
||||
for (Type iface : interfaces) {
|
||||
if (iface instanceof ParameterizedType paramType) {
|
||||
if (IEntityService.class.isAssignableFrom((Class<?>) paramType.getRawType())) {
|
||||
// 获取IEntityService的泛型参数类型
|
||||
Type entityType = paramType.getActualTypeArguments()[0];
|
||||
if (entityType instanceof Class<?>) {
|
||||
return (Class<T>) entityType;
|
||||
} else if (entityType instanceof ParameterizedType) {
|
||||
// 处理参数化类型
|
||||
Type rawType = ((ParameterizedType) entityType).getRawType();
|
||||
if (rawType instanceof Class<?>) {
|
||||
return (Class<T>) rawType;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
```
|
||||
|
||||
### 3.4 Service实现类结构
|
||||
|
||||
以ContractService为例,Service实现类通常同时实现三个接口:
|
||||
|
||||
```java
|
||||
@Service
|
||||
@CacheConfig(cacheNames = "contract")
|
||||
public class ContractService extends EntityService<Contract, ContractVo, Integer>
|
||||
implements IEntityService<Contract>, QueryService<ContractVo>, VoableService<Contract, ContractVo> {
|
||||
// 实现细节...
|
||||
}
|
||||
```
|
||||
|
||||
## 4. 泛型关系影响分析
|
||||
|
||||
### 4.1 类型转换机制
|
||||
|
||||
目前系统的类型转换主要通过以下几种方式实现:
|
||||
|
||||
1. **实体类转VO对象**:实体类实现Voable接口,提供toVo方法
|
||||
2. **VO对象转实体类**:通过VoableService接口的updateByVo方法实现
|
||||
3. **JSON与对象转换**:通过ObjectMapper实现JSON与Java对象的相互转换
|
||||
|
||||
### 4.2 泛型处理中的潜在问题
|
||||
|
||||
1. **类型安全隐患**:在WebSocketServerCallbackManager中存在多处类型转换和泛型擦除相关的代码,可能导致运行时类型错误
|
||||
2. **性能问题**:createNewEntity方法每次调用都需要重新解析泛型参数,没有缓存机制
|
||||
3. **错误处理不完善**:在类型解析失败时,提供的错误信息不够详细,难以定位问题
|
||||
4. **代码复杂性高**:类型解析逻辑较为复杂,增加了代码维护难度
|
||||
|
||||
## 5. 关键问题清单
|
||||
|
||||
| 问题编号 | 问题描述 | 影响范围 | 风险程度 |
|
||||
|---------|---------|---------|---------|
|
||||
| 1 | WebSocketServerCallbackManager中createNewEntity方法缺乏类型缓存机制 | 所有通过WebSocket创建实体的操作 | 中 |
|
||||
| 2 | 类型解析逻辑复杂,容易出错 | WebSocket服务组件 | 高 |
|
||||
| 3 | 缺乏类型安全检查,可能导致运行时异常 | 所有WebSocket服务调用 | 高 |
|
||||
| 4 | 错误处理不完善,异常信息不够详细 | WebSocket服务调试和问题排查 | 中 |
|
||||
| 5 | 对Spring代理类的处理不够健壮 | 使用Spring AOP的Service类 | 中 |
|
||||
|
||||
## 6. 结论与建议
|
||||
|
||||
基于以上分析,WebSocket服务组件在泛型类型处理方面存在一些需要优化的地方。特别是WebSocketServerCallbackManager类中的类型解析和创建逻辑需要改进,以提高系统的稳定性、性能和可维护性。
|
||||
|
||||
建议在后续的修改中重点关注以下几个方面:
|
||||
|
||||
1. 实现类型缓存机制,避免重复解析泛型参数
|
||||
2. 优化类型解析逻辑,提高代码可读性和健壮性
|
||||
3. 增加类型安全检查,防止运行时类型错误
|
||||
4. 完善错误处理机制,提供更详细的异常信息
|
||||
5. 增强对Spring代理类的处理能力
|
||||
|
||||
这些优化将有助于提高WebSocket服务组件的性能和稳定性,减少潜在的类型转换错误。
|
||||
@@ -0,0 +1,285 @@
|
||||
# WebSocket服务组件修改方案
|
||||
|
||||
## 1. 概述
|
||||
|
||||
基于对WebSocket服务组件的分析,本方案提出对WebSocketServerCallbackManager类进行优化,重点改进泛型类型处理逻辑、增强类型安全检查、实现类型缓存机制,以提高系统的性能、稳定性和可维护性。
|
||||
|
||||
## 2. 修改目标
|
||||
|
||||
1. 实现类型缓存机制,避免重复解析泛型参数
|
||||
2. 优化类型解析逻辑,提高代码可读性和健壮性
|
||||
3. 增加类型安全检查,防止运行时类型错误
|
||||
4. 完善错误处理机制,提供更详细的异常信息
|
||||
5. 增强对Spring代理类的处理能力
|
||||
|
||||
## 3. 详细修改方案
|
||||
|
||||
### 3.1 优化WebSocketServerCallbackManager类
|
||||
|
||||
#### 3.1.1 实现类型缓存机制
|
||||
|
||||
在WebSocketServerCallbackManager类中添加一个静态缓存Map,用于存储已解析的实体类型:
|
||||
|
||||
```java
|
||||
// 添加类型缓存,避免重复解析泛型参数
|
||||
private static final Map<Class<?>, Class<?>> ENTITY_TYPE_CACHE = new ConcurrentHashMap<>();
|
||||
|
||||
/**
|
||||
* 从缓存获取实体类型
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private <T> Class<T> getEntityTypeFromCache(Class<?> serviceClass) {
|
||||
return (Class<T>) ENTITY_TYPE_CACHE.get(serviceClass);
|
||||
}
|
||||
|
||||
/**
|
||||
* 缓存实体类型
|
||||
*/
|
||||
private <T> void cacheEntityType(Class<?> serviceClass, Class<T> entityClass) {
|
||||
ENTITY_TYPE_CACHE.put(serviceClass, entityClass);
|
||||
}
|
||||
```
|
||||
|
||||
#### 3.1.2 优化createNewEntity方法
|
||||
|
||||
修改createNewEntity方法,使用缓存机制并优化类型解析逻辑:
|
||||
|
||||
```java
|
||||
private <T> T createNewEntity(IEntityService<T> entityService) {
|
||||
try {
|
||||
// 获取服务类
|
||||
Class<?> serviceClass = entityService.getClass();
|
||||
|
||||
// 1. 尝试从缓存获取实体类型
|
||||
Class<T> entityClass = getEntityTypeFromCache(serviceClass);
|
||||
if (entityClass != null) {
|
||||
return createInstance(entityClass);
|
||||
}
|
||||
|
||||
// 2. 直接检查接口
|
||||
entityClass = findEntityTypeInInterfaces(serviceClass);
|
||||
if (entityClass != null) {
|
||||
cacheEntityType(serviceClass, entityClass);
|
||||
return createInstance(entityClass);
|
||||
}
|
||||
|
||||
// 3. 处理Spring代理类 - 获取原始类
|
||||
Class<?> targetClass = getTargetClass(serviceClass);
|
||||
if (targetClass != serviceClass) {
|
||||
entityClass = findEntityTypeInInterfaces(targetClass);
|
||||
if (entityClass != null) {
|
||||
cacheEntityType(serviceClass, entityClass);
|
||||
return createInstance(entityClass);
|
||||
}
|
||||
}
|
||||
|
||||
// 4. 尝试查找父类
|
||||
entityClass = findEntityTypeInSuperclass(serviceClass);
|
||||
if (entityClass != null) {
|
||||
cacheEntityType(serviceClass, entityClass);
|
||||
return createInstance(entityClass);
|
||||
}
|
||||
|
||||
// 5. 如果上述方法都失败,尝试从参数类型推断
|
||||
entityClass = findEntityTypeFromMethodParameters(serviceClass);
|
||||
if (entityClass != null) {
|
||||
cacheEntityType(serviceClass, entityClass);
|
||||
return createInstance(entityClass);
|
||||
}
|
||||
|
||||
// 如果所有方法都失败,抛出更具描述性的异常
|
||||
throw new UnsupportedOperationException(
|
||||
String.format("无法确定实体类型,请检查服务实现: %s, 实现的接口: %s",
|
||||
serviceClass.getName(),
|
||||
Arrays.toString(serviceClass.getInterfaces())));
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("无法创建Entity实例: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建实例的辅助方法
|
||||
*/
|
||||
private <T> T createInstance(Class<T> entityClass) {
|
||||
try {
|
||||
return entityClass.getDeclaredConstructor().newInstance();
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(
|
||||
String.format("无法创建%s的实例,请确保该类有公共无参构造函数", entityClass.getName()), e);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 3.1.3 增强类型安全检查
|
||||
|
||||
在各个方法中增加类型安全检查,防止类型转换错误:
|
||||
|
||||
```java
|
||||
private Object invokerSaveMethod(Object service, JsonNode argumentsNode) throws JsonMappingException {
|
||||
JsonNode paramsNode = argumentsNode.get(0);
|
||||
if (service instanceof IEntityService<?> entityService) {
|
||||
Object entity = null;
|
||||
if (paramsNode.has("id") && !paramsNode.get("id").isNull()) {
|
||||
int id = paramsNode.get("id").asInt();
|
||||
entity = entityService.getById(id);
|
||||
if (entity == null) {
|
||||
throw new NoSuchElementException("未找到实体: #" + id);
|
||||
}
|
||||
} else {
|
||||
entity = createNewEntity(entityService);
|
||||
}
|
||||
|
||||
if (service instanceof VoableService<?, ?>) {
|
||||
String typeClz = argumentsNode.get(1).asText();
|
||||
Class<?> type = null;
|
||||
try {
|
||||
type = Class.forName(typeClz);
|
||||
// 增加类型安全检查
|
||||
if (!VoableService.class.isAssignableFrom(service.getClass())) {
|
||||
throw new IllegalArgumentException(
|
||||
String.format("服务%s未实现VoableService接口", service.getClass().getName()));
|
||||
}
|
||||
} catch (ClassNotFoundException e) {
|
||||
throw new RuntimeException("无法加载类型: " + typeClz, e);
|
||||
}
|
||||
Object object = objectMapper.convertValue(paramsNode, type);
|
||||
((VoableService<Object, Object>) service).updateByVo(entity, object);
|
||||
} else {
|
||||
objectMapper.updateValue(entity, paramsNode);
|
||||
}
|
||||
return ((IEntityService<Object>) entityService).save(entity);
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException(
|
||||
String.format("服务%s未实现IEntityService接口", service.getClass().getName()));
|
||||
}
|
||||
```
|
||||
|
||||
#### 3.1.4 优化findAll方法的类型处理
|
||||
|
||||
```java
|
||||
private Object invokerFindAllMethod(Object service, JsonNode argumentsNode) throws JsonProcessingException {
|
||||
// 增加类型安全检查
|
||||
if (!(service instanceof QueryService<?>)) {
|
||||
throw new IllegalArgumentException(
|
||||
String.format("服务%s未实现QueryService接口", service.getClass().getName()));
|
||||
}
|
||||
|
||||
JsonNode paramsNode = argumentsNode.get(0);
|
||||
JsonNode pageableNode = argumentsNode.get(1);
|
||||
|
||||
// 增加参数校验
|
||||
if (pageableNode == null) {
|
||||
throw new IllegalArgumentException("分页参数不能为空");
|
||||
}
|
||||
|
||||
PageArgument pageArgument = objectMapper.treeToValue(pageableNode, PageArgument.class);
|
||||
QueryService<?> queryService = (QueryService<?>) service;
|
||||
|
||||
try {
|
||||
Page<?> page = queryService.findAll(paramsNode, pageArgument.toPageable());
|
||||
return PageContent.of(page.map(entity -> {
|
||||
if (entity instanceof Voable<?>) {
|
||||
return ((Voable<?>) entity).toVo();
|
||||
}
|
||||
return entity;
|
||||
}));
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("查询数据失败: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 3.1.5 增强getTargetClass方法
|
||||
|
||||
增强对Spring代理类的处理能力:
|
||||
|
||||
```java
|
||||
/**
|
||||
* 获取被代理的原始类
|
||||
*/
|
||||
private Class<?> getTargetClass(Class<?> proxyClass) {
|
||||
// 处理CGLIB代理类
|
||||
if (proxyClass.getName().contains("$$SpringCGLIB$$")) {
|
||||
Class<?> superClass = proxyClass.getSuperclass();
|
||||
// 确保返回的不是Object类
|
||||
return (superClass != null && superClass != Object.class) ? superClass : proxyClass;
|
||||
}
|
||||
|
||||
// 处理JDK动态代理类
|
||||
if (Proxy.isProxyClass(proxyClass)) {
|
||||
InvocationHandler handler = Proxy.getInvocationHandler(proxyClass);
|
||||
// 这里可以根据实际情况进一步处理JDK代理类
|
||||
}
|
||||
|
||||
return proxyClass;
|
||||
}
|
||||
```
|
||||
|
||||
### 3.2 添加日志和监控
|
||||
|
||||
在关键方法中添加详细日志,方便问题排查和性能监控:
|
||||
|
||||
```java
|
||||
private Object handleAsMessageCallback(SessionInfo session, String messageId, JsonNode jsonNode) throws Exception {
|
||||
long startTime = System.currentTimeMillis();
|
||||
logger.debug("开始处理消息回调: {}, 服务: {}, 方法: {}",
|
||||
messageId,
|
||||
jsonNode.get(WebSocketConstant.SERVICE_FIELD_NAME),
|
||||
jsonNode.get(WebSocketConstant.METHOD_FIELD_NAME));
|
||||
|
||||
try {
|
||||
// 原有逻辑...
|
||||
Object result = null;
|
||||
// 处理逻辑...
|
||||
|
||||
logger.debug("处理消息回调完成: {}, 耗时: {}ms", messageId, (System.currentTimeMillis() - startTime));
|
||||
return result;
|
||||
} catch (Exception e) {
|
||||
logger.error("处理消息回调失败: {}, 耗时: {}ms", messageId, (System.currentTimeMillis() - startTime), e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 4. 测试用例设计
|
||||
|
||||
为了验证修改后的WebSocket服务组件的正确性和性能,建议设计以下测试用例:
|
||||
|
||||
### 4.1 功能测试
|
||||
|
||||
1. **基本CRUD操作测试**:测试通过WebSocket进行实体的创建、查询、更新和删除操作
|
||||
2. **分页查询测试**:测试不同分页参数下的查询功能
|
||||
3. **类型安全测试**:测试各种边缘情况下的类型处理是否正确
|
||||
|
||||
### 4.2 性能测试
|
||||
|
||||
1. **并发请求测试**:模拟多用户并发访问WebSocket服务
|
||||
2. **缓存效果测试**:验证类型缓存机制的性能提升效果
|
||||
|
||||
### 4.3 异常处理测试
|
||||
|
||||
1. **参数错误测试**:测试各种参数错误情况下的异常处理
|
||||
2. **类型转换错误测试**:测试类型转换失败情况下的异常处理
|
||||
|
||||
## 5. 实施计划
|
||||
|
||||
1. **准备阶段**:创建分析报告和修改方案文档
|
||||
2. **编码阶段**:按照修改方案实现代码优化
|
||||
3. **测试阶段**:编写并执行测试用例,验证修改效果
|
||||
4. **验证阶段**:生成验证报告,确认修改满足需求
|
||||
|
||||
## 6. 风险评估
|
||||
|
||||
1. **兼容性风险**:修改可能影响现有功能的兼容性
|
||||
- 应对措施:全面测试,确保与现有系统兼容
|
||||
|
||||
2. **性能风险**:缓存机制可能引入内存占用增加的风险
|
||||
- 应对措施:监控内存使用情况,必要时调整缓存策略
|
||||
|
||||
3. **实现风险**:优化逻辑可能引入新的bug
|
||||
- 应对措施:编写详细的测试用例,进行充分测试
|
||||
|
||||
## 7. 总结
|
||||
|
||||
本修改方案针对WebSocket服务组件中的泛型类型处理问题提出了全面的优化措施,包括实现类型缓存机制、优化类型解析逻辑、增强类型安全检查、完善错误处理机制等。这些优化将有助于提高系统的性能、稳定性和可维护性,为后续的功能扩展奠定良好基础。
|
||||
Reference in New Issue
Block a user