环境:springboot2.2.10.RELEASE
开发步骤:
- 自定义的注解类
@Component
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface CustomEndpoint {
}
@CustomEndpoint注解标识自定义的类(Bean)在该注解中添加了@Component注解为了让容器管理我们的类(生成Bean)。
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface PackMapping {
// 请求URL
String[] value() default {};
// 请求的方法
RequestMethod[] method() default {};
}
该注解就相当于@RequestMapping注解,定义请求接口。
- 自定义HandlerMapping
自定义HandlerMapping的作用是让HandlerMapping与我们自定义的@CustomEndpoint类相关联,简单说就是能够处理标有@CustomEndpoint注解的类。
public class CustomPackRequestHandlerMapping extends RequestMappingHandlerMapping {
// 该方法是必须重写的,使得自定义的HandlerMapping能够处理标有@CustomEndpoint注解的类
@Override
protected boolean isHandler(Class<?> beanType) {
return AnnotatedElementUtils.hasAnnotation(beanType, CustomEndpoint.class);
}
// 该方法也必须重写不然你的这个自定义HandlerMapping将轮不到就被(SimpleUrlHandlerMapping)处理了。
@Override
public void afterPropertiesSet() {
super.setOrder(0); // 这里很重要使得我们的HandlerMapping能够排到最前面
super.afterPropertiesSet();
}
// 该方法必须重写因为你要针对标有自定义@PackMapping注解的方法能够处理你的请求。
@Override
protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
RequestMappingInfo requestMappingInfo = createRequestMappingInfo(method) ;
return requestMappingInfo ;
}
private RequestMappingInfo createRequestMappingInfo(AnnotatedElement element) {
PackMapping packMapping = AnnotatedElementUtils.findMergedAnnotation(element, PackMapping.class);
return (packMapping != null ? builderMapping(packMapping) : null);
}
private RequestMappingInfo builderMapping(PackMapping packMapping) {
RequestMappingInfo.Builder builder = RequestMappingInfo
.paths(packMapping.value())
.methods(packMapping.method())
.params(new String[] {})
.headers(new String[] {})
.consumes(new String[] {})
.produces(new String[] {})
.mappingName("") ;
return builder.build() ;
}
}
以上自定义的HandlerMapping重要的方法已经说明了。
- 配置HandlerMapping
@Configuration
public class CustomEndpointConfig {
@Bean
public CustomPackRequestHandlerMapping customPackRequestHandlerMapping() {
CustomPackRequestHandlerMapping handlerMapping = new CustomPackRequestHandlerMapping() ;
return handlerMapping ;
}
}
- 测试
@CustomEndpoint
@ResponseBody
public class CustomWebController {
@PackMapping("/custom/index")
public Object index(String key) {
return key ;
}
}
到此开发一个自定义的HandlerMapping就完成了。
真正实际的调用(Controller方法)其实是HandlerAdapter。
AbstractHandlerMethodAdapter类中定义了如下方法,是专门为让子类来实现的,我们可以根据自己的需要作相应的重写。
protected abstract boolean supportsInternal(HandlerMethod handlerMethod);
查看
RequestMappingHandlerAdapter类的实现。
@Override
protected boolean supportsInternal(HandlerMethod handlerMethod) {
return true;
}
这里在说下一些核心类之间的关系:
所有的拦截器也是与HandlerMapping关联的:
@Bean
@SuppressWarnings("deprecation")
public RequestMappingHandlerMapping requestMappingHandlerMapping(
@Qualifier("mvcContentNegotiationManager") ContentNegotiationManager contentNegotiationManager,
@Qualifier("mvcConversionService") FormattingConversionService conversionService,
@Qualifier("mvcResourceUrlProvider") ResourceUrlProvider resourceUrlProvider) {
RequestMappingHandlerMapping mapping = createRequestMappingHandlerMapping();
mapping.setOrder(0);
mapping.setInterceptors(getInterceptors(conversionService, resourceUrlProvider));
mapping.setContentNegotiationManager(contentNegotiationManager);
mapping.setCorsConfigurations(getCorsConfigurations());
...
return mapping;
}
mapping.setInterceptors(getInterceptors(conversionService, resourceUrlProvider));
消息转换与HandlerAdapter相关联
@Bean
public RequestMappingHandlerAdapter requestMappingHandlerAdapter(
@Qualifier("mvcContentNegotiationManager") ContentNegotiationManager contentNegotiationManager,
@Qualifier("mvcConversionService") FormattingConversionService conversionService,
@Qualifier("mvcValidator") Validator validator) {
RequestMappingHandlerAdapter adapter = createRequestMappingHandlerAdapter();
adapter.setContentNegotiationManager(contentNegotiationManager);
adapter.setMessageConverters(getMessageConverters());
adapter.setWebBindingInitializer(getConfigurableWebBindingInitializer(conversionService, validator));
adapter.setCustomArgumentResolvers(getArgumentResolvers());
adapter.setCustomReturnValueHandlers(getReturnValueHandlers());
if (jackson2Present) {
adapter.setRequestBodyAdvice(Collections.singletonList(new JsonViewRequestBodyAdvice()));
adapter.setResponseBodyAdvice(Collections.singletonList(new JsonViewResponseBodyAdvice()));
}
AsyncSupportConfigurer configurer = new AsyncSupportConfigurer();
configureAsyncSupport(configurer);
if (configurer.getTaskExecutor() != null) {
adapter.setTaskExecutor(configurer.getTaskExecutor());
}
if (configurer.getTimeout() != null) {
adapter.setAsyncRequestTimeout(configurer.getTimeout());
}
adapter.setCallableInterceptors(configurer.getCallableInterceptors());
adapter.setDeferredResultInterceptors(configurer.getDeferredResultInterceptors());
return adapter;
}
adapter.setMessageConverters(getMessageConverters());
好心人给个关注+转发谢谢啊