最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

SpringAop實(shí)現(xiàn)操作日志記錄

 更新時(shí)間:2020年12月29日 17:34:58   作者:經(jīng)典雞翅  
這篇文章主要介紹了SpringAop實(shí)現(xiàn)操作日志記錄的方法,幫助大家更好的理解和使用SpringAop,感興趣的朋友可以了解下

前言

大家好,這里是經(jīng)典雞翅,今天給大家?guī)?lái)一篇基于SpringAop實(shí)現(xiàn)的操作日志記錄的解決的方案。大家可能會(huì)說(shuō),切,操作日志記錄這么簡(jiǎn)單的東西,老生常談了。不!

網(wǎng)上的操作日志一般就是記錄操作人,操作的描述,ip等。好一點(diǎn)的增加了修改的數(shù)據(jù)和執(zhí)行時(shí)間。那么!我這篇有什么不同呢!今天這種不僅可以記錄上方所說(shuō)的一切,還增加記錄了操作前的數(shù)據(jù),錯(cuò)誤的信息,堆棧信息等。正文開(kāi)始~~~~~

思路介紹

記錄操作日志的操作前數(shù)據(jù)是需要思考的重點(diǎn)。我們以修改場(chǎng)景來(lái)作為探討。當(dāng)我們要完全記錄數(shù)據(jù)的流向的時(shí)候,我們必然要記錄修改前的數(shù)據(jù),而前臺(tái)進(jìn)行提交的時(shí)候,只有修改的數(shù)據(jù),那么如何找到修改前的數(shù)據(jù)呢。有三個(gè)大的要素,我們需要知道修改前數(shù)據(jù)的表名,表的字段主鍵,表主鍵的值。這樣通過(guò)這三個(gè)屬性,我們可以很容易的拼出 select * from 表名 where 主鍵字段 = 主鍵值。我們就獲得了修改前的數(shù)據(jù),轉(zhuǎn)換為json之后就可以存入到數(shù)據(jù)庫(kù)中了。如何獲取三個(gè)屬性就是重中之重了。我們采取的方案是通過(guò)提交的映射實(shí)體,在實(shí)體上打上注解,根據(jù) Java 的反射取到值。再進(jìn)一步拼裝獲得對(duì)象數(shù)據(jù)。那么AOP是在哪里用的呢,我們需要在記錄操作日志的方法上,打上注解,再通過(guò)切面獲取到切點(diǎn),一切的數(shù)據(jù)都通過(guò)反射來(lái)進(jìn)行獲得。

定義操作日志注解

既然是基于spinrg的aop實(shí)現(xiàn)切面。那么必然是需要一個(gè)自定義注解的。用來(lái)作為切點(diǎn)。我們定義的注解,可以帶一些必要的屬性,例如操作的描述,操作的類型。操作的類型需要說(shuō)一下,我們分為新增、修改、刪除、查詢。那么只有修改和刪除的時(shí)候,我們需要查詢一下修改前的數(shù)據(jù)。其他兩種是不需要的,這個(gè)也可以用來(lái)作為判斷。

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface OperateLog {

   String operation() default "";

   String operateType() default "";

}

定義用于找到表和表主鍵的注解

表和表主鍵的注解打在實(shí)體上,內(nèi)部有兩個(gè)屬性 tableName 和 idName。這兩個(gè)屬性的值獲得后,可以進(jìn)行拼接 select * from 表名 where 主鍵字段。

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface SelectTable {

	String tableName() default "";

	String idName() default "";
}

定義獲取主鍵值的注解

根據(jù)上面所說(shuō)的三個(gè)元素,我們還缺最后一個(gè)元素主鍵值的獲取,用于告訴我們,我們應(yīng)該從提交的請(qǐng)求的那個(gè)字段,拿到其中的值。

@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface SelectPrimaryKey {

}

注解的總結(jié)

有了上面的三個(gè)注解,注解的準(zhǔn)備工作已經(jīng)進(jìn)行完畢。我們通過(guò)反射取到數(shù)據(jù),可以獲得一切。接下來(lái)開(kāi)始實(shí)現(xiàn)切面,對(duì)于注解的值進(jìn)行拼接處理,最終存入到我們的數(shù)據(jù)庫(kù)操作日志表中。

