springboot 實現記錄業務日志和異常業務日志的操作

日志記錄到redis展現形式

1.基於註解的方式實現日志記錄,掃描對應的方法實現日志記錄

@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface BussinessLog {
 
    /**
     * 業務的名稱,例如:"修改菜單"
     */
    String value() default "";
 
    /**
     * 被修改的實體的唯一標識,例如:菜單實體的唯一標識為"id"
     */
    String key() default "id";
 
    /**
     * 業務類型
     */
    String type() default "0";
 
    /**
     * 字典(用於查找key的中文名稱和字段的中文名稱)
     */
    Class<? extends AbstractDictMap> dict() default SystemDict.class; 
}

2.掃描的方法,基於註解實現方法掃描並且記錄日志

3.基於@Aspect註解,實現日志掃描,並且記錄日志

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component; 
import java.lang.reflect.Method;
import java.util.Map;
 
/**
 * 日志記錄
 *
 */
@Aspect
@Component
public class LogAop { 
    private Logger log = LoggerFactory.getLogger(this.getClass()); 
    @Pointcut(value = "@annotation(com.stylefeng.guns.core.common.annotion.BussinessLog)")
    public void cutService() {
    }
 
    @Around("cutService()")
    public Object recordSysLog(ProceedingJoinPoint point) throws Throwable {
 
        //先執行業務
        Object result = point.proceed();
 
        try {
            handle(point);
        } catch (Exception e) {
            log.error("日志記錄出錯!", e);
        } 
        return result;
    }
 
    private void handle(ProceedingJoinPoint point) throws Exception {
 
        //獲取攔截的方法名
        Signature sig = point.getSignature();
        MethodSignature msig = null;
        if (!(sig instanceof MethodSignature)) {
            throw new IllegalArgumentException("該註解隻能用於方法");
        }
        msig = (MethodSignature) sig;
        Object target = point.getTarget();
        Method currentMethod = target.getClass().getMethod(msig.getName(), msig.getParameterTypes());
        String methodName = currentMethod.getName();
 
        //如果當前用戶未登錄,不做日志
        ShiroUser user = ShiroKit.getUser();
        if (null == user) {
            return;
        }
 
        //獲取攔截方法的參數
        String className = point.getTarget().getClass().getName();
        Object[] params = point.getArgs();
 
        //獲取操作名稱
        BussinessLog annotation = currentMethod.getAnnotation(BussinessLog.class);
        String bussinessName = annotation.value();
        String key = annotation.key();
        Class dictClass = annotation.dict();
 
        StringBuilder sb = new StringBuilder();
        for (Object param : params) {
            sb.append(param);
            sb.append(" & ");
        }
 
        //如果涉及到修改,比對變化
        String msg;
        if (bussinessName.contains("修改") || bussinessName.contains("編輯")) {
            Object obj1 = LogObjectHolder.me().get();
            Map<String, String> obj2 = HttpContext.getRequestParameters();
            msg = Contrast.contrastObj(dictClass, key, obj1, obj2);
        } else {
            Map<String, String> parameters = HttpContext.getRequestParameters();
            AbstractDictMap dictMap = (AbstractDictMap) dictClass.newInstance();
            msg = Contrast.parseMutiKey(dictMap, key, parameters);
        }
        log.info("[記錄日志][RESULT:{}]",user.getId()+bussinessName+className+methodName+msg.toString());
        LogManager.me().executeLog(LogTaskFactory.bussinessLog(user.getId(), bussinessName, className, methodName, msg));
    }
}

4.比較兩個對象的工具類

import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Date;
import java.util.Map;
 
/**
 * 對比兩個對象的變化的工具類
 *
 * @author ...
 * @Date 2017/3/31 10:36
 */
public class Contrast {
 
    //記錄每個修改字段的分隔符
    public static final String separator = ";;;";
 
