title: 动手撸一个 mvc 框架2
date: 2020/04/22 17:06
本节内容 & 思考题
本节我们将要带大家先过一遍 initHandlerMappings(context) 方法,从中引出我们今天要做的 HandlerMapping
SpringMVC 请求流程?
组合模式
建造者模式
Spring MVC 4.0
1、初始化处理器映射器
2、请求来了怎么处理
我们先看 tag1
看到这里你是不是有点疑惑,这个 MappingRegistry 是什么东西。
那 mappingLookup 中的数据到底是什么时候注册的呢?
当创建 org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping 对象的时候 InitializingBean#afterPropertiesSet
注:RequestMappingHandlerMapping 是怎么放到 bdMap 中的呢? 自己去看 @EnableMvc 注解,我相信你的聪明才智
tag2 与拦截器相关,我们下次在讲
我们实现一下 RequestMappingHandlerMapping 初始化阶段的代码
也就是 InitializingBean#afterPropertiesSet 方法引出的一系列代码。
1、先准备 2 个注解
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Component
public @interface Controller {
/**
* The value may indicate a suggestion for a logical component name,
* to be turned into a Spring bean in case of an autodetected component.
* @return the suggested component name, if any
*/
String value() default "";
}
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RequestMapping {
/**
* 此注释表示的主要映射。
*/
String[] value() default {};
/**
* 要映射到的HTTP请求方法,从而缩小了主要映射:
* GET, POST, HEAD, OPTIONS, PUT, PATCH, DELETE, TRACE.
*/
RequestMethod[] method() default {};
}
2、新增 HandlerMapping 相关抽象
/**
* 由定义请求和处理程序对象之间的映射关系的对象实现的接口。
*
* @author yujx
* @date 2020/04/23 11:29
*/
public interface HandlerMapping {
/**
* 返回此请求的处理程序和所有拦截器。可以根据请求URL,会话状态或实现类选择的任何因素进行选择。
*/
HandlerExecutionChain getHandler(HttpServletRequest request);
}
public abstract class AbstractHandlerMapping implements HandlerMapping {
/**
* 返回此请求的处理程序和所有拦截器。可以根据请求URL,会话状态或实现类选择的任何因素进行选择。
*/
@Override
public final HandlerExecutionChain getHandler(HttpServletRequest request) {
// 根据请求获取处理程序
Object handler = this.getHandlerInternal(request);
if (handler == null) {
return null;
}
// TODO: 2020/4/24 这个地方和拦截器有关,以后再说
// return this.getHandlerExecutionChain(handler, request);
return null;
}
/**
* 查找给定请求的处理程序,如果未找到特定请求,则返回 null。
*/
protected abstract Object getHandlerInternal(HttpServletRequest request);
}
3、新增 RequestMappingHandlerMapping
本节我们只介绍 RequestMappingHandlerMapping 中的 MappingRegistry 内部类数据进行注册部分。
public class RequestMappingHandlerMapping extends AbstractHandlerMapping implements InitializingBean, ApplicationContextAware {
private ApplicationContext applicationContext;
// 映射的注册中心
private final MappingRegistry mappingRegistry = new MappingRegistry();
/**
* 注入 ApplicationContext
*/
@Override
public void setApplicationContext(ApplicationContext ctx) {
this.applicationContext = ctx;
}
class MappingRegistry {
private final Map<RequestMappingInfo, HandlerMethod> mappingLookup = new LinkedHashMap<>();
private final Multimap<String, RequestMappingInfo> urlLookup = ArrayListMultimap.create();
public void register(String beanName, Class<?> beanType, Method method, RequestMappingInfo requestMappingInfo) {
// 创建方法对象
HandlerMethod handlerMethod = new HandlerMethod(beanName, beanType, applicationContext, method);
// 添加到 mappingLookup 中
mappingLookup.put(requestMappingInfo, handlerMethod);
// 将路径匹配放到 urlLookup 中
for (String pattern : requestMappingInfo.getPatternsCondition().getPatterns()) {
urlLookup.put(pattern, requestMappingInfo);
}
}
/**
* 一个 url 对应多个 RequestMappingInfo 的情况一般出现在请求方式不同的情况下
*/
public List<RequestMappingInfo> getMappingsByUrl(String urlPath) {
return new ArrayList<>(this.urlLookup.get(urlPath));
}
public Map<RequestMappingInfo, HandlerMethod> getMappings() {
return mappingLookup;
}
}
/**
* 设置所有提供的bean属性后,由BeanFactory调用。
*/
@Override
public void afterPropertiesSet() {
Map<String, Object> beansOfType = applicationContext.getBeansOfType(Object.class);
beansOfType.forEach((bdName, bean) -> {
Class<?> beanType = bean.getClass();
// 判断这个类上是否包含 @Controller 注解或者 @RequestMapping 注解
if (this.isHandler(beanType)) {
// 如果包含,则解析它方法上的 @RequestMapping 注解,进行注册
this.detectHandlerMethods(bdName, beanType);
}
});
}
private boolean isHandler(Class<?> clazz) {
Controller controller = AnnotationUtil.getAnnotation(clazz, Controller.class);
if (ObjectUtil.isNotNull(controller)) {
return true;
}
RequestMapping requestMapping = AnnotationUtil.getAnnotation(clazz, RequestMapping.class);
if (ObjectUtil.isNotNull(requestMapping)) {
return true;
}
return false;
}
/**
* 找到标注 @RequestMapping 的方法,注册到注册中心
*/
private void detectHandlerMethods(String beanName, Class<?> beanType) {
// 使用 Map 装的目的是为了去重
Map<Method, RequestMappingInfo> methodMap = new LinkedHashMap<Method, RequestMappingInfo>();
Queue<Class<?>> handlerTypes = new LinkedList<>();
handlerTypes.add(beanType);
while (!handlerTypes.isEmpty()) {
Class<?> handlerType = handlerTypes.remove();
// 将他的父类或者接口
handlerTypes.addAll(Arrays.asList(handlerType.getInterfaces()));
Class<?> superclass = handlerType.getSuperclass();
if (ObjectUtil.isNotNull(superclass)) {
handlerTypes.add(superclass);
}
for (Method method : handlerType.getMethods()) {
// 判断当前 method 是否有 @RequestMapping 注解,如果有返回 RequestMappingInfo,没有返回 null
RequestMappingInfo info = this.createRequestMappingInfo(method);
if (ObjectUtil.isNotNull(info)) {
RequestMappingInfo classRequestMappingInfo = this.createRequestMappingInfo(handlerType);
if (ObjectUtil.isNotNull(classRequestMappingInfo)) {
// 使两者结合
info = info.combine(classRequestMappingInfo);
}
}
if (ObjectUtil.isNotNull(info)) {
// 放进 Map 中
methodMap.put(method, info);
}
}
}
methodMap.forEach(((method, info) -> {
// 注册到映射注册中心
mappingRegistry.register(beanName, beanType, method, info);
}));
}
private RequestMappingInfo createRequestMappingInfo(AnnotatedElement element) {
RequestMapping requestMapping = AnnotationUtil.getAnnotation(element, RequestMapping.class);
if (ObjectUtil.isNull(requestMapping)) {
return null;
}
return RequestMappingInfo
.paths(requestMapping.value())
.methods(requestMapping.method())
.build();
}
/**
* 查找给定请求的处理程序,如果未找到特定请求,则返回 null。
*/
@Override
protected HandlerMethod getHandlerInternal(HttpServletRequest request) {
throw new RuntimeException("没有为当前请求找到处理器!");
}
}
4、相关的对象
/**
* 请求条件对象
*/
public interface RequestCondition<T> {
/**
* 将两个 RequestCondition 对象结合
*/
T combine(T other);
/**
* 检查此条件是否与给定请求匹配,并返回潜在的新请求条件,其中包含适合当前请求的内容。
* <p>
* 不匹配返回 null
*/
T getMatchingCondition(HttpServletRequest request);
}
和 3 个实现:
public final class PatternsRequestCondition implements RequestCondition<PatternsRequestCondition> {
private final Set<String> patterns = new HashSet<>();
public PatternsRequestCondition(String... paths) {
this(Arrays.asList(paths));
}
public PatternsRequestCondition(Collection<String> patterns) {
this.patterns.addAll(patterns);
}
public Set<String> getPatterns() {
return this.patterns;
}
/**
* 将两个 RequestCondition 对象结合
*/
@Override
public PatternsRequestCondition combine(PatternsRequestCondition other) {
Set<String> result = new HashSet<>();
result.addAll(this.patterns);
result.addAll(other.patterns);
return new PatternsRequestCondition(result);
}
/**
* 检查此条件是否与给定请求匹配,并返回潜在的新请求条件,其中包含适合当前请求的内容。
* <p>
* 不匹配返回 null
*/
@Override
public PatternsRequestCondition getMatchingCondition(HttpServletRequest request) {
String lookupPath = request.getServletPath();
Set<String> matches = new HashSet<>();
for (String pattern : this.patterns) {
String match = this.getMatchingPattern(pattern, lookupPath);
if (match != null) {
matches.add(match);
}
}
return matches.isEmpty() ? null : new PatternsRequestCondition(matches);
}
private String getMatchingPattern(String pattern, String lookupPath) {
if (ObjectUtil.equal(pattern, lookupPath)) {
return pattern;
}
// 解析 {} 的情况
// TODO: 2020/4/24 我实在不想写啊
return null;
}
}
public class RequestMethodsRequestCondition implements RequestCondition<RequestMethodsRequestCondition> {
private final Set<RequestMethod> methods = new HashSet<>();
public RequestMethodsRequestCondition(RequestMethod... methods) {
this(Arrays.asList(methods));
}
private RequestMethodsRequestCondition(Collection<RequestMethod> requestMethods) {
this.methods.addAll(requestMethods);
}
/**
* 将两个 RequestCondition 对象结合
*/
@Override
public RequestMethodsRequestCondition combine(RequestMethodsRequestCondition other) {
Set<RequestMethod> result = new HashSet<>();
result.addAll(this.methods);
result.addAll(other.methods);
return new RequestMethodsRequestCondition(result);
}
/**
* 检查此条件是否与给定请求匹配,并返回潜在的新请求条件,其中包含适合当前请求的内容。
* <p>
* 不匹配返回 null
*/
@Override
public RequestMethodsRequestCondition getMatchingCondition(HttpServletRequest request) {
if (methods.isEmpty()) {
return null;
}
RequestMethod requestMethod = RequestMethod.valueOf(request.getMethod());
if (methods.contains(requestMethod)) {
return new RequestMethodsRequestCondition(requestMethod);
}
return null;
}
}
public class RequestMappingInfo implements RequestCondition<RequestMappingInfo> {
// 请求路径条件 /rest/xxx
private PatternsRequestCondition patternsCondition;
// 请求方法条件 GET、POST
private RequestMethodsRequestCondition requestMethodsRequestCondition;
private RequestMappingInfo(PatternsRequestCondition patternsCondition, RequestMethodsRequestCondition requestMethodsRequestCondition) {
this.patternsCondition = patternsCondition;
this.requestMethodsRequestCondition = requestMethodsRequestCondition;
}
/**
* 将两个 RequestCondition 对象结合
*/
@Override
public RequestMappingInfo combine(RequestMappingInfo other) {
PatternsRequestCondition patternsRequestCondition = this.patternsCondition.combine(other.patternsCondition);
RequestMethodsRequestCondition requestMethodsRequestCondition = this.requestMethodsRequestCondition.combine(other.requestMethodsRequestCondition);
return new RequestMappingInfo(patternsRequestCondition, requestMethodsRequestCondition);
}
/**
* 检查此条件是否与给定请求匹配,并返回潜在的新请求条件,其中包含适合当前请求的内容。
* <p>
* 不匹配返回 null
*/
@Override
public RequestMappingInfo getMatchingCondition(HttpServletRequest request) {
PatternsRequestCondition pattern = this.patternsCondition.getMatchingCondition(request);
if (ObjectUtil.isNull(pattern)) {
return null;
}
RequestMethodsRequestCondition requestMethods = this.requestMethodsRequestCondition.getMatchingCondition(request);
if (ObjectUtil.isNull(requestMethods)) {
return null;
}
return new RequestMappingInfo(pattern, requestMethods);
}
public static Builder paths(String... paths) {
return new Builder(paths);
}
public PatternsRequestCondition getPatternsCondition() {
return patternsCondition;
}
public RequestMethodsRequestCondition getRequestMethodsRequestCondition() {
return requestMethodsRequestCondition;
}
public static class Builder {
private String[] paths;
private RequestMethod[] methods;
public Builder(String... paths) {
this.paths = paths;
}
public Builder paths(String... paths) {
this.paths = paths;
return this;
}
public Builder methods(RequestMethod... methods) {
this.methods = methods;
return this;
}
public RequestMappingInfo build() {
PatternsRequestCondition patternsCondition = paths != null ? new PatternsRequestCondition(this.paths) : new PatternsRequestCondition();
RequestMethodsRequestCondition requestMethodsRequestCondition = methods != null ? new RequestMethodsRequestCondition(methods) : new RequestMethodsRequestCondition();
return new RequestMappingInfo(patternsCondition, requestMethodsRequestCondition);
}
}
}
5、相关对象2
public class HandlerMethod {
private final String beanName;
private final BeanFactory beanFactory;
private final Class<?> beanType;
private final Method method;
private final MethodParameter[] parameters;
public HandlerMethod(String beanName, Class<?> beanType, BeanFactory beanFactory, Method method) {
this.beanName = beanName;
this.beanType = beanType;
this.beanFactory = beanFactory;
this.method = method;
this.parameters = this.initMethodParameters();
}
private MethodParameter[] initMethodParameters() {
int count = this.method.getParameterTypes().length;
MethodParameter[] result = new MethodParameter[count];
for (int i = 0; i < count; i++) {
result[i] = new MethodParameter(method, i);
}
return result;
}
}
public class MethodParameter {
// 方法
private final Method method;
// 参数对应的索引位置
private final int parameterIndex;
public MethodParameter(Method method, int parameterIndex) {
this.method = method;
this.parameterIndex = parameterIndex;
}
}