切面的實(shí)現(xiàn)

對(duì)于切面來(lái)說(shuō),我們需要實(shí)現(xiàn)切點(diǎn)、數(shù)據(jù)庫(kù)的插入、反射的數(shù)據(jù)獲取。我們先分開(kāi)進(jìn)行解釋,最后給出全面的實(shí)現(xiàn)代碼。方便大家的理解和學(xué)習(xí)。

切面的定義

基于spring的aspect進(jìn)行聲明這是一個(gè)切面。

@Aspect
@Component
public class OperateLogAspect {
}

切點(diǎn)的定義

切點(diǎn)就是對(duì)所有的打上OperateLog的注解的請(qǐng)求進(jìn)行攔截和加強(qiáng)。我們使用annotation進(jìn)行攔截。

	@Pointcut("@annotation(com.jichi.aop.operateLog.OperateLog)")
	private void operateLogPointCut(){
	}

獲取請(qǐng)求ip的共用方法

	private String getIp(HttpServletRequest request){
		String ip = request.getHeader("X-forwarded-for");
		if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
			ip = request.getHeader("Proxy-Client-IP");
		}
		if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
			ip = request.getHeader("WL-Proxy-Client-IP");
		}
		if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
			ip = request.getHeader("HTTP_CLIENT_IP");
		}
		if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
			ip = request.getHeader("HTTP_X_FORWARDED_FOR");
		}
		if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
			ip = request.getRemoteAddr();
		}
		return ip;
	}

數(shù)據(jù)庫(kù)的日志插入操作

我們將插入數(shù)據(jù)庫(kù)的日志操作進(jìn)行單獨(dú)的抽取。

private void insertIntoLogTable(OperateLogInfo operateLogInfo){
	operateLogInfo.setId(UUID.randomUUID().toString().replace("-",""));
	String sql="insert into log values(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)";
	jdbcTemplate.update(sql,operateLogInfo.getId(),operateLogInfo.getUserId(),
		operateLogInfo.getUserName(),operateLogInfo.getOperation(),operateLogInfo.getMethod(),
		operateLogInfo.getModifiedData(),operateLogInfo.getPreModifiedData(),
		operateLogInfo.getResult(),operateLogInfo.getErrorMessage(),operateLogInfo.getErrorStackTrace(),
		operateLogInfo.getExecuteTime(),operateLogInfo.getDuration(),operateLogInfo.getIp(),
		operateLogInfo.getModule(),operateLogInfo.getOperateType());
}

環(huán)繞通知的實(shí)現(xiàn)

日志的實(shí)體類實(shí)現(xiàn)

@TableName("operate_log")
@Data
public class OperateLogInfo {

	//主鍵id
	@TableId
	private String id;
	//操作人id
	private String userId;
	//操作人名稱
	private String userName;
	//操作內(nèi)容
	private String operation;
	//操作方法名稱
	private String method;
	//操作后的數(shù)據(jù)
	private String modifiedData;
	//操作前數(shù)據(jù)
	private String preModifiedData;
	//操作是否成功
	private String result;
	//報(bào)錯(cuò)信息
	private String errorMessage;
	//報(bào)錯(cuò)堆棧信息
	private String errorStackTrace;
	//開(kāi)始執(zhí)行時(shí)間
	private Date executeTime;
	//執(zhí)行持續(xù)時(shí)間
	private Long duration;
	//ip
	private String ip;
	//操作類型
	private String operateType;

}

準(zhǔn)備工作全部完成。接下來(lái)的重點(diǎn)是對(duì)環(huán)繞通知的實(shí)現(xiàn)。思路分為數(shù)據(jù)處理、異常捕獲、finally執(zhí)行數(shù)據(jù)庫(kù)插入操作。環(huán)繞通知的重點(diǎn)類就是ProceedingJoinPoint ,我們通過(guò)它的getSignature方法可以獲取到打在方法上注解的值。例如下方。

