Java实现http接口参数和返回值加密

参数和返回值得加密目的

为了保证接口不被人拦截下来恶意请求,保证程序的稳定性,我们可以使用接口加密的方法来保证参数和返回值的保密性。

具体实现方式

因为本人是写Java 的,所以这里就直接以Java代码为例。思想都是一样的,只是不同的语言都不同的实
现方式。

大致思路:

  • 我们的参数和返回值只需要定义两个参数:ak,ct,ak存放堆成加密的秘钥,ct存放加密之后的请求内容。
  • 加密方式用到AES对称加密和RSA非对称加密,将请求参数使用AES加密,HTTP的POST请求和GET请求都可以将实际请求参数的格式定义成同一种格式(比如:JSON格式),便于后台处理。AES加密使用的秘钥用RSA加密。这样我们就不需要操心参数加密的秘钥是什么了,双方都可以随时更换。
  • 使用过滤器获取到请求的加密内容后,通过RSA解密将参数加密的秘钥解析出来,然后再通过解析出来的秘钥通过AES解密去获取实际的请求内容。

注意:不同的请求方式获取参数的方式不同,需要根据不同的请求方式去做不同的解析

代码实现

  1. 首先需要一个过滤器,因为我们要拦截请求参数,和加密返回的数据。

因为并不是所有的接口都需要加密,过滤器不能想Spring拦截器那样直接配置拦截和不拦截的类,所以定义了一个ALLOWED_PATHS 集合,用于过滤不需要加解密的请求。然后根据不同的请求方式去解析不同的请求内容,我这里只处理了get请求和post请求这两种常用的请求方式。

package com.pay.filter;

import lombok.extern.log4j.Log4j2;
import org.springframework.stereotype.Component;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;

/**
 * 请求信息加解密
 */
@Log4j2
@Component
@WebFilter(filterName = "EncryptionFilter", urlPatterns = {"/*"})
public class EncryptionFilter implements Filter {

    private static final Set<String> ALLOWED_PATHS = Collections.unmodifiableSet(new HashSet<>(
            Arrays.asList("/pay/notify")));
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        if(request instanceof HttpServletRequest) {
            HttpServletRequest hRequest = (HttpServletRequest) request;
            String servletPath = hRequest.getServletPath();
            log.info("request_path:path="+servletPath);
            boolean allow = false;
            for (String allowedPath : ALLOWED_PATHS) {
                if(servletPath.contains(allowedPath)) {
                    allow = true;
                    break;
                }
            }
            if(allow) {
              chain.doFilter(request, response);
              log.info("no Encryption");
              return;
            }
            MyHttpServletResponseWrapper responseWrapper = new MyHttpServletResponseWrapper((HttpServletResponse) response);
            if(hRequest.getMethod().equalsIgnoreCase("post")) {
                MyHttpServletRequestWrapper requestWrapper = null;
                try {
                    requestWrapper = new MyHttpServletRequestWrapper(hRequest);
                } catch (Exception e) {
                    request.getRequestDispatcher("404.html").forward(request,response);
                }
                chain.doFilter(requestWrapper, responseWrapper);
            }else if(!hRequest.getMethod().equalsIgnoreCase("options")){
                log.info("收到 potions请求");
            } else { //其余默认get请求
                ParameterRequestWrapper requestWrapper = null;
                try {
                    requestWrapper = new ParameterRequestWrapper(hRequest);
                } catch (Exception e) {
                    request.getRequestDispatcher("404.html").forward(request,response);
                }
                chain.doFilter(requestWrapper, responseWrapper);
            }
            String resp = responseWrapper.getTextContent(); //获取接口返回内容
            //加密处理返回
            response.getOutputStream().write(HttpEncryptUtil.serverEncrypt(resp).getBytes(StandardCharsets.UTF_8));
        }
    }
}

  1. 对于POST请求,定义一个解析request中参数的类 MyHttpServletRequestWrapper继承HttpServletRequestWrapper ,去解析请求参数。
    具体实现方式如下:
    readBody:实际解析请求参数
package com.pay.filter;

import com.alipay.api.internal.util.file.IOUtils;
import com.pay.util.HttpEncryptUtil;
import org.apache.commons.lang3.StringUtils;

import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;

public class MyHttpServletRequestWrapper extends HttpServletRequestWrapper {

    private String requestBody = null;

    public MyHttpServletRequestWrapper(HttpServletRequest request) throws IOException {
        super(request);
        requestBody = readBody(request);
    }

    @Override
    public BufferedReader getReader() {
        return new BufferedReader(new InputStreamReader(getInputStream()));
    }

    @Override
    public ServletInputStream getInputStream() {
        final ByteArrayInputStream bais = new ByteArrayInputStream(requestBody.getBytes(StandardCharsets.UTF_8));
        return new ServletInputStream() {
            @Override
            public boolean isFinished() {
                return false;
            }

            @Override
            public boolean isReady() {
                return false;
            }

            @Override
            public void setReadListener(ReadListener readListener) {

            }

            @Override
            public int read() throws IOException {
                return bais.read();
            }
        };
    }

    private static String readBody(ServletRequest request) throws IOException {

        String param = IOUtils.toString(request.getInputStream(), StandardCharsets.UTF_8);
        if(StringUtils.isNotBlank(param)) {
            //解密请求参数
            return HttpEncryptUtil.serverDecrypt(param);
        }
        return param;
    }
}

  1. 对于get请求,定义一个参数类ParameterRequestWrapper,继承HttpServletRequestWrapper 去解析URL上的参数
    具体方式如下:
    重载构造方法获取加密之后的参数,去解析出来
    ParameterRequestWrapper(HttpServletRequest request),
    重写getParameter和setParameter等获取参数和设置参数的方法
package com.pay.filter;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.pay.util.HttpEncryptUtil;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.util.HashMap;
import java.util.Map;

public class ParameterRequestWrapper extends HttpServletRequestWrapper {
    private Map<String , String[]> params = new HashMap<String, String[]>();


    @SuppressWarnings("unchecked")
    public ParameterRequestWrapper(HttpServletRequest request) {
        // 将request交给父类,以便于调用对应方法的时候,将其输出,其实父亲类的实现方式和第一种new的方式类似
        super(request);
        //将参数表,赋予给当前的Map以便于持有request中的参数
        //解析
        String ak = request.getParameter("ak");
        String ct = request.getParameter("ct");
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("ak",ak);
        jsonObject.put("ct",ct);
        String s = HttpEncryptUtil.serverDecrypt(jsonObject.toJSONString());
        addAllParameters(JSON.parseObject(s));
    }
    //重载一个构造方法
    public ParameterRequestWrapper(HttpServletRequest request , Map<String , Object> extendParams) {
        this(request);
        addAllParameters(extendParams);//这里将扩展参数写入参数表
    }

    @Override
    public String getParameter(String name) {//重写getParameter,代表参数从当前类中的map获取
        String[]values = params.get(name);
        if(values == null || values.length == 0) {
            return null;
        }
        return values[0];
    }

    public String[] getParameterValues(String name) {//同上
        return params.get(name);
    }

    public void addAllParameters(Map<String , Object>otherParams) {//增加多个参数
        for(Map.Entry<String , Object>entry : otherParams.entrySet()) {
            addParameter(entry.getKey() , entry.getValue());
        }
    }


    public void addParameter(String name , Object value) {//增加参数
        if(value != null) {
            if(value instanceof String[]) {
                params.put(name , (String[])value);
            }else if(value instanceof String) {
                params.put(name , new String[] {(String)value});
            }else {
                params.put(name , new String[] {String.valueOf(value)});
            }
        }
    }
}

注意:为了便于区分我才把获取post请求内容和get请求内容的类写成了两个,其实可以尝试着去将两个解析类合成一个

  1. 返回数据的获取类MyHttpServletResponseWrapper
import javax.servlet.ServletOutputStream;
import javax.servlet.WriteListener;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;

public class MyHttpServletResponseWrapper extends HttpServletResponseWrapper {

