change: 日常小调整.
This commit is contained in:
@@ -18,13 +18,13 @@ public final class ManagerLite {
|
|||||||
init();
|
init();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static SystemInitializer init() {
|
private static void init() {
|
||||||
SystemInitializer systemInitializer = SystemInitializer
|
SystemInitializer systemInitializer = SystemInitializer
|
||||||
.configurer()
|
.configurer()
|
||||||
|
.clearCache(false)
|
||||||
.configuration(new PropertiesConfiguration(SystemConstants.USER_SETTINGS_PROPERTIES_PATH))
|
.configuration(new PropertiesConfiguration(SystemConstants.USER_SETTINGS_PROPERTIES_PATH))
|
||||||
.instanceContext(new FileInstanceContext())
|
.instanceContext(new FileInstanceContext())
|
||||||
.done();
|
.done();
|
||||||
systemInitializer.refresh();
|
systemInitializer.refresh();
|
||||||
return systemInitializer;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
package com.serliunx.ddns.support.feign.client;
|
package com.serliunx.ddns.client;
|
||||||
|
|
||||||
|
import com.serliunx.ddns.client.entity.IPAddressResponse;
|
||||||
import com.serliunx.ddns.support.feign.JacksonDecoder;
|
import com.serliunx.ddns.support.feign.JacksonDecoder;
|
||||||
import com.serliunx.ddns.support.feign.JacksonEncoder;
|
import com.serliunx.ddns.support.feign.JacksonEncoder;
|
||||||
import com.serliunx.ddns.support.feign.client.entity.IPAddressResponse;
|
|
||||||
import feign.Feign;
|
import feign.Feign;
|
||||||
import feign.Logger;
|
import feign.Logger;
|
||||||
import feign.RequestLine;
|
import feign.RequestLine;
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package com.serliunx.ddns.support.feign.client.entity;
|
package com.serliunx.ddns.client.entity;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* IP地址查询响应
|
* IP地址查询响应
|
||||||
@@ -14,15 +14,15 @@ import java.util.Map;
|
|||||||
* @since 2024/5/15
|
* @since 2024/5/15
|
||||||
*/
|
*/
|
||||||
public final class InstanceClasses {
|
public final class InstanceClasses {
|
||||||
|
|
||||||
private InstanceClasses(){throw new UnsupportedOperationException();}
|
private InstanceClasses(){throw new UnsupportedOperationException();}
|
||||||
|
|
||||||
private static final Map<InstanceType, Class<? extends Instance>> instanceTypeMap =
|
private static final Map<InstanceType, Class<? extends Instance>> instanceTypeMap = new HashMap<>();
|
||||||
new HashMap<InstanceType, Class<? extends Instance>>() {
|
|
||||||
{
|
static {
|
||||||
put(InstanceType.ALI_YUN, AliyunInstance.class);
|
instanceTypeMap.put(InstanceType.ALI_YUN, AliyunInstance.class);
|
||||||
put(InstanceType.TENCENT_CLOUD, TencentInstance.class);
|
instanceTypeMap.put(InstanceType.TENCENT_CLOUD, TencentInstance.class);
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
public static Class<? extends Instance> match(InstanceType type) {
|
public static Class<? extends Instance> match(InstanceType type) {
|
||||||
return instanceTypeMap.get(type);
|
return instanceTypeMap.get(type);
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ import static com.serliunx.ddns.util.InstanceUtils.validateInstance;
|
|||||||
*/
|
*/
|
||||||
public abstract class AbstractInstanceContext implements InstanceContext, MultipleSourceInstanceContext {
|
public abstract class AbstractInstanceContext implements InstanceContext, MultipleSourceInstanceContext {
|
||||||
|
|
||||||
private static final Logger log = LoggerFactory.getLogger(AbstractInstanceContext.class);
|
private final Logger log = LoggerFactory.getLogger(this.getClass());
|
||||||
|
|
||||||
private final Set<ListableInstanceFactory> listableInstanceFactories = new HashSet<>();
|
private final Set<ListableInstanceFactory> listableInstanceFactories = new HashSet<>();
|
||||||
|
|
||||||
@@ -63,9 +63,7 @@ public abstract class AbstractInstanceContext implements InstanceContext, Multip
|
|||||||
Set<Instance> instances = new HashSet<>();
|
Set<Instance> instances = new HashSet<>();
|
||||||
|
|
||||||
// 高优先级的实例工厂会覆盖低优先级实例工厂所加载的实例
|
// 高优先级的实例工厂会覆盖低优先级实例工厂所加载的实例
|
||||||
listableInstanceFactories.stream()
|
getSortedListableInstanceFactories().forEach(f -> instances.addAll(f.getInstances()));
|
||||||
.sorted()
|
|
||||||
.forEach(f -> instances.addAll(f.getInstances()));
|
|
||||||
|
|
||||||
// 初次载入
|
// 初次载入
|
||||||
cacheInstanceMap = new HashMap<>(instances.stream().collect(Collectors.toMap(Instance::getName, i -> i)));
|
cacheInstanceMap = new HashMap<>(instances.stream().collect(Collectors.toMap(Instance::getName, i -> i)));
|
||||||
@@ -86,10 +84,11 @@ public abstract class AbstractInstanceContext implements InstanceContext, Multip
|
|||||||
instanceLock.lock();
|
instanceLock.lock();
|
||||||
String name = instance.getName();
|
String name = instance.getName();
|
||||||
Instance instanceExists = instanceMap.get(name);
|
Instance instanceExists = instanceMap.get(name);
|
||||||
if (instanceExists != null)
|
if (instanceExists != null) {
|
||||||
throw new InstanceExistsException("该实例已存在!", name, instanceExists);
|
throw new InstanceExistsException("该实例已存在!", name, instanceExists);
|
||||||
else
|
} else {
|
||||||
instanceMap.put(name, instance);
|
instanceMap.put(name, instance);
|
||||||
|
}
|
||||||
}catch (Exception e){
|
}catch (Exception e){
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}finally {
|
}finally {
|
||||||
@@ -146,7 +145,7 @@ public abstract class AbstractInstanceContext implements InstanceContext, Multip
|
|||||||
protected void clearCache() {
|
protected void clearCache() {
|
||||||
if (cacheInstanceMap != null
|
if (cacheInstanceMap != null
|
||||||
&& !cacheInstanceMap.isEmpty()){
|
&& !cacheInstanceMap.isEmpty()){
|
||||||
int size = cacheInstanceMap.size();
|
final int size = cacheInstanceMap.size();
|
||||||
cacheInstanceMap.clear();
|
cacheInstanceMap.clear();
|
||||||
// 清理实例工厂的缓存信息
|
// 清理实例工厂的缓存信息
|
||||||
listableInstanceFactories.forEach(Clearable::clear);
|
listableInstanceFactories.forEach(Clearable::clear);
|
||||||
|
|||||||
@@ -3,7 +3,10 @@ package com.serliunx.ddns.core.context;
|
|||||||
import com.serliunx.ddns.core.factory.InstanceFactory;
|
import com.serliunx.ddns.core.factory.InstanceFactory;
|
||||||
import com.serliunx.ddns.core.factory.ListableInstanceFactory;
|
import com.serliunx.ddns.core.factory.ListableInstanceFactory;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 多数据源的实例容器, 将多种实例来源汇聚到一起
|
* 多数据源的实例容器, 将多种实例来源汇聚到一起
|
||||||
@@ -26,4 +29,18 @@ public interface MultipleSourceInstanceContext extends InstanceContext, Listable
|
|||||||
* @return 实例工厂列表
|
* @return 实例工厂列表
|
||||||
*/
|
*/
|
||||||
Set<ListableInstanceFactory> getListableInstanceFactories();
|
Set<ListableInstanceFactory> getListableInstanceFactories();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取当前实例容器下所有实例工厂(已排序)
|
||||||
|
* @return 已排序的实例工厂
|
||||||
|
*/
|
||||||
|
default List<ListableInstanceFactory> getSortedListableInstanceFactories() {
|
||||||
|
Set<ListableInstanceFactory> listableInstanceFactories = getListableInstanceFactories();
|
||||||
|
if (listableInstanceFactories == null || listableInstanceFactories.isEmpty())
|
||||||
|
return Collections.emptyList();
|
||||||
|
|
||||||
|
return listableInstanceFactories.stream()
|
||||||
|
.sorted()
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -74,6 +74,11 @@ public abstract class AbstractInstanceFactory implements InstanceFactory, Listab
|
|||||||
clear0();
|
clear0();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return getClass().getSimpleName() + "(priority: " + getPriority() + ")";
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 交由子类去加载实例信息
|
* 交由子类去加载实例信息
|
||||||
* @return 实例信息
|
* @return 实例信息
|
||||||
|
|||||||
@@ -26,6 +26,16 @@ public abstract class FileInstanceFactory extends AbstractInstanceFactory {
|
|||||||
this.instanceDir = instanceDir;
|
this.instanceDir = instanceDir;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getPriority() {
|
||||||
|
return 256;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return getClass().getSimpleName() + "(instanceDir: " + instanceDir + ", priority: " + getPriority() + ")";
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Set<Instance> load() {
|
protected Set<Instance> load() {
|
||||||
Set<File> files = loadFiles();
|
Set<File> files = loadFiles();
|
||||||
@@ -38,11 +48,6 @@ public abstract class FileInstanceFactory extends AbstractInstanceFactory {
|
|||||||
return Collections.emptySet();
|
return Collections.emptySet();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getPriority() {
|
|
||||||
return 256;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 交由具体的子类去加载实例, 比如: json格式的实例信息、xml格式的实例信息
|
* 交由具体的子类去加载实例, 比如: json格式的实例信息、xml格式的实例信息
|
||||||
* @param file 文件信息
|
* @param file 文件信息
|
||||||
|
|||||||
@@ -96,13 +96,15 @@ public class YamlFileInstanceFactory extends FileInstanceFactory {
|
|||||||
f.set(instance, Enum.valueOf((Class<? extends Enum>) clazz, (String) value));
|
f.set(instance, Enum.valueOf((Class<? extends Enum>) clazz, (String) value));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (value != null)
|
if (value != null) {
|
||||||
f.set(instance, value);
|
f.set(instance, value);
|
||||||
|
}
|
||||||
} catch (IllegalAccessException e) {
|
} catch (IllegalAccessException e) {
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}
|
} finally {
|
||||||
f.setAccessible(false);
|
f.setAccessible(false);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ import static com.serliunx.ddns.constant.SystemConstants.XML_ROOT_INSTANCE_NAME;
|
|||||||
@JacksonXmlRootElement(localName = XML_ROOT_INSTANCE_NAME)
|
@JacksonXmlRootElement(localName = XML_ROOT_INSTANCE_NAME)
|
||||||
public abstract class AbstractInstance implements Instance {
|
public abstract class AbstractInstance implements Instance {
|
||||||
|
|
||||||
private static final Logger log = LoggerFactory.getLogger(AbstractInstance.class);
|
private final Logger log = LoggerFactory.getLogger(this.getClass());
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 实例名称
|
* 实例名称
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ public final class Configurer {
|
|||||||
|
|
||||||
private Configuration configuration;
|
private Configuration configuration;
|
||||||
private MultipleSourceInstanceContext instanceContext;
|
private MultipleSourceInstanceContext instanceContext;
|
||||||
|
private boolean clearCache;
|
||||||
|
|
||||||
Configurer(){}
|
Configurer(){}
|
||||||
|
|
||||||
@@ -29,8 +30,13 @@ public final class Configurer {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Configurer clearCache(boolean clearCache) {
|
||||||
|
this.clearCache = clearCache;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public SystemInitializer done() {
|
public SystemInitializer done() {
|
||||||
Assert.notNull(configuration, instanceContext);
|
Assert.notNull(configuration, instanceContext);
|
||||||
return new SystemInitializer(configuration, instanceContext);
|
return new SystemInitializer(configuration, instanceContext, clearCache);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,8 +39,9 @@ public final class NetworkContextHolder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static String getIpAddress() {
|
public static String getIpAddress() {
|
||||||
if(IP_ADDRESS != null)
|
if(IP_ADDRESS != null) {
|
||||||
return IP_ADDRESS;
|
return IP_ADDRESS;
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
if(!IP_CONTEXT_WAIT_LATCH.await(IP_CONTEXT_TIME_OUT, TimeUnit.SECONDS)) {
|
if(!IP_CONTEXT_WAIT_LATCH.await(IP_CONTEXT_TIME_OUT, TimeUnit.SECONDS)) {
|
||||||
log.error("IP地址获取超时.");
|
log.error("IP地址获取超时.");
|
||||||
|
|||||||
@@ -6,8 +6,8 @@ import com.serliunx.ddns.core.Clearable;
|
|||||||
import com.serliunx.ddns.core.Refreshable;
|
import com.serliunx.ddns.core.Refreshable;
|
||||||
import com.serliunx.ddns.core.context.MultipleSourceInstanceContext;
|
import com.serliunx.ddns.core.context.MultipleSourceInstanceContext;
|
||||||
import com.serliunx.ddns.core.instance.Instance;
|
import com.serliunx.ddns.core.instance.Instance;
|
||||||
import com.serliunx.ddns.support.feign.client.IPAddressClient;
|
import com.serliunx.ddns.client.IPAddressClient;
|
||||||
import com.serliunx.ddns.support.feign.client.entity.IPAddressResponse;
|
import com.serliunx.ddns.client.entity.IPAddressResponse;
|
||||||
import com.serliunx.ddns.thread.TaskThreadFactory;
|
import com.serliunx.ddns.thread.TaskThreadFactory;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
@@ -40,14 +40,20 @@ public final class SystemInitializer implements Refreshable, Clearable {
|
|||||||
|
|
||||||
private final Configuration configuration;
|
private final Configuration configuration;
|
||||||
private final MultipleSourceInstanceContext instanceContext;
|
private final MultipleSourceInstanceContext instanceContext;
|
||||||
|
private final boolean clearCache;
|
||||||
|
|
||||||
private ScheduledThreadPoolExecutor scheduledThreadPoolExecutor;
|
private ScheduledThreadPoolExecutor scheduledThreadPoolExecutor;
|
||||||
private Set<Instance> instances;
|
private Set<Instance> instances;
|
||||||
private final Map<String, ScheduledFuture<?>> runningInstances = new HashMap<>(64);
|
private final Map<String, ScheduledFuture<?>> runningInstances = new HashMap<>(64);
|
||||||
|
|
||||||
SystemInitializer(Configuration configuration, MultipleSourceInstanceContext instanceContext) {
|
SystemInitializer(Configuration configuration, MultipleSourceInstanceContext instanceContext, boolean clearCache) {
|
||||||
this.configuration = configuration;
|
this.configuration = configuration;
|
||||||
this.instanceContext = instanceContext;
|
this.instanceContext = instanceContext;
|
||||||
|
this.clearCache = clearCache;
|
||||||
|
}
|
||||||
|
|
||||||
|
SystemInitializer(Configuration configuration, MultipleSourceInstanceContext instanceContext) {
|
||||||
|
this(configuration, instanceContext, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Configurer configurer() {
|
public static Configurer configurer() {
|
||||||
@@ -80,8 +86,10 @@ public final class SystemInitializer implements Refreshable, Clearable {
|
|||||||
// 运行实例
|
// 运行实例
|
||||||
runInstances();
|
runInstances();
|
||||||
|
|
||||||
// 实例提交后, 清理实例、配置缓存, 因为读取一次就不需要了
|
// 清理实例、配置缓存, 正常情况下读取一次就不需要了
|
||||||
|
if (clearCache) {
|
||||||
clear();
|
clear();
|
||||||
|
}
|
||||||
log.info("初始化完成!");
|
log.info("初始化完成!");
|
||||||
InstanceContextHolder.clearAdditional();
|
InstanceContextHolder.clearAdditional();
|
||||||
}
|
}
|
||||||
@@ -120,9 +128,10 @@ public final class SystemInitializer implements Refreshable, Clearable {
|
|||||||
byte[] buffer = new byte[1024];
|
byte[] buffer = new byte[1024];
|
||||||
int bytesRead;
|
int bytesRead;
|
||||||
if (inputStream != null) {
|
if (inputStream != null) {
|
||||||
while ((bytesRead = inputStream.read(buffer)) != -1)
|
while ((bytesRead = inputStream.read(buffer)) != -1) {
|
||||||
outputStream.write(buffer, 0, bytesRead);
|
outputStream.write(buffer, 0, bytesRead);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
outputStream.close();
|
outputStream.close();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("文件 {} 解压失败!, 原因: {}", resourceName, e.getMessage());
|
log.error("文件 {} 解压失败!, 原因: {}", resourceName, e.getMessage());
|
||||||
@@ -140,7 +149,7 @@ public final class SystemInitializer implements Refreshable, Clearable {
|
|||||||
}
|
}
|
||||||
// 初始化实例
|
// 初始化实例
|
||||||
i.refresh();
|
i.refresh();
|
||||||
ScheduledFuture<?> future = scheduledThreadPoolExecutor.scheduleWithFixedDelay(i, 0,
|
ScheduledFuture<?> future = scheduledThreadPoolExecutor.scheduleWithFixedDelay(i, 5,
|
||||||
i.getInterval(), TimeUnit.SECONDS);
|
i.getInterval(), TimeUnit.SECONDS);
|
||||||
runningInstances.put(i.getName(), future);
|
runningInstances.put(i.getName(), future);
|
||||||
log.info("{}({})已启动, 运行周期 {} 秒.", i.getName(), i.getType(), i.getInterval());
|
log.info("{}({})已启动, 运行周期 {} 秒.", i.getName(), i.getType(), i.getInterval());
|
||||||
@@ -187,15 +196,15 @@ public final class SystemInitializer implements Refreshable, Clearable {
|
|||||||
boolean result = scheduledThreadPoolExecutor.awaitTermination(5, TimeUnit.SECONDS);
|
boolean result = scheduledThreadPoolExecutor.awaitTermination(5, TimeUnit.SECONDS);
|
||||||
if (result) {
|
if (result) {
|
||||||
log.error("线程池无法在正常的时间范围内关闭, 将强制关闭线程池!");
|
log.error("线程池无法在正常的时间范围内关闭, 将强制关闭线程池!");
|
||||||
if (!scheduledThreadPoolExecutor.isShutdown())
|
if (!scheduledThreadPoolExecutor.isShutdown()) {
|
||||||
scheduledThreadPoolExecutor.shutdownNow();
|
scheduledThreadPoolExecutor.shutdownNow();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
} catch (InterruptedException e) {
|
} catch (InterruptedException e) {
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}
|
} finally {
|
||||||
|
|
||||||
instances.clear();
|
instances.clear();
|
||||||
|
|
||||||
runningInstances.clear();
|
runningInstances.clear();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,9 +34,8 @@ public class ContextTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testFileContext(){
|
public void testFileContext(){
|
||||||
MultipleSourceInstanceContext context = new FileInstanceContext();
|
FileInstanceContext context = new FileInstanceContext();
|
||||||
|
context.getSortedListableInstanceFactories().forEach(System.out::println);
|
||||||
context.getInstances().forEach(System.out::println);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
@@ -1,9 +1,15 @@
|
|||||||
package com.serliunx.ddns.test;
|
package com.serliunx.ddns.test;
|
||||||
|
|
||||||
|
import com.serliunx.ddns.constant.InstanceType;
|
||||||
import com.serliunx.ddns.constant.SystemConstants;
|
import com.serliunx.ddns.constant.SystemConstants;
|
||||||
|
import com.serliunx.ddns.core.factory.InstanceFactory;
|
||||||
|
import com.serliunx.ddns.core.factory.ListableInstanceFactory;
|
||||||
import com.serliunx.ddns.core.factory.YamlFileInstanceFactory;
|
import com.serliunx.ddns.core.factory.YamlFileInstanceFactory;
|
||||||
|
import com.serliunx.ddns.core.instance.Instance;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author SerLiunx
|
* @author SerLiunx
|
||||||
* @since 1.0
|
* @since 1.0
|
||||||
@@ -12,9 +18,14 @@ public class FactoryTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testYamlFileFactory() {
|
public void testYamlFileFactory() {
|
||||||
YamlFileInstanceFactory factory = new YamlFileInstanceFactory(SystemConstants.USER_INSTANCE_DIR);
|
ListableInstanceFactory factory = new YamlFileInstanceFactory(SystemConstants.USER_INSTANCE_DIR);
|
||||||
factory.refresh();
|
factory.refresh();
|
||||||
factory.getInstances().forEach(System.out::println);
|
factory.getInstances().forEach(System.out::println);
|
||||||
factory.clear();
|
|
||||||
|
Map<String, Instance> instances = factory.getInstanceOfType(InstanceType.TENCENT_CLOUD);
|
||||||
|
|
||||||
|
instances.forEach((k, v) -> {
|
||||||
|
System.out.println(k + ": " + v);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
package com.serliunx.ddns.test.support;
|
package com.serliunx.ddns.test.support;
|
||||||
|
|
||||||
import com.serliunx.ddns.support.feign.client.IPAddressClient;
|
import com.serliunx.ddns.client.IPAddressClient;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in New Issue
Block a user