假日尾声:技术进阶与自我反思
前言
于是,假日迎来了它的尾声,把快乐和焦躁都留存在昨天。
我只觉情感的自相矛盾在加重,学习让我焦躁,纵欲无法填补空虚,于是我的心被拖入了无止尽的拉扯中。
我还没有找到必须留存的理由,但反之而言,我也远没有到可以决定自己生命自由的时刻。
还是闭上双眼,向前走吧,明日难测,却因昨日懊悔。
今天来统计一下剩余的进阶要求和已经完成的进阶要求,再针对性地完成。
日程
9点半,开始学习吧。
- 上午:连接池动态扩缩容问题弄了一早上
- 下午:完成了简单结果集映射
- 晚上8点:做完结果集相关的blog
- 嘻嘻,五一的学科作业还没写,我完蛋了。
学习内容
省流
- 连接池动态扩缩容
- 项目阶段性进度检查
- 简单结果集映射
1. 连接池动态扩缩容
1)动态扩容
在等待线程大于最大等待线程值时,临时将线程池的大小扩大1.5倍。
if (rawConn == null && waitCount.get() > hakimiConfig.getMaxWaitThreads()) {
int temporaryMax = (int) (hakimiConfig.getMaxSize() * 1.5); // 扩容上限:maxSize 的 1.5 倍
int current;
do {
current = createdCount.get();
if (current >= temporaryMax) {
break; // 已达到扩容上限
}
} while (!createdCount.compareAndSet(current, current + 1));
if (current < temporaryMax) {
log.warn("已启用动态扩容");
try {
rawConn = createPhysicalConnection();
} catch (SQLException e) {
createdCount.decrementAndGet(); // 创建失败时回滚计数器
throw e;
}
}
}
2)动态缩容
这里使用了 ScheduledExecutorService
定时任务调度器,scheduleWithFixedDelay
会周期性地触发里面的回调函数。
ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();
// 启动缩容任务(每5分钟检查一次)
public void startShrinkTask() {
scheduler.scheduleWithFixedDelay(() -> {
try {
int currentIdle = idleConnections.size();
int minIdle = hakimiConfig.getMinIdle();
// 仅当空闲连接 > minIdle 时才尝试缩容
if (currentIdle <= minIdle) {
return;
}
// 计算最多可回收的连接数(避免过度缩容)
int maxShrink = currentIdle - minIdle;
int shrunk = 0;
while (shrunk < maxShrink) {
// 非阻塞取出连接(避免长时间锁住队列)
Connection conn = idleConnections.poll(10, TimeUnit.MILLISECONDS);
if (conn == null) {
break; // 队列已空
}
// 检查是否超时
if (isIdleTimeout(conn, hakimiConfig.getIdleTimeoutMillis())) {
closeConnection(conn);
createdCount.decrementAndGet();
log.warn("关闭空闲连接");
shrunk++;
} else {
// 未超时,放回队列(避免误杀)
idleConnections.offer(conn);
}
}
} catch (Exception e) {
log.error("Shrink task error", e);
}
}, 300, 300, TimeUnit.SECONDS);
}
// 检查连接是否空闲超时
private boolean isIdleTimeout(Connection conn, long idleTimeoutMillis) {
if (conn instanceof Proxy) {
InvocationHandler handler = Proxy.getInvocationHandler(conn);
if (handler instanceof HakimiConnectionPool.ConnectionInvocationHandler) {
long idleTime = System.currentTimeMillis() -
((HakimiConnectionPool.ConnectionInvocationHandler) handler).lastUsedTime;
return idleTime > idleTimeoutMillis;
}
}
return false;
}
2. 项目阶段性进度检查
已完成进阶需求
- Maven 分模块化构建项目。
- 完成数据库连接池,支持动态扩缩容(c3p0、druid)(并发安全(hard))。
- 将常量配置转移到
yml
配置文件。 - 输出日志文件。
- 通过依赖注入(DI)进行控制反转(IOC),使用全局上下文进行全局对象的对象间依赖注入(使用注解)。
- 实现 AOP 动态代理。
目前需要完成的进阶需求
- SQL 构建器 + 自定义 SQL,高兼容性的结果映射。
- 用
DispatcherController
来统一接收数据,并转发给路径给对应 Controller 处理。 - 自定义异常抛出,全局异常处理器,过滤器。
- 后端以多线程模式运行,保证并发安全。(考虑方法级锁)
往后的进阶需求
- 统一接收 JSON 格式数据,对数据进行混合加密(RSA + AES)。
- 判题机制,可以运行 C++ 代码。
- Nginx 部署前端 + Docker 容器技术。
- 将项目部署到公网。
3. 简单结果集映射
1)关键方法:预包装代理方法
private static <T> BiFunction<ResultSet, Integer, T> createMapper(Class<T> targetClass) {
try {
// 获取或创建构造函数句柄
Constructor<T> constructor = targetClass.getDeclaredConstructor();
constructor.setAccessible(true);
MethodHandle constructorHandle = MethodHandles.lookup().unreflectConstructor(constructor); // 使用MethodHandle包装构造器,比传统反射调用性能更高
// 从缓存获取或创建字段信息
Map<String, MethodHandle> setters = Cache.SETTER_CACHE
.computeIfAbsent(targetClass, KatSimpleMapper::createSetters);
Map<String, Class<?>> fieldTypes = Cache.FIELD_TYPE_CACHE
.computeIfAbsent(targetClass, KatSimpleMapper::getFieldTypes);
return (rs, index) -> {
try {
@SuppressWarnings("unchecked")
T instance = (T) constructorHandle.invoke(); // 创建目标对象实例
for (Map.Entry<String, MethodHandle> entry : setters.entrySet()) {
String fieldName = entry.getKey();
String columnName = namingStrategy.convert(fieldName);
try {
Object value = rs.getObject(columnName);
if (value != null) {
Class<?> fieldType = fieldTypes.get(fieldName);
Object convertedValue = convertType(value, fieldType); // 类型转换
entry.getValue().invoke(instance, convertedValue); // 设置属性值
}
} catch (SQLException e) {
// 列不存在时跳过
}
}
return instance;
} catch (Throwable e) {
throw new RuntimeException("Mapping failed for " + targetClass.getName(), e);
}
};
} catch (Exception e) {
throw new RuntimeException("Mapper creation failed for " + targetClass.getName(), e);
}
}
2)包装方法:负责将触发代理方法,将mapper结果映射到结果集中
public static <T> List<T> map(ResultSet rs, Class<T> targetClass) throws SQLException {
@SuppressWarnings("unchecked")
BiFunction<ResultSet, Integer, T> mapper = (BiFunction<ResultSet, Integer, T>)
Cache.MAPPER_CACHE.computeIfAbsent(targetClass, KatSimpleMapper::createMapper);
List<T> results = new ArrayList<>();
while (rs.next()) {
results.add(mapper.apply(rs, 1));
}
return results;
}
3)辅助方法
- 数据库字段类型到java字段类型的转换
private static Object convertType(Object value, Class<?> targetType) {
if (value == null) return null;
if (targetType.isInstance(value)) return value;
// 数值类型转换
if (value instanceof Number number) {
if (targetType == Double.class || targetType == double.class) {
return number.doubleValue();
}
if (targetType == Float.class || targetType == float.class) {
return number.floatValue();
}
if (targetType == Integer.class || targetType == int.class) {
return number.intValue();
}
if (targetType == Long.class || targetType == long.class) {
return number.longValue();
}
if (targetType == Short.class || targetType == short.class) {
return number.shortValue();
}
}
// 日期类型转换
if (value instanceof java.sql.Date sqlDate) {
if (targetType == LocalDate.class) {
return sqlDate.toLocalDate();
}
if (targetType == LocalDateTime.class) {
return sqlDate.toLocalDate().atStartOfDay();
}
}
// 布尔类型转换
if (value instanceof Boolean bool) {
if (targetType == Integer.class || targetType == int.class) {
return bool ? 1 : 0;
}
if (targetType == Double.class || targetType == double.class) {
return bool ? 1.0 : 0.0;
}
}
// 时间戳转换
if (value instanceof Timestamp timestamp) {
if (targetType == LocalDateTime.class) {
return timestamp.toLocalDateTime();
}
if (targetType == LocalDate.class) {
return timestamp.toLocalDateTime().toLocalDate();
}
}
return value;
}
- 创建setter方法并缓存
private static <T> Map<String, MethodHandle> createSetters(Class<T> targetClass) {
try {
Map<String, MethodHandle> setters = new HashMap<>();
MethodHandles.Lookup lookup = MethodHandles.lookup(); // 获取MethodHandles.Lookup实例,用于方法/字段查找
for (Field field : targetClass.getDeclaredFields()) {
try {
MethodHandle setter = lookup.unreflectSetter(field); // 尝试通过标准setter方法获取MethodHandle
setters.put(field.getName(), setter);
} catch (IllegalAccessException e) {
// 如果字段没有setter,尝试直接设置字段值
field.setAccessible(true);
MethodHandle setter = lookup.unreflectSetter(field);
setters.put(field.getName(), setter);
}
}
return Collections.unmodifiableMap(setters); // 返回不可修改的Map
} catch (Exception e) {
throw new RuntimeException("Failed to create setters for " + targetClass.getName(), e);
}
}
结语
。