readme 修改

Signed-off-by: SmileToCandy <smiletocandy@qq.com>
后台运营yy
SmileToCandy 2 years ago committed by yangyang
parent 7a6d30454c
commit 7e90c26b68

8
.idea/.gitignore vendored

@ -0,0 +1,8 @@
# 默认忽略的文件
/shelf/
/workspace.xml
# 基于编辑器的 HTTP 客户端请求
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectRootManager" version="2" project-jdk-name="17 (2)" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/out" />
</component>
</project>

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/tamguo.iml" filepath="$PROJECT_DIR$/.idea/tamguo.iml" />
</modules>
</component>
</project>

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$" />
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
</component>
</project>

@ -18,7 +18,8 @@
- 管理员账号system 密码123456 **因为线上数据和测试数据没有做到隔离,作者已经把密码修改,可用.sql在本地运行看后台效果。**
加QQ群937899574 可免费获取SQL基本
现在作者组建一个团队来对这个项目进行迭代升级,需要前端、后端、设计人员
有兴趣加入的小伙伴,可以加作者微信: tamgoooo

@ -3,62 +3,76 @@ package com.tamguo.common.encryption;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
// ShaEncrypt类用于实现SHA加密相关的功能提供了生成SHA-256和SHA-512加密字符串的便捷方法
public class ShaEncrypt {
/**
* SHA-256
* SHA-256
* SHA"SHA-256"
*
* @param strText
* @return
* @param strText
* @return SHA-256null
*/
public static String SHA256(final String strText) {
return SHA(strText, "SHA-256");
}
/**
* SHA-512
* SHA-512
* SHA"SHA-512"
*
* @param strText
* @return
* @param strText
* @return SHA-512null
*/
public static String SHA512(final String strText) {
return SHA(strText, "SHA-512");
}
/**
* SHA
* SHA
*
* @param strSourceText
* @return
* @param strText
* @param strType SHA"SHA-256""SHA-512"使SHA
* @return null
*/
private static String SHA(final String strText, final String strType) {
// 返回
// 用于存储最终加密后的十六进制字符串结果初始化为null若加密成功则会被赋值并返回
String strResult = null;
// 是否是有效字符串
// 首先判断传入的待加密字符串是否有效,即不能为空且要有一定长度,才进行后续的加密操作
if (strText!= null && strText.length() > 0) {
try {
// SHA加密开始
// 创建加密对象 并傳入加密類型
// 创建MessageDigest对象用于实现指定类型的消息摘要算法这里就是SHA加密算法通过传入加密算法类型字符串来获取对应的算法实现实例。
// 如果传入的算法类型不存在会抛出NoSuchAlgorithmException异常。
MessageDigest messageDigest = MessageDigest.getInstance(strType);
// 传入要加密的字符串
// 使用创建好的MessageDigest对象传入要加密的字符串对应的字节数组更新摘要信息准备进行加密计算。
messageDigest.update(strText.getBytes());
// 得到 byte 類型结果
// 执行加密计算,得到加密后的字节数组结果,这个结果是二进制形式的加密数据表示。
byte byteBuffer[] = messageDigest.digest();
// 將 byte 轉換爲 string
// 创建一个StringBuffer对象用于后续拼接十六进制的加密结果字符串方便对字节数组中的每个字节进行转换和拼接操作。
StringBuffer strHexString = new StringBuffer();
// 遍歷 byte buffer
// 遍历加密后的字节数组将每个字节转换为十六进制字符串表示形式并添加到strHexString中。
for (int i = 0; i < byteBuffer.length; i++) {
// 将字节转换为十六进制字符串通过与0xff进行按位与操作确保得到的是无符号的字节值对应的十六进制表示。
String hex = Integer.toHexString(0xff & byteBuffer[i]);
// 如果转换后的十六进制字符串长度为1例如字节值小于16时则在前面补0以保证每个字节对应的十六进制表示都是两位。
if (hex.length() == 1) {
strHexString.append('0');
}
// 将处理后的十六进制字符串添加到结果字符串缓冲区中
strHexString.append(hex);
}
// 得到返回結果
// 将最终拼接好的十六进制字符串赋值给strResult作为加密后的结果返回。
strResult = strHexString.toString();
} catch (NoSuchAlgorithmException e) {
// 如果在创建MessageDigest对象时指定的加密算法类型不存在会捕获该异常并打印异常堆栈信息同时返回null表示加密失败。
e.printStackTrace();
}
}

@ -1,37 +1,53 @@
package com.tamguo.common.id;
public class IdGen
{
// IdGen类用于生成唯一标识符ID很可能采用了类似雪花算法Snowflake Algorithm的思路来生成分布式环境下的唯一ID
public class IdGen {
// 工作机器ID用于区分不同的工作节点在分布式系统中每个节点有唯一的workerId
private long workerId;
// 数据中心ID用于区分不同的数据中心不同的数据中心此ID不同
private long datacenterId;
// 序列号用于在同一毫秒内对生成的ID进行区分每生成一个新ID该值可能会递增
private long sequence = 0L;
// 一个基准时间戳以毫秒为单位通常是一个过去的固定时间点用于计算时间差值来生成ID中的时间部分
private long twepoch = 1288834974657L;
// 工作机器ID所占的位数这里定义为5位可以表示的范围是0 - 312^5 - 1
private long workerIdBits = 5L;
private long datacenterIdBits = 5L;
// 数据中心ID所占的位数同样定义为5位可表示范围也是0 - 312^5 - 1
private long maxWorkerId = -1L ^ (-1L << workerIdBits);
private long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);
// 序列号所占的位数这里定义为12位可表示范围是0 - 40952^12 - 1
private long sequenceBits = 12L;
// 用于计算最终ID时将workerId左移的位数基于sequenceBits的值来确定
private long workerIdShift = sequenceBits;
// 用于计算最终ID时将datacenterId左移的位数基于sequenceBits和workerIdBits的值来确定
private long datacenterIdShift = sequenceBits + workerIdBits;
// 用于计算最终ID时将时间戳部分左移的位数综合考虑了sequenceBits、workerIdBits和datacenterIdBits的值
private long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;
// 序列号的掩码用于对sequence进行位运算操作保证其在合法范围内循环使用
private long sequenceMask = -1L ^ (-1L << sequenceBits);
// 上次生成ID时的时间戳用于对比当前时间戳判断是否需要处理时间回拨等情况
private long lastTimestamp = -1L;
// 要拼接在生成ID上的字符串后缀或前缀取决于flag的值可为空字符串表示不拼接
private String suffix;
// 用于标识suffix是作为前缀true还是后缀false拼接在生成的ID上
private boolean flag;
// 内部静态类用于实现单例模式保证整个应用中只有一个IdGen实例默认构造方式时
private static class IdGenHolder {
private static final IdGen instance = new IdGen();
}
// 获取IdGen的单例实例默认无后缀的情况
public static IdGen get() {
return IdGenHolder.instance;
}
/**
* id
* @param suffix
* @param flag true: false:
* @return
* IdGen
*
* @param suffix null
* @param flag suffixtruefalseID
* @return IdGensuffix
*/
public static IdGen get(String suffix, boolean flag) {
if (suffix == null || suffix.trim().length() == 0)
@ -41,16 +57,19 @@ public class IdGen
}
}
// 默认构造函数调用另一个带有默认workerId和datacenterId都为0的构造函数进行初始化
public IdGen() {
this(0L, 0L);
}
// 带有后缀字符串和拼接方式标识的构造函数用于初始化suffix和flag成员变量
public IdGen(String suffix, boolean flag) {
this.suffix = suffix;
this.flag = flag;
}
// 构造函数用于初始化workerId和datacenterId同时会对传入的参数进行合法性校验
// 如果workerId或datacenterId超出合法范围大于最大允许值或者小于0则抛出异常
public IdGen(long workerId, long datacenterId) {
if (workerId > maxWorkerId || workerId < 0) {
throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0", maxWorkerId));
@ -62,28 +81,38 @@ public class IdGen
this.datacenterId = datacenterId;
}
// 生成下一个唯一ID的方法是整个类的核心方法需要保证线程安全使用了synchronized关键字
public synchronized String nextId() {
// 获取当前时间戳(以毫秒为单位)
long timestamp = timeGen();
// 如果当前时间戳小于上次生成ID的时间戳说明系统时间可能出现了回拨情况抛出异常避免生成重复ID
if (timestamp < lastTimestamp) {
throw new RuntimeException(String.format(
"Clock moved backwards. Refusing to generate id for %d milliseconds", lastTimestamp - timestamp));
}
// 如果当前时间戳和上次时间戳相同表示在同一毫秒内需要递增序列号sequence
if (lastTimestamp == timestamp) {
sequence = (sequence + 1) & sequenceMask;
// 如果序列号达到最大值sequenceMask对应的最大值则需要等待到下一个毫秒再生成ID
if (sequence == 0) {
timestamp = tilNextMillis(lastTimestamp);
}
} else {
// 如果时间戳不同说明进入了新的一毫秒重置序列号为0
sequence = 0L;
}
// 更新上次生成ID的时间戳为当前时间戳
lastTimestamp = timestamp;
// 组合生成最终的唯一序列号通过位运算将时间戳、数据中心ID、工作机器ID和序列号按规则拼接在一起
long serialNumber = ((timestamp - twepoch) << timestampLeftShift) | (datacenterId << datacenterIdShift)
| (workerId << workerIdShift) | sequence;
// 根据suffix和flag的值决定是否将suffix拼接在生成的ID上以及拼接的位置前缀或后缀然后返回最终的ID字符串表示形式
return (suffix == null || suffix.trim().length() == 0)? serialNumber + "" : (flag? (new StringBuffer()).append(suffix).append(serialNumber).toString() : (new StringBuffer()).append(serialNumber).append(suffix).toString());
}
// 用于等待直到下一个毫秒的方法,通过不断获取当前时间戳,直到其大于传入的上次时间戳为止
protected long tilNextMillis(long lastTimestamp) {
long timestamp = timeGen();
while (timestamp <= lastTimestamp) {
@ -92,8 +121,8 @@ public class IdGen
return timestamp;
}
// 获取当前系统时间戳(以毫秒为单位)的方法,供其他方法调用,获取当前时间情况
protected long timeGen() {
return System.currentTimeMillis();
}
}

@ -24,93 +24,129 @@ import org.patchca.utils.encoder.EncoderHelper;
import org.patchca.word.RandomWordFactory;
/**
*
*
* Patchca
* @author ThinkGem
* @version 20171223
*/
public class CaptchaUtils {
// 用于生成随机数,在验证码生成过程中多处用于随机取值,比如字体颜色、噪点位置等的随机化处理
private static Random random = new Random();
// 可配置的验证码服务对象,用于设置和管理验证码生成的各项参数及执行生成操作
private static ConfigurableCaptchaService ccs;
private static WobbleRippleFilterFactory wrff; // 摆波纹
private static DoubleRippleFilterFactory doff; // 双波纹
private static CurvesRippleFilterFactory crff; // 曲线波纹
private static DiffuseRippleFilterFactory drff; // 漫纹波
private static MarbleRippleFilterFactory mrff; // 大理石
// 摆波纹滤镜工厂对象,用于创建摆波纹效果的滤镜,应用在验证码图片上以增加干扰效果和美观度
private static WobbleRippleFilterFactory wrff;
// 双波纹滤镜工厂对象,类似地,用于生成双波纹效果的滤镜
private static DoubleRippleFilterFactory doff;
// 曲线波纹滤镜工厂对象,用于生成曲线波纹效果的滤镜
private static CurvesRippleFilterFactory crff;
// 漫纹波滤镜工厂对象,用于生成漫纹波效果的滤镜
private static DiffuseRippleFilterFactory drff;
// 大理石滤镜工厂对象,用于生成大理石纹理效果的滤镜
private static MarbleRippleFilterFactory mrff;
/**
* Double-Checked LockingConfigurableCaptchaService
* 线线线使
*/
private static void initialize() {
if (ccs == null) {
// 使用类级别的锁,确保在多线程环境下对初始化代码块的互斥访问,防止多次初始化
synchronized (CaptchaUtils.class) {
if (ccs == null) {
// 配置初始化
// 实例化ConfigurableCaptchaService对象用于后续配置和生成验证码
ccs = new ConfigurableCaptchaService();
// 设置图片大小
// 设置验证码图片的宽度和高度单位为像素这里设置宽度为100像素高度为28像素
ccs.setWidth(100);
ccs.setHeight(28);
// 设置文字数量
// 配置随机文字生成工厂,用于确定验证码中出现的字符范围、长度等信息
RandomWordFactory wf = new RandomWordFactory();
// 设置验证码文字可使用的字符集合,排除了容易混淆的字符,提高验证码的辨识度
wf.setCharacters("ABDEFGHKMNRSWX2345689");
// 设置验证码文字的最小长度为4个字符
wf.setMinLength(4);
// 设置验证码文字的最大长度也为4个字符保证生成的验证码长度固定为4
wf.setMaxLength(4);
ccs.setWordFactory(wf);
// 设置字体大小
// 配置随机字体生成工厂,用于设置验证码文字的字体大小范围
RandomFontFactory ff = new RandomFontFactory();
// 设置字体的最小尺寸为28像素确保字体大小合适且相对统一
ff.setMinSize(28);
// 设置字体的最大尺寸为28像素与最小尺寸相同保证字体大小固定
ff.setMaxSize(28);
ccs.setFontFactory(ff);
// 设置文字渲染边距
// 配置文字渲染器,用于设置验证码文字在图片上的边距,确保文字显示位置合适且美观
BestFitTextRenderer tr = new BestFitTextRenderer();
// 设置文字距离图片顶部的边距为3像素
tr.setTopMargin(3);
// 设置文字距离图片右侧的边距为3像素
tr.setRightMargin(3);
// 设置文字距离图片底部的边距为3像素
tr.setBottomMargin(3);
// 设置文字距离图片左侧的边距为3像素
tr.setLeftMargin(3);
ccs.setTextRenderer(tr);
// 设置字体颜色
// 配置字体颜色工厂,用于随机生成验证码文字的颜色,颜色取值范围在较浅的色彩区间内,以保证对比度和可读性
ccs.setColorFactory(new ColorFactory() {
@Override
public Color getColor(int x) {
// 随机生成红色分量范围在0 - 90之间使得颜色相对较浅
int r = random.nextInt(90);
// 随机生成绿色分量范围在0 - 90之间同样保证较浅的颜色
int g = random.nextInt(90);
// 随机生成蓝色分量范围在0 - 90之间
int b = random.nextInt(90);
return new Color(r, g, b);
}
});
// 设置背景
// 配置背景工厂,用于绘制验证码图片的背景,包括填充背景颜色、添加噪点和干扰线等操作,以增加验证码的复杂性和安全性
ccs.setBackgroundFactory(new BackgroundFactory() {
@Override
public void fillBackground(BufferedImage image) {
// 获取图片的图形上下文对象,用于在图片上进行绘制操作
Graphics graphics = image.getGraphics();
// 验证码图片的宽高
// 获取验证码图片的宽度,单位为像素
int imgWidth = image.getWidth();
// 获取验证码图片的高度,单位为像素
int imgHeight = image.getHeight();
// 填充为白色背景
// 设置填充颜色为白色,用于将整个验证码图片背景填充为白色
graphics.setColor(Color.WHITE);
// 使用白色填充整个图片区域,左上角坐标为(0, 0),宽度和高度为图片的实际宽高
graphics.fillRect(0, 0, imgWidth, imgHeight);
// 画 50 个噪点(颜色及位置随机)
// 循环绘制50个噪点噪点的颜色、位置、旋转角度和大小都是随机生成的增加验证码的干扰性和安全性
for (int i = 0; i < 50; i++) {
// 随机颜色
// 随机生成红色分量范围在50 - 150之间使得噪点颜色不过于浅或深,保证一定的辨识度
int rInt = random.nextInt(100) + 50;
// 随机生成绿色分量范围在50 - 150之间
int gInt = random.nextInt(100) + 50;
// 随机生成蓝色分量范围在50 - 150之间
int bInt = random.nextInt(100) + 50;
graphics.setColor(new Color(rInt, gInt, bInt));
// 随机位置
// 随机生成噪点的x坐标范围在0到图片宽度减3之间避免超出图片边界
int xInt = random.nextInt(imgWidth - 3);
// 随机生成噪点的y坐标范围在0到图片高度减2之间同样避免超出边界
int yInt = random.nextInt(imgHeight - 2);
// 随机旋转角度
// 随机生成噪点的起始旋转角度范围在0 - 360度之间增加噪点的随机性
int sAngleInt = random.nextInt(360);
// 随机生成噪点的结束旋转角度范围在0 - 360度之间
int eAngleInt = random.nextInt(360);
// 随机大小
// 随机生成噪点的宽度范围在0 - 6像素之间控制噪点大小
int wInt = random.nextInt(6);
// 随机生成噪点的高度范围在0 - 6像素之间
int hInt = random.nextInt(6);
// 填充背景
// 使用生成的参数绘制一个填充的弧形(可以看作是不规则形状的噪点),起始角度和结束角度、宽度和高度共同决定了弧形的形状和大小
graphics.fillArc(xInt, yInt, wInt, hInt, sAngleInt, eAngleInt);
// 画5条干扰线
// 每间隔10个噪点即i % 10 == 0时绘制一条干扰线干扰线的起点是当前噪点位置终点是图片内的随机位置进一步增加验证码的干扰性
if (i % 10 == 0) {
int xInt2 = random.nextInt(imgWidth);
int yInt2 = random.nextInt(imgHeight);
@ -120,32 +156,30 @@ public class CaptchaUtils {
}
});
// 效果初始化
wrff = new WobbleRippleFilterFactory(); // 摆波纹
doff = new DoubleRippleFilterFactory(); // 双波纹
crff = new CurvesRippleFilterFactory(ccs.getColorFactory()); // 曲线波纹
drff = new DiffuseRippleFilterFactory(); // 漫纹波
mrff = new MarbleRippleFilterFactory(); // 大理石
// 初始化各种滤镜工厂对象,用于后续根据随机选择为验证码图片应用不同的滤镜效果
wrff = new WobbleRippleFilterFactory();
doff = new DoubleRippleFilterFactory();
crff = new CurvesRippleFilterFactory(ccs.getColorFactory());
drff = new DiffuseRippleFilterFactory();
mrff = new MarbleRippleFilterFactory();
}
}
}
}
/**
*
* @param request
* @param response
* @throws IOException
* @return
* ConfigurableCaptchaServicePNG
*
* @param outputStream HttpServletResponsePNG
* @throws IOException I/O
* @return 使
*/
public static String generateCaptcha(OutputStream outputStream) throws IOException {
// 初始化设置
// 调用初始化方法,确保验证码生成相关的配置和资源已正确初始化
initialize();
// 随机选择一个样式
switch (random.nextInt(3)) {
// 随机选择一个滤镜样式共5种可选并将对应的滤镜工厂设置到ConfigurableCaptchaService中用于为验证码图片添加相应的滤镜效果
switch (random.nextInt(5)) {
case 0:
ccs.setFilterFactory(wrff); // 摆波纹
break;
@ -163,7 +197,7 @@ public class CaptchaUtils {
break;
}
// 生成验证码
// 使用EncoderHelper工具类结合已配置好的ConfigurableCaptchaService对象生成验证码图片并将其以PNG格式写入指定的输出流中同时返回验证码的字符内容
String s = EncoderHelper.getChallangeAndWriteImage(ccs, "png", outputStream);
// System.out.println(s);

@ -6,9 +6,19 @@ import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
// ObjectUtil类继承自SerializeTranscoder类主要用于对象的序列化、反序列化以及对象相等性比较的相关操作
public class ObjectUtil extends SerializeTranscoder {
/**
*
* nullnull
*
* @param value null
* @return I/O
*/
@Override
public byte[] serialize(Object value) {
// 若传入的对象为null直接抛出空指针异常明确提示不能序列化null对象
if (value == null) {
throw new NullPointerException("Can't serialize null");
}
@ -16,52 +26,91 @@ public class ObjectUtil extends SerializeTranscoder {
ByteArrayOutputStream bos = null;
ObjectOutputStream os = null;
try {
// 创建一个ByteArrayOutputStream用于将对象序列化后的字节数据写入其中
bos = new ByteArrayOutputStream();
// 使用ByteArrayOutputStream创建ObjectOutputStreamObjectOutputStream用于将对象转换为字节流进行序列化
os = new ObjectOutputStream(bos);
// 将传入的对象写入到ObjectOutputStream中触发对象的序列化过程
os.writeObject(value);
// 关闭ObjectOutputStream释放相关资源
os.close();
// 关闭ByteArrayOutputStream释放相关资源
bos.close();
// 将ByteArrayOutputStream中的字节数据转换为字节数组作为序列化的最终结果
result = bos.toByteArray();
} catch (IOException e) {
// 如果在序列化过程中出现I/O异常抛出包含异常信息的非法参数异常提示对象不可序列化
throw new IllegalArgumentException("Non-serializable object", e);
} finally {
// 调用close方法关闭ObjectOutputStream确保资源被正确释放即使出现异常也能执行
close(os);
// 调用close方法关闭ByteArrayOutputStream确保资源被正确释放
close(bos);
}
return result;
}
/**
*
* nullnullI/O
*
* @param in null
* @return nullnullnull
*/
@Override
public Object deserialize(byte[] in) {
Object result = null;
ByteArrayInputStream bis = null;
ObjectInputStream is = null;
try {
// 首先判断传入的字节数组是否为null如果为null则直接返回null不进行后续反序列化操作
if (in!= null) {
// 创建ByteArrayInputStream将传入的字节数组作为数据源用于后续反序列化读取字节数据
bis = new ByteArrayInputStream(in);
// 使用ByteArrayInputStream创建ObjectInputStreamObjectInputStream用于从字节流中读取并还原对象
is = new ObjectInputStream(bis);
// 从ObjectInputStream中读取对象触发反序列化过程将字节数据还原为对象
result = is.readObject();
// 关闭ObjectInputStream释放相关资源
is.close();
// 关闭ByteArrayInputStream释放相关资源
bis.close();
}
} catch (IOException e) {
// 如果在反序列化过程中出现I/O异常打印异常堆栈信息方便排查问题但继续执行后续代码最终返回null
e.printStackTrace();
} catch (ClassNotFoundException e) {
// 如果在反序列化过程中出现找不到对应类的异常例如序列化的对象类在反序列化时类路径下不存在了打印异常堆栈信息继续执行后续代码最终返回null
e.printStackTrace();
} finally {
// 调用close方法关闭ObjectInputStream确保资源被正确释放即使出现异常也能执行
close(is);
// 调用close方法关闭ByteArrayInputStream确保资源被正确释放
close(bis);
}
return result;
}
/**
*
* true
* nullfalse
* nullequals
*
* @param o1 null
* @param o2 null
* @return truefalse
*/
public static boolean equals(Object o1, Object o2) {
// 如果两个对象是同一个对象内存地址相同直接返回true
if (o1 == o2) {
return true;
} else if (o1 == null || o2 == null) {
// 如果其中一个对象为null则返回false表示两个对象不相等
return false;
} else {
// 两个对象都不为null时调用对象的equals方法来判断它们是否相等并返回结果
return o1.equals(o2);
}
}

@ -2,17 +2,43 @@ package com.tamguo.common.serialize;
import java.io.Closeable;
// SerializeTranscoder是一个抽象类用于定义对象序列化和反序列化相关操作的抽象方法同时提供了一个关闭资源的通用方法。
// 它作为一种抽象的规范,子类需要实现具体的序列化和反序列化逻辑,可用于在不同的序列化场景下进行统一的操作处理。
public abstract class SerializeTranscoder {
/**
*
* JavaJSON
*
* @param value
* @return
*/
public abstract byte[] serialize(Object value);
/**
*
*
*
* @param in
* @return
*/
public abstract Object deserialize(byte[] in);
/**
* Closeable使
*
*
* @param closeable Closeablenull
*/
public void close(Closeable closeable) {
// 首先判断传入的要关闭的资源对象是否为null如果为null则不需要进行关闭操作直接返回
if (closeable!= null) {
try {
// 调用Closeable接口定义的close方法来关闭资源释放相关的系统资源如文件句柄、网络连接等具体取决于资源类型
closeable.close();
} catch (Exception e) {
// 如果在关闭资源过程中出现任何异常(例如文件已被删除导致关闭流失败等情况),则打印异常堆栈信息,方便排查问题,
// 但不会将异常继续向上抛出,避免影响程序的整体运行逻辑(除非上层代码明确需要处理这种关闭资源的异常情况)
e.printStackTrace();
}
}

@ -4,81 +4,118 @@ import java.io.InterruptedIOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
// AbstractRunningLogHandler是一个抽象类实现了LogHandler接口从代码结构推测虽然这里没展示LogHandler接口的定义内容
// 主要用于处理日志相关操作,重点提供了获取运行时调用栈信息以及一些抽象的日志记录方法,子类可以根据具体需求重写这些日志记录方法来实现不同的日志处理逻辑。
public abstract class AbstractRunningLogHandler implements LogHandler {
// 通过反射获取Throwable类的getStackTrace方法用于获取异常的调用栈信息初始化为null在静态代码块中尝试进行初始化。
private static Method getStackTraceMethod;
// 通过反射获取StackTraceElement类的getClassName方法用于获取栈元素对应的类名初始化为null同样在静态代码块中初始化。
private static Method getClassNameMethod;
// 通过反射获取StackTraceElement类的getMethodName方法用于获取栈元素对应的方法名初始化为null在静态代码块里进行初始化操作。
private static Method getMethodNameMethod;
// 通过反射获取StackTraceElement类的getFileName方法用于获取栈元素对应的文件名初始化为null会在静态块中尝试初始化。
private static Method getFileNameMethod;
// 通过反射获取StackTraceElement类的getLineNumber方法用于获取栈元素对应的行号初始化为null依靠静态块来初始化。
private static Method getLineNumberMethod;
// 静态代码块,在类加载时执行,用于通过反射获取相关的方法对象,以便后续在获取调用栈信息等操作中使用。
// 如果在获取方法过程中出现ClassNotFoundException比如对应的类找不到可能是运行环境问题或者NoSuchMethodException要获取的方法不存在异常
// 则会在日志中记录将使用JDK 1.4之前的方法来确定位置相关信息(这里只是简单记录提示,具体如何使用旧方法后续代码中并未明确体现)。
static {
try {
// 定义一个表示无参数的Class数组用于指定获取方法时的参数类型情况这里获取的方法都无参数所以初始化为null。
Class<?>[] noArgs = null;
// 通过反射获取Throwable类的getStackTrace方法后续可利用此方法获取异常的调用栈信息。
getStackTraceMethod = Throwable.class.getMethod("getStackTrace", noArgs);
// 通过反射获取StackTraceElement类因为后续要获取它里面的多个方法所以先获取类对象。
Class<?> stackTraceElementClass = Class.forName("java.lang.StackTraceElement");
// 获取StackTraceElement类的getClassName方法用于获取栈元素对应的类的全限定名。
getClassNameMethod = stackTraceElementClass.getMethod("getClassName", noArgs);
// 获取StackTraceElement类的getMethodName方法用于获取栈元素对应的方法名称。
getMethodNameMethod = stackTraceElementClass.getMethod("getMethodName", noArgs);
// 获取StackTraceElement类的getFileName方法用于获取栈元素对应的源文件名。
getFileNameMethod = stackTraceElementClass.getMethod("getFileName", noArgs);
// 获取StackTraceElement类的getLineNumber方法用于获取栈元素对应的行号信息。
getLineNumberMethod = stackTraceElementClass.getMethod("getLineNumber", noArgs);
} catch (ClassNotFoundException ex) {
// 如果出现ClassNotFoundException异常记录日志提示将使用pre-JDK 1.4方法来确定位置这里的LogDebug.debug应该是自定义的日志记录方法具体实现未展示
LogDebug.debug("will use pre-JDK 1.4 methods to determine location.");
} catch (NoSuchMethodException ex) {
// 如果出现NoSuchMethodException异常同样记录日志提示使用pre-JDK 1.4方法来确定位置。
LogDebug.debug("will use pre-JDK 1.4 methods to determine location.");
}
}
/**
* classStackTraceElement
* classStackTraceElementThrowable
* StackTraceElement
* 访StackTraceElementcreateDefaultStackTrace
*
* @param t
* @param fqnOfCallingClass
*
* @return
* @param t Throwable
* @param fqnOfCallingClass
* @return StackTraceElementStackTraceElement
*/
protected StackTraceElement getRunningStackTrace(Throwable t, String fqnOfCallingClass) {
if (getLineNumberMethod!= null) {
try {
// 定义一个表示无参数的Object数组用于在反射调用方法时传递参数这里调用的方法都无参数所以初始化为null
Object[] noArgs = null;
// 通过反射调用Throwable的getStackTrace方法获取异常的调用栈元素数组返回的是Object数组实际元素类型是StackTraceElement需要后续进行类型转换处理。
Object[] elements = (Object[]) getStackTraceMethod.invoke(t, noArgs);
// 从后往前遍历调用栈元素数组,通常是希望找到离当前调用最近的与指定类相关的栈元素,因为栈的顺序是从外层调用逐步进入内层调用的。
for (int i = elements.length - 1; i >= 0; i--) {
// 通过反射调用StackTraceElement的getClassName方法获取当前栈元素对应的类的全限定名并转换为String类型进行比较。
String thisClass = (String) getClassNameMethod.invoke(elements[i], noArgs);
// 判断当前栈元素的类名是否与传入要查找的类的全限定名相等,如果相等则表示找到了目标栈元素,开始解析并封装相关信息。
if (fqnOfCallingClass.equals(thisClass)) {
// 执行class名称
// 获取并记录目标栈元素对应的类名这里直接使用传入的fqnOfCallingClass作为类名因为前面已经验证相等了。
String className = fqnOfCallingClass;
// 执行方法名称
// 通过反射调用StackTraceElement的getMethodName方法获取目标栈元素对应的方法名并转换为String类型进行记录。
String methodName = (String) getMethodNameMethod.invoke(elements[i], noArgs);
// 执行class文件名称
// 通过反射调用StackTraceElement的getFileName方法获取目标栈元素对应的文件名并转换为String类型进行记录。
String fileName = (String) getFileNameMethod.invoke(elements[i], noArgs);
// 执行到行号
// 通过反射调用StackTraceElement的getLineNumber方法获取目标栈元素对应的行号并转换为int类型进行记录注意这里做了类型转换将Integer对象转换为基本数据类型int。
int lineNumber = ((Integer) getLineNumberMethod.invoke(elements[i], noArgs)).intValue();
// 使用获取到的类名、方法名、文件名和行号信息创建并返回一个StackTraceElement对象用于表示找到的目标栈元素信息。
return new StackTraceElement(className, methodName, fileName, lineNumber);
}
}
} catch (IllegalAccessException ex) {
// 如果在反射调用过程中出现非法访问异常比如方法不可访问等原因记录日志提示使用JDK 1.4方法失败并传入异常对象方便排查问题这里的日志记录方式同样是通过LogDebug.debug具体实现未展示
LogDebug.debug("failed using JDK 1.4 methods", ex);
} catch (InvocationTargetException ex) {
// 如果在反射调用过程中出现调用目标异常,需要进一步判断异常的目标异常类型,
// 如果目标异常是InterruptedException线程中断异常或者InterruptedIOExceptionI/O操作被中断异常
// 则重新设置当前线程的中断状态以符合Java中断机制的处理规范然后记录日志提示使用JDK 1.4方法失败,并传入异常对象用于排查问题。
if (ex.getTargetException() instanceof InterruptedException
|| ex.getTargetException() instanceof InterruptedIOException) {
Thread.currentThread().interrupt();
}
LogDebug.debug("failed using JDK 1.4 methods", ex);
} catch (RuntimeException ex) {
// 如果在反射调用过程中出现运行时异常记录日志提示使用JDK 1.4方法失败,并传入异常对象用于排查问题。
LogDebug.debug("failed using JDK 1.4 methods", ex);
}
}
// 如果前面获取目标StackTraceElement对象失败比如反射方法获取失败或者遍历调用栈没找到对应元素等情况则调用createDefaultStackTrace方法创建并返回默认的StackTraceElement对象。
return this.createDefaultStackTrace();
}
/**
* StackTraceElement
* StackTraceElement
* StackTraceElement使AbstractRunningLogHandler"log"0
*
* @return
* @return StackTraceElement
*/
private StackTraceElement createDefaultStackTrace() {
return new StackTraceElement(this.getClass().getName(), "log", this.getClass().getName(), 0);
}
// 以下是一系列抽象的日志记录方法由实现LogHandler接口而来这些方法在AbstractRunningLogHandler类中没有具体实现逻辑
// 子类需要根据具体的日志记录需求比如记录到文件、输出到控制台、发送到远程日志服务器等重写这些方法来实现不同级别的日志记录功能info、error、debug、warning等
// 每个方法都接收一个消息字符串以及要记录日志对应的类的全限定名作为参数部分重载方法还额外接收一个Throwable对象用于记录异常相关的日志信息。
@Override
public void info(String msg, String fqnOfCallingClass) {
}

Loading…
Cancel
Save