1.从request获取推送xml包
String callBackXml = testNoticeService.formatNoticeParams(request);
public static String formatNoticeParams(HttpServletRequest request){
    
    try(ByteArrayOutputStream output = new ByteArrayOutputStream();
         InputStream input = request.getInputStream()){    
        byte[] by = new byte[1024];
        int length = 0;
        while((length = input .read(by)) != -1){
             output.write(by, 0, length);
        
        }
        return new String(output.toByteArray(), "UTF-8");
    }catch(IOException e){
         throw new BusinessException(ConstCode.PARAM.getCode(), "回调参数解析异常");
    }
}2.转换成map
Map<String, String> baseMap = new Parser(callBackXml).xmlToMap();
public final class Parser {
     private String xmlString;
    
     public Parser(String xmlString) {
        if (StringUtils.isEmpty(xmlString)) {
            throw new BusinessException(ConstCode.FAILED.getCode(), "解析内容为空");
        }
        if (!isXML(xmlString)) {
            throw new BusinessException(ConstCode.FAILED.getCode(), "解析内容不是合法的xml格式");
        }
        this.xmlString = xmlString;
    }
}
private boolean isXML(String value) {
        try {
            DocumentHelper.parseText(value);
        } catch (DocumentException e) {
            return false;
        }
        return true;
    }
  private static InputStream getStringStream(String sInputString) {
        ByteArrayInputStream tInputStringStream = null;
        if (sInputString != null && !"".equals(sInputString.trim())) {
            tInputStringStream = new ByteArrayInputStream(sInputString.getBytes());
        }
        return tInputStringStream;
    }
 public synchronized Map<String, String> xmlToMap() throws ParserConfigurationException, IOException, SAXException {
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        DocumentBuilder builder = factory.newDocumentBuilder();
        InputStream is = getStringStream(xmlString);
        Document document = builder.parse(is);
        NodeList allNodes = document.getFirstChild().getChildNodes();
        Node node;
        Map<String, String> map = new HashMap<>(0);
        int i = 0;
        while (i < allNodes.getLength()) {
            node = allNodes.item(i);
            if (node instanceof Element) {
                map.put(node.getNodeName(), node.getTextContent());
            }
            i++;
        }
        return map;
    }3.初始化并获得解密消息内容,appId,token,encodingAesKey从微信公众号平台基本配置获取到
String xmlContent = new WxMsgCryptUtils(appId,token,encodingAesKey).decrypt(baseMap.get("Encrypt")); 
public class WxMsgCryptUtils {
    /**
     * 公众平台上,开发者设置的 token
     */
    private static String token;
    /**
     * 公众平台 appID
     */
    private static String appID;
    /**
     * 公众平台上,开发者设置的 EncodingAESKey
     */
    private static byte[] aesKey;
    /**
     * UTF_8 字符集
     */
    private static final Charset UTF_8 = StandardCharsets.UTF_8;
     /**
     * 初始化
     *
     * @param appID  公众平台 appID
     * @param token  公众平台上,开发者设置的 token
     * @param aesKey 公众平台上,开发者设置的 EncodingAESKey
     */
    public WxMsgCryptUtils(String appID, String token, String aesKey) {
        if (StrUtil.hasBlank(appID, token, aesKey) || aesKey.length() != 43) {
            throw new IllegalArgumentException("微信公众号配置信息有误");
        }
        this.appID = appID;
        this.token = token;
        this.aesKey = Base64.decode(aesKey);
    }
      /**
     * 4 个字节的网络字节序 bytes 数组还原成一个数字.
     */
    public static int bytesNetworkOrder2Number(byte[] bytesInNetworkOrder) {
        int sourceNumber = 0;
        for (int i = 0; i < 4; i++) {
            sourceNumber <<= 8;
            sourceNumber |= bytesInNetworkOrder[i] & 0xff;
        }
        return sourceNumber;
    }
   /**
     * 删除解密后明文的补位字符.
     *
     * @param decrypted 解密后的明文
     * @return 删除补位字符后的明文
     */
    public static byte[] decode(byte[] decrypted) {
        int pad = decrypted[decrypted.length - 1];
        if (pad < 1 || pad > 32) {
            pad = 0;
        }
        return Arrays.copyOfRange(decrypted, 0, decrypted.length - pad);
    }
    /**
     * 对密文进行解密.
     *
     * @param cipherText 需要解密的密文
     * @return 解密得到的明文
     */
 public String decrypt(String cipherText) throws Exception  {
        // 设置解密模式为AES的CBC模式
        Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
        SecretKeySpec keySpec = new SecretKeySpec(aesKey, "AES");
        IvParameterSpec iv = new IvParameterSpec(Arrays.copyOfRange(aesKey, 0, 16));
        cipher.init(Cipher.DECRYPT_MODE, keySpec, iv);
        // 使用BASE64对密文进行解码
        byte[] encrypted = Base64.decode(cipherText);
        // 解密
        byte[] original = cipher.doFinal(encrypted);
        byte[] bytes = decode(original);
        // 分离16位随机字符串,网络字节序和AppId
        byte[] networkOrder = Arrays.copyOfRange(bytes, 16, 20);
        int xmlLength = bytesNetworkOrder2Number(networkOrder);
        String xmlContent = new String(Arrays.copyOfRange(bytes, 20, 20 + xmlLength), UTF_8);
        return xmlContent;
}
      
}4.解析成实体对象
WechatFollowVo wechatFollowVo = (WechatFollowVo) XmlUtil.fromXML(xmlContent, WechatFollowVo.class);
public class XmlUtil {
    /**
     * xml⽂档解析为对象
     * @param xml   xml文档
     * @param clazz 要转换的类
     * @return
     */
     public static Object fromXML(String xml, Class clazz){
        XStream xmlStream = new XStream(new XppDriver(new XmlFriendlyNameCoder("_-", "_")));
        XStream.setupDefaultSecurity(xmlStream);
        xmlStream.processAnnotations(new Class[]{clazz});
        xmlStream.allowTypes(new Class[]{clazz});
        xmlStream.ignoreUnknownElements();
        Object result = xmlStream.fromXML(xml);
        return result;
    }
}@Data
@Accessors(chain = true)
@XStreamAlias(value = "xml")
public class WechatFollowVo implements Serializable {
    private static final long serialVersionUID = 1L;
    @ApiModelProperty("开发者微信号")
    @XStreamAlias(value = "ToUserName")
    private String toUserName;
    @ApiModelProperty("发送?帐号(?个OpenID)")
    @XStreamAlias(value = "FromUserName")
    private String fromUserName;
    @ApiModelProperty("消息创建时间 (整型)")
    @XStreamAlias(value = "CreateTime")
    private Long createTime;
    @ApiModelProperty("消息类型,event")
    @XStreamAlias(value = "MsgType")
    private String messageType;
    @ApiModelProperty("事件类型,subscribe(订阅)、unsubscribe(取消订阅)")
    @XStreamAlias(value = "Event")
    private String event;
    @ApiModelProperty("事件 KEY 值,未关注:qrscene_为前缀,后面为二维码的参数值,以关注:是一个32位无符号整数,即创建二维码时的二维码scene_id")
    @XStreamAlias(value = "EventKey")
    private String eventKey;
    @ApiModelProperty("二维码的ticket,可用来换取二维码图片")
    @XStreamAlias(value = "Ticket")
    private String ticket;
    @ApiModelProperty("消息内容")
    @XStreamAlias(value = "Content")
    private String content;
}以上步骤已经完成了xml数据包转换成实体对象。当时写到try()里的声明,有些不明,也去查看了百度。好似有些懂了。Java7的新特性,作用相当于finally手动释放被占用的资源,如果使用之后,就会在程序块结束时自动释放掉占用的资源,代码也更简洁美观。前提是括号声明的资源实现类实现了Closeable或AutoCloseable接口。
注意:多个资源需要分号隔开



 
 



















