欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

SpringBoot實現(xiàn)字段自動填充的兩種方式

 更新時間:2024年11月11日 11:21:49   作者:waterme1onY  
每個字段在插入數(shù)據(jù)庫,或者更新時都要在serviceimpl層對creatby,updateby等字段進行填充,這個太繁瑣了,所以本文給大家介紹了SpringBoot實現(xiàn)字段自動填充的兩種方式,需要的朋友可以參考下

creatby,updateby等字段自動填充

每個字段在插入數(shù)據(jù)庫,或者更新時都要在serviceimpl層對creatby,updateby等字段進行填充,這個太繁瑣了,以下兩種方法可以實現(xiàn)字段的自動填充。本項目使用第一種。

方法一:

首先創(chuàng)建一個AutoFillInterceptor類。下面會對代碼逐行分析。
以下代碼也可以直接復制粘貼,但前提是你的實體類中的自動填充的字段和下面四個靜態(tài)常量名字一樣。

@Component
@Intercepts({
        @Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class})
})
public class AutoFillInterceptor implements Interceptor {


    private static final String CREATE_BY = "createdBy";
    private static final String UPDATE_BY = "updatedBy";

    private static final String CREATE_TIME = "createdAt";
    private static final String UPDATE_TIME = "updatedAt";

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        Object[] args = invocation.getArgs();
        MappedStatement ms = (MappedStatement) args[0];
        SqlCommandType sqlCommandType = ms.getSqlCommandType();
        Object parameter = args[1];
        if(parameter != null && sqlCommandType!=null){
            if(SqlCommandType.INSERT.equals(sqlCommandType)){
                if(parameter instanceof MapperMethod.ParamMap){
                    MapperMethod.ParamMap paramMap = (MapperMethod.ParamMap) parameter;
                    ArrayList list= (ArrayList) paramMap.get("list");
                    list.forEach(v->{
                        setFieldValByName(CREATE_TIME, LocalDateTime.now(), v);
                        setFieldValByName(UPDATE_TIME, LocalDateTime.now(), v);
                    });
                    paramMap.put("list", list);
                }else{
                    // 單條插入的情況
                    // 設置創(chuàng)建人和創(chuàng)建時間字段值
                    setFieldValByName(CREATE_TIME, LocalDateTime.now(), parameter);
                    setFieldValByName(UPDATE_TIME, LocalDateTime.now(), parameter);
                }
            }
            else if(SqlCommandType.UPDATE.equals(sqlCommandType)){
                // 更新操作
                // 設置更新人和更新時間字段值
                setFieldValByName(UPDATE_TIME, LocalDateTime.now(), parameter);
            }
        }

        // 繼續(xù)執(zhí)行原始方法
        return invocation.proceed();
    }

    private void setFieldValByName(String fieldName, Object fieldVal, Object parameter) {
        MetaObject metaObject = SystemMetaObject.forObject(parameter);

        if (metaObject.hasSetter(fieldName)) {
            metaObject.setValue(fieldName, fieldVal);
        }
    }

    @Override
    public void setProperties(Properties properties) {
        Interceptor.super.setProperties(properties);
    }

    @Override
    public Object plugin(Object target) {
        return Interceptor.super.plugin(target);
    }
}

代碼結(jié)構(gòu)與作用

這是一個實現(xiàn)了MyBatis攔截器(Interceptor接口)的類AutoFillInterceptor,用于在執(zhí)行SQL操作(INSERT或UPDATE)時,自動填充一些通用字段,比如創(chuàng)建時間(createdAt)、更新時間(updatedAt)等。

在企業(yè)級項目中,通常需要記錄數(shù)據(jù)的創(chuàng)建時間和修改時間,這個攔截器就是為了解決這種需求,在新增和修改數(shù)據(jù)時自動填充這些字段。下面我們來逐行分析代碼。

代碼逐行解析

