环境: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,包装的HandlerMethodHandlerAdapter
根据上面的确定的HandlerMethod, 找到能够处理该Handler的Adapter,进行调用ViewResolver
如果返回的ModelAndView对象那么会通过相应的ViewResolver进行渲染输出
了解了上面的几个核心组件之后,接下来就是自定义实现上面的核心类,来完成接口的请求处理。
2. 实战案例
2.1 自定义Endpoint
自定义@PackEndpoint注解,该注解的功能就类似@Controller标记这个类是一个处理器类。
@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@Documentedpublic @interface PackEndpoint {}
参数注解,该注解的作用就类似@RequestParam
@Target(ElementType.PARAMETER)@Retention(RetentionPolicy.RUNTIME)@Documentedpublic @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<>();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注解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) ;}}) ;}}}}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 {public boolean supportsParameter(PackMethodParameter methodParameter) {return methodParameter.hasParameterAnnotation(PackParam.class) ;}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{private ConversionService conversionService ;private PackParamHandlerMethodArgumentResolver argumentResolver = new PackParamHandlerMethodArgumentResolver() ;public boolean supports(Object handler) {return handler instanceof PackMethodHandler;}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 ;}public long getLastModified(HttpServletRequest request, Object handler) {return -1 ;}}
2.6 测试
public class UserController {public Object index( Long id, String name) {return "id = " + id + ", name = " + name ;}}

通过以上的步骤就完成了一个完全自定义SpringMVC核心组件的实现。而这就是底层SpringMVC的核心工作原理。
以上是本篇文章的全部内容,希望对你有帮助。
完毕!!!