MethodSignature signature = (MethodSignature) pjp.getSignature();
OperateLog declaredAnnotation = signature.getMethod().getDeclaredAnnotation(OperateLog.class);
operateLogInfo.setOperation(declaredAnnotation.operation());
operateLogInfo.setModule(declaredAnnotation.module());
operateLogInfo.setOperateType(declaredAnnotation.operateType());
//獲取執(zhí)行的方法
String method = signature.getDeclaringType().getName() + "." + signature.getName();
operateLogInfo.setMethod(method);
String operateType = declaredAnnotation.operateType();

獲取請(qǐng)求的數(shù)據(jù),也是通過(guò)這個(gè)類來(lái)實(shí)現(xiàn),這里有一點(diǎn)是需要注意的,就是我們要約定參數(shù)的傳遞必須是第一個(gè)參數(shù)。這樣才能保證我們?nèi)〉降臄?shù)據(jù)是提交的數(shù)據(jù)。

if(pjp.getArgs().length>0){
	Object args = pjp.getArgs()[0];
	operateLogInfo.setModifiedData(new Gson().toJson(args));
}

接下來(lái)的一步就是對(duì)修改前的數(shù)據(jù)進(jìn)行拼接。之前我們提到過(guò)如果是修改和刪除,我們才會(huì)進(jìn)行數(shù)據(jù)的拼接獲取,主要是通過(guò)類來(lái)判斷書(shū)否存在注解,如果存在注解,那么就要判斷注解上的值是否是控制或者,非空才能正確的進(jìn)行拼接。取field的值的時(shí)候,要注意私有的變量需要通過(guò)setAccessible(true)才可以進(jìn)行訪問(wèn)。

if(GlobalStaticParas.OPERATE_MOD.equals(operateType) ||
	GlobalStaticParas.OPERATE_DELETE.equals(operateType)){
	String tableName = "";
	String idName = "";
	String selectPrimaryKey = "";
	if(pjp.getArgs().length>0){
		Object args = pjp.getArgs()[0];
		//獲取操作前的數(shù)據(jù)
		boolean selectTableFlag = args.getClass().isAnnotationPresent(SelectTable.class);
		if(selectTableFlag){
			tableName = args.getClass().getAnnotation(SelectTable.class).tableName();
			idName = args.getClass().getAnnotation(SelectTable.class).idName();
		}else {
			throw new RuntimeException("操作日志類型為修改或刪除,實(shí)體類必須指定表面和主鍵注解!");
		}
		Field[] fields = args.getClass().getDeclaredFields();
		Field[] fieldsCopy = fields;
		boolean isFindField = false;
		int fieldLength = fields.length;
		for(int i = 0; i < fieldLength; ++i) {
			Field field = fieldsCopy[i];
			boolean hasPrimaryField = field.isAnnotationPresent(SelectPrimaryKey.class);
			if (hasPrimaryField) {
				isFindField = true;
				field.setAccessible(true);
				selectPrimaryKey = (String)field.get(args);
			}
		}
		if(!isFindField){
			throw new RuntimeException("實(shí)體類必須指定主鍵屬性!");
		}
	}
	if(StringUtils.isNotEmpty(tableName) &&
		StringUtils.isNotEmpty(idName)&&
		StringUtils.isNotEmpty(selectPrimaryKey)){
		StringBuffer sb = new StringBuffer();
		sb.append(" select * from ");
		sb.append(tableName);
		sb.append(" where ");
		sb.append(idName);
		sb.append(" = ? ");
		String sql = sb.toString();
		try{
			List<Map<String, Object>> maps = jdbcTemplate.queryForList(sql, selectPrimaryKey);
			if(maps!=null){
				operateLogInfo.setPreModifiedData(new Gson().toJson(maps));
			}
		}catch (Exception e){
			e.printStackTrace();
			throw new RuntimeException("查詢操作前數(shù)據(jù)出錯(cuò)!");
		}
	}else {
		throw new RuntimeException("表名、主鍵名或主鍵值 存在空值情況,請(qǐng)核實(shí)!");
	}
}else{
	operateLogInfo.setPreModifiedData("");
}

切面的完整實(shí)現(xiàn)代碼

@Aspect
@Component
public class OperateLogAspect {

	@Autowired
	private JdbcTemplate jdbcTemplate;