@Component
@Intercepts({
        @Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class})
})
  • @Component:Spring的注解,將這個類注冊為一個Spring Bean,便于管理。
  • @Intercepts:MyBatis的注解,聲明這是一個攔截器,并指定要攔截的目標。
    • @Signature:定義攔截器的具體攔截方法。
      • type = Executor.class:表示攔截MyBatis的Executor類。
      • method = "update":表示攔截update方法,這個方法用于執(zhí)行更新操作(包括INSERT、UPDATE、DELETE)。
      • args = {MappedStatement.class, Object.class}:指定update方法的參數(shù)類型,即SQL映射信息MappedStatement和參數(shù)對象Object。
public class AutoFillInterceptor implements Interceptor {
  • 這幾行定義了一些常量,分別表示字段名稱,如創(chuàng)建者、修改者、創(chuàng)建時間和修改時間。這些常量將在攔截邏輯中用來自動填充字段。
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        Object[] args = invocation.getArgs();
        MappedStatement ms = (MappedStatement) args[0];
        SqlCommandType sqlCommandType = ms.getSqlCommandType();
        Object parameter = args[1];
  • intercept方法是攔截器的核心邏輯。
    • Object[] args = invocation.getArgs():獲取攔截方法的參數(shù)。
    • MappedStatement ms = (MappedStatement) args[0]:獲取MappedStatement,包含了有關(guān)SQL語句的信息。
    • SqlCommandType sqlCommandType = ms.getSqlCommandType():獲取SQL的操作類型(INSERT、UPDATE、DELETE)。
    • Object parameter = args[1]:獲取參數(shù)對象,通常是用戶要插入或更新的數(shù)據(jù)。
        if(parameter != null && sqlCommandType != null){
  • 檢查參數(shù)是否為空,并確認操作類型是否非空,確保有必要繼續(xù)執(zhí)行后續(xù)操作。
            if(SqlCommandType.INSERT.equals(sqlCommandType)){
                if(parameter instanceof MapperMethod.ParamMap){
                    MapperMethod.ParamMap paramMap = (MapperMethod.ParamMap) parameter;
                    ArrayList list= (ArrayList) paramMap.get("list");
                    list.forEach(v -> {
                        setFieldValByName(CREATE_TIME, LocalDateTime.now(), v);
                        setFieldValByName(UPDATE_TIME, LocalDateTime.now(), v);
                    });
                    paramMap.put("list", list);
                } else {
                    // 單條插入的情況
                    // 設置創(chuàng)建人和創(chuàng)建時間字段值
                    setFieldValByName(CREATE_TIME, LocalDateTime.now(), parameter);
                    setFieldValByName(UPDATE_TIME, LocalDateTime.now(), parameter);
                }
            }
  • if (SqlCommandType.INSERT.equals(sqlCommandType)):如果當前SQL是INSERT操作:
    • if (parameter instanceof MapperMethod.ParamMap):判斷參數(shù)是否是MapperMethod.ParamMap類型,這通常用于批量插入。
      • ArrayList list = (ArrayList) paramMap.get("list"):從參數(shù)Map中獲取名為list的參數(shù),這是批量插入的數(shù)據(jù)集合。
      • list.forEach(v -> {...}):對每個元素進行操作,調(diào)用setFieldValByName方法設置createdAtupdatedAt為當前時間。
    • else部分:處理單條插入的情況,直接給parameter對象設置創(chuàng)建時間和更新時間。
            else if(SqlCommandType.UPDATE.equals(sqlCommandType)){
                // 更新操作
                // 設置更新人和更新時間字段值
                setFieldValByName(UPDATE_TIME, LocalDateTime.now(), parameter);
            }
  • else if (SqlCommandType.UPDATE.equals(sqlCommandType)):如果當前SQL是UPDATE操作:
    • 使用setFieldValByName方法將updatedAt字段設置為當前時間。
        }

