携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第12天,点击查看活动详情
接上一篇分析继续
这时,这一步执行完成,我们返回一个Map集合,此时的泛型 T 就是RequestMappingInfo类型。 然后传回detectHandlerMethods方法里面的lambda函数,就是我们上面所说的函数。
Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
(MethodIntrospector.MetadataLookup<T>) method -> {
try {
return getMappingForMethod(method, userType);
}
catch (Throwable ex) {
throw new IllegalStateException("Invalid mapping on handler class [" +
userType.getName() + "]: " + method, ex);
}
});
这个Map集合method,他的key是方法method,他的value值就是方法对应的RequestMapping注解所封装好的RequestMappingInfo对象,注意,此时这个RequestMappingInfo对象是已经合并好的。然后进行了一些日志的操作之后,执行到这个地方。
循环刚刚获得到的Map集合。两个方法。
methods.forEach((method, mapping) -> {
Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
registerHandlerMethod(handler, invocableMethod, mapping);
});
-
selectInvocableMethod方法public static Method selectInvocableMethod(Method method, @Nullable Class<?> targetType) { if (targetType == null) { return method; } Method methodToUse = MethodIntrospector.selectInvocableMethod(method, targetType); // 不能是私有的,不能是静态的,不能是spring代理的 if (Modifier.isPrivate(methodToUse.getModifiers()) && !Modifier.isStatic(methodToUse.getModifiers()) && SpringProxy.class.isAssignableFrom(targetType)) { throw new IllegalStateException(String.format( "Need to invoke method '%s' found on proxy for target class '%s' but cannot " + "be delegated to target bean. Switch its visibility to package or protected.", method.getName(), method.getDeclaringClass().getSimpleName())); } return methodToUse; }这个方法其实就是去判断你这个方法,是不是私有的,是不是静态的,是不是spring代理的,如果是,就抛出异常。这里我们也就知道,我们所执行的方法,不能是私有的,静态的或者是spring代理的。筛选完可执行的方法之后,再去执行
registerHandlerMethod方法。 -
registerHandlerMethod方法protected void registerHandlerMethod(Object handler, Method method, T mapping) { this.mappingRegistry.register(mapping, handler, method); }->
// mapping是RequestMappingInfo对象,method是处理方法,handler是方法所对应Controller类的类型 public void register(T mapping, Object handler, Method method) { this.readWriteLock.writeLock().lock(); try { // 根据处理类和处理方法创建HandlerMethod类 // 把方法封装成了HandlerMethod对象 // 把方法中的参数列表封装成了MethodParameter对象 HandlerMethod handlerMethod = createHandlerMethod(handler, method); validateMethodMapping(handlerMethod, mapping); Set<String> directPaths = AbstractHandlerMethodMapping.this.getDirectPaths(mapping); for (String path : directPaths) { // urlLookup---->(url,RequestMappingInfo) this.pathLookup.add(path, mapping); } String name = null; if (getNamingStrategy() != null) { name = getNamingStrategy().getName(handlerMethod, mapping); addMappingName(name, handlerMethod); } CorsConfiguration corsConfig = initCorsConfiguration(handler, method, mapping); if (corsConfig != null) { corsConfig.validateAllowCredentials(); this.corsLookup.put(handlerMethod, corsConfig); } this.registry.put(mapping, new MappingRegistration<>(mapping, handlerMethod, directPaths, name, corsConfig != null)); } finally { this.readWriteLock.writeLock().unlock(); } }我们直接来看
register这个方法,首先就用传过来的处理类和处理方法去创建一个HandlerMethod对象。protected HandlerMethod createHandlerMethod(Object handler, Method method) { return handler instanceof String ? new HandlerMethod((String)handler, this.obtainApplicationContext().getAutowireCapableBeanFactory(), method) : new HandlerMethod(handler, method); } public HandlerMethod(Object bean, Method method) { Assert.notNull(bean, "Bean is required"); Assert.notNull(method, "Method is required"); this.bean = bean; this.beanFactory = null; this.beanType = ClassUtils.getUserClass(bean); this.method = method; this.bridgedMethod = BridgeMethodResolver.findBridgedMethod(method); this.parameters = this.initMethodParameters(); this.evaluateResponseStatus(); this.description = initDescription(this.beanType, this.method); }这个类中也有很多的属性,这个方法其实就是去new一个
HandlerMethod对象,在他的构造方法中,对这些属性一一赋值,也就是说,一个方法对应一个HandlerMethod对象。值得注意的是,这里的parameters类型为MethodParameter[],就是说HandlerMethod把方法参数列表封装成了一个MethodParameter数组。然后在mappingLookup这个集合中,存储mapping和handlerMethod,也就是我们的RequestMappingInfo和其对应的处理方法。接着它遍历从mapping(RequestMappingInfo)拿到的Urls,在把他们放到urlLookup集合中,url为key,mapping为value。此时我们有两个集合,mappingLookup和urlLookup。-
mappingLookup--------->(RequestMappingInfo,handlerMethod)
-
urlLookup----------------->(url, RequestMappingInfo)
也就是说,当我们拿到http请求时候,获取url,根据
urlLookup这个集合能得到对应的@RequestMapping注解对应的RequestMappingInfo对象,然后从mappingLookup中根据对应的RequestMappingInfo对象去查找对应的处理方法,这样就能进行处理了。处理完之后,RequestMappingHandlerMapping就真正的创建完成了。 -
总结
- 我们从initStrategies方法进入到
this.initHandlerMappings(context);方法中 - 在容器中获取不到
HandlerMapping实例的时候就会去创建 - 因为在
DispatcherServlet加载的时候会把它类路径下的DispatcherServlet.properties一起加载放入到属性中,所以直接通过HandlerMapping的全类名可以获取到对应的HandlerMapping类型。 - 获取到类型之后就去创建,但由于他是
InitializingBean的子类,所以会去调用afterPropertiesSet方法。 - 通过一系列的调用之后,会拿到spring容器中的所有beanName去一一筛选类上标注了Controller或者RequestMapping的注解的类。
- 筛选完成之后,会解析类和方法上面的RequestMapping注解,并把他们封装成RequestMappingInfo对象,然后进行合并,合并完成之后,放到一个map集合中
- 遍历刚刚获得的map集合,将
RequestMappingInfo对象和对应处理方法放入到mappingLookup,将RequestMappingInfo中的url和对应RequestMappingInfo对象放入到urlLookup中。此时HandlerMapping真正的创建完成。