    private ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
    private PrintWriter printWriter = new PrintWriter(outputStream);
    public MyHttpServletResponseWrapper(HttpServletResponse response) {
        super(response);
    }
    @Override
    public PrintWriter getWriter() throws IOException {
        return printWriter;
    }
    @Override
    public ServletOutputStream getOutputStream() {
        return new ServletOutputStream() {
            @Override
            public boolean isReady() {
                return false;
            }
            @Override
            public void setWriteListener(WriteListener writeListener) {
            }
            @Override
            public void write(int b) throws IOException {
                outputStream.write(b);
            }
        };
    }
    public void flush() {
        try {
            printWriter.flush();
            printWriter.close();
            outputStream.flush();
            outputStream.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    public ByteArrayOutputStream getByteArrayOutputStream(){
        return outputStream;
    }
    public String getTextContent() throws UnsupportedEncodingException {
        flush();
        return outputStream.toString("UTF-8");
    }
}

  1. 加解密工具类 HttpEncryptUtil ,和两个网上百度的AESUtil,RSAUtil
package com.pay.util;

import com.alibaba.fastjson.JSONObject;
import com.pay.common.Constant;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang3.StringUtils;

import java.nio.charset.StandardCharsets;
import java.security.PrivateKey;
import java.security.interfaces.RSAPublicKey;

public class HttpEncryptUtil {

    //解密请求内容
    public static String serverDecrypt(String content) {
        JSONObject result = JSONObject.parseObject(content);
        String encryptAesKeyStr = result.getString("ak");
        String encryptContent = result.getString("ct");
        //使用私钥解密 获取解密内容的AES密钥
//        encryptAesKeyStr = decodeBase64String(encryptAesKeyStr); //base64解码

        PrivateKey privateKey = RSAUtil.getPrivateKey(HTTP_PRIVATE_KEY);
        String aesKey = RSAUtil.decryptByPrivate(decodeBase64String(encryptAesKeyStr), privateKey);
        //使用解密出来的key解密content:使用AES
        return AESUtil.decryptAES(decodeBase64String(encryptContent), aesKey);
    }

    public static String serverDecryptByPublic(String content) {
        JSONObject result = JSONObject.parseObject(content);
        String encryptAesKeyStr = result.getString("ak");
        String encryptContent = result.getString("ct");
        //使用私钥解密 获取解密内容的AES密钥
//        encryptAesKeyStr = decodeBase64String(encryptAesKeyStr); //base64解码
        RSAPublicKey publicKey = RSAUtil.getPublicKey(HTTP_PUBLIC_KEY);
        String aesKey = RSAUtil.decryptByPublic(decodeBase64String(encryptAesKeyStr), publicKey);
        //使用解密出来的key解密content:使用AES
        return AESUtil.decryptAES(decodeBase64String(encryptContent), aesKey);
    }

    //加密返回内容
    public static String serverEncrypt(String content) {
        String aesKey = HTTP_CONTENT_KEY;
        //使用私钥加密AES的key
        PrivateKey privateKey = RSAUtil.getPrivateKey(HTTP_PRIVATE_KEY);
        String ak = RSAUtil.encryptByPrivate(aesKey.getBytes(StandardCharsets.UTF_8), privateKey);
        String ct = AESUtil.encryptAES(content, aesKey);
        JSONObject result = new JSONObject();
        result.put("ak",encodeBase64String(ak));
        result.put("ct",encodeBase64String(ct));
        return result.toJSONString();
    }

    public static String serverEncryptByPublic(String content) {
        String aesKey = HTTP_CONTENT_KEY;
        //使用公钥钥加密AES的key
        RSAPublicKey publicKey = RSAUtil.getPublicKey(HTTP_PUBLIC_KEY);
        String ak = RSAUtil.encryptByPublic(aesKey.getBytes(StandardCharsets.UTF_8), publicKey);
        String ct = AESUtil.encryptAES(content, aesKey);
        JSONObject result = new JSONObject();
        result.put("ak",encodeBase64String(ak));
        result.put("ct",encodeBase64String(ct));
        return result.toJSONString();
    }


    public static String encodeBase64String(String content) {
        if(StringUtils.isBlank(content)) {
            return null;
        }
        return Base64.encodeBase64String(content.getBytes(StandardCharsets.UTF_8));
    }

    public static String decodeBase64String(String content) {
        if(StringUtils.isBlank(content)) {
            return null;
        }
        return new String(Base64.decodeBase64(content), StandardCharsets.UTF_8);
    }
}

package com.pay.util;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import org.apache.commons.codec.binary.Base64;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

public class AESUtil {

    public static final Charset CHARSET = StandardCharsets.UTF_8;
    public static final String ALGORITHMS_MD5 = "MD5";
    public static final String SHA = "SHA1PRNG";

    public static final String ALGORITHM = "AES";

    /**
     * 加密
     *
     * @param content  需要加密的内容
     * @param password 加密密码
     * @return
     */
    public static byte[] encrypt(String content, String password) {
        try {
            KeyGenerator kgen = KeyGenerator.getInstance(ALGORITHM);
            SecureRandom random = SecureRandom.getInstance(SHA);
            random.setSeed(password.getBytes());
            kgen.init(128, random);
            SecretKey secretKey = kgen.generateKey();
            byte[] enCodeFormat = secretKey.getEncoded();
            SecretKeySpec key = new SecretKeySpec(enCodeFormat, ALGORITHM);
            Cipher cipher = Cipher.getInstance(ALGORITHM);// 创建密码器
            byte[] byteContent = content.getBytes(CHARSET);
            cipher.init(Cipher.ENCRYPT_MODE, key);// 初始化
            return cipher.doFinal(byteContent); // 加密
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 解密
     *
     * @param content  待解密内容
     * @param password 解密密钥
     * @return
     */
    public static byte[] decrypt(byte[] content, String password) {
        try {
            KeyGenerator kgen = KeyGenerator.getInstance(ALGORITHM);
            SecureRandom random = SecureRandom.getInstance(SHA);
            random.setSeed(password.getBytes());
            kgen.init(128, random);
            SecretKey secretKey = kgen.generateKey();
            byte[] enCodeFormat = secretKey.getEncoded();
            SecretKeySpec key = new SecretKeySpec(enCodeFormat, ALGORITHM);
            Cipher cipher = Cipher.getInstance(ALGORITHM);// 创建密码器
            cipher.init(Cipher.DECRYPT_MODE, key);// 初始化
            return cipher.doFinal(content); // 加密
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 将二进制转换成16进制
     *
     * @param buf
     * @return
     */
    public static String parseByte2HexStr(byte buf[]) {
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < buf.length; i++) {
            String hex = Integer.toHexString(buf[i] & 0xFF);
            if (hex.length() == 1) {
                hex = '0' + hex;
            }
            sb.append(hex.toUpperCase());
        }
        return sb.toString();
    }

    /**
     * 将16进制转换为二进制
     *
     * @param hexStr
     * @return
     */
    public static byte[] parseHexStr2Byte(String hexStr) {
        if (hexStr.length() < 1)
            return null;
        byte[] result = new byte[hexStr.length() / 2];
        for (int i = 0; i < hexStr.length() / 2; i++) {
            int high = Integer.parseInt(hexStr.substring(i * 2, i * 2 + 1), 16);
            int low = Integer.parseInt(hexStr.substring(i * 2 + 1, i * 2 + 2), 16);
            result[i] = (byte) (high * 16 + low);
        }
        return result;
    }

    private static byte[] doSign(String algorithms, String inputStr) throws NoSuchAlgorithmException {
        MessageDigest messageDigest = MessageDigest.getInstance(algorithms);
        messageDigest.update(inputStr.getBytes(CHARSET));
        return messageDigest.digest();
    }

    private static String signToHexStr(String algorithms, String inputStr) {
        try {
            return parseByte2HexStr(doSign(algorithms, inputStr));
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
            return null;
        }
    }

    public static String sign(String key, String sourceStr) {
        String md5Str1 = signToHexStr(ALGORITHMS_MD5, sourceStr);
        String sourceStr2 = md5Str1 + key;
        return signToHexStr(ALGORITHMS_MD5, sourceStr2);
    }

    public static String toDecryptParam(String sourceStr, String password){
        try {
            byte[] sourceByte = parseHexStr2Byte(sourceStr);
            byte[] bytes = decrypt(sourceByte, password);
            return new String(bytes,CHARSET);
        }catch (Exception e){
            e.printStackTrace();
        }
        return "";
    }

    public static String toEncryptResult(Object sourceStr, String password){
        String parameterJson = JSONObject.toJSONString(sourceStr);
        byte[] encode = encrypt(parameterJson, password);
        return parseByte2HexStr(encode);
    }

    public static String encryptAES(String content, String password) {
        try {
            Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
            byte[] byteContent = content.getBytes("utf-8");
            cipher.init(1, getSecretKey(password));
            byte[] result = cipher.doFinal(byteContent);
            return Base64.encodeBase64String(result);
        } catch (Exception var5) {
            return null;
        }
    }

    public static String decryptAES(String content, String password) {
        try {
            Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
            cipher.init(2, getSecretKey(password));
            byte[] result = cipher.doFinal(Base64.decodeBase64(content));
            return new String(result, "utf-8");
        } catch (Exception var4) {
            return null;
        }
    }
    private static SecretKeySpec getSecretKey(String password) {
        KeyGenerator kg = null;

        try {
            kg = KeyGenerator.getInstance("AES");
            SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
            random.setSeed(password.getBytes());
            kg.init(128, random);
            SecretKey secretKey = kg.generateKey();
            return new SecretKeySpec(secretKey.getEncoded(), "AES");
        } catch (NoSuchAlgorithmException var4) {
            return null;
        }
    }
package com.pay.util;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import java.io.*;
import java.security.*;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.*;

/**
 * RSA算法加密/解密工具类
 */
@Slf4j
public class RSAUtil {
    /** 算法名称 */
    private static final String ALGORITHM =  "RSA";
    /** 默认密钥大小 */
    private static final int KEY_SIZE = 1024;
    /** 用来指定保存密钥对的文件名和存储的名称 */
    private static final String PUBLIC_KEY_NAME = "publicKey";
    private static final String PRIVATE_KEY_NAME = "privateKey";
    private static final String PUBLIC_FILENAME = "publicKey.properties";
    private static final String PRIVATE_FILENAME = "privateKey.properties";
    /** 密钥对生成器 */
    private static 
    KeyPairGenerator keyPairGenerator = null;

    private static KeyFactory keyFactory = null;
    /** 缓存的密钥对 */
    private static KeyPair keyPair = null;
    /** Base64 编码/解码器 JDK1.8 */
    private static Base64.Decoder decoder = Base64.getDecoder();
    private static Base64.Encoder encoder = Base64.getEncoder();
    /** 初始化密钥工厂 */
    static{
        try {
            keyPairGenerator = KeyPairGenerator.getInstance(ALGORITHM);
            keyFactory = KeyFactory.getInstance(ALGORITHM);
        } catch (NoSuchAlgorithmException e) {
            log.error("RSAUtil[ERROR]:e={}",e);
        }
    }
    /** 私有构造器 */
    private RSAUtil(){}

    /**
     * 生成密钥对
     * 将密钥分别用Base64编码保存到#publicKey.properties#和#privateKey.properties#文件中
     * 保存的默认名称分别为publicKey和privateKey
     */
    public static synchronized Map<String, Object> generateKeyPair(){
        try {
            keyPairGenerator.initialize(KEY_SIZE,new SecureRandom(UUID.randomUUID().toString().replaceAll("-","").getBytes()));
            keyPair = keyPairGenerator.generateKeyPair();
        } catch (InvalidParameterException e){
            log.error("KeyPairGenerator does not support a key length of " + KEY_SIZE + ".",e);
        } catch (NullPointerException e){
            log.error("RSAUtil#key_pair_gen is null,can not generate KeyPairGenerator instance.",e);
        }
        RSAPublicKey rsaPublicKey = (RSAPublicKey)keyPair.getPublic();
        RSAPrivateKey rsaPrivateKey = (RSAPrivateKey)keyPair.getPrivate();
        String publicKeyString = encoder.encodeToString(rsaPublicKey.getEncoded());
        String privateKeyString = encoder.encodeToString(rsaPrivateKey.getEncoded());
        storeKey(publicKeyString,PUBLIC_KEY_NAME,PUBLIC_FILENAME);
        storeKey(privateKeyString,PRIVATE_KEY_NAME,PRIVATE_FILENAME);
        Map<String, Object> keyPair = new HashMap<>();
        keyPair.put("public", publicKeyString);
        keyPair.put("private", privateKeyString);
        return keyPair;
    }

    /**
     * 将指定的密钥字符串保存到文件中,如果找不到文件,就创建
     * @param keyString 密钥的Base64编码字符串(值)
     * @param keyName  保存在文件中的名称(键)
     * @param fileName 目标文件名
     */
    private static void storeKey(String keyString,String keyName,String fileName){
        Properties properties = new Properties();
        //存放密钥的绝对地址
        String path = null;
        try{
            path = RSAUtil.class.getClassLoader().getResource(fileName).toString();
            path = path.substring(path.indexOf(":") + 1);
        }catch (NullPointerException e){
            //如果不存#fileName#就创建
            log.warn("storeKey()# " + fileName + " is not exist.Begin to create this file.");
            String classPath = RSAUtil.class.getClassLoader().getResource("").toString();
            String prefix = classPath.substring(classPath.indexOf(":") + 1);
            String suffix = fileName;
            File file = new File(prefix + suffix);
            try {
                file.createNewFile();
                path = file.getAbsolutePath();
            } catch (IOException e1) {
                log.error(fileName +" create fail.",e1);
            }
        }
        try(OutputStream out = new FileOutputStream(path)){
            properties.setProperty(keyName,keyString);
            properties.store(out,"There is " + keyName);
        } catch (FileNotFoundException e) {
            log.error("ModulusAndExponent.properties is not found.",e);
        } catch (IOException e) {
            log.error("OutputStream output failed.",e);
        }
    }

    /**
     * 获取密钥字符串
     * @param keyName 需要获取的密钥名
     * @param fileName 密钥所在文件
     * @return Base64编码的密钥字符串
     */
    private static String getKeyString(String keyName,String fileName){
        if (RSAUtil.class.getClassLoader().getResource(fileName) == null){
            log.warn("getKeyString()# " + fileName + " is not exist.Will run #generateKeyPair()# firstly.");
            generateKeyPair();
        }
        try(InputStream in = RSAUtil.class.getClassLoader().getResource(fileName).openStream()){
            Properties properties = new Properties();
            properties.load(in);
            return properties.getProperty(keyName);
        } catch (IOException e) {
            log.error("getKeyString()#" + e.getMessage(),e);
        }
        return  null;
    }

    /**
     * 从文件获取RSA公钥
     * @return RSA公钥
     * @throws InvalidKeySpecException
     */
    public static RSAPublicKey getPublicKey(){
        try {
            byte[] keyBytes = decoder.decode(getKeyString(PUBLIC_KEY_NAME,PUBLIC_FILENAME));
            X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(keyBytes);
            return (RSAPublicKey)keyFactory.generatePublic(x509EncodedKeySpec);
        }catch (InvalidKeySpecException e) {
            log.error("getPublicKey()#" + e.getMessage(),e);
        }
        return null;
    }

    public static RSAPublicKey getPublicKey(String publicKeyString) {
        if(StringUtils.isBlank(publicKeyString)) {
            return null;
        }
        try {
            byte[] keyBytes = decoder.decode(publicKeyString);
            X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(keyBytes);
            return (RSAPublicKey)keyFactory.generatePublic(x509EncodedKeySpec);
        }catch (InvalidKeySpecException e) {
            log.error("getPublicKey()#" + e.getMessage(),e);
        }
        return null;
    }

    /**
     * 从文件获取RSA私钥
     * @return RSA私钥
     * @throws InvalidKeySpecException
     */
    public static RSAPrivateKey getPrivateKey(){
        try {
            byte[] keyBytes = decoder.decode(getKeyString(PRIVATE_KEY_NAME,PRIVATE_FILENAME));
            PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(keyBytes);
            return (RSAPrivateKey)keyFactory.generatePrivate(pkcs8EncodedKeySpec);
        } catch (InvalidKeySpecException e) {
            log.error("getPrivateKey()#" + e.getMessage(),e);
        }
        return null;
    }

    public static RSAPrivateKey getPrivateKey(String privateKeyString) {
        if(StringUtils.isBlank(privateKeyString)) {
            return null;
        }
        try {
            byte[] keyBytes = decoder.decode(privateKeyString);
            PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(keyBytes);
            return (RSAPrivateKey)keyFactory.generatePrivate(pkcs8EncodedKeySpec);
        } catch (InvalidKeySpecException e) {
            log.error("getPrivateKey()#" + e.getMessage(),e);
        }
        return null;
    }

    /**
     * RSA公钥加密
     * @param content 等待加密的数据
     * @param publicKey RSA 公钥 if null then getPublicKey()
     * @return 加密后的密文(16进制的字符串)
     */
    public static String encryptByPublic(byte[] content,PublicKey publicKey){
        if (publicKey == null){
            publicKey = getPublicKey();
        }
        try {
            Cipher cipher = Cipher.getInstance("RSA");
            cipher.init(Cipher.ENCRYPT_MODE,publicKey);
            //该密钥能够加密的最大字节长度
            int splitLength = ((RSAPublicKey)publicKey).getModulus().bitLength() / 8 -11;
            byte[][] arrays = splitBytes(content,splitLength);
            StringBuffer stringBuffer = new StringBuffer();
            for (byte[] array : arrays){
                stringBuffer.append(bytesToHexString(cipher.doFinal(array)));
            }
            return stringBuffer.toString();
        } catch (NoSuchAlgorithmException e) {
            log.error("encrypt()#NoSuchAlgorithmException",e);
        } catch (NoSuchPaddingException e) {
            log.error("encrypt()#NoSuchPaddingException",e);
        } catch (InvalidKeyException e) {
            log.error("encrypt()#InvalidKeyException",e);
        } catch (BadPaddingException e) {
            log.error("encrypt()#BadPaddingException",e);
        } catch (IllegalBlockSizeException e) {
            log.error("encrypt()#IllegalBlockSizeException",e);
        }
        return null;
    }

    /**
     * RSA私钥加密
     * @param content 等待加密的数据
     * @param privateKey RSA 私钥 if null then getPrivateKey()
     * @return 加密后的密文(16进制的字符串)
     */
    public static String encryptByPrivate(byte[] content,PrivateKey privateKey){
        if (privateKey == null){
            privateKey = getPrivateKey();
        }
        try {
            Cipher cipher = Cipher.getInstance("RSA");
            cipher.init(Cipher.ENCRYPT_MODE,privateKey);
            //该密钥能够加密的最大字节长度
            int splitLength = ((RSAPrivateKey)privateKey).getModulus().bitLength() / 8 -11;
            byte[][] arrays = splitBytes(content,splitLength);
            StringBuffer stringBuffer = new StringBuffer();
            for(byte[] array : arrays){
                stringBuffer.append(bytesToHexString(cipher.doFinal(array)));
            }
            return stringBuffer.toString();
        } catch (NoSuchAlgorithmException e) {
            log.error("encrypt()#NoSuchAlgorithmException",e);
        } catch (NoSuchPaddingException e) {
            log.error("encrypt()#NoSuchPaddingException",e);
        } catch (InvalidKeyException e) {
            log.error("encrypt()#InvalidKeyException",e);
        } catch (BadPaddingException e) {
            log.error("encrypt()#BadPaddingException",e);
        } catch (IllegalBlockSizeException e) {
            log.error("encrypt()#IllegalBlockSizeException",e);
        }
        return null;
    }



    /**
     * RSA私钥解密
     * @param content 等待解密的数据
     * @param privateKey RSA 私钥 if null then getPrivateKey()
     * @return 解密后的明文
     */
    public static String decryptByPrivate(String content,PrivateKey privateKey){
        if (privateKey == null){
            privateKey = getPrivateKey();
        }
        try {
            Cipher cipher = Cipher.getInstance("RSA");
            cipher.init(Cipher.DECRYPT_MODE,privateKey);
            //该密钥能够加密的最大字节长度
            int splitLength = ((RSAPrivateKey)privateKey).getModulus().bitLength() / 8;
            byte[] contentBytes = hexStringToBytes(content);
            byte[][] arrays = splitBytes(contentBytes,splitLength);
            StringBuffer stringBuffer = new StringBuffer();
            String sTemp = null;
            for (byte[] array : arrays){
                stringBuffer.append(new String(cipher.doFinal(array)));
            }
            return stringBuffer.toString();
        } catch (NoSuchAlgorithmException e) {
            log.error("encrypt()#NoSuchAlgorithmException",e);
        } catch (NoSuchPaddingException e) {
            log.error("encrypt()#NoSuchPaddingException",e);
        } catch (InvalidKeyException e) {
            log.error("encrypt()#InvalidKeyException",e);
        } catch (BadPaddingException e) {
            log.error("encrypt()#BadPaddingException",e);
        } catch (IllegalBlockSizeException e) {
            log.error("encrypt()#IllegalBlockSizeException",e);
        }
        return null;
    }

    /**
     * RSA公钥解密
     * @param content 等待解密的数据
     * @param publicKey RSA 公钥 if null then getPublicKey()
     * @return 解密后的明文
     */
    public static String decryptByPublic(String content,PublicKey publicKey){
        if (publicKey == null){
            publicKey = getPublicKey();
        }
        try {
            Cipher cipher = Cipher.getInstance("RSA");
            cipher.init(Cipher.DECRYPT_MODE,publicKey);
            //该密钥能够加密的最大字节长度
            int splitLength = ((RSAPublicKey)publicKey).getModulus().bitLength() / 8;
            byte[] contentBytes = hexStringToBytes(content);
            byte[][] arrays = splitBytes(contentBytes,splitLength);
            StringBuffer stringBuffer = new StringBuffer();
            String sTemp = null;
            for (byte[] array : arrays){
                stringBuffer.append(new String(cipher.doFinal(array)));
            }
            return stringBuffer.toString();
        } catch (NoSuchAlgorithmException e) {
            log.error("encrypt()#NoSuchAlgorithmException",e);
        } catch (NoSuchPaddingException e) {
            log.error("encrypt()#NoSuchPaddingException",e);
        } catch (InvalidKeyException e) {
            log.error("encrypt()#InvalidKeyException",e);
        } catch (BadPaddingException e) {
            log.error("encrypt()#BadPaddingException",e);
        } catch (IllegalBlockSizeException e) {
            log.error("encrypt()#IllegalBlockSizeException",e);
        }
        return null;
    }



    /**
     * 根据限定的每组字节长度,将字节数组分组
     * @param bytes 等待分组的字节组
     * @param splitLength 每组长度
     * @return 分组后的字节组
     */
    public static byte[][] splitBytes(byte[] bytes,int splitLength){
        //bytes与splitLength的余数
        int remainder = bytes.length % splitLength;
        //数据拆分后的组数,余数不为0时加1
        int quotient = remainder != 0 ? bytes.length / splitLength + 1:bytes.length / splitLength;
        byte[][] arrays = new byte[quotient][];
        byte[] array = null;
        for (int i =0;i<quotient;i++){
            //如果是最后一组(quotient-1),同时余数不等于0,就将最后一组设置为remainder的长度
            if (i == quotient -1 && remainder != 0){
                array = new byte[remainder];
                System.arraycopy(bytes,i * splitLength,array,0,remainder);
            } else {
                array = new byte[splitLength];
                System.arraycopy(bytes,i*splitLength,array,0,splitLength);
            }
            arrays[i] = array;
        }
        return arrays;
    }

    /**
     * 将字节数组转换成16进制字符串
     * @param bytes 即将转换的数据
     * @return 16进制字符串
     */
    public static String bytesToHexString(byte[] bytes){
        StringBuffer sb = new StringBuffer(bytes.length);
        String temp = null;
        for (int i = 0;i< bytes.length;i++){
            temp = Integer.toHexString(0xFF & bytes[i]);
            if(temp.length() <2){
                sb.append(0);
            }
            sb.append(temp);
        }
        return sb.toString();
    }

    /**
     * 将16进制字符串转换成字节数组
     * @param hex 16进制字符串
     * @return byte[]
     */
    public static byte[] hexStringToBytes(String hex){
        int len = (hex.length() / 2);
        hex = hex.toUpperCase();
        byte[] result = new byte[len];
        char[] chars = hex.toCharArray();
        for (int i= 0;i<len;i++){
            int pos = i * 2;
            result[i] = (byte)(toByte(chars[pos]) << 4 | toByte(chars[pos + 1]));
        }
        return result;
    }

    /**
     * 将char转换为byte
     * @param c char
     * @return byte
     */
    private static byte toByte(char c){
        return (byte)"0123456789ABCDEF".indexOf(c);
    }

身份检验

除了参数加密之外,我们还可以做一个简单的身份校验。这里就需要使用到Spring
的拦截器了。
可以在header中放一个身份token,拦截器里面校验
具体实现:
一个拦截器 TokenInterceptor.java 和配置类 WebConfig.java

package com.pay.filter;

import com.alibaba.fastjson.JSONObject;
import com.pay.common.ErrorCode;
import com.pay.common.GlobalEnums;
import com.pay.entity.SourceConfig;
import com.pay.service.SourceConfigService;
import com.pay.util.RedisUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@Component
@Slf4j
public class TokenInterceptor implements HandlerInterceptor {

    @Autowired
    private SourceConfigService sourceConfigService;

    @Override
    public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object handler) throws Exception {
        String token = httpServletRequest.getHeader("token");
        httpServletResponse.setContentType("text/html;charset=utf-8");
        httpServletResponse.setCharacterEncoding("utf-8");
        ServletOutputStream outputStream = httpServletResponse.getOutputStream();
        JSONObject jsonObject =new JSONObject();
        if(StringUtils.isBlank(token)) {
            jsonObject.put("code", ErrorCode.Ax000014.getCode());
            jsonObject.put("message", ErrorCode.Ax000014.getMessage());
            outputStream.write(jsonObject.toJSONString().getBytes());
            return false;
        }
        SourceConfig sourceConfig = sourceConfigService.selectBySource(token);
        if(sourceConfig == null) {
            jsonObject.put("code", ErrorCode.Ax000014.getCode());
            jsonObject.put("message", ErrorCode.Ax000014.getMessage());
            outputStream.write(jsonObject.toJSONString().getBytes());
            return false;
        }
        if(sourceConfig.getStatus().equals(GlobalEnums.EnableEnum.FALSE.getEnable())) {
            jsonObject.put("code", ErrorCode.Ax000014.getCode());
            jsonObject.put("message", ErrorCode.Ax000014.getMessage());
            outputStream.write(jsonObject.toJSONString().getBytes());
            return false;
        }
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object arg2, Exception arg3)
            throws Exception {
        RequestTokenHolder.remove();
    }

}
package com.pay.config;
import com.pay.filter.TokenInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import javax.annotation.Resource;
@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void addViewControllers(ViewControllerRegistry registry) {

    }

    @Resource
    private TokenInterceptor tokenInterceptor;

    /**
     * 这个方法用来注册拦截器,我们自己写好的拦截器需要通过这里添加注册才能生效
     *
     * @param registry ""
     */
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(tokenInterceptor)
                .addPathPatterns("/**")
                .excludePathPatterns(
                        "/pay/notify/**"//登录接口
                )
                // 过滤swagger
                .excludePathPatterns("/swagger-resources/**", "/webjars/**", "/v2/**", "/swagger-ui.html/**");
    }
}
  • 9
    点赞
  • 34
    收藏
    觉得还不错? 一键收藏
  • 22
    评论
一、Java基础知识 1.Java有那些基本数据类型,String是不是基本数据类型,他们有何区别。 2.字符串的操作: 写一个方法,实现字符串的反转,如:输入abc,输出cba 写一个方法,实现字符串的替换,如:输入bbbwlirbbb,输出bbbhhtccc。 3.数据类型之间的转换 如何将数值型字符转换为数字(Integer,Double) 如何将数字转换为字符 如何取小数点前两位,并四舍五入。 4.日期和时间 如何取得年月日,小时分秒 如何取得从1970年到现在的毫秒数 如何获取某个日期是当月的最后一天 如何格式化日期 5.数组和集合 6.文件和目录(I/O)操作 如何列出某个目录下的所有文件 如何列出某个目录下的所有子目录 判断一个文件或目录是否存在 如何读写文件 7.Java多态的实现(继承、重载、覆盖) 8.编码转换,怎样实现将GB2312编码的字符串转换为ISO-8859-1编码的字符串。 9.Java中访问数据库的步骤,Statement和PreparedStatement之间的区别。 10.找出下列代码可能存在的错误,并说明原因: 二、JSP&Servlet技术 1.描述JSP和Servlet的区别、共同点、各自应用的范围 2.在Web开发中需要处理HTML标记时,应做什么样的处理,要筛选那些字符( & “”) 3.在JSP中如何读取客户端的请求,如何访问CGI变量,如何确定某个Jsp文件的真实路径。 4.描述Cookie和Session的作用,区别和各自的应用范围,Session工作原理。 5.列出Jsp中包含外部文件的方式,两者有何区别。 6.说明Jsp中errorPage的作用,应用范围。 7.介绍在Jsp中如何使用JavaBeans。 8.简单介绍JSP的标记库 9.Jsp和Servlet中的请求转发分别如何实现。 三、J2EE相关知识 1.介绍J2EE、J2SE、J2SE的区别。 2.J2EE是一种技术还是一种平台,他提供了那些技术。 3.什么是Application Server,它有什么功能和优点。 4.简单介绍连接池的优点和原理。 5.Web.xml的作用 四、其他 1.Web安全性的考虑(表单验证、浏览器Basic方式的验证,应用程序的安全性,SSL,代码考虑) 2.简单介绍您所了解的MVC。 3.简单介绍所了解的XML。 4.文档和编码规范 5.Java中的分页、效率考虑。 6.简单介绍您所了解的structs。 1.xml在项目中的作用 2.s-EJB 与 e-EJB的区别 3.会话面的作用 4.cmp与bmp的优缺点 5.j2me程序的必需的几个部分 6.c/s与b/s的区别 7.构建一个connect pool,然后再调用它, 8.j2ee平台与dotnet平台的区别 9.ejb的life cycle 10.session bean 和 entity bean的区别 11.ejb中的transaction机制 12.synchronized (生产者和消费) 13.String 和 StringBuffer 14.Serializable 15.MVC (Struts的工作流程) 16.什么是MDA 17.tcp与udp的区别 18.链表与散列表和数组的区别 19.堆和栈的区别 20.ejb的分类及区别 21.你对现在软件业以及国内软件业的看法 22.谈谈java多线程 23.谈谈文件加密技术 24.软件开发生命周期 25.路由协议种类及特点 26.java的awt和swing组件的GUI设计的关键 27.对于java流的认识 28.简单描述一下awt与swing区别。 29.简述java编程中事件处理模式。 30.你编写过applet吗?applet的安全权限如何?试列举java application或者applet中与servlet/jsp通信可以采用的方式。 31.简述逻辑操作(如&,|)与条件操作(如&&,||)的区别。 32.简述 Java Server Page 和 Servlet 的联系和区别。 33.简述synchronized和java.util.concurrent.locks.Lock的异同 ? 34.EJB规范规定EJB中禁止的操作有哪些? 35.java除了8种基本类型外,在虚拟机里还有哪一种,有什么作用? 36.除了使用new关键字创建对象意外,试列举另外三种以上创建实例的方式? 37.classloader中,JDK的API、Classpath中的同web-inf中的class加载方式有什么区别? 38.列举三种以上垃圾回收算法,并比较其优缺点? 39.编写代码实现一个线程池 40.描述一下JVM加载class文件的原理机制? 41.试举例说明一个典型的垃圾回收算法? 42.请用java写二叉树算法,实现添加数据形成二叉树功能,并以先序的方式打印出来. 43.请写一个java程序实现线程连接池功能? 44.给定一个C语言函数,要求实现java类中进行调用。 45.如何获得数组的长度? 46.访问修饰符“public/private/protected/缺省的修饰符”的使用 47.用关键字final修饰一个类或者方法时,有何意义? 48.掌握类和对象的概念,掌握面向对象编程的本质 49.静态变量和静态方法的意义,如何引用一个类的静态变量或者静态方法? 50.JAVA语言如何进行异常处理,关键字:thorws,throw,try,catch,finally 51.Object类(或者其子类)的finalize()方法在什么情况下被调用? 52.一个“.java”原文件中是否可以包括多个类(不是内部类)? 53.掌握内部类和接口的概念 54.StringTokenizer类的使用 55.数据结构,如何遍历List中的元素? 如果要按照键值保存或者访问数据,使用什么数据结构? 要掌握Collection相关的接口和类的使用 56.使用StringBuffer类与String类进行字符串连接时有何区别? 57.调用Thread类的destroy()方法有什么后果? 58.多线程,用什么关键字修饰同步方法?stop()和suspend()方法为何不推荐使用? 59.使用socket建立客户端与服务器的通信的过程 60.JAVA语言国际化应用,Locale类,Unicode 61.描述反射机制的作用 62.如何读写一个文件? 63.在图形界面中,一个按钮如何处理鼠标点击事件? 64.在图形界面中,一个表格,如何实现编辑单元格时弹出下拉框? 65.如何加载图片? 66.什么是模态对话框? 67.阐述MVC的概念 68.GUI布局管理器的使用,FlowLayout,BorderLayout,GridBagLayout 69.如何构造一棵树?选择树的一个节点时,如何得到这个节点? 70.向编辑框中输入字符时,如何控制只输入整数? 71.描述使用JDBC连接数据库的过程 72.EJB分为几类?什么是BMP,CMP? 73.什么是JNDI? 74.ADO是什么?ActiveX数据对象,是一个应用级程序接口. 75.四种JDBC方式?目前的版本? 76.EJB有哪几种?区别是什么? 77.JavaBean与EJB有什么区别? 78.软件开发生命周期有哪几个阶段? 79.软件开发有哪些因素? 80.软件开发中如何进行版本控制? 81.UML中,类视图如何表示类中的继承与聚合? 82.客户端游标与服务器端游标的区别? 83.动态游标与静态游标的区别? 84.dotnet由哪几个基本框架组成? 85.Oracle中SGA是什么? 86.web servers是什么? 87.UNIX中QT是什么意思? 88.在软件开发生命周期中的哪个阶段开始测试? 89.dotnet与J2EE的比较? 90.什么是ActiveX? 91.Java中IDL是什么? 92.ISO9000和CMM是什么?IS09000和CMM(软件能力成熟度模型)认证是国际上通用的软件质量评估方法.CMM的五个成熟度等级。 第一,谈谈final, finally, finalize的区别。 final?修饰符(关键字)如果一个类被声明为final,意味着它不能再派生出新的子类,不能作为父类被继承。因此一个类不能既被声明为 abstract的,又被声明为final的。将变量或方法声明为final,可以保证它们在使用中不被改变。被声明为final的变量必须在声明时给定初值,而在以后的引用中只能读取,不可修改。被声明为final的方法也同样只能使用,不能重载 finally?再异常处理时提供 finally 块来执行任何清除操作。如果抛出一个异常,那么相匹配的 catch 子句就会执行,然后控制就会进入 finally 块(如果有的话)。 finalize?方法名。Java 技术允许使用 finalize() 方法在垃圾收集器将对象从内存中清除出去之前做必要的清理工作。这个方法是由垃圾收集器在确定这个对象没有被引用时对这个对象调用的。它是在 Object 类中定义的,因此所有的类都继承了它。子类覆盖 finalize() 方法以整理系统资源或者执行其他清理工作。finalize() 方法是在垃圾收集器删除对象之前对这个对象调用的。 第二,Anonymous Inner Class (匿名内部类) 是否可以extends(继承)其它类,是否可以implements(实现)interface(接口)? 匿名的内部类是没有名字的内部类。不能extends(继承) 其它类,但一个内部类可以作为一个接口,由另一个内部类实现。 第三,Static Nested Class 和 Inner Class的不同,说得越多越好(面试题有的很笼统)。 Nested Class (一般是C++的说法),Inner Class (一般是JAVA的说法)。Java内部类与C++嵌套类最大的不同就在于是否有指向外部的引用上。具体可见http: //www.frontfree.net/articles/services/view.asp?id=704&page=1 注: 静态内部类(Inner Class)意味着1创建一个static内部类的对象,不需要一个外部类对象,2不能从一个static内部类的一个对象访问一个外部类对象 第四,&和&&的区别。 &是位运算符。&&是布尔逻辑运算符。 第五,HashMap和Hashtable的区别。 都属于Map接口的类,实现了将惟一键映射到特定的值上。 HashMap 类没有分类或者排序。它允许一个 null 键和多个 null 值。 Hashtable 类似于 HashMap,但是不允许 null 键和 null 值。它也比 HashMap 慢,因为它是同步的。 第六,Collection 和 Collections的区别。 Collections是个java.util下的类,它包含有各种有关集合操作的静态方法。 Collection是个java.util下的接口,它是各种集合结构的父接口。 第七,什么时候用assert。 断言是一个包含布尔表达式的语句,在执行这个语句时假定该表达式为 true。如果表达式计算为 false,那么系统会报告一个 AssertionError。它用于调试目的: assert(a > 0); // throws an AssertionError if a <= 0 断言可以有两种形式: assert Expression1 ; assert Expression1 : Expression2 ; Expression1 应该总是产生一个布尔值。 Expression2 可以是得出一个值的任意表达式。这个值用于生成显示更多调试信息的 String 消息。 断言在默认情况下是禁用的。要在编译时启用断言,需要使用 source 1.4 标记: javac -source 1.4 Test.java 要在运行时启用断言,可使用 -enableassertions 或者 -ea 标记。 要在运行时选择禁用断言,可使用 -da 或者 -disableassertions 标记。 要系统类中启用断言,可使用 -esa 或者 -dsa 标记。还可以在包的基础上启用或者禁用断言。 可以在预计正常情况下不会到达的任何位置上放置断言。断言可以用于验证传递给私有方法的参数。不过,断言不应该用于验证传递给公有方法的参数,因为不管是否启用了断言,公有方法都必须检查其参数。不过,既可以在公有方法中,也可以在非公有方法中利用断言测试后置条件。另外,断言不应该以任何方式改变程序的状态。 第八,GC是什么? 为什么要有GC? (基础)。 GC是垃圾收集器。Java 程序员不用担心内存管理,因为垃圾收集器会自动进行管理。要请求垃圾收集,可以调用下面的方法之一: System.gc() Runtime.getRuntime().gc() 第九,String s = new String("xyz");创建了几个String Object? 两个对象,一个是“xyx”,一个是指向“xyx”的引用对象s。 第十,Math.round(11.5)等於多少? Math.round(-11.5)等於多少? Math.round(11.5)返回(long)12,Math.round(-11.5)返回(long)-11; 第十一,short s1 = 1; s1 = s1 + 1;有什么错? short s1 = 1; s1 += 1;有什么错? short s1 = 1; s1 = s1 + 1;有错,s1是short型,s1+1是int型,不能显式转化为short型。可修改为s1 =(short)(s1 + 1) 。short s1 = 1; s1 += 1正确。 第十二,sleep() 和 wait() 有什么区别? 搞线程的最爱 sleep()方法是使线程停止一段时间的方法。在sleep 时间间隔期满后,线程不一定立即恢复执行。这是因为在那个时刻,其它线程可能正在运行而且没有被调度为放弃执行,除非(a)“醒来”的线程具有更高的优先级 (b)正在运行的线程因为其它原因而阻塞。 wait()是线程交互时,如果线程对一个同步对象x 发出一个wait()调用,该线程会暂停执行,被调对象进入等待状态,直到被唤醒或等待时间到。 第十三,Java有没有goto? Goto?java中的保留字,现在没有在java中使用。 第十四,数组有没有length()这个方法? String有没有length()这个方法? 数组没有length()这个方法,有length的属性。 String有有length()这个方法。 第十五,Overload和Override的区别。Overloaded的方法是否可以改变返回值的类型? 方法的重写Overriding和重载Overloading是Java多态性的不同表现。重写Overriding是父类与子类之间多态性的一种表现,重载Overloading是一个类中多态性的一种表现。如果在子类中定义某方法与其父类有相同的名称和参数,我们说该方法被重写 (Overriding)。子类的对象使用这个方法时,将调用子类中的定义,对它而言,父类中的定义如同被“屏蔽”了。如果在一个类中定义了多个同名的方法,它们或有不同的参数个数或有不同的参数类型,则称为方法的重载(Overloading)。Overloaded的方法是可以改变返回值的类型。 第十六,Set里的元素是不能重复的,那么用什么方法来区分重复与否呢? 是用==还是equals()? 它们有何区别? Set里的元素是不能重复的,那么用iterator()方法来区分重复与否。equals()是判读两个Set是否相等。 equals()和==方法决定引用值是否指向同一对象equals()在类中被覆盖,为的是当两个分离的对象的内容和类型相配的话,返回真值。 第十七,给我一个你最常见到的runtime exception。 ArithmeticException, ArrayStoreException, BufferOverflowException, BufferUnderflowException, CannotRedoException, CannotUndoException, ClassCastException, CMMException, ConcurrentModificationException, DOMException, EmptyStackException, IllegalArgumentException, IllegalMonitorStateException, IllegalPathStateException, IllegalStateException, ImagingOpException, IndexOutOfBoundsException, MissingResourceException, NegativeArraySizeException, NoSuchElementException, NullPointerException, ProfileDataException, ProviderException, RasterFormatException, SecurityException, SystemException, UndeclaredThrowableException, UnmodifiableSetException, UnsupportedOperationException 第十八,error和exception有什么区别? error 表示恢复不是不可能但很困难的情况下的一种严重问题。比如说内存溢出。不可能指望程序能处理这样的情况。 exception 表示一种设计或实现问题。也就是说,它表示如果程序运行正常,从不会发生的情况。 第十九,List, Set, Map是否继承自Collection接口? List,Set是 Map不是 第二十,abstract class和interface有什么区别? 声明方法的存在而不去实现它的类被叫做抽象类(abstract class),它用于要创建一个体现某些基本行为的类,并为该类声明方法,但不能在该类中实现该类的情况。不能创建abstract 类的实例。然而可以创建一个变量,其类型是一个抽象类,并让它指向具体子类的一个实例。不能有抽象构造函数或抽象静态方法。Abstract 类的子类为它们父类中的所有抽象方法提供实现,否则它们也是抽象类为。取而代之,在子类中实现该方法。知道其行为的其它类可以在类中实现这些方法。 接口(interface)是抽象类的变体。在接口中,所有方法都是抽象的。多继承性可通过实现这样的接口而获得。接口中的所有方法都是抽象的,没有一个有程序体。接口只可以定义static final成员变量。接口实现与子类相似,除了该实现类不能从接口定义中继承行为。当类实现特殊接口时,它定义(即将程序体给予)所有这种接口的方法。然后,它可以在实现了该接口的类的任何对象上调用接口的方法。由于有抽象类,它允许使用接口名作为引用变量的类型。通常的动态联编将生效。引用可以转换到接口类型或从接口类型转换,instanceof 运算符可以用来决定某对象的类是否实现接口。 第二十一,abstract的method是否可同时是static,是否可同时是native,是否可同时是synchronized? 都不能 第二十二,接口是否可继承接口? 抽象类是否可实现(implements)接口? 抽象类是否可继承实体类(concrete class)? 接口可以继承接口。抽象类可以实现(implements)接口,抽象类是否可继承实体类,但前提是实体类必须有明确的构造函数。 第二十三,启动一个线程是用run()还是start()? 启动一个线程是调用start()方法,使线程所代表的虚拟处理机处于可运行状态,这意味着它可以由JVM调度并执行。这并不意味着线程就会立即运行。run()方法可以产生必须退出的标志来停止一个线程。 第二十四,构造器Constructor是否可被override? 构造器Constructor不能被继承,因此不能重写Overriding,但可以被重载Overloading。 第二十五,是否可以继承String类? String类是final类故不可以继承。 第二十六,当一个线程进入一个对象的一个synchronized方法后,其它线程是否可进入此对象的其它方法? 不能,一个对象的一个synchronized方法只能由一个线程访问。 第二十七,try {}里有一个return语句,那么紧跟在这个try后的finally {}里的code会不会被执行,什么时候被执行,在return前还是后? 会执行,在return前执行。 第二十八,编程题: 用最有效率的方法算出2乘以8等於几? 有C背景的程序员特别喜欢问这种问题。 2 << 3 第二十九,两个对象值相同(x.equals(y) == true),但却可有不同的hash code,这句话对不对? 不对,有相同的hash code。 第三十,当一个对象被当作参数传递到一个方法后,此方法可改变这个对象的属性,并可返回变化后的结果,那么这里到底是值传递还是引用传递? 是值传递。Java 编程语言只由值传递参数。当一个对象实例作为一个参数被传递到方法中时,参数的值就是对该对象的引用。对象的内容可以在被调用的方法中改变,但对象的引用是永远不会改变的。 第三十一,swtich是否能作用在byte上,是否能作用在long上,是否能作用在String上? switch(expr1)中,expr1是一个整数表达式。因此传递给 switch 和 case 语句的参数应该是 int、 short、 char 或者 byte。long,string 都不能作用于swtich。 第三十二,编程题: 写一个Singleton出来。 Singleton模式主要作用是保证在Java应用程序中,一个类Class只有一个实例存在。 一般Singleton模式通常有几种种形式: 第一种形式: 定义一个类,它的构造函数为private的,它有一个static的private的该类变量,在类初始化时实例话,通过一个public的getInstance方法获取对它的引用,继而调用其中的方法。 public class Singleton {   private Singleton(){}   //在自己内部定义自己一个实例,是不是很奇怪?   //注意这是private 只供内部调用   private static Singleton instance = new Singleton();   //这里提供了一个供外部访问本class的静态方法,可以直接访问     public static Singleton getInstance() {     return instance;       } } 第二种形式: public class Singleton {   private static Singleton instance = null;   public static synchronized Singleton getInstance() {   //这个方法比上面有所改进,不用每次都进行生成对象,只是第一次        //使用时生成实例,提高了效率!   if (instance==null)     instance=new Singleton(); return instance;   } } 其他形式: 定义一个类,它的构造函数为private的,所有方法为static的。 一般认为第一种形式要更加安全些 ---- Java面试题和答案 (http://www.bioon.net/dispbbs.asp?boardid=169&id=108010) -- 作者:jiajia1983 -- 发布时间:2005-3-9 17:29:00 -- Java面试题和答案 JAVA相关基础知识 1、面向对象的特征有哪些方面 1.抽象: 抽象就是忽略一个主题中与当前目标无关的那些方面,以便更充分地注意与当前目标有关的方面。抽象并不打算了解全部问题,而只是选择其中的一部分,暂时不用部分细节。抽象包括两个方面,一是过程抽象,二是数据抽象。 2.继承: 继承是一种联结类的层次模型,并且允许和鼓励类的重用,它提供了一种明确表述共性的方法。对象的一个新类可以从现有的类中派生,这个过程称为类继承。新类继承了原始类的特性,新类称为原始类的派生类(子类),而原始类称为新类的基类(父类)。派生类可以从它的基类那里继承方法和实例变量,并且类可以修改或增加新的方法使之更适合特殊的需要。 3.封装: 封装是把过程和数据包围起来,对数据的访问只能通过已定义的界面。面向对象计算始于这个基本概念,即现实世界可以被描绘成一系列完全自治、封装的对象,这些对象通过一个受保护的接口访问其他对象。 4. 多态性: 多态性是指允许不同类的对象对同一消息作出响应。多态性包括参数化多态性和包含多态性。多态性语言具有灵活、抽象、行为共享、代码共享的优势,很好的解决了应用程序函数同名问题。 2、String是最基本的数据类型吗? 基本数据类型包括byte、int、char、long、float、double、boolean和short。 java.lang.String类是final类型的,因此不可以继承这个类、不能修改这个类。为了提高效率节省空间,我们应该用StringBuffer类 3、int 和 Integer 有什么区别 Java 提供两种不同的类型:引用类型和原始类型(或内置类型)。Int是java的原始数据类型,Integer是java为int提供的封装类。Java为每个原始类型提供了封装类。 原始类型 封装类 boolean Boolean char Character byte Byte short Short int Integer long Long float Float double Double 引用类型和原始类型的行为完全不同,并且它们具有不同的语义。引用类型和原始类型具有不同的特征和用法,它们包括:大小和速度问题,这种类型以哪种类型的数据结构存储,当引用类型和原始类型用作某个类的实例数据时所指定的缺省值。对象引用实例变量的缺省值为 null,而原始类型实例变量的缺省值与它们的类型有关。 4、String 和StringBuffer的区别 JAVA平台提供了两个类:String和StringBuffer,它们可以储存和操作字符串,即包含多个字符的字符数据。这个String类提供了数值不可改变的字符串。而这个StringBuffer类提供的字符串进行修改。当你知道字符数据要改变的时候你就可以使用StringBuffer。典型地,你可以使用StringBuffers来动态构造字符数据。 5、运行时异常与一般异常有何异同? 异常表示程序运行过程中可能出现的非正常状态,运行时异常表示虚拟机的通常操作中可能遇到的异常,是一种常见运行错误。java编译器要求方法必须声明抛出可能发生的非运行时异常,但是并不要求必须声明抛出未被捕获的运行时异常。 6、说出Servlet的生命周期,并说出Servlet和CGI的区别。 Servlet被服务器实例化后,容器运行其init方法,请求到达时运行其service方法,service方法自动派遣运行与请求对应的doXXX方法(doGet,doPost)等,当服务器决定将实例销毁的时候调用其destroy方法。 与cgi的区别在于servlet处于服务器进程中,它通过多线程方式运行其service方法,一个实例可以服务于多个请求,并且其实例一般不会销毁,而CGI对每个请求都产生新的进程,服务完成后就销毁,所以效率上低于servlet。 7、说出ArrayList,Vector, LinkedList的存储性能和特性 ArrayList和Vector都是使用数组方式存储数据,此数组元素数大于实际存储的数据以便增加和插入元素,它们都允许直接按序号索引元素,但是插入元素要涉及数组元素移动等内存操作,所以索引数据快而插入数据慢,Vector由于使用了synchronized方法(线程安全),通常性能上较ArrayList差,而LinkedList使用双向链表实现存储,按序号索引数据需要进行前向或后向遍历,但是插入数据时只需要记录本项的前后项即可,所以插入速度较快。 8、EJB是基于哪些技术实现的?并说出SessionBean和EntityBean的区别,StatefulBean和StatelessBean的区别。 EJB包括Session Bean、Entity Bean、Message Driven Bean,基于JNDI、RMI、JAT等技术实现。 SessionBean在J2EE应用程序中被用来完成一些服务器端的业务操作,例如访问数据库、调用其他EJB组件。EntityBean被用来代表应用系统中用到的数据。 对于客户机,SessionBean是一种非持久性对象,它实现某些在服务器上运行的业务逻辑。 对于客户机,EntityBean是一种持久性对象,它代表一个存储在持久性存储器中的实体的对象视图,或是一个由现有企业应用程序实现的实体。 Session Bean 还可以再细分为 Stateful Session Bean 与 Stateless Session Bean ,这两种的 Session Bean都可以将系统逻辑放在 method之中执行,不同的是 Stateful Session Bean 可以记录呼叫者的状态,因此通常来说,一个使用者会有一个相对应的 Stateful Session Bean 的实体。Stateless Session Bean 虽然也是逻辑组件,但是他却不负责记录使用者状态,也就是说当使用者呼叫 Stateless Session Bean 的时候,EJB Container 并不会找寻特定的 Stateless Session Bean 的实体来执行这个 method。换言之,很可能数个使用者在执行某个 Stateless Session Bean 的 methods 时,会是同一个 Bean 的 Instance 在执行。从内存方面来看, Stateful Session Bean 与 Stateless Session Bean 比较, Stateful Session Bean 会消耗 J2EE Server 较多的内存,然而 Stateful Session Bean 的优势却在于他可以维持使用者的状态。 9、Collection 和 Collections的区别。   Collection是集合类的上级接口,继承与他的接口主要有Set 和List. Collections是针对集合类的一个帮助类,他提供一系列静态方法实现对各种集合的搜索、排序、线程安全化等操作。 10、&和&&的区别。 &是位运算符,表示按位与运算,&&是逻辑运算符,表示逻辑与(and)。 11、HashMap和Hashtable的区别。 HashMap是Hashtable的轻量级实现(非线程安全的实现),他们都完成了Map接口,主要区别在于HashMap允许空(null)键值(key),由于非线程安全,效率上可能高于Hashtable。 HashMap允许将null作为一个entry的key或者value,而Hashtable不允许。 HashMap把Hashtable的contains方法去掉了,改成containsvalue和containsKey。因为contains方法容易让人引起误解。 Hashtable继承自Dictionary类,而HashMap是Java1.2引进的Map interface的一个实现。 最大的不同是,Hashtable的方法是Synchronize的,而HashMap不是,在多个线程访问Hashtable时,不需要自己为它的方法实现同步,而HashMap 就必须为之提供外同步。 Hashtable和HashMap采用的hash/rehash算法都大概一样,所以性能不会有很大的差异。 12、final, finally, finalize的区别。   final 用于声明属性,方法和类,分别表示属性不可变,方法不可覆盖,类不可继承。 finally是异常处理语句结构的一部分,表示总是执行。
jwx是开源的java公众号开发MVC框架,基于spring配置文件和微信消息或事件注解,通过微信上下文处理一个或多个微信公众号服务请求。目的主要有两个,其一生封装微信请求xml消息为java实体对象,将返回对象转换为xml响应消息;其二是封装微信接口java服务。微信公众号采用web服务作为消息与第三方平台发生交互,数据格式主要是xml和json,普通的web请求响应机制采用xml数据格式交互,微信接口服务采用json数据格式。jwx主要对这两个方面做了封装处理,另外借鉴springmvc的请求处理方式,以WeixinDispatcherServlet类作为消息分发控制器,通过消息组装工厂生成请求消息或事件实体,根据消息或事件类型,在消息策略处理工厂查找处理策略,获取相应的微信处理方法,Servlet获取到处理方法后,请求线程池获取线程调用微信方法,根据微信方法的返回值,生成请求的xml响应。本说明文档将分章节说明jwx框架的特征、快速入门、配置、扩展等各个方面。 一、特征 消息重排自动处理,提供消息重排缓存接口 明文/加密模式无感知切换 常用的微信接口服务封装 提供线程池执行微信方法,方法调用线程池大小可配置 长任务消息推送 通过微信上下文配置支持多个微信公众号处理 提供统一的异常处理机制 提供access_token自动更新机制 请求消息组装 灵活的响应消息类型 二、快速入门 本章教材提供一个最简单的例子,用户在微信公众号发一条foo的文本请求消息,公众号响应一条bar的文本响应消息。 1、maven配置文件 通过maven生成一个webapp项目,例如项目名为weixin,在maven配置文件pom.xml中添加jwx依赖,jwx的1.1.1jar包已经提交到maven中心仓库,通过中心仓库搜索jwx关键字可以获取jar包依赖配置。 com.github.jweixin jwx 1.1.1 2、web.xml文件配置 web.xml是web应用的配置文件,jwx从spring配置文件中获取配置信息,所以必须配置spring上下文环境;另外,需要配置微信消息处理分发Servlet(WeixinDispatcherServlet),用于处理微信送过来的请求消息或事件。jwx对springmvc没有依赖关系,web mvc框架可以根据实际需要配置。 org.springframework.web.context.ContextLoaderListener weixin com.github.jweixin.jwx.servlet.WeixinDispatcherServlet 1 weixin /wx/* load-on-startup表示Servlet在web应用启动阶段加载,数字代表了启动次序,如果项目使用了springmvc框架,可以调整该数字为2,放到springmvc框架后面启动加载,但实际上Servlet的启动次序并没有太大的关系。 spring配置是jwx必须的,如果没有配置spring上下文,jwx在启动阶段会报错。 url-pattern模式匹配微信公众号平台服务器配置的URL配置,如果需要处理多个微信公众号,可以配置多个servlet-mapping或者使用路径通配符匹配多个url链接。 3、spring配置文件 spring配置文件applicationContext.xml里面需要配置WeixinConfigurer,这是jwx唯一必须配置项,如果没有配置,启动阶段会报错。 com.telecomjs.yc.controller component-scan配置了微信接口服务类,里面包含常用的微信公众号接口服务,例如菜单管理、消息服务、二维码服务、用户管理、微信网页授权、素材管理等服务内容,在web应用控制器类和微信控制器类里面可以通过@Autowired注解来注入服务。本配置并不是必须项。 WeixinConfigurer是唯一需要配置的部分,packages属性必须配置,里面是微信控制器包路径列表,WeixinDispatcherServlet在启动阶段会扫描包路径及其下面的子包路径,如果类拥有@Weixin注解,则该类会被当作微信控制器类加载到微信上下文。 除了packages属性是必须配置的,其他配置都有缺省值,包括消息缓存、微信方法线程池的大小、微信方法调用超时阀值等,这部分内容放在配置部分说明了。 4、编写微信控制器类 当配置完上面的3个部分,所有的配置文件部分就结束了,是不是很简单呢。下面我们只需要写微信控制器类就能让我们的微信公众号活起来了。微信控制器类是用@Weixin注解的普通类,与sprngmvc里面的controller很类似,方法的执行也很类似。我们在com.telecomjs.yc.controller包下建一个java类WeixinController,如下: package com.telecomjs.yc.controller; import com.github.jweixin.jwx.context.Weixin; import com.github.jweixin.jwx.message.annotation.TextMsg; @Weixin(value="/wx/coreServlet", appID="xxx", appSecret="xxx", encodingAESKey="xxx", token="xxx") public class WeixinController { @TextMsg("foo") public String foo(){ return "bar"; } } @Weixin需要配置value值,这个实际就是微信服务器配置里面URL最后的部分,当然不包含域名和web应用的上下文,切记,不能包含web应用上下文,其他4个部分配置内容也是公众号配置内容,我们只需要登录到公众号看下填进去就行了。如果没有配置encodingAESKey,那么是不能处理加密消息的,如果有log4j的配置文件,启动阶段会给出告警信息的。 同一个公众号可以配置多个@Weixin注解控制器类,其中只需要一个有其他4项配置就可以了,如果多个控制器类配置了其他4个配置项,如果相对应的配置项值不相同,启动阶段会报错。 不同微信公众号是通过@Weixin的value值区分的,该值同时是微信上下文的查找关键字。 foo方法上面有@TextMsg注解,是定义的微信方法,在Servlet启动时通过包扫描加载到微信上下文对象中。jwx针对微信消息或事件类型设计了一组微信注解,基本涵盖了微信公众号定义的消息和事件类型。 @TextMsg是文本消息注解,代表请求类型的是文本消息,value值是发送的文本消息内容。处理文本适配模式,@TextMsg还支持正则表达式适配模式,这部分内容在使用参考部分说明。 本例中微信方法并没有设置参数,实际可以灵活设置参数,例如我们可以在方法中设置HttpServletRequest request,HttpServletResponse response,InMessage in, WeixinContext context等参数,这部分内容也放在使用参考部分说明。 本例中方法的返回类型是String,代表响应的消息内容是文本消息,jwx提供了丰富的返回值类型,这部分内容会在使用参考部分详细说明。 5、启动web应用 上面就是这个最简单例子的全部内容,让我们启动web应用,进入到我们的公众号,输入foo文本提交,看看返回的是不是bar这个内容了,如果是,恭喜你,你已经初步掌握了jwx的使用方法。下面更多的内容等着你呢! 三、配置说明 spring配置文件中唯一需要配置的bean是WeixinConfigurer类,是可选配置,但里面封装了微信接口服务类,建议一定要配置进spring配置文件中。 1、微信接口服务 微信接口服务类位于com.github.jweixin.jwx.weixin.service包中,在spring配置文件中通过扫描包载入服务,在web mvc框架和微信控制器类中都可以通过@Autowired注解注入,与其他spring普通的服务类主键使用方式一致,服务类每个方法都有accessToken参数,这个参数指的是微信access_token,在微信控制器类方法中,可以通过设置方法的WeixinContext context参数获取,在web mvc框架中,可以通过WeixinContextHelper类的静态方法getAccessToken(String url)获取。 CustomMsgService 客服消息服务 MassMsgService 群发消息服务 MaterialService 永久素材管理 MediaService 临时素材管理 MenuService 菜单服务 QrcodeService 二维码服务 TagService 标签服务 TemplateService 模板管理及消息发送 WebAuthService 微信网页授权服务 UserService 微信用户服务 SystemService 获取地址列表及长链接转短链接等其他类型服务 2、WeixinConfigurer配置 WeixinConfigurer是微信上下文全局配置类,里面包含了处理微信类扫描、微信消息重排处理、微信方法执行线程池大小、微信方法调用超时阀值等方面的配置,packages包扫描配置是唯一必须的配置部分,这个配置在快速入门部分已经描述,其他部分配置都有缺省配置,不是必须配置部分。 a、微信消息重排处理messageKeyCache配置 微信在处理消息推送时,如果没有获得响应,会隔5秒重试,最多重试3次。jwx在接到消息推送时,需要判断该消息是否已经接受过,如果接受过,则需要放弃处理。jwx设计了MessageKeyCache接口用于处理消息重排,里面需要实现唯一的方法public boolean hasMessageKey(String key);如果系统已经缓存了消息key值,返回true。jwx实现了一个默认的消息key值缓存ConcurrentHashMapMessageKeyCache。如果我们要设置缓存清理间隔,可以采用如下配置: <!-- 设置消息key缓存清理间隔,单位秒 --> com.telecomjs.yc.controller 另外我们可以实现自己的消息key缓存类,只需要实现MessageKeyCache接口就可以了,比如我们可以采用redis作为消息key值缓存数据库。 b、微信方法线程池大小threadPoolSize设置 微信方法是由Servlet在获取请求消息或事件的策略后取得,Servlet取得微信方法后,在线程池中获取线程执行微信方法。缺省线程池的大小是10个,如果微信公众并发比较频繁,我们可以调整线程池的大小,以提高处理效率。 如果我们调整线程池大小为100,可以采用如下配置: <!-- 设置消息key缓存清理间隔,单位秒 --> com.telecomjs.yc.controller <!-- 设置微信方法执行线程池大小 --> c、微信方法调用超时阀值weixinMethodTimeoutThreshold设置 微信推送消息或事件如果超过5秒,微信会中断连接,有时候微信方法的执行会超过5秒钟,针对这种情况,jwx采用微信方法调用超时阀值机制,如果微信方法调用线程不能在超时阀值内处理完毕,Servlet会先行返回http响应,后续Servlet会等待方法执行完毕,然后通过客服消息返回响应,对用户来说并没有感知。缺省的微信方法调用超时阀值是3000毫秒,该值可以通过配置调整,如下我们将超时阀值改成4秒: <!-- 设置消息key缓存清理间隔,单位秒 --> com.telecomjs.yc.controller <!-- 设置微信方法执行线程池大小 --> <!-- 设置微信方法调用超时阀值,单位毫秒 --> 四、使用参考 本部分会全面讲解jwx的概念及使用方法。 1、主要概念 微信上下文:微信上下文(WexinContext)是jwx最重要的部分,jwx可以同时处理多个微信公众号,每个公众号在jwx框架中对应一个微信上下文,微信上下文持有一个微信公众号所有的配置信息及处理策略。url是微信公众号配置的服务器地址的最后部分(不包括域名和web应用上下文),是识别微信公众号的唯一标识,透过url我们可以通过微信上下文帮助类(WeixinContextHelper)的静态方法获取到微信上下文及访问token,另外,在微信方法中我们也可以通过注入WeixinContext参数来获得微信上下文。微信上下文还包含了微信的access_token、appID、appSecret、encodingAESKey这些微信公众号的配置内容。微信上下文还保存微信方法与消息注解的策略对应关系,是微信消息能够得到处理的最重要的部分。微信上下文通过@Weixin注解来配置。 微信消息注解:jwx定义了14个消息或事件注解,涵盖了目前所有的微信消息和事件类型,这些注解定义在包com.github.jweixin.jwx.message.annotation中,微信注解代表了消息或事件类型,可以通过微信注解配置识别请求消息类型,获取相应的微信处理方法。 微信方法:被微信消息注解包围的方法,通过微信方法,我们可以处理微信公众号请求消息,返回公众号的响应消息。 2、@Weixin注解 @Weixin是用来配置微信上下文的,该注解使用在微信控制器类上。每个被@Weixin注解包围的类会在web应用启动时被扫描,配置项会加载到微信上下文中,@Weixin注解的参数说明: value:代表微信上下文关键字,不能为空,在微信公众号基本配置中,处于URL配置的最后部分。例如微信公众号的URL(服务器地址)配置是:http://nalan_weixin.tunnel.qydev.com/weixin/wx/coreServlet,其中http://nalan_weixin.tunnel.qydev.com是主机栏,/weixin是web应用的上下文栏,那么value值应该是/wx/coreServlet,一个公众号可以有多个类拥有@Weixin注解,如果多个注解的value相同,则会认为是同一个微信上下文,在jwx中,区分上下文的唯一标识就是@Weixin注解的value值配置。@Weixin注解还有其他4个配置项,都有缺省值,在一个微信控制器类中配置了其他4个值,那么相同value值得控制器类只需要配置value项就可以了,如果value配置项相同,而其他4个配置项的同项配置不同,jwx在初始启动扫描阶段会给出报错提示。 token:代表微信公众号基本配置中的Token(令牌)项的值。 encodingAESKey:代表微信公众号基本配置中的EncodingAESKey(消息加解密密钥),该项如果没有配置,那么jwx不能处理加密的请求消息,在jwx初始启动阶段会给出告警提示。如果我们配置了消息加解密方式为安全模式,没有配置encodingAESKey项,则运行阶段会报错。另外如果在加密请求消息到达时报如下错误:java.security.InvalidKeyException:illegal Key Size,则说明当前运行的JDK没有用JCE无限制权限策略文件替换相应的安全jar包,**解决方案:在官方网站下载JCE无限制权限策略文件(请到官网下载对应的版本, 例如JDK7的下载地址:http://www.oracle.com/technetwork/java/javase/downloads/jce-7-download-432124.html下载后解压,可以看到local_policy.jar和US_export_policy.jar以及readme.txt,如果安装了JRE,将两个jar文件放到%JRE_HOME%\lib\security目录下覆盖原来的文件;如果安装了JDK,将两个jar文件放到%JDK_HOME%\jre\lib\security目录下覆盖原来文件**。 appID:代表微信公众号基本配置中的AppID(应用ID)。 appSecret:代表微信公众号基本配置中的AppSecret(应用密钥)。 3、微信方法 在微信控制器类中,被微信消息(事件)注解包围的方法,被称为微信方法,微信方法是MVC框架里面的C部分,它控制着对请求消息的处理逻辑,并且返回响应消息。微信注解主要用于适配请求消息(事件)的类型及关键字内容,当适配成功后,由对应的微信方法执行处理逻辑,并且通过方法的返回值返回响应消息。微信方法的参数在请求消息到达时由Servlet注入,目前微信方法参数可以是HttpServletRequest request,HttpServletResponse response,InMessage in, WeixinContext context里面的任意次序和数量的组合,参数中InMessage可以是与注解对应的子类,对于@ExceptionHandler注解,可以添加Throwable及其子类作为方法参数,需要注意的是如果参数类型与实际消息类型或异常类型不能匹配,则该参数会被置为空。 4、微信注解 jwx定义了14个消息或事件注解,涵盖了目前所有的微信消息和事件类型,下面逐个讲解没有注解的使用。 @TextMsg @ClickEvent 5、响应类型
第一章 流与文件 1.1 流 1.1.1 读入和写出字节 1.1.2 完整的流家族 1.1.3 组合流过滤器 1.2 文本输入与输出 1.2.1 如何写出文本输出 1.2.2 如何读入文本输入 1.2.3 以文本格式存储对象 1.2.4 字符集 1.3 读入和写出二进制数据 1.3.1 随机访问文件 1.4 ZIP文档 1.5 对象流与序列化 1.5.1 理解对象序列化的文件格式 1.5.2 修改缺省的序列化机制 1.5.3 序列化单例和类型安全的枚举 1.5.4 版本管理 1.5.5 为克隆使用序列化 1.6 文件管理 1.7 新I/O 1.7.1 内存映射文件 1.7.2 缓冲区数据结构 1.7.3 文件加锁机制 1.8 正则表达式 第十二章 XML 2.1 XML概述 2.1.1 XML文档的结构 2.2 解析XML文档 2.3 验证XML文档 2.3.1 文档类型定义 2.3.2 XML Schema 2.3.3 实用示例 2.4 使用XPath来定位信息 2.5 使用名字空间 2.6 流机制解析器 2.6.1 使用SAX解析器 2.6.2 使用StAX解析器 2.7 生成XML文档 2.7.1 使用StAX写出XML文档 2.8 XSL转换 第三章 网络 3.1 连接到服务器 3.1.1 套接字超时 3.1.2 因特网地址 3.2 实现服务器 3.2.1 为多个客户端服务 3.2.2 半关闭 3.3 可中断套接字 3.4 发送E-Mail 3.5 建立URL连接 3.5.1 URL和URI 3.5.2 使用URLConnection获取信息 3.5.3 提交表单数据 第四章 数据库编程 4.1 JDBC的设计 4.1.1 JDBC驱动程序类型 4.1.2 JDBC的典型用法 4.2 结构化查询语言 4.3 JDBC配置 4.3.1 数据库URL 4.3.2 驱动程序JAR文件 4.3.3 启动数据库 4.3.4 注册驱动器类 4.3.5 连接到数据库 4.4 执行SQL语句 4.4.1 管理连接、语句和结果集 4.4.2 分析SQL异常 4.4.3 组装数据库 4.5 执行查询操作 4.5.1 预备语句 4.5.2 读取和写出LOB 4.5.3 SQL转义 4.5.4 多结果集 4.5.5 获取自动生成键 4.6 可滚动和可更新的结果集 4.6.1 可滚动的结果集 4.6.2 可更新的结果集 4.7 行集 4.7.1 被缓存的行集 4.8 元数据 4.9 事务 4.9.1 保存点 4.9.2 批量更新 4.9.3 高级SQL类型 4.10 Web与企业应用中的连接管理 4.11 LDAP介绍 4.11.1 配置LDAP服务器 4.11.2 访问LDAP目录信息 第五章 国际化 5.1 Locales 5.2 数字格式 5.2.1 货币 5.3 日期和时间 5.4 排序 5.4.1 排序强度 5.4.2 分解 10.5 消息格式化 10.5.1 选择格式 10.6 文本文件和字符集 10.6.1 源文件的字符编码 10.7 资源包 10.7.1 定位资源包 10.7.2 属性文件 10.7.3 包类 10.8 一个完整的例子 第六章 高级Swing 6.1 列表 6.1.1 JList构件 6.1.2 列表模式 6.1.3 插入和移除值 6.1.4 值的绘制 6.2 表格 6.2.1 简单表格 6.2.2 表格模型 6.2.3对行和列的操作 6.3 树 6.3.1 简单的树 6.3.2 结点枚举 6.2.3 绘制结点 6.2.4 监听树事件 6.2.5 定制树模型 6.4 文本构件 6.4.1 文本构件中的修改跟踪 6.4.2 格式化的输入框 6.4.3 JSpinner构件 6.4.4 用JEditorPane显示HTML 6.5 进度指示器 6.5.1 进度条 6.5.2 进度监视器 6.5.3 监视输入流的进度 6.6 构件组织器 6.6.1 分割面板 6.6.2 选项卡面板 6.6.3 桌面面板和内部框体 6.6.4 级联与平铺 6.6.5 否决属性设置 第七章 高级 AWT 7.1 绘图操作流程 7.2 形状 7.2.1 使用形状类 7.3 区域 7.4 笔划 7.5 着色 7.6 坐标变换 7.7 剪切 7.8 透明与组合 7.9 绘图提示 7.10 图像的读取器和写入器 7.10.1 获得图像文件类型的读取器和写入器 7.10.2 读取和写入带有多个图像的文件 7.11 图像处理 7.11.1 构建光栅图像 7.11.2 图像过滤 7.12 打印 7.12.1 图形打印 7.12.2 打印多页文件 7.12.3 打印预览 7.12.4 打印服务程序 7.12.5 流打印服务程序 7.12.6 打印属性 7.13 剪贴板 7.13.1 数据传递的类和接口 7.13.2 传递文本 7.13.3 可传递的接口数据风格 7.13.4 构建一个可传递的图像 7.13.5 通过系统剪贴板传递Java对象 7.13.6 使用本地剪贴板来传递对象引用 7.14 拖放操作 7.14.1 Swing对数据传递的支持 7.14.2 拖曳源 7.14.3 放置目标 7.15 平台集成 7.15.1 闪屏 7.15.2 启动桌面应用程序 7.15.3 系统托盘 第八章 JavaBean构件 8.1 为何是Bean? 8.2 编写Bean的过程 8.3 使用Bean构造应用程序 8.3.1 将Bean打包成JAR文件 8.3.2 在开发环境中组合Bean 8.4 Bean属性与事件的命名模式 8.5 Bean属性的类型 8.5.1 简单属性 8.5.2 索引属性 8.5.3 绑定属性 8.5.4 约束属性 8.6 BeanInfo类 8.7 属性编辑器 8.7.1 编写一个属性编辑器 8.8 定制器 8.8.1 编写一个定制器类 8.9 JavaBean持久化 8.9.1 JavaBean持久化可用于任何数据 8.9.2 一个JavaBean持久化的完整示例 第九章 安全 9.1 类加载器 9.1.1 类加载器的层次结构 9.1.2 将类加载器作为命名空间 9.1.3 编写你自己的类加载器 9.2 字节码校验 9.3 安全管理器与访问权限 9.3.1 Java 平台安全性 9.3.2 安全策略文件 9.3.3 定制权限 9.3.4 实现权限类 9.4 用户认证 9.4.1 JAAS 登录模块 9.5 数字签名 9.5.1 消息摘要 9.5.2 消息签名 9.5.3 X.509证书格式 9.5.4 校验签名 9.5.5 认证问题 9.5.6 证书签名 9.5.7 证书请求 9.6 代码签名 9.6.1 JAR文件签名 9.6.2 软件开发者证书 9.7 加密 9.7.1 对称密码 9.7.2 密钥生成 9.7.3 密码流 9.7.4 公共密钥密码 第十章 分布式对象 10.1 客户与服务器的角色 10.2 远程方法调用 10.2.1 存根与参数编组 10.3 配置远程方法调用 10.3.1 接口实现 10.3.2 RMI注册表 10.3.3 部署程序 10.3.4 记录RMI活动 10.4 远程方法中的参数返回值 10.4.1 传递远程对象 10.4.2 传递非远程对象 10.4.3 动态类加载 10.4.4 具有多重接口的远程引用 10.4.5 远程对象与equals、hashCode和clone方法 10.5 远程对象激活 10.6 Web Services与JAX-WS 10.6.1 使用JAX-WS 10.6.2 Web服务的客户端 10.6.3 Amazon的E-Commerce服务 第十一章 脚本、编译与注解处理 11.1 Java平台的脚本 11.1.1 获取脚本引擎 11.1.2 脚本赋值与绑定 11.1.3 重定向输入和输出 11.1.4 调用脚本的函数和方法 11.1.5 编译脚本 11.1.6 一个示例:用脚本处理GUI事件 11.2 编译器API 11.2.1 编译便捷之法 11.2.2 使用编译工具 11.2.3 一个示例:动态Java代码生成 11.3 使用注解 11.3.1 一个示例:注解事件处理器 11.4 注解语法 11.5 标准注解 11.5.1 用于编译的注解 11.5.2 用于管理资源的注解 11.5.3 元注解 11.6 源码级注解处理 11.7 字节码工程 11.7.1 载入时修改字节码 第十二章 本地方法 12.1 从Java程序中调用C函数 12.2 数值参数返回值 12.2.1 用printf格式化数字 12.3 字符串参数 12.4 访问对象域 12.4.1 访问实例域 12.4.2 访问静态域 12.5 编码签名 12.6 调用Java方法 12.6.1 实例方法 12.6.2 静态方法 12.6.3 构造器 12.6.4 替代方法调用 12.7 访问数组元素 12.8 错误处理 12.9 使用调用API 12.10 完整的示例:访问Windows注册表 12.10.1 Windows注册表概述 12.10.2 访问注册表的Java平台接口 12.10.3 以本地方法方式实现注册表访问函数

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 22
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值