        // 繼續(xù)執(zhí)行原始方法
        return invocation.proceed();
    }
  • 最終通過invocation.proceed()調(diào)用被攔截的方法,繼續(xù)執(zhí)行原始的數(shù)據(jù)庫操作。
    private void setFieldValByName(String fieldName, Object fieldVal, Object parameter) {
        MetaObject metaObject = SystemMetaObject.forObject(parameter);

        if (metaObject.hasSetter(fieldName)) {
            metaObject.setValue(fieldName, fieldVal);
        }
    }
  • setFieldValByName方法用于設置對象中指定字段的值:
    • MetaObject metaObject = SystemMetaObject.forObject(parameter):創(chuàng)建MetaObject,用于操作傳入對象的元數(shù)據(jù)。
    • if (metaObject.hasSetter(fieldName)):檢查對象是否有對應字段的setter方法。
    • metaObject.setValue(fieldName, fieldVal):如果有setter方法,則設置字段的值。
    @Override
    public void setProperties(Properties properties) {
        Interceptor.super.setProperties(properties);
    }

    @Override
    public Object plugin(Object target) {
        return Interceptor.super.plugin(target);
    }
}
  • setPropertiesplugin方法是Interceptor接口的默認實現(xiàn),plugin方法用于生成代理對象。

總結(jié)

  • 這個攔截器的作用是自動填充createdAtupdatedAt字段,以便在執(zhí)行INSERT和UPDATE操作時自動記錄創(chuàng)建和更新時間。
  • 主要攔截Executorupdate方法,通過判斷SQL類型來確定是INSERT還是UPDATE操作,從而設置相應字段。
  • 使用了MyBatis的MetaObject工具類來動態(tài)操作參數(shù)對象的字段值。

通過這個攔截器,開發(fā)者不需要在業(yè)務代碼中手動設置createdAtupdatedAt,大大減少了重復代碼,也保證了這些公共字段的一致性和正確性。

方法二:

這個方法是使用自定義注解來寫的,所以要在需要填充的sql上加上這個注解。這個可能更加靈活更加簡單把。

公共字段自動填充

技術(shù)點:枚舉、注解、AOP、反射
創(chuàng)建時間、修改時間、創(chuàng)建人、修改人這4個公共字段。
為mapper方法加注解AutoFill,標識需要進行公共字段自動填充
自定義切面類AutoFillAspect,統(tǒng)一攔截加入了AutoFill注解的方法,通過反射為公共字段賦值。
在Mapper的方法上接入AutoFill注解。

public enum OperationType {
    更新操作
    UPDATE,
    插入操作
    INSERT
}

@Target(ElementType.METHOD)當前注解加在什么位置
@Retention(RetentionPolicy.RUNTIME)
public @interface AutoFill {
    //數(shù)據(jù)庫操作類型:UPDATE INSERT
    OperationType value();
}

補充注解基本知識

public @interface MyAnnotation {
    // 定義注解的成員
    String value(); // 這是一個名為"value"的成員
    int count() default 1; // 這是一個名為"count"的成員,帶有默認值
}

@MyAnnotation(value = "Hello", count = 3)
public class MyClass {
    // 類的代碼
}

對于AutoFillAspect類切點、execution表達式

/**
 * 自定義切面,實現(xiàn)公共字段自動填充處理邏輯
 */
@Aspect
@Component
@Slf4j
public class AutoFillAspect {


    /**
     * 切入點
     */
     									所有的類,所有的方法,所有的參數(shù)類型
    @Pointcut("execution(* com.sky.mapper.*.*(..)) && @annotation(com.sky.annotation.AutoFill)")
    public void autoFillPointCut(){}

