栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 游戏开发 > Cocos2d-x

看了同事写的代码,我竟然开始默默的模仿了。。。

Cocos2d-x 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

看了同事写的代码,我竟然开始默默的模仿了。。。

背景

事情是这样的,目前我正在参与 XXXX 项目的搭建,需要与第三方对接接口。在对方的接口中存在几个异步通知,为了接口的安全性,需要对接口的参数进行验签处理。

为了方便大家对异步通知返回参数的处理,Z 同事提出要将该验签功能进行统一封装,到时候大家只需要关注自己的业务逻辑即可。


小伙伴们有兴趣想了解内容和更多相关学习资料的请点赞收藏+评论转发+关注我,后面会有很多干货。我有一些面试题、架构、设计类资料可以说是程序员面试必备!
所有资料都整理到网盘了,需要的话欢迎下载!私信我回复【111】即可免费获取

Z 同事的解决方案

Z 同事选择的是“自定义参数解析器”的解决方案,接下来我们通过代码来了解一下。

自定义注解

@Documented@Retention(RetentionPolicy.RUNTIME)@Target({ElementType.PARAMETER})public @interface RsaVerify {            boolean verifySign() default true;}

自定义方法参数解析器

@AllArgsConstructor@Component//实现 HandlerMethodArgumentResolver 接口public class RsaVerifyArgumentResolver implements HandlerMethodArgumentResolver {
    private final SecurityService securityService;
        @Override    public boolean supportsParameter(MethodParameter parameter) {        return parameter.hasParameterAnnotation(RsaVerify.class);    }
        @Override    public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {        RsaVerify parameterAnnotation = parameter.getParameterAnnotation(RsaVerify.class);        if (!parameterAnnotation.verifySign()) {            return mavContainer.getModel();        }                //对参数进行处理并验签的逻辑        ......                //返回处理后的实体类参数        return ObjectMapperFactory                .getDateTimeObjectMapper("yyyyMMddHHmmss")                .readValue(StringUtil.queryParamsToJson(sb.toString()), parameter.getParameterType());    }   }

创建配置类

​​​​​​​

@Configuration@AllArgsConstructorpublic class PayTenantWebConfig implements WebMvcConfigurer {
    private final RsaVerifyArgumentResolver rsaVerifyArgumentResolver;            @Override    public void addArgumentResolvers(List resolvers) {        resolvers.add(rsaVerifyArgumentResolver);    }}

使用

使用方法非常简单,只需要在参数上引入注解就可以了

​​​​​​​

@RestController@Slf4j@RequestMapping("/xxx")public class XxxCallbackController {
        @PostMapping("/callback")    public String callback(@RsaVerify CallbackReq params) {        log.info("receive callback req={}", params);  //业务逻辑处理  .....          return "success";    }}

问题

问题一

看到这,细心的朋友应该会有所疑问:既然这边用到了自定义的注解,为什么不用切面来实现,而是使用自定义的参数解析器呢?Very Good!这也是阿Q提出的疑问,同事说是因为 jackson 的反序列化动作优先级远高于切面的优先级,所以还没进入切面就已经报反序列化失败的错误了。

问题二

为什么在 controller 中注解 @RequestBody 不见了?

要回答这个问题,我们就得了解下HandlerMethodArgumentResolverComposite 这个类了,以下简称Composite。SpringMVC 在启动时会将所有的参数解析器放到 Composite 中,Composite 是所有参数的一个集合。当对参数进行解析时就会从该参数解析器集合中选择一个支持对 parameter 解析的参数解析器,然后使用该解析器进行参数解析。

又因为@RequestBody 所以使用的参数解析器RequestResponseBodyMethodProcessor 优先级高于我们自定义的参数解析器,所以如果共用会被前者拦截解析,所以为了正常使用,我们需要将@RequestBody 注解去掉。​​​​​​​

@Nullableprivate HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) {    HandlerMethodArgumentResolver result = this.argumentResolverCache.get(parameter);    if (result == null) {        for (HandlerMethodArgumentResolver resolver : this.argumentResolvers) {            if (resolver.supportsParameter(parameter)) {                result = resolver;                this.argumentResolverCache.put(parameter, result);                break;            }        }    }    return result;}

C 同事的解决方案

上边 Z 同事的方案已经可以解决该问题了,但是该方案还有两个不足之处:

  • 需要每一个回调都去创建自己的 controller 层,没有一个对外的统一入口;

  • 需要在方法上添加自定义注解,侵入性比较强;

因此经过我们的商议,决定摒弃该方案,但是该方案的思想值得我们学习。接下来让我们分析一下新的解决方案:

定义业务接口类

业务接口类包含两个方法:具体业务处理的类型;业务的具体处理方法。

​​​​​​​

public interface INotifyService {  public String handleType();  Integer handle(String notifyBody);
}

异步通知统一入口

​​​​​​​

@AllArgsConstructor@RestController@RequestMapping(value = "/notify")public class NotifyController { private IService service;
    @PostMapping(value = "/receive")    public String receive(@RequestBody String body) {        //处理通知        Integer status = service.handle(body);        return "success";    }}

在 Iservice 中做两个步骤:

  • 在 spring 启动之后,收集所有的类型为 INotifyService的类并放入map中;

  • 将参数进行处理转化,并验签处理;

private ApplicationContext applicationContext;private Map notifyServiceMap;
@PostConstructpublic void init(){ Map map = applicationContext.getBeansOfType(INotifyService.class); Collection services = map.values(); if(CollectionUtils.isEmpty(services)){  return; } notifyServiceMap = services.stream().collect(Collectors.toMap(INotifyService::handleType, x -> x));}
@Overridepublic Map getNotifyServiceMap() { return notifyServiceMap;}
@Overridepublic Integer handle(String body) { //参数处理+验签逻辑    ......         //获取具体的业务实现类 INotifyService notifyService=notifyServiceMap.get(notifyType); Integer status=null; if(Objects.nonNull(notifyService)) {  //执行具体业务  try {   status=notifyService.handle(JSON.toJSONString(requestParameter));  } catch (Exception e) {   e.printStackTrace();  } } //后续逻辑处理    ......         return status;}

业务具体实现

​​​​​​​

@Servicepublic class NotifySignServiceImpl implements INotifyService {
    @Override    public String handleType() {        return "type_sign";    }
    @Override    @Transactional    public Integer handle(String notifyBody) {        //具体的业务处理        ......    }}

小结

  • 此方案提供统一的异步通知入口,把公共的参数处理和验签逻辑与业务逻辑剥离。

  • 利用 java 动态加载类的特性,将实现类通过类型进行收集。

  • 利用 java 多态的特性,通过不同的实现类来处理不同的业务逻辑。

看到这,相信大家已经对这两种实现方案有了一定的理解,大家可以试着在以后的项目中应用一下,体验一把!

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/967869.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号