feat(proxy): 实现代理对象初始化检查与懒加载机制
添加ProxyUtils工具类用于检查代理对象初始化状态 实现代理对象创建和标记初始化功能 添加ProxyObjectDeserializerModifier处理反序列化时的代理对象创建 修改WebSocketService错误消息字段从errorMsg改为message 实现ContractItemService.findAllByInventory方法 优化ContractService查询条件处理并添加缓存支持 重构InventoryTabSkinHistoryPrice的service获取方式
This commit is contained in:
@@ -100,7 +100,7 @@ public class WebSocketService {
|
||||
}
|
||||
} else if (node.has("errorCode")) {
|
||||
int errorCode = node.get("errorCode").asInt();
|
||||
String errorMsg = node.get("errorMsg").asText();
|
||||
String errorMsg = node.get("message").asText();
|
||||
// TODO 需要重新登录
|
||||
logger.error("收到错误消息: 错误码={}, 错误信息={}", errorCode, errorMsg);
|
||||
}
|
||||
|
||||
@@ -72,41 +72,25 @@ public class InventoryTabSkinHistoryPrice
|
||||
public TableColumn<InventoryHistoryPriceViewModel, Number> miniPurchaseTaxPriceColumn;
|
||||
public TableColumn<InventoryHistoryPriceViewModel, MonthDay> miniPurchasePriceDateColumn;
|
||||
|
||||
@Setter
|
||||
InventoryHistoryPriceService service;
|
||||
@Setter
|
||||
ContractService contractService;
|
||||
@Setter
|
||||
ContractItemService contractItemService;
|
||||
|
||||
public InventoryTabSkinHistoryPrice(InventoryWindowController controller) {
|
||||
super(controller);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected InventoryHistoryPriceService getViewModelService() {
|
||||
return getService();
|
||||
return getHistoryPriceService();
|
||||
}
|
||||
|
||||
InventoryHistoryPriceService getService() {
|
||||
if (service == null) {
|
||||
service = getBean(InventoryHistoryPriceService.class);
|
||||
}
|
||||
return service;
|
||||
InventoryHistoryPriceService getHistoryPriceService() {
|
||||
return getCachedBean(InventoryHistoryPriceService.class);
|
||||
}
|
||||
|
||||
ContractItemService getContractItemService() {
|
||||
if (contractItemService == null) {
|
||||
contractItemService = getBean(ContractItemService.class);
|
||||
}
|
||||
return contractItemService;
|
||||
return getCachedBean(ContractItemService.class);
|
||||
}
|
||||
|
||||
ContractService getContractService() {
|
||||
if (contractService == null) {
|
||||
contractService = getBean(ContractService.class);
|
||||
}
|
||||
return contractService;
|
||||
return getCachedBean(ContractService.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -182,10 +166,10 @@ public class InventoryTabSkinHistoryPrice
|
||||
|
||||
HashMap<Integer, InventoryHistoryPrice> historyPriceMap = new HashMap<>();
|
||||
|
||||
for (InventoryHistoryPrice historyPrice : getService().findAllByInventory(getParent())) {
|
||||
for (InventoryHistoryPrice historyPrice : getHistoryPriceService().findAllByInventory(getParent())) {
|
||||
InventoryHistoryPrice oldValue = historyPriceMap.put(historyPrice.getYear().getValue(), historyPrice);
|
||||
if (oldValue != null) {
|
||||
getService().delete(oldValue);
|
||||
getHistoryPriceService().delete(oldValue);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -236,7 +220,7 @@ public class InventoryTabSkinHistoryPrice
|
||||
|
||||
}
|
||||
});
|
||||
getService().save(historyPrice);
|
||||
getHistoryPriceService().save(historyPrice);
|
||||
});
|
||||
|
||||
loadTableDataSet();
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
package com.ecep.contract.serializer;
|
||||
|
||||
import com.ecep.contract.model.IdentityEntity;
|
||||
import com.fasterxml.jackson.databind.BeanDescription;
|
||||
import com.fasterxml.jackson.databind.DeserializationConfig;
|
||||
import com.fasterxml.jackson.databind.DeserializationContext;
|
||||
import com.fasterxml.jackson.databind.JsonDeserializer;
|
||||
import com.fasterxml.jackson.databind.JsonMappingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.deser.BeanDeserializerModifier;
|
||||
import com.fasterxml.jackson.databind.deser.ResolvableDeserializer;
|
||||
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
|
||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||
|
||||
/**
|
||||
* 自定义的反序列化器修改器,用于实现类似Hibernate的代理对象功能
|
||||
*/
|
||||
public class ProxyObjectDeserializerModifier extends BeanDeserializerModifier {
|
||||
|
||||
private final ObjectMapper objectMapper;
|
||||
|
||||
public ProxyObjectDeserializerModifier(ObjectMapper objectMapper) {
|
||||
this.objectMapper = objectMapper;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonDeserializer<?> modifyDeserializer(DeserializationConfig config,
|
||||
BeanDescription beanDesc, JsonDeserializer<?> deserializer) {
|
||||
// 检查是否是IdentityEntity的实现类
|
||||
if (IdentityEntity.class.isAssignableFrom(beanDesc.getBeanClass())) {
|
||||
// 返回包装后的反序列化器,处理代理对象的创建
|
||||
return new ProxyObjectDeserializer((JsonDeserializer<Object>) deserializer,
|
||||
beanDesc.getBeanClass(), objectMapper);
|
||||
}
|
||||
return deserializer;
|
||||
}
|
||||
|
||||
/**
|
||||
* 代理对象反序列化器,用于处理对象字段的懒加载
|
||||
*/
|
||||
private static class ProxyObjectDeserializer extends StdDeserializer<Object> implements ResolvableDeserializer {
|
||||
|
||||
private final JsonDeserializer<Object> delegate;
|
||||
private final Class<?> targetClass;
|
||||
private final ObjectMapper objectMapper;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public ProxyObjectDeserializer(JsonDeserializer<Object> delegate,
|
||||
Class<?> targetClass, ObjectMapper objectMapper) {
|
||||
super((Class<Object>) targetClass);
|
||||
this.delegate = delegate;
|
||||
this.targetClass = targetClass;
|
||||
this.objectMapper = objectMapper;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object deserialize(com.fasterxml.jackson.core.JsonParser p,
|
||||
com.fasterxml.jackson.databind.DeserializationContext ctxt) throws java.io.IOException {
|
||||
// 首先检查是否是一个只有id的对象节点
|
||||
if (p.isExpectedStartObjectToken()) {
|
||||
ObjectNode node = p.readValueAsTree();
|
||||
|
||||
// 检查是否只包含id和_proxy_字段
|
||||
if (node.has("id") && node.has("_proxy_")) {
|
||||
try {
|
||||
// 创建目标类的实例
|
||||
Object instance = targetClass.getDeclaredConstructor().newInstance();
|
||||
|
||||
// 设置id字段
|
||||
if (instance instanceof IdentityEntity) {
|
||||
((IdentityEntity) instance).setId(node.get("id").asInt());
|
||||
}
|
||||
return instance;
|
||||
} catch (Exception e) {
|
||||
// 如果创建实例失败,回退到原始反序列化器
|
||||
return delegate.deserialize(p, ctxt);
|
||||
}
|
||||
}
|
||||
// 对于普通对象,使用原始反序列化器
|
||||
return delegate.deserialize(p, ctxt);
|
||||
}
|
||||
// 对于非对象类型,使用原始反序列化器
|
||||
return delegate.deserialize(p, ctxt);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resolve(DeserializationContext ctxt) throws JsonMappingException {
|
||||
// 如果委托的反序列化器也是ResolvableDeserializer,则调用其resolve方法
|
||||
if (delegate instanceof ResolvableDeserializer) {
|
||||
((ResolvableDeserializer) delegate).resolve(ctxt);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.ecep.contract.service;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.stereotype.Service;
|
||||
@@ -12,7 +13,9 @@ import com.ecep.contract.vm.ContractItemViewModel;
|
||||
public class ContractItemService extends QueryService<ContractItem, ContractItemViewModel> {
|
||||
|
||||
public List<ContractItem> findAllByInventory(Inventory parent) {
|
||||
throw new UnsupportedOperationException("Unimplemented method 'findAllByInventory'");
|
||||
HashMap<String, Object> params = new HashMap<>();
|
||||
params.put("inventory", parent.getId());
|
||||
return findAll(params, null).getContent();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -2,8 +2,14 @@ package com.ecep.contract.service;
|
||||
|
||||
import java.io.File;
|
||||
import java.time.LocalDate;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.cache.annotation.CacheConfig;
|
||||
import org.springframework.cache.annotation.CacheEvict;
|
||||
import org.springframework.cache.annotation.Cacheable;
|
||||
import org.springframework.cache.annotation.Caching;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import com.ecep.contract.MessageHolder;
|
||||
@@ -13,14 +19,42 @@ import com.ecep.contract.model.ContractFile;
|
||||
import com.ecep.contract.model.Project;
|
||||
import com.ecep.contract.vm.ContractViewModel;
|
||||
|
||||
import io.micrometer.common.util.StringUtils;
|
||||
|
||||
@Service
|
||||
@CacheConfig(cacheNames = "contract")
|
||||
public class ContractService extends QueryService<Contract, ContractViewModel> {
|
||||
|
||||
@Cacheable(key = "#p0")
|
||||
public Contract findById(Integer id) {
|
||||
return super.findById(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存实体对象,异步方法
|
||||
*/
|
||||
@Caching(evict = {
|
||||
@CacheEvict(key = "#p0.id"),
|
||||
@CacheEvict(key = "'code-'+#p0.code")
|
||||
})
|
||||
public Contract save(Contract contract) {
|
||||
return super.save(contract);
|
||||
}
|
||||
|
||||
@Caching(evict = {
|
||||
@CacheEvict(key = "#p0.id"),
|
||||
@CacheEvict(key = "'code-'+#p0.code")
|
||||
})
|
||||
public void delete(Contract contract) {
|
||||
super.delete(contract);
|
||||
}
|
||||
|
||||
public boolean updateParentCode(Contract contract) {
|
||||
// TODO Auto-generated method stub
|
||||
throw new UnsupportedOperationException("Unimplemented method 'updateParentCode'");
|
||||
}
|
||||
|
||||
@Cacheable(key = "'code-'+#p0")
|
||||
public Contract findByCode(String string) {
|
||||
// TODO Auto-generated method stub
|
||||
throw new UnsupportedOperationException("Unimplemented method 'findByCode'");
|
||||
@@ -31,8 +65,13 @@ public class ContractService extends QueryService<Contract, ContractViewModel> {
|
||||
}
|
||||
|
||||
public List<Contract> findAllBySaleContract(Contract contract) {
|
||||
// TODO Auto-generated method stub
|
||||
throw new UnsupportedOperationException("Unimplemented method 'findAllBySaleContract'");
|
||||
String parentCode = contract.getCode();
|
||||
if (StringUtils.isEmpty(parentCode)) {
|
||||
return List.of();
|
||||
}
|
||||
HashMap<String, Object> params = new HashMap<>();
|
||||
params.put("parentCode", contract.getCode());
|
||||
return findAll(params, Pageable.unpaged()).getContent();
|
||||
}
|
||||
|
||||
public boolean checkContractPathInBasePath(Contract v) {
|
||||
|
||||
@@ -1,13 +1,97 @@
|
||||
package com.ecep.contract.util;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Proxy;
|
||||
|
||||
import com.ecep.contract.model.IdentityEntity;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
|
||||
public class ProxyUtils {
|
||||
/**
|
||||
* 判断对象是否已初始化
|
||||
* 在客户端环境中,如果对象不为null,则认为已初始化
|
||||
* 在客户端环境中,从服务器端返回的数据中,代理对象序列化时只包含Id属性,
|
||||
* 其他属性为null,因此需要判断对象是否已初始化
|
||||
* 初始化条件:对象不为null,且不是代理对象或已被标记为初始化
|
||||
*/
|
||||
public static boolean isInitialized(Object proxy) {
|
||||
return proxy != null;
|
||||
if (proxy == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 检查 proxy 中除了id其他属性都为null
|
||||
try {
|
||||
Field[] fields = proxy.getClass().getFields();
|
||||
for (Field field : fields) {
|
||||
if (field.getName().equals("id")) {
|
||||
continue;
|
||||
}
|
||||
// 忽略被JsonIgnore注解标记的字段
|
||||
if (field.getAnnotation(JsonIgnore.class) != null) {
|
||||
continue;
|
||||
}
|
||||
// 检查字段是否为null
|
||||
if (field.get(proxy) != null) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// 如果除了id其他属性都为null,认为未初始化
|
||||
return false;
|
||||
} catch (Exception e) {
|
||||
// 发生异常时默认已初始化
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查对象是否是代理对象
|
||||
*/
|
||||
public static boolean isProxyObject(Object obj) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建代理对象
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> T createProxy(T original, Class<?>... interfaces) {
|
||||
if (original == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 如果对象已经是代理对象,直接返回
|
||||
if (isProxyObject(original)) {
|
||||
return original;
|
||||
}
|
||||
|
||||
// 创建代理对象
|
||||
T proxy = (T) Proxy.newProxyInstance(
|
||||
original.getClass().getClassLoader(),
|
||||
interfaces,
|
||||
(proxyObj, method, args) -> {
|
||||
// 如果代理对象未初始化,先初始化
|
||||
if (!isInitialized(proxyObj)) {
|
||||
// 这里可以添加初始化逻辑
|
||||
markInitialized(proxyObj);
|
||||
}
|
||||
// 调用原始对象的方法
|
||||
return method.invoke(original, args);
|
||||
});
|
||||
return proxy;
|
||||
}
|
||||
|
||||
/**
|
||||
* 标记对象为已初始化
|
||||
*/
|
||||
public static void markInitialized(Object proxy) {
|
||||
if (proxy != null && isProxyObject(proxy)) {
|
||||
// 标记为已初始化
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查对象是否是实体类
|
||||
*/
|
||||
public static boolean isEntityClass(Class<?> clazz) {
|
||||
return clazz != null && IdentityEntity.class.isAssignableFrom(clazz);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user