    /**
     * 前置通知,在通知中進行公共字段的賦值
     */
    @Before("autoFillPointCut()")指定切入點
    public void autoFill(JoinPoint joinPoint){連接點
        log.info("開始進行公共字段自動填充...");

        //獲取到當前被攔截的方法上的數(shù)據(jù)庫操作類型
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();//方法簽名對象
        AutoFill autoFill = signature.getMethod().getAnnotation(AutoFill.class);//獲得方法上的注解對象
        OperationType operationType = autoFill.value();//獲得數(shù)據(jù)庫操作類型

        //獲取到當前被攔截的方法的參數(shù)--實體對象	做一個約定,實體對象放第一個
        Object[] args = joinPoint.getArgs();
        if(args == null || args.length == 0){
            return;
        }

        Object entity = args[0];實體

        //準備賦值的數(shù)據(jù)
        LocalDateTime now = LocalDateTime.now();
        Long currentId = BaseContext.getCurrentId();

        //根據(jù)當前不同的操作類型,為對應的屬性通過反射來賦值
        if(operationType == OperationType.INSERT){
            //為4個公共字段賦值
            try {
                Method setCreateTime = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_CREATE_TIME, LocalDateTime.class);
                Method setCreateUser = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_CREATE_USER, Long.class);
                Method setUpdateTime = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_TIME, LocalDateTime.class);
                Method setUpdateUser = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_USER, Long.class);

                //通過反射為對象屬性賦值
                setCreateTime.invoke(entity,now);
                setCreateUser.invoke(entity,currentId);
                setUpdateTime.invoke(entity,now);
                setUpdateUser.invoke(entity,currentId);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }else if(operationType == OperationType.UPDATE){
            //為2個公共字段賦值
            try {
                Method setUpdateTime = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_TIME, LocalDateTime.class);
                Method setUpdateUser = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_USER, Long.class);

                //通過反射為對象屬性賦值
                setUpdateTime.invoke(entity,now);
                setUpdateUser.invoke(entity,currentId);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

}

使用

@AutoFill(value = OperationType.UPDATE)
void update(Employee employee);

自定義切面:實現(xiàn)公共字段的自動填充

這段代碼使用了 Spring AOP(面向切面編程)來實現(xiàn)對數(shù)據(jù)庫操作時,自動填充一些公共字段,例如創(chuàng)建時間、更新時間、創(chuàng)建人、更新人等。接下來,我們逐行解析這段代碼,以幫助你理解各個部分的功能和實現(xiàn)邏輯。

代碼結(jié)構(gòu)概覽

@Aspect
@Component
@Slf4j
public class AutoFillAspect {
    // 切入點
    @Pointcut("execution(* com.sky.mapper.*.*(..)) && @annotation(com.sky.annotation.AutoFill)")
    public void autoFillPointCut(){}

    // 前置通知
    @Before("autoFillPointCut()")
    public void autoFill(JoinPoint joinPoint){
        log.info("開始進行公共字段自動填充...");
        ...
    }
}

這段代碼定義了一個切面 AutoFillAspect,它會在符合條件的數(shù)據(jù)庫操作方法執(zhí)行之前,通過前置通知 (@Before) 自動對某些公共字段進行填充。

注解解釋

  1. @Aspect:表示當前類是一個切面類,用于定義通知和切入點。
  2. @Component:把這個切面類注冊為 Spring 容器中的一個組件。
  3. @Slf4j:用來啟用日志功能,以方便調(diào)試和記錄信息。

切入點定義

@Pointcut("execution(* com.sky.mapper.*.*(..)) && @annotation(com.sky.annotation.AutoFill)")
public void autoFillPointCut(){}

解釋:

  • @Pointcut:用于定義一個切入點,描述哪些方法需要被切面邏輯攔截。
  • execution(* com.sky.mapper.*.*(..)):匹配 com.sky.mapper 包下的所有類和所有方法,(..) 表示任意參數(shù)類型和數(shù)量。
  • && @annotation(com.sky.annotation.AutoFill):表示只攔截被 @AutoFill 注解標記的方法。

通過這種定義,只有符合指定包下的類且有 @AutoFill 注解的方法,才會被切面邏輯攔截。

前置通知(Before Advice)

@Before("autoFillPointCut()")
public void autoFill(JoinPoint joinPoint) {
    log.info("開始進行公共字段自動填充...");
    ...
}
  • @Before("autoFillPointCut()"):這是前置通知,表示在切入點所匹配的方法執(zhí)行之前,執(zhí)行 autoFill() 方法。
  • JoinPoint joinPoint:JoinPoint 是一個連接點,表示被攔截的方法,允許獲取到目標方法的一些信息,比如方法名和參數(shù)等。

獲取注解和方法信息