	@Pointcut("@annotation(com.jichi.aop.operateLog.OperateLog)")
	private void operateLogPointCut(){
	}

	@Around("operateLogPointCut()")
	public Object around(ProceedingJoinPoint pjp) throws Throwable {
		Object responseObj = null;
		OperateLogInfo operateLogInfo = new OperateLogInfo();
		String flag = "success";
		try{
			HttpServletRequest request = SpringContextUtil.getHttpServletRequest();
			DomainUserDetails currentUser = SecurityUtils.getCurrentUser();
			if(currentUser!=null){
				operateLogInfo.setUserId(currentUser.getId());
				operateLogInfo.setUserName(currentUser.getUsername());
			}
			MethodSignature signature = (MethodSignature) pjp.getSignature();
			OperateLog declaredAnnotation = signature.getMethod().getDeclaredAnnotation(OperateLog.class);
			operateLogInfo.setOperation(declaredAnnotation.operation());
			operateLogInfo.setModule(declaredAnnotation.module());
			operateLogInfo.setOperateType(declaredAnnotation.operateType());
			//獲取執(zhí)行的方法
			String method = signature.getDeclaringType().getName() + "." + signature.getName();
			operateLogInfo.setMethod(method);
			String operateType = declaredAnnotation.operateType();
			if(pjp.getArgs().length>0){
				Object args = pjp.getArgs()[0];
				operateLogInfo.setModifiedData(new Gson().toJson(args));
			}
			if(GlobalStaticParas.OPERATE_MOD.equals(operateType) ||
				GlobalStaticParas.OPERATE_DELETE.equals(operateType)){
				String tableName = "";
				String idName = "";
				String selectPrimaryKey = "";
				if(pjp.getArgs().length>0){
					Object args = pjp.getArgs()[0];
					//獲取操作前的數(shù)據(jù)
					boolean selectTableFlag = args.getClass().isAnnotationPresent(SelectTable.class);
					if(selectTableFlag){
						tableName = args.getClass().getAnnotation(SelectTable.class).tableName();
						idName = args.getClass().getAnnotation(SelectTable.class).idName();
					}else {
						throw new RuntimeException("操作日志類型為修改或刪除,實(shí)體類必須指定表面和主鍵注解!");
					}
					Field[] fields = args.getClass().getDeclaredFields();
					Field[] fieldsCopy = fields;
					boolean isFindField = false;
					int fieldLength = fields.length;
					for(int i = 0; i < fieldLength; ++i) {
						Field field = fieldsCopy[i];
						boolean hasPrimaryField = field.isAnnotationPresent(SelectPrimaryKey.class);
						if (hasPrimaryField) {
							isFindField = true;
							field.setAccessible(true);
							selectPrimaryKey = (String)field.get(args);
						}
					}
					if(!isFindField){
						throw new RuntimeException("實(shí)體類必須指定主鍵屬性!");
					}
				}
				if(StringUtils.isNotEmpty(tableName) &&
					StringUtils.isNotEmpty(idName)&&
					StringUtils.isNotEmpty(selectPrimaryKey)){
					StringBuffer sb = new StringBuffer();
					sb.append(" select * from ");
					sb.append(tableName);
					sb.append(" where ");
					sb.append(idName);
					sb.append(" = ? ");
					String sql = sb.toString();
					try{
						List<Map<String, Object>> maps = jdbcTemplate.queryForList(sql, selectPrimaryKey);
						if(maps!=null){
							operateLogInfo.setPreModifiedData(new Gson().toJson(maps));
						}
					}catch (Exception e){
						e.printStackTrace();
						throw new RuntimeException("查詢操作前數(shù)據(jù)出錯(cuò)!");
					}
				}else {
					throw new RuntimeException("表名、主鍵名或主鍵值 存在空值情況,請(qǐng)核實(shí)!");
				}
			}else{
				operateLogInfo.setPreModifiedData("");
			}
			//操作時(shí)間
			Date beforeDate = new Date();
			Long startTime = beforeDate.getTime();
			operateLogInfo.setExecuteTime(beforeDate);
			responseObj = pjp.proceed();
			Date afterDate = new Date();
			Long endTime = afterDate.getTime();
			Long duration = endTime - startTime;
			operateLogInfo.setDuration(duration);
			operateLogInfo.setIp(getIp(request));
			operateLogInfo.setResult(flag);
		}catch (RuntimeException e){
			throw new RuntimeException(e);
		}catch (Exception e){
			flag = "fail";
			operateLogInfo.setResult(flag);
			operateLogInfo.setErrorMessage(e.getMessage());
			operateLogInfo.setErrorStackTrace(e.getStackTrace().toString());
			e.printStackTrace();
		}finally {
			insertIntoLogTable(operateLogInfo);
		}
		return responseObj;
	}

