您当前的位置:首页 > 电脑百科 > 程序开发 > 框架

完全自定义实现SpringMVC核心组件

时间:2022-12-05 16:00:07  来源:今日头条  作者:Spring全家桶实战案例

环境:Spring5.3.23


概述

通过Spring定义请求接口非常容器,通过几个注解就可以完成,如下:

@RestController
@RequestMApping("/demos")
public class DemoController {
  @GetMapping("/index")
  public Object index() {
    return "index" ;
  }
}

通过上面的@RestController, @RequestMapping就完成了一个简单的接口定义。

实际Spring Web底层是做了很多的工作,其核心组件有HandlerMappingHandlerAdapterViewResolver等组件。

  1. HandlerMapping
    根据当前请求的URI,查找对应的Handler,如:HandlerExecutionChAIn,包装的HandlerMethod
  2. HandlerAdapter
    根据上面的确定的HandlerMethod, 找到能够处理该Handler的Adapter,进行调用
  3. ViewResolver
    如果返回的ModelAndView对象那么会通过相应的ViewResolver进行渲染输出

了解了上面的几个核心组件之后,接下来就是自定义实现上面的核心类,来完成接口的请求处理。

自定义Endpoint

自定义注解,标记Controller类及请求参数

 @Target(ElementType.TYPE)
 @Retention(RetentionPolicy.RUNTIME)
 @Documented
 public @interface PackEndpoint {
 }

参数标记,用来对接口参数进行注解

 @Target(ElementType.PARAMETER)
 @Retention(RetentionPolicy.RUNTIME)
 @Documented
 public @interface PackParam {
 }

Endpoint接口参数封装对象

该对象用来保存记录,方法参数由@PackParam注解的参数

public class PackMethodParameter {

  // 用来解析接口参数的名称
  private ParameterNameDiscoverer parameterNameDiscoverer = new LocalVariableTableParameterNameDiscoverer() ;
  private String name ;
  private Executable executable ;
  private int parameterIndex ;
  private Class<?> type ;
  
  public PackMethodParameter(String name, int parameterIndex, Executable executable) {
    this.name = name;
    this.parameterIndex = parameterIndex ;
    this.executable = executable ;
  }
  
  public PackMethodParameter(int parameterIndex, Executable executable, Class<?> type) {
    this.parameterIndex = parameterIndex ;
    this.executable = executable ;
    this.type = type ;
  }
  
  public boolean hasParameterAnnotation(Class<? extends Annotation> clazz) {
    Method method = (Method) this.executable ;
    Parameter[] parameters = method.getParameters() ;
    return parameters[this.parameterIndex].isAnnotationPresent(clazz) ;
  }
  
  public String getParameterName() {
    String[] parameterNames = parameterNameDiscoverer.getParameterNames((Method) this.executable) ;
    return parameterNames[this.parameterIndex] ;
  }
  
}

自定义HandlerMapping

自定义实现了SpringMVC标准的HandlerMapping,这样在DispatcherServlet中才能够识别。

public class PackHandlerMapping implements HandlerMapping, InitializingBean, ApplicationContextAware {

  private ApplicationContext context;
  private Map<String, PackMethodHandler> mapping = new HashMap<>();

