首页>国内 > 正文

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

2022-12-07 10:07:26来源:今日头条

概述

通过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,包装的HandlerMethodHandlerAdapter根据上面的确定的HandlerMethod, 找到能够处理该Handler的Adapter,进行调用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 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 mapping = new HashMap<>();  @Override  public HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {    String requestPath = request.getRequestURI();    Optional 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核心组件的实现。

关键词: 核心组件 参数信息 请求处理 进行处理 类型转换

相关新闻

Copyright 2015-2020   三好网  版权所有 联系邮箱:435 22 640@qq.com  备案号: 京ICP备2022022245号-21