	private void insertIntoLogTable(OperateLogInfo operateLogInfo){
		operateLogInfo.setId(UUID.randomUUID().toString().replace("-",""));
		String sql="insert into energy_log values(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)";
		jdbcTemplate.update(sql,operateLogInfo.getId(),operateLogInfo.getUserId(),
			operateLogInfo.getUserName(),operateLogInfo.getOperation(),operateLogInfo.getMethod(),
			operateLogInfo.getModifiedData(),operateLogInfo.getPreModifiedData(),
			operateLogInfo.getResult(),operateLogInfo.getErrorMessage(),operateLogInfo.getErrorStackTrace(),
			operateLogInfo.getExecuteTime(),operateLogInfo.getDuration(),operateLogInfo.getIp(),
			operateLogInfo.getModule(),operateLogInfo.getOperateType());
	}

	private String getIp(HttpServletRequest request){
		String ip = request.getHeader("X-forwarded-for");
		if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
			ip = request.getHeader("Proxy-Client-IP");
		}
		if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
			ip = request.getHeader("WL-Proxy-Client-IP");
		}
		if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
			ip = request.getHeader("HTTP_CLIENT_IP");
		}
		if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
			ip = request.getHeader("HTTP_X_FORWARDED_FOR");
		}
		if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
			ip = request.getRemoteAddr();
		}
		return ip;
	}
}

示例的使用方式

針對(duì)于示例來(lái)說(shuō)我們要在controller上面打上操作日志的注解。

  @PostMapping("/updateInfo")
  @OperateLog(operation = "修改信息",operateType = GlobalStaticParas.OPERATE_MOD)
  public void updateInfo(@RequestBody Info info) {
    service.updateInfo(info);
  }

針對(duì)于Info的實(shí)體類,我們則要對(duì)其中的字段和表名進(jìn)行標(biāo)識(shí)。

@Data
@SelectTable(tableName = "info",idName = "id")
public class Info {

  @SelectPrimaryKey
  private String id;
  
  private String name;

}

總結(jié)

文章寫(xiě)到這,也就結(jié)束了,文中難免有不足,歡迎大家批評(píng)指正

以上就是SpringAop實(shí)現(xiàn)操作日志記錄的詳細(xì)內(nèi)容,更多關(guān)于SpringAop 操作日志記錄的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • String轉(zhuǎn)JSONObject的兩種方式

    String轉(zhuǎn)JSONObject的兩種方式

    這篇文章主要介紹了String轉(zhuǎn)JSONObject,本文通過(guò)實(shí)例代碼給大家介紹兩種方式轉(zhuǎn)換,結(jié)合實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2023-07-07
  • 使用SpringBoot生成war包的流程步驟

    使用SpringBoot生成war包的流程步驟

    一般情況下,在SpringBoot項(xiàng)目開(kāi)發(fā)完成進(jìn)行服務(wù)器部署時(shí),都是打成JAR包進(jìn)行部署運(yùn)行的,但是在有些情況下也需要將其打成War包使用Tomcat進(jìn)行部署,本篇文章就簡(jiǎn)單介紹一下SpringBoot如何打成War包,需要的朋友可以參考下
    2024-10-10
  • Spring中DeferredResult異步處理

    Spring中DeferredResult異步處理

    DeferredResult是Spring中處理異步請(qǐng)求的強(qiáng)大工具,可以幫助改善應(yīng)用程序的性能和用戶體驗(yàn),本文就來(lái)介紹一下Spring中DeferredResult異步處理,感興趣的可以了解一下
    2023-12-12
  • 一文掌握Springboot集成RabbitMQ的方法

    一文掌握Springboot集成RabbitMQ的方法

    RabbitMQ是開(kāi)源的消息隊(duì)列服務(wù)器,實(shí)現(xiàn)了AMQP高級(jí)消息隊(duì)列協(xié)議標(biāo)準(zhǔn),AMQP?是一種應(yīng)用層協(xié)議,為面向消息的中間件設(shè)計(jì),基于此協(xié)議的客戶端與消息中間件可傳遞消息,并不受產(chǎn)品、開(kāi)發(fā)語(yǔ)言等條件的限制,這篇文章主要介紹了Springboot集成RabbitMQ的過(guò)程,需要的朋友可以參考下
    2024-04-04
  • 使用Feign調(diào)用第三方http接口

    使用Feign調(diào)用第三方http接口

    這篇文章主要介紹了使用Feign調(diào)用第三方http接口,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-03-03
  • java執(zhí)行SQL語(yǔ)句實(shí)現(xiàn)查詢的通用方法詳解

    java執(zhí)行SQL語(yǔ)句實(shí)現(xiàn)查詢的通用方法詳解

    這篇文章主要介紹了java執(zhí)行SQL語(yǔ)句實(shí)現(xiàn)查詢的通用方法詳解,具有一定借鑒價(jià)值,需要的朋友可以參考下。
    2017-12-12
  • Java中的字符編碼問(wèn)題處理心得總結(jié)

    Java中的字符編碼問(wèn)題處理心得總結(jié)

    Java中的String類默認(rèn)是Unicode編碼,大家平常所說(shuō)的UTF-8就是使用最廣的一種unicode的實(shí)現(xiàn)方式,以下我們還是來(lái)看一下Java中的字符編碼問(wèn)題處理心得總結(jié):
    2016-06-06
  • Spring中依賴注入(DI)幾種方式解讀

    Spring中依賴注入(DI)幾種方式解讀

    這篇文章主要介紹了Spring中依賴注入(DI)幾種方式解讀,構(gòu)造器依賴注入通過(guò)容器觸發(fā)一個(gè)類的構(gòu)造器來(lái)實(shí)現(xiàn)的,該類有一系列參數(shù),每個(gè)參數(shù)代表一個(gè)對(duì)其他類的依賴,需要的朋友可以參考下
    2024-01-01
  • IntelliJ IDEA中ajax開(kāi)發(fā)實(shí)現(xiàn)分頁(yè)查詢示例

    IntelliJ IDEA中ajax開(kāi)發(fā)實(shí)現(xiàn)分頁(yè)查詢示例

    這篇文章主要介紹了IntelliJ IDEA中ajax開(kāi)發(fā)實(shí)現(xiàn)分頁(yè)查詢,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2018-03-03
  • 淺談Java序列化和反序列化為何要實(shí)現(xiàn)Serializable接口

    淺談Java序列化和反序列化為何要實(shí)現(xiàn)Serializable接口

    這篇文章主要介紹了淺談Java序列化和反序列化為何要實(shí)現(xiàn)Serializable接口,序列化最重要的作用是在傳遞和保存對(duì)象時(shí).保證對(duì)象的完整性和可傳遞性,對(duì)象轉(zhuǎn)換為有序字節(jié)流,以便在網(wǎng)絡(luò)上傳輸或者保存在本地文件中,需要的朋友可以參考下
    2023-12-12

最新評(píng)論

钦州市| 云阳县| 定西市| 乌拉特中旗| 津市市| 龙南县| 锡林浩特市| 佛坪县| 武胜县| 凤城市| 宜兰县| 宁明县| 治县。| 灌南县| 琼海市| 嘉禾县| 阿克苏市| 托克托县| 永年县| 称多县| 乐平市| 仁化县| 喀喇| 富锦市| 尉犁县| 申扎县| 鄱阳县| 都匀市| 安远县| 册亨县| 普定县| 珠海市| 崇文区| 陈巴尔虎旗| 改则县| 云南省| 丹阳市| 衢州市| 丹巴县| 滨海县| 仪陇县|