成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

本篇文章將帶你真正的搞定SpringMVC工作原理

開發 前端
參數解析器的作用就是用來解析通過請求URI找到對應的處理器方法(PackMethodHandler)。也就是從上面PackHandlerMapping類中保存到Map集合中的通過請求的URI找到對應的PackMethodHandler對象。

環境:Spring5.3.23

1. 簡介

在Spring中要定義一個接口是非常的簡單,如下示例:

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

通過上面的@RestController, @RequestMapping就完成了一個簡單的接口定義。

實際Spring Web底層是做了很多的工作,其核心組件有HandlerMapping, HandlerAdapter, ViewResolver等組件。

  • HandlerMapping
    根據當前請求的URI,查找對應的Handler,如:HandlerExecutionChain,包裝的HandlerMethod
  • HandlerAdapter
    根據上面的確定的HandlerMethod, 找到能夠處理該Handler的Adapter,進行調用
  • ViewResolver
    如果返回的ModelAndView對象那么會通過相應的ViewResolver進行渲染輸出

了解了上面的幾個核心組件之后,接下來就是自定義實現上面的核心類,來完成接口的請求處理。

2. 實戰案例

2.1 自定義Endpoint

自定義@PackEndpoint注解,該注解的功能就類似@Controller標記這個類是一個處理器類。

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

參數注解,該注解的作用就類似@RequestParam

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

2.2 參數封裝對象

該類的作用用來保存方法的參數相關的信息進行封裝,如:參數名稱,參數的類型及對應的方法Method。

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] ;
  }


}

2.3 自定義HandlerMapping

自定義實現了SpringMVC標準的HandlerMapping,這樣在DispatcherServlet中才能夠識別。HandlerMapping的作用就是用來匹配一個請求的URI與那個處理器(Controller)進行對應。

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) {
          // 遍歷當前類中的所有方法,查找使用了@RequestMapping注解的方法
          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) ;
              // 將請求的URI及對應的Handler進行對應,這樣就可以通過請求的URI找到對應的處理器方法了
              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 ;
    }
  }
}

2.4 自定義參數解析器

參數解析器的作用就是用來解析通過請求URI找到對應的處理器方法(PackMethodHandler)。也就是從上面PackHandlerMapping類中保存到Map集合中的通過請求的URI找到對應的PackMethodHandler對象。

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 ;
  }


}

2.5 自定義HandlerAdapter

自定義實現了SpringMVC標準的HandlerAdatper,這樣在DispatcherServlet中才能夠識別。該類的核心就是用來真正的調用目標方法的(PackMethodHandler)。

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 ;
  }


}

2.6 測試

@PackEndpoint
@RequestMapping("/users")
public class UserController {
  
  @GetMapping("/index")
  public Object index(@PackParam Long id, @PackParam String name) {
    return "id = " + id + ", name = " + name ;
  }
}

通過以上的步驟就完成了一個完全自定義SpringMVC核心組件的實現。而這就是底層SpringMVC的核心工作原理。

以上是本篇文章的全部內容,希望對你有幫助。

責任編輯:武曉燕 來源: Spring全家桶實戰案例源碼
相關推薦

2020-02-28 11:29:00

ElasticSear概念類比

2021-05-15 10:16:14

Python匿名函數

2024-04-17 13:21:02

Python匿名函數

2022-05-28 15:59:55

PythonPandas數據可視化

2021-03-06 10:05:03

Python函數變量

2021-11-10 09:19:41

PythonShutil模塊

2021-11-17 10:11:08

PythonLogging模塊

2022-03-30 10:51:40

JavaScript性能調優

2021-05-31 08:59:57

Java數據庫訪問JDBC

2021-03-15 08:38:42

StringBuffeJava基礎Java開發

2021-02-20 10:06:14

語言文件操作

2021-02-27 10:20:18

Go語言flag包開發技術

2021-01-13 08:40:04

Go語言文件操作

2021-11-13 10:11:45

Pythonurllib庫Python基礎

2022-04-14 10:10:59

Nginx開源Linux

2022-02-21 09:44:45

Git開源分布式

2023-05-12 08:19:12

Netty程序框架

2021-06-30 00:20:12

Hangfire.NET平臺

2023-09-06 14:57:46

JavaScript編程語言

2020-12-08 08:09:49

SVG圖標Web
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 亚洲精品视频免费观看 | 国产主播第一页 | 国产在线一级片 | 天堂成人国产精品一区 | 国产精品福利在线观看 | 国产午夜在线 | 一区二区视频 | 自拍 亚洲 欧美 老师 丝袜 | 久久精品视频播放 | 国产高清精品一区二区三区 | 亚洲a在线视频 | 欧美黑人又粗大 | 狠狠干美女 | 成人动漫一区二区 | 国产午夜一级 | 日日爱视频| 精品视频国产 | 国产精品国色综合久久 | av在线免费看网址 | 欧美男人天堂 | av电影一区| 亚洲热在线视频 | 国产伦精品一区二区三区视频金莲 | 中文字幕国产第一页 | 亚洲一区二区久久 | 热99在线 | www.久久久久久久久久久久 | 日日日色 | 久久久久久久av麻豆果冻 | 亚洲 中文 欧美 日韩 在线观看 | 超碰97干| 国产一区二区久久 | 天天看天天操 | 天天干视频 | 欧美日韩一区二区三区四区 | 国产成人精品久久二区二区91 | 天天干天天操天天射 | 欧美日韩亚洲视频 | 亚洲成人自拍网 | 色综合天天天天做夜夜夜夜做 | 精品亚洲永久免费精品 |