jdk动态代理

Posted IT的鱼

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了jdk动态代理相关的知识,希望对你有一定的参考价值。

/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2021-2021. All rights reserved.
 */

package com.huawei.it.external.config;

import com.alibaba.fastjson.JSON;
import com.huawei.it.external.service.ITWoRemoteService;
import com.huawei.it.jalor5.core.exception.ApplicationException;
import com.huawei.it.jalor5.core.log.ILogger;
import com.huawei.it.jalor5.core.log.JalorLoggerFactory;
import com.huawei.it.jalor5.vegahsa.client.rpc.factory.RpcClientFactory;
import com.huawei.it.util.excetion.BizApplicationException;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.util.StringUtils;
import org.springframework.web.client.HttpClientErrorException;
import org.springframework.web.client.HttpServerErrorException;

import javax.inject.Named;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * 功能描述:封装ITWoRemoteService的代理对象,对远程调用的异常情况进行处理
 * 起因:通过Jalor RPC调用,远程调用发生异常时,会将远程异常封装为非ApplicationException
 *      具体封装的异常详见VegaRestTemplate.getErrorHandler().handleError()
 *      而由于Jalor统一对异常进行了封装,详见WebServiceExceptionMapper,此种方式会导致
 *      非ApplicationException异常,code统一为unknown,message统一为标准异常术语,最终
 *      的结果是远程服务抛出的异常提示无法在客户端正常显示
 * 这个类封装了什么:本类对异常各种异常进行了捕获,并对异常提示进行封装
 * (1)对于4xx异常,抛出异常为”远程服务网络不通,或您无权限进行相关操作”
 * (2)对于5xx异常,从code中解析异常信息
 * (WO系统中直接使用throw new BizApplicationException("异常信息"),实际异常信息是封装在code中的)
 * 如果异常信息不为null且不为"",则直接构建BizApplicationException抛出
 * 如果异常信息为null或为"",抛出异常为“服务调用发生异常,请稍后重试”
 * (3)对于其他异常,统一抛出“发生未知异常,请联系相关人员”
 *
 * @author YaoJiang-wx1047757
 * @since 2021-05-08
 */
@Named("tWoRemoteService")
public class ITWoRemoteServiceFactoryBean implements FactoryBean<ITWoRemoteService> {
    private static final ILogger LOGGER = JalorLoggerFactory.getLogger(ITWoRemoteService.class);

    // unknown异常及正常5xx在返回体中的特征值
    private static final String UNKNOWN = "code=unknown";
    private static final String ERRCODE_PREFIX = "code=JALOR_EX$";
    private static final Integer PREFIX_LENGTH = 14;

    // 实际用于执行远程调用的类,是ITWoRemoteService的一个代理类
    private static final ITWoRemoteService REMOTE_TARGET = RpcClientFactory.getSubAppRpcClinet(ITWoRemoteService.class);

    /**
     * 功能描述:重写FactoryBean的getObject()方法
     * 内部对ITWoRemoteService接口创建了代理,代理内部实际上是使用Jalor生成的远程调用代理
     * 执行远程调用,对调用过程中产生的异常进行捕获,将异常拆分为5xx/4xx/其他异常等三种类型
     *
     * @author YaoJiang-wx1047757
     * @since 2021-05-08
     * @return com.huawei.it.external.service.ITWoRemoteService ITWoRemoteService代理对象
     * @throws ApplicationException 创建代理后,类型转换异常时抛出异常
     */
    @Override
    public ITWoRemoteService getObject() throws ApplicationException {
        Object proxy = Proxy.newProxyInstance(ITWoRemoteService.class.getClassLoader(),
                        new Class[] {ITWoRemoteService.class}, new RemoteRequestInvocationHandler());
        if (proxy instanceof ITWoRemoteService) {
            return (ITWoRemoteService) proxy;
        }
        throw new BizApplicationException("======>>> ITWoRemoteServiceFactoryBean创建代理失败");
    }

    /**
     * 功能描述:获取当前Bean的类型,当前Bean的类型为ITwoRemoteService.class
     *
     * @author YaoJiang-wx1047757
     * @since 2021-05-08
     * @return java.lang.Class Bean的类型
     */
    @Override
    public Class<?> getObjectType() {
        return ITWoRemoteService.class;
    }

    /**
     * 功能描述:具体代理执行类,包含了对异常捕捉的动作
     *
     * @author YaoJiang-wx1047757
     * @since 2021-05-08
     */
    static class RemoteRequestInvocationHandler implements InvocationHandler {
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            try {
                LOGGER.info2("======>>> ITWoRemoteService准备发起远程调用,method:{},参数:{}",
                        method.getName(), JSON.toJSONString(args));
                return method.invoke(REMOTE_TARGET, args);
            } catch (InvocationTargetException e) {
                LOGGER.error2("======>>> ITWoRemoteService调用发生异常,实际异常类型:{},异常堆栈信息:",
                        e.getTargetException().getClass().getName(), e.getTargetException());
                // 获取具体异常信息
                Throwable te = e.getTargetException();

                // 5xx异常处理
                if (te instanceof HttpServerErrorException) {
                    HttpServerErrorException he = (HttpServerErrorException) te;
                    String statusText = he.getStatusText();

                    // 异常信息截取 WO系统中直接使用throw new BizApplicationException("异常信息"),实际异常信息是封装在code中
                    // 异常信息格式为:{code=JALOR_EX$异常信息$(unknown), message=标准异常术语}
                    String errorMsg = null;
                    if (!StringUtils.isEmpty(statusText)) {
                        if (!statusText.contains(UNKNOWN) && statusText.contains(ERRCODE_PREFIX)) {
                            int errorCodeStart = statusText.indexOf(ERRCODE_PREFIX) + PREFIX_LENGTH;
                            int errorCodeEnd = statusText.indexOf("$", errorCodeStart);
                            errorMsg = statusText.substring(errorCodeStart, errorCodeEnd);
                        }
                    }
                    if (StringUtils.isEmpty(errorMsg)) {
                        errorMsg = "服务调用发生异常,请稍后重试";
                    }
                    throw new BizApplicationException(errorMsg);
                }

                // 4xx异常处理
                if (te instanceof HttpClientErrorException) {
                    throw new BizApplicationException("远程服务网络不通,或您无权限进行相关操作");
                }

                // 其他异常处理
                throw new BizApplicationException("发生未知异常,请联系相关人员");
            }
        }
    }
}

以上是关于jdk动态代理的主要内容,如果未能解决你的问题,请参考以下文章

JDK动态代理CGLIB动态代理

JDK动态代理CGLIB动态代理

(java反射-JDK动态代理)+CGLIB动态代理

JDK动态代理与Cglib动态代理

JDK动态代理与CGLib动态代理

代理模式(静态代理jdk动态代理CGLib动态代理)