環(huán)境:Spring5.3.23
概述
通過(guò)Spring定義請(qǐng)求接口非常容器,通過(guò)幾個(gè)注解就可以完成,如下:
@RestController
@RequestMApping("/demos")
public class DemoController {
@GetMapping("/index")
public Object index() {
return "index" ;
}
}
通過(guò)上面的@RestController, @RequestMapping就完成了一個(gè)簡(jiǎn)單的接口定義。
實(shí)際Spring Web底層是做了很多的工作,其核心組件有HandlerMapping, HandlerAdapter, ViewResolver等組件。
- HandlerMapping
根據(jù)當(dāng)前請(qǐng)求的URI,查找對(duì)應(yīng)的Handler,如:HandlerExecutionChain,包裝的HandlerMethod - HandlerAdapter
根據(jù)上面的確定的HandlerMethod, 找到能夠處理該Handler的Adapter,進(jìn)行調(diào)用 - ViewResolver
如果返回的ModelAndView對(duì)象那么會(huì)通過(guò)相應(yīng)的ViewResolver進(jìn)行渲染輸出
了解了上面的幾個(gè)核心組件之后,接下來(lái)就是自定義實(shí)現(xiàn)上面的核心類,來(lái)完成接口的請(qǐng)求處理。
自定義Endpoint
自定義注解,標(biāo)記Controller類及請(qǐng)求參數(shù)
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface PackEndpoint {
}
參數(shù)標(biāo)記,用來(lái)對(duì)接口參數(shù)進(jìn)行注解
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface PackParam {
}
Endpoint接口參數(shù)封裝對(duì)象
該對(duì)象用來(lái)保存記錄,方法參數(shù)由@PackParam注解的參數(shù)
public class PackMethodParameter {
// 用來(lái)解析接口參數(shù)的名稱
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
自定義實(shí)現(xiàn)了SpringMVC標(biāo)準(zhǔn)的HandlerMapping,這樣在DispatcherServlet中才能夠識(shí)別。
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初始化時(shí),從容器中查找所有符合條件的Bean對(duì)象,即Bean對(duì)象上有@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() ;
// 判斷當(dāng)前的Bean上是否有PackEndpoint注解,只對(duì)有該注解的類進(jìn)行處理
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;
}
// 該類的作用:用來(lái)記錄接口對(duì)應(yīng)的信息,方法,對(duì)應(yīng)的實(shí)例,參數(shù)信息
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 ;
}
}
}
自定義參數(shù)解析器
專門用來(lái)解析處理接口方法中的參數(shù)信息然后從請(qǐng)求中讀取。
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
自定義實(shí)現(xiàn)了SpringMVC標(biāo)準(zhǔn)的HandlerAdatper,這樣在DispatcherServlet中才能夠識(shí)別。
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])) {
// 解析對(duì)應(yīng)的方法參數(shù)
args[i] = this.argumentResolver.resolveArgument(parameters[i], request) ;
// 類型轉(zhuǎn)換
args[i] = this.conversionService.convert(args[i], parameters[i].getType()) ;
}
}
// 調(diào)用目標(biāo)方法
Object result = methodHandler.getMethod().invoke(methodHandler.getInstance(), args) ;
// 設(shè)置響應(yīng)header,輸出內(nèi)容
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 ;
}
}
通過(guò)以上的步驟就完成了一個(gè)完全自定義SpringMVC核心組件的實(shí)現(xiàn)。
在下一篇中我們將介紹如何將上面的代碼跑到一個(gè)嵌入式的Tomcat中,敬請(qǐng)期待。