MethodSignature signature = (MethodSignature) joinPoint.getSignature();
AutoFill autoFill = signature.getMethod().getAnnotation(AutoFill.class);
OperationType operationType = autoFill.value();
  1. MethodSignature signature = (MethodSignature) joinPoint.getSignature();:獲取當前攔截的方法的簽名信息,轉(zhuǎn)換為 MethodSignature 類型。
  2. AutoFill autoFill = signature.getMethod().getAnnotation(AutoFill.class);:獲取方法上的 @AutoFill 注解對象。
  3. OperationType operationType = autoFill.value();:獲取注解中指定的數(shù)據(jù)庫操作類型(例如 INSERT 或 UPDATE)。

獲取方法參數(shù)

Object[] args = joinPoint.getArgs();
if (args == null || args.length == 0) {
    return;
}
Object entity = args[0];
  • Object[] args = joinPoint.getArgs();:獲取當前被攔截的方法的參數(shù)。
  • if (args == null || args.length == 0):如果沒有參數(shù),直接返回。
  • Object entity = args[0];:假設第一個參數(shù)是實體對象,用于操作數(shù)據(jù)庫。這里有一個約定,即實體對象總是第一個參數(shù)。

準備賦值的數(shù)據(jù)

LocalDateTime now = LocalDateTime.now();
Long currentId = BaseContext.getCurrentId();
  • LocalDateTime now = LocalDateTime.now();:獲取當前時間,用于填充創(chuàng)建時間和更新時間。
  • Long currentId = BaseContext.getCurrentId();:獲取當前操作用戶的 ID,用于填充創(chuàng)建人和更新人信息。

根據(jù)操作類型進行賦值

插入操作(INSERT)

if (operationType == OperationType.INSERT) {
    try {
        Method setCreateTime = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_CREATE_TIME, LocalDateTime.class);
        Method setCreateUser = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_CREATE_USER, Long.class);
        Method setUpdateTime = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_TIME, LocalDateTime.class);
        Method setUpdateUser = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_USER, Long.class);

        setCreateTime.invoke(entity, now);
        setCreateUser.invoke(entity, currentId);
        setUpdateTime.invoke(entity, now);
        setUpdateUser.invoke(entity, currentId);
    } catch (Exception e) {
        e.printStackTrace();
    }
}
  • if (operationType == OperationType.INSERT):如果數(shù)據(jù)庫操作類型是插入(INSERT)。
  • 通過反射的方式獲取實體類中的 setCreateTime、setCreateUser、setUpdateTime 和 setUpdateUser 方法。
  • invoke() 方法用于調(diào)用這些 setter 方法并傳入相應的值,完成公共字段的賦值。

更新操作(UPDATE)

else if (operationType == OperationType.UPDATE) {
    try {
        Method setUpdateTime = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_TIME, LocalDateTime.class);
        Method setUpdateUser = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_USER, Long.class);

        setUpdateTime.invoke(entity, now);
        setUpdateUser.invoke(entity, currentId);
    } catch (Exception e) {
        e.printStackTrace();
    }
}
  • else if (operationType == OperationType.UPDATE):如果操作類型是更新(UPDATE)。
  • 這里只需填充更新相關(guān)的字段,即更新時間和更新人。

小結(jié)

這段代碼實現(xiàn)了對數(shù)據(jù)庫操作的公共字段自動填充,具體如下:

  • 定義一個切面 AutoFillAspect,用于攔截特定包中的方法,并且方法需要用 @AutoFill 注解進行標記。
  • 使用 AOP 的前置通知在方法執(zhí)行前進行字段自動填充。
  • 通過反射機制獲取實體對象的方法并進行賦值,根據(jù)操作類型填充不同的字段。

這使得代碼變得更加簡潔和可維護,減少了重復的公共字段賦值邏輯,也方便對創(chuàng)建時間、更新時間等公共屬性的一致性管理。

以上就是SpringBoot實現(xiàn)字段自動填充的兩種方式的詳細內(nèi)容,更多關(guān)于SpringBoot字段自動填充的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Java之jdbc連接mysql數(shù)據(jù)庫的方法步驟詳解

    Java之jdbc連接mysql數(shù)據(jù)庫的方法步驟詳解

    這篇文章主要介紹了Java之jdbc連接mysql數(shù)據(jù)庫的方法步驟詳解,JCBC技術(shù)是java開發(fā)必備的只是,jdbc連接mysql數(shù)據(jù)庫,這是一個比較簡單的方法,有興趣的可以了解一下
    2020-07-07
  • 詳解springcloud Feign的Hystrix支持

    詳解springcloud Feign的Hystrix支持

    這篇文章主要介紹了詳解springcloud Feign的Hystrix支持,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-01-01
  • 關(guān)于MVC與SpringMVC的介紹、區(qū)別、執(zhí)行流程

    關(guān)于MVC與SpringMVC的介紹、區(qū)別、執(zhí)行流程

    這篇文章主要介紹了關(guān)于MVC與SpringMVC的介紹、區(qū)別、執(zhí)行流程,MVC框架的主要目標是將應用程序的業(yè)務邏輯(Model)與用戶界面(View)分離開來,從而提高應用程序的可維護性和可擴展性,需要的朋友可以參考下
    2023-05-05
  • Mybatis下動態(tài)sql中##和$$的區(qū)別講解

    Mybatis下動態(tài)sql中##和$$的區(qū)別講解

    今天小編就為大家分享一篇關(guān)于Mybatis下動態(tài)sql中##和$$的區(qū)別講解,小編覺得內(nèi)容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧
    2019-03-03
  • 純Java代碼實現(xiàn)流星劃過天空

    純Java代碼實現(xiàn)流星劃過天空

    本文給大家介紹純java代碼實現(xiàn)流星劃過天空,包括流星個數(shù),流星飛行的速度,色階,流星大小相關(guān)變量設置。對java流星劃過天空特效代碼感興趣的朋友可以參考下本文
    2015-10-10
  • SpringBoot實現(xiàn)kafka多源配置的示例代碼

    SpringBoot實現(xiàn)kafka多源配置的示例代碼

    實際開發(fā)中,不同的topic可能來自不同的集群,所以就需要配置不同的kafka數(shù)據(jù)源,基于springboot自動配置的思想,最終通過配置文件的配置,自動生成生產(chǎn)者及消費者的配置,本文介紹了SpringBoot實現(xiàn)kafka多源配置,需要的朋友可以參考下
    2024-06-06
  • Java編程刪除鏈表中重復的節(jié)點問題解決思路及源碼分享

    Java編程刪除鏈表中重復的節(jié)點問題解決思路及源碼分享

    這篇文章主要介紹了Java編程刪除鏈表中重復的節(jié)點問題解決思路及源碼分享,具有一定參考價值,這里分享給大家,供需要的朋友了解。
    2017-10-10
  • 使用Spring底層組件實現(xiàn)Aware接口

    使用Spring底層組件實現(xiàn)Aware接口

    這篇文章主要介紹了使用Spring底層組件實現(xiàn)Aware接口,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2023-07-07
  • Java實現(xiàn)輕松處理日期和時間的API小結(jié)

    Java實現(xiàn)輕松處理日期和時間的API小結(jié)

    這篇文章主要為大家詳細介紹了Java中的日期和時間API,可以輕松處理日期和時間,文中的示例代碼講解詳細,感興趣的小伙伴可以跟隨小編一起學習一下
    2024-03-03
  • Java利用Request請求如何獲取IP地址對應的省份、城市詳解

    Java利用Request請求如何獲取IP地址對應的省份、城市詳解

    之前已經(jīng)給大家介紹了關(guān)于Java用Request請求獲取IP地址的相關(guān)內(nèi)容,那么下面這篇文章將給大家進入深入的介紹,關(guān)于Java利用Request請求如何獲取IP地址對應省份、城市的相關(guān)資料,需要的朋友可以參考借鑒,下面來一起看看吧。
    2017-10-10

最新評論