    /**
     * 比較兩個對象,並返回不一致的信息
     *
     * @author ...
     * @Date 2017/5/9 19:34
     */
    public static String contrastObj(Object pojo1, Object pojo2) {
        String str = "";
        try {
            Class clazz = pojo1.getClass();
            Field[] fields = pojo1.getClass().getDeclaredFields();
            int i = 1;
            for (Field field : fields) {
                if ("serialVersionUID".equals(field.getName())) {
                    continue;
                }
                PropertyDescriptor pd = new PropertyDescriptor(field.getName(), clazz);
                Method getMethod = pd.getReadMethod();
                Object o1 = getMethod.invoke(pojo1);
                Object o2 = getMethod.invoke(pojo2);
                if (o1 == null || o2 == null) {
                    continue;
                }
                if (o1 instanceof Date) {
                    o1 = DateUtil.getDay((Date) o1);
                }
                if (!o1.toString().equals(o2.toString())) {
                    if (i != 1) {
                        str += separator;
                    }
                    str += "字段名稱" + field.getName() + ",舊值:" + o1 + ",新值:" + o2;
                    i++;
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return str;
    }
 
    /**
     * 比較兩個對象pojo1和pojo2,並輸出不一致信息
     *
     * @author ...
     * @Date 2017/5/9 19:34
     */
    public static String contrastObj(Class dictClass, String key, Object pojo1, Map<String, String> pojo2) throws IllegalAccessException, InstantiationException {
        AbstractDictMap dictMap = (AbstractDictMap) dictClass.newInstance();
        String str = parseMutiKey(dictMap, key, pojo2) + separator;
        try {
            Class clazz = pojo1.getClass();
            Field[] fields = pojo1.getClass().getDeclaredFields();
            int i = 1;
            for (Field field : fields) {
                if ("serialVersionUID".equals(field.getName())) {
                    continue;
                }
                PropertyDescriptor pd = new PropertyDescriptor(field.getName(), clazz);
                Method getMethod = pd.getReadMethod();
                Object o1 = getMethod.invoke(pojo1);
                Object o2 = pojo2.get(StrKit.firstCharToLowerCase(getMethod.getName().substring(3)));
                if (o1 == null || o2 == null) {
                    continue;
                }
                if (o1 instanceof Date) {
                    o1 = DateUtil.getDay((Date) o1);
                } else if (o1 instanceof Integer) {
                    o2 = Integer.parseInt(o2.toString());
                }
                if (!o1.toString().equals(o2.toString())) {
                    if (i != 1) {
                        str += separator;
                    }
                    String fieldName = dictMap.get(field.getName());
                    String fieldWarpperMethodName = dictMap.getFieldWarpperMethodName(field.getName());
                    if (fieldWarpperMethodName != null) {
                        Object o1Warpper = DictFieldWarpperFactory.createFieldWarpper(o1, fieldWarpperMethodName);
                        Object o2Warpper = DictFieldWarpperFactory.createFieldWarpper(o2, fieldWarpperMethodName);
                        str += "字段名稱:" + fieldName + ",舊值:" + o1Warpper + ",新值:" + o2Warpper;
                    } else {
                        str += "字段名稱:" + fieldName + ",舊值:" + o1 + ",新值:" + o2;
                    }
                    i++;
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return str;
    }
 
    /**
     * 比較兩個對象pojo1和pojo2,並輸出不一致信息
     *
     * @author ...
     * @Date 2017/5/9 19:34
     */
    public static String contrastObjByName(Class dictClass, String key, Object pojo1, Map<String, String> pojo2) throws IllegalAccessException, InstantiationException {
        AbstractDictMap dictMap = (AbstractDictMap) dictClass.newInstance();
        String str = parseMutiKey(dictMap, key, pojo2) + separator;
        try {
            Class clazz = pojo1.getClass();
            Field[] fields = pojo1.getClass().getDeclaredFields();
            int i = 1;
            for (Field field : fields) {
                if ("serialVersionUID".equals(field.getName())) {
                    continue;
                }
                String prefix = "get";
                int prefixLength = 3;
                if (field.getType().getName().equals("java.lang.Boolean")) {
                    prefix = "is";
                    prefixLength = 2;
                }
                Method getMethod = null;
                try {
                    getMethod = clazz.getDeclaredMethod(prefix + StrKit.firstCharToUpperCase(field.getName()));
                } catch (NoSuchMethodException e) {
                    System.err.println("this className:" + clazz.getName() + " is not methodName: " + e.getMessage());
                    continue;
                }
                Object o1 = getMethod.invoke(pojo1);
                Object o2 = pojo2.get(StrKit.firstCharToLowerCase(getMethod.getName().substring(prefixLength)));
                if (o1 == null || o2 == null) {
                    continue;
                }
                if (o1 instanceof Date) {
                    o1 = DateUtil.getDay((Date) o1);
                } else if (o1 instanceof Integer) {
                    o2 = Integer.parseInt(o2.toString());
                }
                if (!o1.toString().equals(o2.toString())) {
                    if (i != 1) {
                        str += separator;
                    }
                    String fieldName = dictMap.get(field.getName());
                    String fieldWarpperMethodName = dictMap.getFieldWarpperMethodName(field.getName());
                    if (fieldWarpperMethodName != null) {
                        Object o1Warpper = DictFieldWarpperFactory.createFieldWarpper(o1, fieldWarpperMethodName);
                        Object o2Warpper = DictFieldWarpperFactory.createFieldWarpper(o2, fieldWarpperMethodName);
                        str += "字段名稱:" + fieldName + ",舊值:" + o1Warpper + ",新值:" + o2Warpper;
                    } else {
                        str += "字段名稱:" + fieldName + ",舊值:" + o1 + ",新值:" + o2;
                    }
                    i++;
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return str;
    }
 
    /**
     * 解析多個key(逗號隔開的)
     *
     * @author ...
     * @Date 2017/5/16 22:19
     */
    public static String parseMutiKey(AbstractDictMap dictMap, String key, Map<String, String> requests) {
        StringBuilder sb = new StringBuilder();
        if (key.indexOf(",") != -1) {
            String[] keys = key.split(",");
            for (String item : keys) {
                String fieldWarpperMethodName = dictMap.getFieldWarpperMethodName(item);
                String value = requests.get(item);
                if (fieldWarpperMethodName != null) {
                    Object valueWarpper = DictFieldWarpperFactory.createFieldWarpper(value, fieldWarpperMethodName);
                    sb.append(dictMap.get(item) + "=" + valueWarpper + ",");
                } else {
                    sb.append(dictMap.get(item) + "=" + value + ",");
                }
            }
            return StrKit.removeSuffix(sb.toString(), ",");
        } else {
            String fieldWarpperMethodName = dictMap.getFieldWarpperMethodName(key);
            String value = requests.get(key);
            if (fieldWarpperMethodName != null) {
                Object valueWarpper = DictFieldWarpperFactory.createFieldWarpper(value, fieldWarpperMethodName);
                sb.append(dictMap.get(key) + "=" + valueWarpper);
            } else {
                sb.append(dictMap.get(key) + "=" + value);
            }
            return sb.toString();
        }
    } 
}

5.根據輸入方法獲取數據字典的數據

import java.lang.reflect.Method;  
public class DictFieldWarpperFactory {  
    public static Object createFieldWarpper(Object parameter, String methodName) {
        IConstantFactory constantFactory = ConstantFactory.me();
        try {
            Method method = IConstantFactory.class.getMethod(methodName, parameter.getClass());
            return method.invoke(constantFactory, parameter);
        } catch (Exception e) {
            try {
                Method method = IConstantFactory.class.getMethod(methodName, Integer.class);
                return method.invoke(constantFactory, Integer.parseInt(parameter.toString()));
            } catch (Exception e1) {
                throw new RuntimeException("BizExceptionEnum.ERROR_WRAPPER_FIELD");
            }
        }
    } 
}

6.對應獲取數據字典的方法

public interface IConstantFactory { 
    /**
     * 獲取狀態
     */
    String getWordStatus(Integer DATA_STATUS); 
}
import com.qihoinfo.dev.log.util.SpringContextHolder;
import org.anyline.service.AnylineService;
import org.springframework.context.annotation.DependsOn;
import org.springframework.stereotype.Component;  
@Component
@DependsOn("springContextHolder")
public class ConstantFactory implements IConstantFactory { 
    private AnylineService anylineService = SpringContextHolder.getBean(AnylineService.class); 
    public static IConstantFactory me() {
        return SpringContextHolder.getBean("constantFactory");
    }
 
    @Override
    public String getWordStatus(Integer DATA_STATUS) {
        if ("1".equals(DATA_STATUS.toString())) {
            return "啟用";
        } else {
            return "停用";
        }
    } 
}

7.spring根據方法名獲取對應容器中的對象

import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component; 
/**
 * Spring的ApplicationContext的持有者,可以用靜態方法的方式獲取spring容器中的bean
 */
@Component
public class SpringContextHolder implements ApplicationContextAware {  
    private static ApplicationContext applicationContext; 
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        SpringContextHolder.applicationContext = applicationContext;
    }
 
    public static ApplicationContext getApplicationContext() {
        assertApplicationContext();
        return applicationContext;
    }
 
    @SuppressWarnings("unchecked")
    public static <T> T getBean(String beanName) {
        assertApplicationContext();
        return (T) applicationContext.getBean(beanName);
    }
 
    public static <T> T getBean(Class<T> requiredType) {
        assertApplicationContext();
        return applicationContext.getBean(requiredType);
    }
 
    private static void assertApplicationContext() {
        if (SpringContextHolder.applicationContext == null) {
            throw new RuntimeException("applicaitonContext屬性為null,請檢查是否註入瞭SpringContextHolder!");
        }
    } 
}

8.字符串工具類

/**
 * 字符串工具類
 */
public class StrKit {   
    /**
     * 首字母變小寫
     */
    public static String firstCharToLowerCase(String str) {
        char firstChar = str.charAt(0);
        if (firstChar >= 'A' && firstChar <= 'Z') {
            char[] arr = str.toCharArray();
            arr[0] += ('a' - 'A');
            return new String(arr);
        }
        return str;
    }
    /**
     * 首字母變大寫
     */
    public static String firstCharToUpperCase(String str) {
        char firstChar = str.charAt(0);
        if (firstChar >= 'a' && firstChar <= 'z') {
            char[] arr = str.toCharArray();
            arr[0] -= ('a' - 'A');
            return new String(arr);
        }
        return str;
    }
    /**
     * 去掉指定後綴
     */
    public static String removeSuffix(String str, String suffix) {
        if (isEmpty(str) || isEmpty(suffix)) {
            return str;
        }
        if (str.endsWith(suffix)) {
            return str.substring(0, str.length() - suffix.length());
        }
        return str;
    }
    /**
     * 字符串是否為空,空的定義如下 1、為null <br>
     * 2、為""<br>
     */
    public static boolean isEmpty(String str) {
        return str == null || str.length() == 0;
    }
}
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;  
public class ToolUtil {
    public static final int SALT_LENGTH = 6;
 
    public ToolUtil() {
    }
 
    public static String getExceptionMsg(Throwable e) {
        StringWriter sw = new StringWriter();
 
        try {
            e.printStackTrace(new PrintWriter(sw));
        } finally {
            try {
                sw.close();
            } catch (IOException var8) {
                var8.printStackTrace();
            }
 
        } 
        return sw.getBuffer().toString().replaceAll("\\$", "T");
    }
}

9.獲取數據字典的類

import java.util.HashMap;  
public abstract class AbstractDictMap { 
    protected HashMap<String, String> dictory = new HashMap<>();
    protected HashMap<String, String> fieldWarpperDictory = new HashMap<>(); 
    public AbstractDictMap() {
        put("ID", "主鍵ID");
        init();
        initBeWrapped();
    }
 
    public abstract void init(); 
    protected abstract void initBeWrapped(); 
    public String get(String key) {
        return this.dictory.get(key);
    }
 
    public void put(String key, String value) {
        this.dictory.put(key, value);
    }
 
    public String getFieldWarpperMethodName(String key) {
        return this.fieldWarpperDictory.get(key);
    }
 
    public void putFieldWrapperMethodName(String key, String methodName) {
        this.fieldWarpperDictory.put(key, methodName);
    }
}
public class SystemDict extends AbstractDictMap { 
    @Override
    public void init() {
    }
 
    @Override
    protected void initBeWrapped() {
 
    }
}
public class WordMap extends AbstractDictMap { 
    @Override
    public void init() {
        put("EN", "英文");
        put("CN", "中文");
        put("SHORT", "簡稱");
        put("REMARK", "備註");
        put("DATA_STATUS", "狀態");
    }
 
    @Override
    protected void initBeWrapped() {
        putFieldWrapperMethodName("DATA_STATUS","getWordStatus");
    } 
}

10.獲取緩存對象的bean

import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import org.springframework.web.context.WebApplicationContext; 
import java.io.Serializable;  
@Component
@Scope(scopeName = WebApplicationContext.SCOPE_SESSION)
public class LogObjectHolder implements Serializable{ 
    private Object object = null; 
    public void set(Object obj) {
        this.object = obj;
    }
 
    public Object get() {
        return object;
    }
 
    public static LogObjectHolder me(){
        LogObjectHolder bean = SpringContextHolder.getBean(LogObjectHolder.class);
        return bean;
    } 
}

11.運行時異常的獲取

@ControllerAdvice
public class GlobalExceptionHandler extends BasicMemberJSONController { 
    private Logger log = LoggerFactory.getLogger(this.getClass()); 
    @ExceptionHandler(RuntimeException.class)
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    @ResponseBody
    public String notFount(RuntimeException e) {
        String userName = curManage().get("USERNAME").toString();
        LogManager.me().executeLog(LogTaskFactory.exceptionLog(userName, e));
        log.error("運行時異常:", e);
        return fail();
    }
}

12.使用線程池創建操作日志

import java.util.Date;  
public class LogFactory {  
    /**
     * 創建操作日志
     */
    public static DataRow createOperationLog(LogType logType, String userName, String bussinessName, String clazzName, String methodName, String msg, LogSucceed succeed) {
        DataRow operationLog = new DataRow();
        operationLog.put("log_type", logType.getMessage());
        operationLog.put("USER_NAME", userName);
        operationLog.put("log_name", bussinessName);
        operationLog.put("CLASS_NAME", clazzName);
        operationLog.put("METHOD", methodName);
        operationLog.put("CREATE_TIME", new Date());
        operationLog.put("SUCCEED", succeed.getMessage());
        if (msg.length() > 800) {
            msg = msg.substring(0, 800);
            operationLog.put("MESSAGE", msg);
        } else {
            operationLog.put("MESSAGE", msg);
        }
        return operationLog;
    }
}
import java.util.TimerTask;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit; 
 
public class LogManager { 
    //日志記錄操作延時
    private final int OPERATE_DELAY_TIME = 10;
 
    //異步操作記錄日志的線程池
    private ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(10); 
    private LogManager() {
    }
 
    public static LogManager logManager = new LogManager(); 
    public static LogManager me() {
        return logManager;
    }
 
    public void executeLog(TimerTask task) {
        executor.schedule(task, OPERATE_DELAY_TIME, TimeUnit.MILLISECONDS);
    } 
}
public enum LogSucceed { 
    SUCCESS("成功"),
    FAIL("失敗");
 
    String message; 
    LogSucceed(String message) {
        this.message = message;
    }
 
    public String getMessage() {
        return message;
    }
 
    public void setMessage(String message) {
        this.message = message;
    } 
}
import com.qihoinfo.dev.log.annotation.RedisDb;
import com.qihoinfo.dev.log.util.ToolUtil;
import org.anyline.entity.DataRow;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.DependsOn;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component; 
import java.util.TimerTask; 
 
@Component
@DependsOn("springContextHolder")
public class LogTaskFactory {
 
    private static Logger logger = LoggerFactory.getLogger(LogManager.class); 
    private static StringRedisTemplate redisTemplate = RedisDb.getMapper(StringRedisTemplate.class);
 
    public static TimerTask bussinessLog(final String userName, final String bussinessName, final String clazzName, final String methodName, final String msg) {
        return new TimerTask() {
            @Override
            public void run() {
                DataRow operationLog = LogFactory.createOperationLog(
                        LogType.BUSSINESS, userName, bussinessName, clazzName, methodName, msg, LogSucceed.SUCCESS);
                try {
 
                    redisTemplate.opsForList().rightPush("sys_operation_log", operationLog.getJson());
                } catch (Exception e) {
                    logger.error("創建業務日志異常!", e);
                }
            }
        };
    }
 
    public static TimerTask exceptionLog(final String userName, final Exception exception) {
        return new TimerTask() {
            @Override
            public void run() {
                String msg = ToolUtil.getExceptionMsg(exception);
                DataRow operationLog = LogFactory.createOperationLog(
                        LogType.EXCEPTION, userName, "", null, null, msg, LogSucceed.FAIL);
                try {
                    redisTemplate.opsForList().rightPush("sys_operation_log", operationLog.getJson());
                } catch (Exception e) {
                    logger.error("創建異常日志異常!", e);
                }
            }
        };
    } 
}
public enum LogType {
 
    EXCEPTION("異常日志"),
    BUSSINESS("業務日志");
 
    String message; 
    LogType(String message) {
        this.message = message;
    }
 
    public String getMessage() {
        return message;
    }
 
    public void setMessage(String message) {
        this.message = message;
    } 
}

13.將日志記錄到redis數據庫

package com.qihoinfo.dev.log.annotation;  
import com.qihoinfo.dev.log.util.SpringContextHolder;
import org.springframework.data.redis.core.StringRedisTemplate; 
public class RedisDb<T> {  
    private Class<T> clazz; 
    private StringRedisTemplate baseMapper; 
 
    private RedisDb(Class clazz) {
        this.clazz = clazz;
        this.baseMapper = (StringRedisTemplate) SpringContextHolder.getBean(clazz);
    } 
 
    public static <T> RedisDb<T> create(Class<T> clazz) {
        return new RedisDb<T>(clazz);
    } 
 
    public StringRedisTemplate getMapper() {
        return this.baseMapper;
    } 
 
    public static <T> T getMapper(Class<T> clazz) {
        return SpringContextHolder.getBean(clazz);
    }  
}
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes; 
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
 
/**
 * @Description:
 * @Auther: wj
 * @Date: 2019/5/28 13:56
 */
public class HttpContext {
    public HttpContext() {
    }
 
    public static String getIp() {
        HttpServletRequest request = getRequest();
        return request == null ? "127.0.0.1" : request.getRemoteHost();
    }
 
    public static HttpServletRequest getRequest() {
        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        return requestAttributes == null ? null : requestAttributes.getRequest();
    }
 
    public static HttpServletResponse getResponse() {
        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        return requestAttributes == null ? null : requestAttributes.getResponse();
    }
 
    public static Map<String, String> getRequestParameters() {
        HashMap<String, String> values = new HashMap();
        HttpServletRequest request = getRequest();
        if (request == null) {
            return values;
        } else {
            Enumeration enums = request.getParameterNames();
 
            while (enums.hasMoreElements()) {
                String paramName = (String) enums.nextElement();
                String paramValue = request.getParameter(paramName);
                values.put(paramName, paramValue);
            }
 
            return values;
        }
    }
}

以上為個人經驗,希望能給大傢一個參考,也希望大傢多多支持WalkonNet。

推薦閱讀: