精华内容
下载资源
问答
  • 主要介绍了springboot+dubbo+validation 进行rpc参数校验的实现方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
  • 自己实现dubbo参数校验

    千次阅读 2020-06-03 15:00:26
    因为工作中经常需要做参数校验,在springboot项目中使用@Valid+@NotNull、@NotBlank…注解开发API接口非常丝滑,相反在开发RPC接口时却还是需要编写大量的参数判断,严重影响主业务流程的开发(公司目前用的是Dubbo...

    背景

    因为平时的业务开发中经常需要做参数校验,在SpringBoot项目中使用@Valid+@NotNull、@NotBlank…注解开发API接口非常丝滑,相反在开发RPC接口时却还是需要编写大量的参数判断,严重影响主业务流程的开发(公司目前用的是alibaba Dubbo2.6.2)且代码整洁度、风格都受到了挑战。基于以上原因萌生了写一个PRC接口的验证,新版的dubbo据说已经默认支持了调用参数校验

    原理

    我们要在consumer调用provider的过程中实现参数校验,而这个逻辑可以做为一个逻辑层或者说横切面,是否联想到AOP?而dubbo提供的filter机制刚好符合我们的要求。校验逻辑可以使用目前主流的校验框架hibernate-validator(省时省力又完善)。

    自定义实现

    这里DubboServiceParameterFilter类是我自己定义的dubbo filter类,当然作为filter需要使用dubbo的spi机制,即需要在
    /META-INF/dubbo/com.alibaba.rpc.Filter文件中添加

    dubboServiceParameterFilter=com.xxxx.filter.DubboServiceParameterFilter
    

    Filter代码

    @Activate(group = {Constants.PROVIDER}, order = 1)
    public class DubboServiceParameterFilter implements Filter {
    
        private static final Logger logger = LoggerFactory.getLogger(DubboServiceParameterFilter.class);
    
        private static ExecutableValidator executableValidator = Validation.buildDefaultValidatorFactory().getValidator().forExecutables();
    
        private static final String VALIDATOR_MESSAGE= "参数验证失败";
    
        @Override
        public Result invoke(Invoker<?> invoker, Invocation invocation) {
    
            logger.info("===================== param filter ========================");
            Method method = getMethod(invoker, invocation);
            if (Objects.nonNull(method)) {
                Class inf = invoker.getInterface();
                Object object = ServiceBean.getSpringContext().getBean(inf);
                Object[] paramList = invocation.getArguments();
                //用校验器,校验此object的method方法的paramList是否合法,返回校验结果
                Set<ConstraintViolation<Object>> constraintViolations = executableValidator.validateParameters(object, method, paramList);
                Response response = getValidationResult(constraintViolations);
                if (Objects.nonNull(response.getData())) {
                    return new RpcResult(response);
                }
            }
            return invoker.invoke(invocation);
        }
    
        /**
         * 获取校验方法
         */
        private static Method getMethod(Invoker<?> invoker, Invocation invocation) {
    
            Method[] methods = invoker.getInterface().getDeclaredMethods();
            for (Method m : methods) {
                //即获取此次dubbo invocation对应的method对象
                boolean needCheck = m.getName().equals(invocation.getMethodName()) && invocation.getArguments().length == m.getParameterCount();
                if (needCheck) {
                    if (matchMethod(invocation.getParameterTypes(), m.getParameterTypes())) {
                        return m;
                    }
                }
            }
            return null;
        }
    
        //获取匹配的方法
        private static boolean matchMethod(Class[] invokerMethodParamClassList, Class[] matchMethodParamClassList) {
            for (int i = 0; i < invokerMethodParamClassList.length; i++) {
                if (!invokerMethodParamClassList[i].equals(matchMethodParamClassList[i])) {
                    return false;
                }
            }
            return true;
        }
    
        /**
         * 校验结果转换返回对象
         */
        private static <T> Response getValidationResult(Set<ConstraintViolation<T>> set) {
            if (set != null && !set.isEmpty()) {
                Map<String, String> errorMsg = Maps.newHashMap();
                for (ConstraintViolation<T> violation : set) {
                    errorMsg.put(violation.getPropertyPath().toString(), violation.getMessage());
                }
                return Response.createError(VALIDATOR_MESSAGE, errorMsg);
            }
            return Response.createError(VALIDATOR_MESSAGE);
        }
    

    测试请求:用了以下3种测试请求

    在这里插入图片描述
    API测试代码
    在这里插入图片描述
    在这里插入图片描述
    注意这里有个细节:API侧有一个全局异常处理器

    RPC测试代码在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    效果在3个请求返回结果中

    转载请注明出处:https://blog.csdn.net/chenghaitao111111/article/details/106523864

    展开全文
  • dubbo】参数校验

    千次阅读 2019-03-10 19:46:00
    dubbo的SPI扩展是有关于参数校验的SP扩展,校难的原理其实也是用的拦截器原理。dubbo包有一个 ValidationFilter的拦截器实现。dubbo是有自己的校验器实现的JValidator,如果需要自定义的话,则需要实现 ...

    dubbo的SPI扩展是有关于参数校验的SP扩展,校难的原理其实也是用的拦截器原理。dubbo包有一个

    ValidationFilter的拦截器实现。dubbo是有自己的校验器实现的JValidator,如果需要自定义的话,则需要实现
    Validation接口
    
    
    @SPI("jvalidation")
    public interface Validation {
        @Adaptive({"validation"})
        Validator getValidator(URL var1);
    }
    

    首先编写自定义的校验器。实现Validation接口。再编写自己的校验者。让自定义的校验器指定检验者为自定义的校验者。我这里检验者继承了原生的JValidator校验。也可以自己实现Validator进行自己完全的自定义。当有检验异常的时候,报出自定义的异常。再使用dubbo的的拦截器机制捕捉自定义异常。就进行自定义异常结果处理了。

    public class FlashValidation implements Validation {
    
        @Override
        public Validator getValidator(URL url) {
            return new FlashValidator(url);
        }
    }
    
    
    public class FlashValidator extends JValidator {
    
        public FlashValidator(URL url) {
            super(url);
        }
    
        @Override
        public void validate(String s, Class<?>[] classes, Object[] objects) throws Exception {
            try {
                super.validate(s, classes, objects);
            } catch (Exception e) {
                if (e instanceof ConstraintViolationException) {
                    ConstraintViolation constraintViolation = ((ConstraintViolationException) e).getConstraintViolations().stream().findFirst().get();
                    throw new ParamValidationException(constraintViolation.getMessageTemplate());
                } else {
                    throw e;
                }
            }
        }
    }
    

    还需要创建文件META-INF/dubbo/com.alibaba.dubbo.validation.Validation, 编辑内容为自己拦截器

    xxxValidation=com.xxx.xxx.validation.FlashValidation

    那么在服务注册的时候指定校验器,同理消费者进行校验的话,需要在service注入的时候指定validation。

    这里建议是消费方开启参数校验就行了。这样还没有进行网络传输的时候进行校验,如果失败了。也减少了不必要的网络传输。

    //@Service(group = "order", version = "1.0", timeout = 60000,validation = "true")
    //validation = "true" 这里如果写true则调用的就是dubbo默认的参数校验处理JValidator
    
    @Service(group = "order", version = "1.0", timeout = 60000,validation = "ParamValidation")
    public class OrderServiceImpl implements OrderService {
    
        //...
    }
    
    
    @Reference(group = "order", version = "1.0.0", timeout = 30000,validation = "ParamValidation")
    OrderService orderService;
    

    被校验的实体对象需要加上注解validation

    //实体对象
    public class Order{
        //这里指定校验的所属组。    
        @NotNull(groups = {AppService.AddAppInfo.class}, message = "责任人名称为空")
        private String liableUserName;
    }
    
    
       
    
    
    //服务接口
    public interface OrderService {
        
        //这里用interface注解指定一个接口当作参数校验的一个接口。那么标注了这个组的validation注解。调用此方式就会进行校验
        @interface AddAppInfo {}
        SystemAppInfo addAppInfo(Order order);
    
        @interface GetAppList {}
        List<SystemAppInfo> getAppList();
    
    }
    

     

    展开全文
  • Dubbo RPC接口的参数校验

    千次阅读 2019-07-08 15:08:30
    服务A调用服务B时,调用时即对服务B的接口参数进行校验 ,无需进入到服务B 重点就是添加 validation = "true" 1、pom文件 <dependency> <groupId>javax.validation</groupId> <...

    服务A调用服务B时,调用时即对服务B的接口参数进行校验 ,无需进入到服务B

    重点就是添加 validation = "true"

    1、pom文件

    <dependency>
        <groupId>javax.validation</groupId>
        <artifactId>validation-api</artifactId>
        <version>1.1.0.Final</version>
    </dependency>
    <dependency>
        <groupId>org.hibernate</groupId>
        <artifactId>hibernate-validator</artifactId>
        <version>5.0.1.Final</version>
    </dependency>

    2、dubbo xml配置 或者Config类修改 或者@Reference时添加

    xml:

    在客户端验证参数

    <dubbo:reference id="validationService" interface="com.alibaba.dubbo.examples.validation.api.ValidationService" validation="true" />
    

    在服务器端验证参数

    <dubbo:service interface="com.alibaba.dubbo.examples.validation.api.ValidationService" validation="true" />

    config类: 

    @Bean
    public ReferenceBean<PLRepaymentPlanService> repaymentPlanServiceReferenceBean(){
        ReferenceBean<PLRepaymentPlanService> referenceBean = new ReferenceBean<>();
        referenceBean.setInterface(PLRepaymentPlanService.class);
        referenceBean.setCheck(false);
        referenceBean.setValidation("true");
        return referenceBean;
    }

    @Reference:

    @Reference(validation = "true")
    TestService testService;

    3、服务B的接口

    Integer createRepaymentPlan(@Valid RepaymentPlanRequestDTO dto,@NotNull Integer age) throws PostLoanException;
    

    DTO 内容

    @Data
    public class RepaymentPlanRequestDTO implements Serializable {
    
        /**
         * 业务源
         **/
        @NotNull
        private Integer businessSource;
        /**
         * 业务类型0,消费贷; 1,现金贷; 2,租房; 3:payday;4:无预约现金贷 5有钱贷,6指尖贷,7多享贷,8租机,9白条,10医美,11前置扣款
         **/
        @NotNull
        private Integer type;
        /**
         * 业务规则
         */
        private String businessRule;
        /**
         * 订单id
         **/
        @NotNull
        private Integer orderId;
        /**
         * 期数
         **/
        @NotNull
        private Integer period;
        /**
         * 应还总额
         **/
        @NotNull
        private BigDecimal total;
        /**
         * 每期应还
         **/
        @NotNull
        private BigDecimal every;
        /**
         * 本金
         **/
        @NotNull
        private BigDecimal principal;
        /**
         * 每期利率
         **/
        @NotNull
        private BigDecimal rateEvery;
        /**
         * 服务费
         **/
        private BigDecimal serviceFee;
        /**
         * 用户id
         **/
        @NotNull
        private Integer userinfoId;
        /**
         * 商品id
         **/
        private Integer goodsId;
        /**
         * 渠道id
         **/
        private Integer channelId;
        /**
         * 资方id
         **/
        private Integer capitalId;
        /**
         * 起息日
         */
        private LocalDateTime startInterestDate;
        /**
         * 贷款天数
         **/
        private Integer payDays;
        /**
         * 提单编号(规则O2O+订单编号+资方+重试次数)
         */
        private String channelOrderNO;
        /**
         * 是否通知
         * 需要资方生成还款计划后资方平台通知/回调的订单
         */
        private boolean hasNotified;
        /**
         * 订单四要素
         */
        @Valid
        private PLUserInfoDTO plUserInfoDTO;
    
    }

    可以嵌套校验 但需要在嵌套校验的字段上加@Valid

    实现类上需要加@Validated

    启动服务B,服务A调用接口,符合添加的valid校验标准

     

    展开全文
  • dubbo框架Validation校验问题

    千次阅读 2019-04-19 21:49:08
    服务在调用外部接口时抛出异常,问题已经有人提出,新版本也已修复https://github.com/apache/incubator-dubbo/issues/712 原因分析 下面我来一步步分析该问题出现的原因 2019-04-18 10:45:37,095 WARN [Thread-12]...

    服务在调用外部接口时抛出异常,问题已经有人提出,新版本也已修复https://github.com/apache/incubator-dubbo/issues/712

    原因分析

    下面我来一步步分析该问题出现的原因

    2019-04-18 10:45:37,095 WARN [Thread-12] com.alibaba.dubbo.validation.support.jvalidation.JValidator:getMethodParameterBean:158  [DUBBO] .......provider.RiderBlackListProvider_RiderBlackListParameter_java.lang.String class is frozen, dubbo version: 2.5.6.2-D-RELEASE, current host: 192.168.96.162
    java.lang.RuntimeException: .......provider.RiderBlackListProvider_RiderBlackListParameter_java.lang.String class is frozen
    	at javassist.CtClassType.checkModify(CtClassType.java:309) ~[javassist-3.21.0-GA.jar:?]
    	at javassist.CtField.setModifiers(CtField.java:240) ~[javassist-3.21.0-GA.jar:?]
    	at javassist.compiler.Javac.compileField(Javac.java:135) ~[javassist-3.21.0-GA.jar:?]
    	at javassist.compiler.Javac.compile(Javac.java:93) ~[javassist-3.21.0-GA.jar:?]
    	at javassist.CtField.make(CtField.java:162) ~[javassist-3.21.0-GA.jar:?]
    	at com.alibaba.dubbo.validation.support.jvalidation.JValidator.getMethodParameterBean(JValidator.java:145) [dubbo-2.5.6.2-D-RELEASE.jar:2.5.6.2-D-RELEASE]
    	at com.alibaba.dubbo.validation.support.jvalidation.JValidator.validate(JValidator.java:242) [dubbo-2.5.6.2-D-RELEASE.jar:2.5.6.2-D-RELEASE]
    	at com.alibaba.dubbo.validation.filter.ValidationFilter.invoke(ValidationFilter.java:49) [dubbo-2.5.6.2-D-RELEASE.jar:2.5.6.2-D-RELEASE]
    	at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:69) [dubbo-2.5.6.2-D-RELEASE.jar:2.5.6.2-D-RELEASE]
    	at com.alibaba.dubbo.monitor.support.MonitorFilter.invoke(MonitorFilter.java:75) [dubbo-2.5.6.2-D-RELEASE.jar:2.5.6.2-D-RELEASE]
    	at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:69) [dubbo-2.5.6.2-D-RELEASE.jar:2.5.6.2-D-RELEASE]
    	at com.alibaba.dubbo.rpc.protocol.dubbo.filter.FutureFilter.invoke(FutureFilter.java:54) [dubbo-2.5.6.2-D-RELEASE.jar:2.5.6.2-D-RELEASE]
    	at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:69) [dubbo-2.5.6.2-D-RELEASE.jar:2.5.6.2-D-RELEASE]
    	at com.alibaba.dubbo.rpc.filter.ConsumerContextFilter.invoke(ConsumerContextFilter.java:48) [dubbo-2.5.6.2-D-RELEASE.jar:2.5.6.2-D-RELEASE]
    	at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:69) [dubbo-2.5.6.2-D-RELEASE.jar:2.5.6.2-D-RELEASE]
    	at ....dubbo.filter.ConsumerTrackingFilter.invoke(ConsumerTrackingFilter.java:30) [dubbo-integration-0.0.2-20180420.072844-18.jar:0.0.2-SNAPSHOT]
    	at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:69) [dubbo-2.5.6.2-D-RELEASE.jar:2.5.6.2-D-RELEASE]
    	at com.alibaba.dubbo.rpc.listener.ListenerInvokerWrapper.invoke(ListenerInvokerWrapper.java:74) [dubbo-2.5.6.2-D-RELEASE.jar:2.5.6.2-D-RELEASE]
    	at com.alibaba.dubbo.rpc.protocol.InvokerWrapper.invoke(InvokerWrapper.java:53) [dubbo-2.5.6.2-D-RELEASE.jar:2.5.6.2-D-RELEASE]
    	at com.alibaba.dubbo.rpc.cluster.support.FailoverClusterInvoker.doInvoke(FailoverClusterInvoker.java:77) [dubbo-2.5.6.2-D-RELEASE.jar:2.5.6.2-D-RELEASE]
    	at com.alibaba.dubbo.rpc.cluster.support.AbstractClusterInvoker.invoke(AbstractClusterInvoker.java:229) [dubbo-2.5.6.2-D-RELEASE.jar:2.5.6.2-D-RELEASE]
    	at com.alibaba.dubbo.rpc.cluster.support.wrapper.MockClusterInvoker.invoke(MockClusterInvoker.java:72) [dubbo-2.5.6.2-D-RELEASE.jar:2.5.6.2-D-RELEASE]
    	at com.alibaba.dubbo.rpc.proxy.InvokerInvocationHandler.invoke(InvokerInvocationHandler.java:74) [dubbo-2.5.6.2-D-RELEASE.jar:2.5.6.2-D-RELEASE]
    	at com.alibaba.dubbo.common.bytecode.proxy1.riderBlackList(proxy1.java) [dubbo-2.5.6.2-D-RELEASE.jar:2.5.6.2-D-RELEASE]
    	at com.gallant.dispatch.RiderBlacklistFilterTest$1.run(RiderBlacklistFilterTest.java:35) [test-classes/:?]
    	at java.lang.Thread.run(Thread.java:748) [?:1.8.0_131]
    2019-04-18 10:45:37,097 WARN [Thread-13] com.alibaba.dubbo.validation.support.jvalidation.JValidator:getMethodParameterBean:158  [DUBBO] by java.lang.LinkageError: loader (instance of  sun/misc/Launcher$AppClassLoader): attempted  duplicate class definition for name: ".../provider/RiderBlackListProvider_RiderBlackListParameter_java/lang/String", dubbo version: 2.5.6.2-D-RELEASE, current host: 192.168.96.162
    javassist.CannotCompileException: by java.lang.LinkageError: loader (instance of  sun/misc/Launcher$AppClassLoader): attempted  duplicate class definition for name: ".../provider/RiderBlackListProvider_RiderBlackListParameter_java/lang/String"
    	at javassist.ClassPool.toClass(ClassPool.java:1170) ~[javassist-3.21.0-GA.jar:?]
    	at javassist.ClassPool.toClass(ClassPool.java:1113) ~[javassist-3.21.0-GA.jar:?]
    	at javassist.ClassPool.toClass(ClassPool.java:1071) ~[javassist-3.21.0-GA.jar:?]
    	at javassist.CtClass.toClass(CtClass.java:1275) ~[javassist-3.21.0-GA.jar:?]
    	at com.alibaba.dubbo.validation.support.jvalidation.JValidator.getMethodParameterBean(JValidator.java:149) [dubbo-2.5.6.2-D-RELEASE.jar:2.5.6.2-D-RELEASE]
    	at com.alibaba.dubbo.validation.support.jvalidation.JValidator.validate(JValidator.java:242) [dubbo-2.5.6.2-D-RELEASE.jar:2.5.6.2-D-RELEASE]
    	at com.alibaba.dubbo.validation.filter.ValidationFilter.invoke(ValidationFilter.java:49) [dubbo-2.5.6.2-D-RELEASE.jar:2.5.6.2-D-RELEASE]
    	at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:69) [dubbo-2.5.6.2-D-RELEASE.jar:2.5.6.2-D-RELEASE]
    	at com.alibaba.dubbo.monitor.support.MonitorFilter.invoke(MonitorFilter.java:75) [dubbo-2.5.6.2-D-RELEASE.jar:2.5.6.2-D-RELEASE]
    	at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:69) [dubbo-2.5.6.2-D-RELEASE.jar:2.5.6.2-D-RELEASE]
    	at com.alibaba.dubbo.rpc.protocol.dubbo.filter.FutureFilter.invoke(FutureFilter.java:54) [dubbo-2.5.6.2-D-RELEASE.jar:2.5.6.2-D-RELEASE]
    	at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:69) [dubbo-2.5.6.2-D-RELEASE.jar:2.5.6.2-D-RELEASE]
    	at com.alibaba.dubbo.rpc.filter.ConsumerContextFilter.invoke(ConsumerContextFilter.java:48) [dubbo-2.5.6.2-D-RELEASE.jar:2.5.6.2-D-RELEASE]
    	at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:69) [dubbo-2.5.6.2-D-RELEASE.jar:2.5.6.2-D-RELEASE]
    	at ....dubbo.filter.ConsumerTrackingFilter.invoke(ConsumerTrackingFilter.java:30) [dubbo-integration-0.0.2-20180420.072844-18.jar:0.0.2-SNAPSHOT]
    	at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:69) [dubbo-2.5.6.2-D-RELEASE.jar:2.5.6.2-D-RELEASE]
    	at com.alibaba.dubbo.rpc.listener.ListenerInvokerWrapper.invoke(ListenerInvokerWrapper.java:74) [dubbo-2.5.6.2-D-RELEASE.jar:2.5.6.2-D-RELEASE]
    	at com.alibaba.dubbo.rpc.protocol.InvokerWrapper.invoke(InvokerWrapper.java:53) [dubbo-2.5.6.2-D-RELEASE.jar:2.5.6.2-D-RELEASE]
    	at com.alibaba.dubbo.rpc.cluster.support.FailoverClusterInvoker.doInvoke(FailoverClusterInvoker.java:77) [dubbo-2.5.6.2-D-RELEASE.jar:2.5.6.2-D-RELEASE]
    	at com.alibaba.dubbo.rpc.cluster.support.AbstractClusterInvoker.invoke(AbstractClusterInvoker.java:229) [dubbo-2.5.6.2-D-RELEASE.jar:2.5.6.2-D-RELEASE]
    	at com.alibaba.dubbo.rpc.cluster.support.wrapper.MockClusterInvoker.invoke(MockClusterInvoker.java:72) [dubbo-2.5.6.2-D-RELEASE.jar:2.5.6.2-D-RELEASE]
    	at com.alibaba.dubbo.rpc.proxy.InvokerInvocationHandler.invoke(InvokerInvocationHandler.java:74) [dubbo-2.5.6.2-D-RELEASE.jar:2.5.6.2-D-RELEASE]
    	at com.alibaba.dubbo.common.bytecode.proxy1.riderBlackList(proxy1.java) [dubbo-2.5.6.2-D-RELEASE.jar:2.5.6.2-D-RELEASE]
    	at com.gallant.dispatch.RiderBlacklistFilterTest$1.run(RiderBlacklistFilterTest.java:35) [test-classes/:?]
    	at java.lang.Thread.run(Thread.java:748) [?:1.8.0_131]
    Caused by: java.lang.LinkageError: loader (instance of  sun/misc/Launcher$AppClassLoader): attempted  duplicate class definition for name: ".../provider/RiderBlackListProvider_RiderBlackListParameter_java/lang/String"
    	at java.lang.ClassLoader.defineClass1(Native Method) ~[?:1.8.0_131]
    	at java.lang.ClassLoader.defineClass(ClassLoader.java:763) ~[?:1.8.0_131]
    	at java.lang.ClassLoader.defineClass(ClassLoader.java:642) ~[?:1.8.0_131]
    	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:1.8.0_131]
    	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[?:1.8.0_131]
    	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[?:1.8.0_131]
    	at java.lang.reflect.Method.invoke(Method.java:498) ~[?:1.8.0_131]
    	at javassist.ClassPool.toClass2(ClassPool.java:1183) ~[javassist-3.21.0-GA.jar:?]
    	at javassist.ClassPool.toClass(ClassPool.java:1164) ~[javassist-3.21.0-GA.jar:?]
    	... 24 more
    2019-04-18 10:45:37,090 WARN [Thread-14] com.alibaba.dubbo.validation.support.jvalidation.JValidator:getMethodParameterBean:158  [DUBBO] cannot add, dubbo version: 2.5.6.2-D-RELEASE, current host: 192.168.96.162
    javassist.CannotCompileException: cannot add
    	at javassist.CtClassType.addField(CtClassType.java:1310) ~[javassist-3.21.0-GA.jar:?]
    	at javassist.CtClass.addField(CtClass.java:1100) ~[javassist-3.21.0-GA.jar:?]
    	at com.alibaba.dubbo.validation.support.jvalidation.JValidator.getMethodParameterBean(JValidator.java:147) [dubbo-2.5.6.2-D-RELEASE.jar:2.5.6.2-D-RELEASE]
    	at com.alibaba.dubbo.validation.support.jvalidation.JValidator.validate(JValidator.java:242) [dubbo-2.5.6.2-D-RELEASE.jar:2.5.6.2-D-RELEASE]
    	at com.alibaba.dubbo.validation.filter.ValidationFilter.invoke(ValidationFilter.java:49) [dubbo-2.5.6.2-D-RELEASE.jar:2.5.6.2-D-RELEASE]
    	at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:69) [dubbo-2.5.6.2-D-RELEASE.jar:2.5.6.2-D-RELEASE]
    	at com.alibaba.dubbo.monitor.support.MonitorFilter.invoke(MonitorFilter.java:75) [dubbo-2.5.6.2-D-RELEASE.jar:2.5.6.2-D-RELEASE]
    	at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:69) [dubbo-2.5.6.2-D-RELEASE.jar:2.5.6.2-D-RELEASE]
    	at com.alibaba.dubbo.rpc.protocol.dubbo.filter.FutureFilter.invoke(FutureFilter.java:54) [dubbo-2.5.6.2-D-RELEASE.jar:2.5.6.2-D-RELEASE]
    	at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:69) [dubbo-2.5.6.2-D-RELEASE.jar:2.5.6.2-D-RELEASE]
    	at com.alibaba.dubbo.rpc.filter.ConsumerContextFilter.invoke(ConsumerContextFilter.java:48) [dubbo-2.5.6.2-D-RELEASE.jar:2.5.6.2-D-RELEASE]
    	at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:69) [dubbo-2.5.6.2-D-RELEASE.jar:2.5.6.2-D-RELEASE]
    	at ....dubbo.filter.ConsumerTrackingFilter.invoke(ConsumerTrackingFilter.java:30) [dubbo-integration-0.0.2-20180420.072844-18.jar:0.0.2-SNAPSHOT]
    	at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:69) [dubbo-2.5.6.2-D-RELEASE.jar:2.5.6.2-D-RELEASE]
    	at com.alibaba.dubbo.rpc.listener.ListenerInvokerWrapper.invoke(ListenerInvokerWrapper.java:74) [dubbo-2.5.6.2-D-RELEASE.jar:2.5.6.2-D-RELEASE]
    	at com.alibaba.dubbo.rpc.protocol.InvokerWrapper.invoke(InvokerWrapper.java:53) [dubbo-2.5.6.2-D-RELEASE.jar:2.5.6.2-D-RELEASE]
    	at com.alibaba.dubbo.rpc.cluster.support.FailoverClusterInvoker.doInvoke(FailoverClusterInvoker.java:77) [dubbo-2.5.6.2-D-RELEASE.jar:2.5.6.2-D-RELEASE]
    	at com.alibaba.dubbo.rpc.cluster.support.AbstractClusterInvoker.invoke(AbstractClusterInvoker.java:229) [dubbo-2.5.6.2-D-RELEASE.jar:2.5.6.2-D-RELEASE]
    	at com.alibaba.dubbo.rpc.cluster.support.wrapper.MockClusterInvoker.invoke(MockClusterInvoker.java:72) [dubbo-2.5.6.2-D-RELEASE.jar:2.5.6.2-D-RELEASE]
    	at com.alibaba.dubbo.rpc.proxy.InvokerInvocationHandler.invoke(InvokerInvocationHandler.java:74) [dubbo-2.5.6.2-D-RELEASE.jar:2.5.6.2-D-RELEASE]
    	at com.alibaba.dubbo.common.bytecode.proxy1.riderBlackList(proxy1.java) [dubbo-2.5.6.2-D-RELEASE.jar:2.5.6.2-D-RELEASE]
    	at com.gallant.dispatch.RiderBlacklistFilterTest$1.run(RiderBlacklistFilterTest.java:35) [test-classes/:?]
    	at java.lang.Thread.run(Thread.java:748) [?:1.8.0_131]
    

    老规矩,跟着堆栈看源码
    查看javassist的ClassPool源码,frozen问题正是该处抛出,那么getCache获取到之后发现类处于冻结状态,所以抛出了异常

    private static Object getMethodParameterBean(Class<?> clazz, Method method, Object[] args) {
    	...
        try {
    		//获取class名称
            String parameterClassName = generateMethodParameterClassName(clazz, method);
            Class<?> parameterClass;
            try {
    		//根据名称获取class,如果获取失败则创建并缓存
                parameterClass = (Class<?>) Class.forName(parameterClassName, true, clazz.getClassLoader());
            } catch (ClassNotFoundException e) {
                ClassPool pool = ClassGenerator.getClassPool(clazz.getClassLoader());
                CtClass ctClass = pool.makeClass(parameterClassName);
                ...创建,拼接class内容
    			//创建class字节码成功后并冻结
                parameterClass = ctClass.toClass();
            }
            ...
    }
    //创建class
    public synchronized CtClass makeClass(String classname, CtClass superclass)
        throws RuntimeException
    {
        checkNotFrozen(classname);
        CtClass clazz = new CtNewClass(classname, this, false, superclass);
        cacheCtClass(classname, clazz, true);
        return clazz;
    }
    //如果已冻结则抛出异常
    void checkNotFrozen(String classname) throws RuntimeException {
        CtClass clazz = getCached(classname);
        if (clazz == null) {
            if (!childFirstLookup && parent != null) {
                try {
                    clazz = parent.get0(classname, true);
                }
                catch (NotFoundException e) {}
                if (clazz != null)
                    throw new RuntimeException(classname
                            + " is in a parent ClassPool.  Use the parent.");
            }
        }
        else
            if (clazz.isFrozen())
                throw new RuntimeException(classname
                                        + ": frozen class (cannot edit)");
    }
    

    创建CtNewClass对象的wasFrozen状态是false状态,缓存class文件,在文件创建的时候如果再次发起请求进入校验模块,那么从缓存中获取的class对象校验其状态便一定会是冻结状态。当然,前提是反射获取类失败的情况

    为什么Class.forName会出现两次都获取不到class呢?创建之后没有加载至对应的classloader?

    我们看到反射获取类时使用的是接口class的classloader。创建类是使用的ctClass.toClass(),其实就是当前线程的classloader。
    那么如果当前线程的classloader与接口的classloader不匹配那么就会出现ClassNotFoundException异常导致再次重复创建。所以就出现了我们看到的异常。

    什么情况下会出现classloader不匹配呢?

    1. 首先我们先看下接口的classloader,也就是JValidator的clazz的classloader,可以看到其实就是ClassHelper.getClassLoader();里面其实就是当前线程上下文的classloader
    public static ClassLoader getClassLoader(Class<?> cls) {
        ClassLoader cl = null;
        try {
            cl = Thread.currentThread().getContextClassLoader();
        } catch (Throwable ex) {
            // Cannot access thread context ClassLoader - falling back to system class loader...
        }
        if (cl == null) {
            // No thread context class loader -> use class loader of this class.
            cl = cls.getClassLoader();
        }
        return cl;
    }
    
    1. 再看ClassPool加载ctClass的classloader,也是当前线程上下文的classloader
    public Class toClass(CtClass clazz) throws CannotCompileException {
        // Some subclasses of ClassPool may override toClass(CtClass,ClassLoader).
        // So we should call that method instead of toClass(.., ProtectionDomain).
        return toClass(clazz, getClassLoader()); 
    }
    public ClassLoader getClassLoader() {
        return getContextClassLoader();
    }
    static ClassLoader getContextClassLoader() {
        return Thread.currentThread().getContextClassLoader();
    }
    
    1. 如果两处获取当前线程上下文classloader存在并发问题导致不匹配就可能出现类似问题。

    问题复现

    1. 那么来验证场景,模拟高并发场景进行校验,果然复现了问题(不是必现),也就验证了我们的分析
    2. 从新版本修复改问题的方式也验证了我们的分析,即使用clazz的classloader进行加载接口代理类
    /**
     * 问题复现
     * @author 会灰翔的灰机
     * @date 2019/4/18
     */
    public class RiderBlacklistFilterTest extends UnitTestBase {
    	@Resource
    	private RiderBlackListProvider riderBlackListProvider;
    
    	@Test
    	public void riderBlackList(){
    		int size = 30;
    		CyclicBarrier cb = new CyclicBarrier(size);
    		for (int i=0;i<size;i++) {
    			new Thread(new Runnable() {
    				@Override
    				public void run() {
    					try {
    						cb.await();
    					} catch (InterruptedException | BrokenBarrierException e) {
    						e.printStackTrace();
    					}
    					System.out.println(this + ":" + System.currentTimeMillis());
    					riderBlackListProvider.riderBlackList("123");
    				}
    			}).start();
    		}
    	}
    }
    

    问题思考

    1. 那么两个都是当前线程上下文的classloader为什么不匹配呢?
    2. 那么有可能是存在并发的场景设置当前线程上下文classloader,于是我们查看了该方法的调用场景果然有很多地方使用,那么下一步只需要分析是否存在并发场景设置classloader产生冲突的问题即可1
    展开全文
  • Eclipse里面dubbo校验报错解决方案

    千次阅读 2015-09-30 18:20:52
    Eclipse里面dubbo校验报错解决方案 Multiple annotations found at this line: - cvc-complex-type.2.4.c: The matching wildcard is strict, but no declaration can be found for element 'dubbo:application'.
  • Dubbo参数验证(五)

    千次阅读 2019-06-03 18:38:07
    1、参考 ...Dubbo中的参数验证基于JSR-303,它是JAVA EE 6中的一项子规范,通过注解的方式用来对 Java Bean中字段的值进行简单验证。Consumer端要调用Provider端的接口,调用接口的话就会有参数...
  • dubbo分布式服务框架实现2、实现对登录提交的用户名密码进行远程校验。 public Boolean CheckLogin(String username, String password)3、服务端代码要求连接Mysql数据库进行数据校验
  • 接上篇文章... 完善service参数校验 service-api模块依赖 <dependencies> <!-- 参数校验相关依赖 --> <dependency> <groupId>javax.validation</groupId> &l
  • Dubbo 统一参数验证

    2017-09-05 12:34:00
    需求: 方法注解实现参数验证 消息文件存储消息字符串 实现: 注解定义 @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited public @interface V...
  • dubbo校验xsd文件

    2018-04-19 22:11:29
    dubbo.xsd,有需要的可以下载dubbo.xsd,有需要的可以下载dubbo.xsd,有需要的可以下载dubbo.xsd,有需要的可以下载
  • mars-validated springmvc springboot springcloud dubbo 参数校验 简单好用的 springmvc springboot springcloud dubbo 参数校验 validated 是 控制 springmvc springboot 的验证框架。此框架基于spring 开发。 ...
  • public Boolean CheckLogin(String username, String password) 服务端代码要求连接Mysql数据库进行数据校验; 客户端要求:用RMI技术进行远程过程调用实现登录过程。根据远程返回值分别进入“登录成功”或“登录...
  • maven依赖 <dependency> <groupId>javax.validationgroupId> ... } } dubbo开启入参校验 服务端远程参数校验 <dubbo:provider validation="true"/> 消费者本地参数校验 <dubbo:consumer validation="true"/>
  • 注意:本文dubbo 版本 2.8.4 springboot 版本 2.0.4.RELEASE 项目结构 test-rest (前端消费着,controller 层,springboot+maven项目) test-api (dubbo服务 的 api ,只记录 service 接口和 model ,maven 项目) ...
  • 普通web请求可以通过 javax.validation.constraints package下的注解进行参数校验校验的结果会被Spring Mvc包装处理返回到前端,那么rpc调用如何通过注解进行参数校验呢? 查找资料发现可以通过 hibernate ...
  • 比如判断一个手机号是否格式正确,消费者的controller 层已经校验了,那么service层调Dubbo服务,Dubbo的提供者还需要在数据校验一次吗? 还一种业务校验应该放在哪里? 比如修改一个商品信息, 那么需要判断传入的...
  • 使用dubbo 框架 使用 validation 进行参数校验 首先创建类 import com.fasterxml.jackson.annotation.JsonProperty; import lombok.Getter; import lombok.Setter; import lombok.ToString; import javax....
  • dubbo开启参数校验时-序列化异常

    千次阅读 2020-01-10 15:28:44
    开启dubbo参数校验时 服务1调用服务2的方法,参数1为@NotNull 调用时未传 但报的异常是序列化问题 异常信息如下: org.apache.dubbo.rpc.RpcException: Failed to invoke the method ...
  • Dubbo的RPC调用中Consumer和 Provider端都可以对调用的方法做传参验证,参数的验证可以通过JSR303规范 (Java Specification Requests)提到的 Bean Validation方式来验证,Dubbo官方也是这么推荐的。最佳实践中分包...
  • 1.前言 用过SpringMVC的参数校验框架的同学都知道,通过注解的方式,可以优雅的实现Restful接口的参数校验。例如: @NotBlank(message = "parameter appId required") ...那么基于Dubbo框架的RPC接口是否...
  • import com.xsyx.logistics.zxc.contract.CommonResponse; ... import lombok.extern.slf4j.Slf4j; import org.apache.dubbo.common.extension.Activate; import org.apache.dubbo.rpc.*; import javax.validation.
  • Dubbo参数验证

    千次阅读 2019-09-02 16:10:43
    参数验证功能 [备注1] 是基于 JSR303 实现的,用户只需标识 ... } } } 备注 自 2.1.0 版本开始支持, 如何使用可以参考 dubbo 项目中的示例代码 ↩︎ 验证方式可扩展,扩展方式参见开发者手册中的验证扩展 ↩︎
  • Dubbo的启动时服务依赖检查是,对象实例化时,判断远程调用是否成功,对象是否能够实例化成功。 参数可配置。 若项目启动时,依赖的服务没有提供,则消费者也无法启动。 [img]...
  • dubbo之参数验证

    2021-11-10 18:26:08
    dubbo的参数验证基于JSR303规范就是定义了校验注解,错误信息如何提示等的规范,我们只需要使用规定的注解,并声明Filter就可以了。 源码。 1:改造rpc-service-api 1.1:引入依赖 <dependencies> <!-- ...
  • // 需要进行token校验的方法上配置ValidateToken注解 ValidateToken methodComment = method . getAnnotation ( ValidateToken . class ) ; if ( methodComment == null ) { return ; } String...
  • <p>@Reference(interfaceClass = XXXXXXXX.class, version = "1.0.0", registry = {"zkone"}, timeout = 45000, check = true)</p>

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 10,693
精华内容 4,277
关键字:

dubbo校验