  @Override
  public HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
    String requestPath = request.getRequestURI();
    Optional<PackMethodHandler> opt = mapping.entrySet().stream().filter(entry -> entry.getKey().equals(requestPath)).findFirst()
        .map(Map.Entry::getValue);
    if (opt.isPresent()) {
      HandlerExecutionChain executionChain = new HandlerExecutionChain(opt.get()) ;
      return executionChain ;
    }
    return null;
  }

  // Bean初始化时,从容器中查找所有符合条件的Bean对象,即Bean对象上有@PackEndpoint注解
  @Override
  public void afterPropertiesSet() throws Exception {
    String[] beanNames = context.getBeanNamesForType(Object.class) ;
    for (String beanName : beanNames) {
      Object bean = this.context.getBean(beanName) ;
      Class<?> clazz = bean.getClass() ;
      // 判断当前的Bean上是否有PackEndpoint注解,只对有该注解的类进行处理
      if (clazz.getAnnotation(PackEndpoint.class) != null) {
        RequestMapping clazzMapping = clazz.getAnnotation(RequestMapping.class) ;
        String rootPath = clazzMapping.value()[0] ;
        if (clazzMapping != null) {
          ReflectionUtils.doWithMethods(clazz, method -> {
            RequestMapping nestMapping = AnnotatedElementUtils.findMergedAnnotation(method, RequestMapping.class) ;
            if (nestMapping != null) {
              String nestPath = nestMapping.value()[0] ;
              String path = rootPath + nestPath ;
              PackMethodHandler handler = new PackMethodHandler(method, bean) ;
              mapping.put(path, handler) ;
            }
          }) ;
        }
      }
    }
  }

  @Override
  public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
    this.context = applicationContext;
  }

  // 该类的作用:用来记录接口对应的信息,方法,对应的实例,参数信息
  public static class PackMethodHandler {
    private Method method;
    private Object instance;
    private PackMethodParameter[] parameters ;
    public Method getMethod() {
      return method;
    }
    public void setMethod(Method method) {
      this.method = method;
    }
    public Object getInstance() {
      return instance;
    }
    public void setInstance(Object instance) {
      this.instance = instance;
    }
    public PackMethodHandler(Method method, Object instance) {
      super();
      this.method = method;
      this.instance = instance;
      Parameter[] params = method.getParameters() ;
      this.parameters = new PackMethodParameter[params.length] ;
      for (int i = 0; i < params.length; i++) {
        this.parameters[i] = new PackMethodParameter(i, method, params[i].getType()) ;
      }
    }
    public PackMethodParameter[] getParameter() {
      return this.parameters ;
    }
  }
}

自定义参数解析器

专门用来解析处理接口方法中的参数信息然后从请求中读取。

public interface PackHandlerMethodArgumentResolver {

  boolean supportsParameter(PackMethodParameter methodParameter) ;
  
  Object resolveArgument(PackMethodParameter methodParameter, HttpServletRequest request);
  
}
public class PackParamHandlerMethodArgumentResolver implements PackHandlerMethodArgumentResolver {

  @Override
  public boolean supportsParameter(PackMethodParameter methodParameter) {
    return methodParameter.hasParameterAnnotation(PackParam.class) ;
  }

  @Override
  public Object resolveArgument(PackMethodParameter methodParameter, HttpServletRequest request) {
    String name = methodParameter.getParameterName() ;
    Object arg = null;
    String[] parameterValues = request.getParameterValues(name) ;
    if (parameterValues != null) {
      arg = parameterValues.length == 1 ? parameterValues[0] : parameterValues ;
    }
    return arg ;
  }

}

自定义HandlerAdapter

自定义实现了SpringMVC标准的HandlerAdatper,这样在DispatcherServlet中才能够识别。

public class PackHandlerAdapter implements HandlerAdapter{
  
  @Resource
  private ConversionService conversionService ;

  private PackParamHandlerMethodArgumentResolver argumentResolver = new PackParamHandlerMethodArgumentResolver() ;
  
  @Override
  public boolean supports(Object handler) {
    return handler instanceof PackMethodHandler;
  }

  @Override
  public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
      throws Exception {
    PackMethodHandler methodHandler = (PackMethodHandler) handler ;
    
    PackMethodParameter[] parameters = methodHandler.getParameter() ;
    Object[] args = new Object[parameters.length] ;
    for (int i = 0; i < args.length; i++) {
      if (this.argumentResolver.supportsParameter(parameters[i])) {
        // 解析对应的方法参数
        args[i] = this.argumentResolver.resolveArgument(parameters[i], request) ;
        // 类型转换
        args[i] = this.conversionService.convert(args[i], parameters[i].getType()) ;
      }
    }
    // 调用目标方法
    Object result = methodHandler.getMethod().invoke(methodHandler.getInstance(), args) ;
    // 设置响应header,输出内容
    response.setHeader("Content-Type", "text/plain;charset=utf8") ;
    PrintWriter out = response.getWriter() ;
    out.write((String) result) ;
    out.flush() ;
    out.close() ; 
    return null ;
  }

  @Override
  public long getLastModified(HttpServletRequest request, Object handler) {
    return -1 ;
  }

}

通过以上的步骤就完成了一个完全自定义SpringMVC核心组件的实现。

在下一篇中我们将介绍如何将上面的代码跑到一个嵌入式的Tomcat中,敬请期待。



Tags:SpringMVC   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,不构成投资建议。投资者据此操作,风险自担。如有任何标注错误或版权侵犯请与我们联系,我们将及时更正、删除。
▌相关推荐
SpringBoot对SpringMVC的自动配置,你知道多少?
Spring MVC自动配置Spring Boot为Spring MVC提供了自动配置,可以在大多数应用程序中很好地工作。自动配置在Spring默认的基础上添加了以下特性: 包含ContentNegotiatingViewR...【详细内容】
2023-03-23  Search: SpringMVC  点击:(145)  评论:(0)  加入收藏
SpringMVC的异常处理机制详细分析
环境:Springboot2.4.12请求入口SpringMVC的请求处理入口是DispatcherServlet,不过该Servlet不做实际的处理而实际的处理是由可其它配置的委托组件执行的。DispatcherServlet...【详细内容】
2023-03-08  Search: SpringMVC  点击:(134)  评论:(0)  加入收藏
完全自定义实现SpringMVC核心组件
环境:Spring5.3.23概述通过Spring定义请求接口非常容器,通过几个注解就可以完成,如下:@RestController@RequestMapping("/demos")public class DemoController { @GetMapping("...【详细内容】
2022-12-05  Search: SpringMVC  点击:(386)  评论:(0)  加入收藏
熬夜手写个SpringMVC框架
在spring全家桶流行的当下,只要你做的是Java技术栈基本上95%以上都使用的spring或springboot框架,剩下的5%基本上是一些老项目,政府项目,银行项目之类对安全性要求比较高的项目,...【详细内容】
2022-03-24  Search: SpringMVC  点击:(292)  评论:(0)  加入收藏
超级赞!SpringMVC中的请求参数的完美处理技巧
本篇文章我们来学习一下SpringMVC中是如何处理请求中的参数的。回想一下原生Servlet是如何处理请求参数的?我们需要使用HttpServletRequest调用getParameter方法进行获取,就像...【详细内容】
2021-08-16  Search: SpringMVC  点击:(324)  评论:(0)  加入收藏
记一次springmvc设置aop切面失败到解决
背景:项目是老项目,而且比较旧为springmvc项目。项目使用的框架为公司内部自己开发,目前已经没有框架的源码可供修改,配置文件写在底层框架内,可以看到但修改不到。目的是为了实...【详细内容】
2021-08-11  Search: SpringMVC  点击:(422)  评论:(0)  加入收藏
重学SpringMVC:框架原理解读 + 简单入门程序+组件分析.
一、什么是springmvc?我们知道三层架构的思想,并且如果你知道ssh的话,就会更加透彻地理解这个思想,struts2在web层,spring在中间控制,hibernate在dao层与数据库打交道,而前面刚写...【详细内容】
2021-07-14  Search: SpringMVC  点击:(316)  评论:(0)  加入收藏
SpringMVC流程及源码分析
前言学了一遍SpringMVC以后,想着做一个总结,复习一下。复习写下面的总结的时候才发现,其实自己学得并不彻底、牢固、也没有学全,视频跟书本是要结合起来一起,每一位老师的视频可...【详细内容】
2021-03-08  Search: SpringMVC  点击:(470)  评论:(0)  加入收藏
一篇文章搞懂SpringMVC核心执行原理
对Java程序员来讲,做web开发最熟悉的框架莫过于SpringMVC了。之所以它能一统江湖,不是自己太优秀,而是对手太坑了,不知道大家还记不记得2017年左右Struts2爆出了一个大漏洞,自此...【详细内容】
2020-10-14  Search: SpringMVC  点击:(263)  评论:(0)  加入收藏
SpringMVC:进阶
Ajax 异步交互SpringMVC 默认用 MappingJackson2HttpMessageConverter 对 JSON 数据进行转换,需要加入 Jackson 的包;同时在 spring-mvc.xml 使用 <mvc:annotation-driven />....【详细内容】
2020-09-14  Search: SpringMVC  点击:(257)  评论:(0)  加入收藏
▌简易百科推荐
Qt与Flutter:在跨平台UI框架中哪个更受欢迎?
在跨平台UI框架领域,Qt和Flutter是两个备受瞩目的选择。它们各自具有独特的优势,也各自有着广泛的应用场景。本文将对Qt和Flutter进行详细的比较,以探讨在跨平台UI框架中哪个更...【详细内容】
2024-04-12  刘长伟    Tags:UI框架   点击:(1)  评论:(0)  加入收藏
Web Components实践:如何搭建一个框架无关的AI组件库
一、让人又爱又恨的Web ComponentsWeb Components是一种用于构建可重用的Web元素的技术。它允许开发者创建自定义的HTML元素,这些元素可以在不同的Web应用程序中重复使用,并且...【详细内容】
2024-04-03  京东云开发者    Tags:Web Components   点击:(8)  评论:(0)  加入收藏
Kubernetes 集群 CPU 使用率只有 13% :这下大家该知道如何省钱了
作者 | THE STACK译者 | 刘雅梦策划 | Tina根据 CAST AI 对 4000 个 Kubernetes 集群的分析,Kubernetes 集群通常只使用 13% 的 CPU 和平均 20% 的内存,这表明存在严重的过度...【详细内容】
2024-03-08  InfoQ    Tags:Kubernetes   点击:(19)  评论:(0)  加入收藏
Spring Security:保障应用安全的利器
SpringSecurity作为一个功能强大的安全框架,为Java应用程序提供了全面的安全保障,包括认证、授权、防护和集成等方面。本文将介绍SpringSecurity在这些方面的特性和优势,以及它...【详细内容】
2024-02-27  风舞凋零叶    Tags:Spring Security   点击:(55)  评论:(0)  加入收藏
五大跨平台桌面应用开发框架:Electron、Tauri、Flutter等
一、什么是跨平台桌面应用开发框架跨平台桌面应用开发框架是一种工具或框架,它允许开发者使用一种统一的代码库或语言来创建能够在多个操作系统上运行的桌面应用程序。传统上...【详细内容】
2024-02-26  贝格前端工场    Tags:框架   点击:(47)  评论:(0)  加入收藏
Spring Security权限控制框架使用指南
在常用的后台管理系统中,通常都会有访问权限控制的需求,用于限制不同人员对于接口的访问能力,如果用户不具备指定的权限,则不能访问某些接口。本文将用 waynboot-mall 项目举例...【详细内容】
2024-02-19  程序员wayn  微信公众号  Tags:Spring   点击:(39)  评论:(0)  加入收藏
开发者的Kubernetes懒人指南
你可以将本文作为开发者快速了解 Kubernetes 的指南。从基础知识到更高级的主题,如 Helm Chart,以及所有这些如何影响你作为开发者。译自Kubernetes for Lazy Developers。作...【详细内容】
2024-02-01  云云众生s  微信公众号  Tags:Kubernetes   点击:(51)  评论:(0)  加入收藏
链世界:一种简单而有效的人类行为Agent模型强化学习框架
强化学习是一种机器学习的方法,它通过让智能体(Agent)与环境交互,从而学习如何选择最优的行动来最大化累积的奖励。强化学习在许多领域都有广泛的应用,例如游戏、机器人、自动驾...【详细内容】
2024-01-30  大噬元兽  微信公众号  Tags:框架   点击:(68)  评论:(0)  加入收藏
Spring实现Kafka重试Topic,真的太香了
概述Kafka的强大功能之一是每个分区都有一个Consumer的偏移值。该偏移值是消费者将读取的下一条消息的值。可以自动或手动增加该值。如果我们由于错误而无法处理消息并想重...【详细内容】
2024-01-26  HELLO程序员  微信公众号  Tags:Spring   点击:(88)  评论:(0)  加入收藏
SpringBoot如何实现缓存预热?
缓存预热是指在 Spring Boot 项目启动时,预先将数据加载到缓存系统(如 Redis)中的一种机制。那么问题来了,在 Spring Boot 项目启动之后,在什么时候?在哪里可以将数据加载到缓存系...【详细内容】
2024-01-19   Java中文社群  微信公众号  Tags:SpringBoot   点击:(86)  评论:(0)  加入收藏
站内最新
站内热门
站内头条