前言
在使用SpringMvc
時坑傅,通過Controller
跟RequestMapping
,就能實現(xiàn)網(wǎng)絡(luò)請求的處理疮方。那么控嗜,這是怎么實現(xiàn)的呢?請求是如何從Tomcat
進(jìn)入到controller
里的方法的呢骡显?
核心流程概覽
宏觀上看疆栏,流程如下:
- 創(chuàng)建
DispatcherServlet
實例 - 創(chuàng)建
Tomcat
實例 - 通過
ServletContainerInitializer
以及ServletContextInitializer
將DispatcherServlet
注冊到Tomcat
的Servlet
容器里 - 將
HandlerMapping
綁定到DispatcherServlet
- 請求通過
Tomcat
進(jìn)到DispatcherServlet
-
DispatcherServlet
根據(jù)request path
到HandlerMapping
查找請求處理方法
源碼分析
1. 注冊controller
Spring
在初始化RequestMappingHandlerMapping
這個Bean時會將Controller
層帶有RequestMapping等相關(guān)注解的方法跟注解信息的PATH分別作為key value注冊到RequestMappingHandlerMapping
中。然后RequestMappingHandlerMapping
會在第一次請求到來時被注冊到DispatcherServlet
1.1 初始化RequestMappingHandlerMapping
RequestMappingHandlerMapping
繼承了InitializingBean
蟆盐,在Spring創(chuàng)建RequestMappingHandlerMapping
的實例時會去調(diào)用其afterPropertiesSet
方法進(jìn)行初始化
public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory
implements AutowireCapableBeanFactory {
protected void invokeInitMethods(String beanName, Object bean, @Nullable RootBeanDefinition mbd)
throws Throwable {
//判斷當(dāng)前bean是否繼承了InitializingBean
boolean isInitializingBean = (bean instanceof InitializingBean);
if (isInitializingBean) {
//初始化
((InitializingBean) bean).afterPropertiesSet();
}
}
}
1.2 遍歷Controller
RequestMappingHandlerMapping
會把所有的Spring Bean
對應(yīng)的類里有Controller
或者RequestMapping
注解的類拿出來處理
public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMapping implements InitializingBean {
protected void processCandidateBean(String beanName) {
Class<?> beanType = null;
try {
beanType = obtainApplicationContext().getType(beanName);
}
catch (Throwable ex) {
...
}
//只處理有相應(yīng)注解的bean
if (beanType != null && isHandler(beanType)) {
detectHandlerMethods(beanName);
}
}
//判斷是否有相應(yīng)注解
protected boolean isHandler(Class<?> beanType) {
return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) ||
AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class));
}
}
1.3 生成RequestMappingInfo
把1.2里得到的類里的所有帶有RequestMapping
注解的方法拿出來包裝成RequestMappingInfo
并塞到RequestMappingHandlerMapping
內(nèi)部的hashMap
中
public final class MethodIntrospector {
public static <T> Map<Method, T> selectMethods(Class<?> targetType, final MetadataLookup<T> metadataLookup) {
final Map<Method, T> methodMap = new LinkedHashMap<>();
Set<Class<?>> handlerTypes = new LinkedHashSet<>();
Class<?> specificHandlerType = null;
handlerTypes.addAll(ClassUtils.getAllInterfacesForClassAsSet(targetType));
for (Class<?> currentHandlerType : handlerTypes) {
final Class<?> targetClass = (specificHandlerType != null ? specificHandlerType : currentHandlerType);
//通過反射找到targetType下的所有method
ReflectionUtils.doWithMethods(currentHandlerType, method -> {
Method specificMethod = ClassUtils.getMostSpecificMethod(method, targetClass);
//判斷method上是否有@RequestMapping承边,沒有則返回null
T result = metadataLookup.inspect(specificMethod);
if (result != null) {
//如果是橋接方法,就返回被橋接的方法石挂。否則返回specificMethod
Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(specificMethod);
//如果specificMethod是橋接方法博助,則不添加到methodMap中。否則同一個方法就會被添加兩次
if (bridgedMethod == specificMethod || metadataLookup.inspect(bridgedMethod) == null) {
methodMap.put(specificMethod, result);
}
}
}, ReflectionUtils.USER_DECLARED_METHODS);
}
return methodMap;
}
}
public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMapping implements InitializingBean {
// metadataLookup.inspect(specificMethod)是MetadataLookup#inspect的一個匿名實現(xiàn)
protected void detectHandlerMethods(Object handler) {
...
Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
(MethodIntrospector.MetadataLookup<T>) method -> {
//根據(jù)方法是否被RequestMapping.class標(biāo)注來判斷方法是否可以被注冊
return getMappingForMethod(method, userType);
});
methods.forEach((method, mapping) -> {
Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
//準(zhǔn)備注冊
registerHandlerMethod(handler, invocableMethod, mapping);
});
...
}
}
public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMapping
implements MatchableHandlerMapping, EmbeddedValueResolverAware {
//判斷方法是否應(yīng)該被注冊
private RequestMappingInfo createRequestMappingInfo(AnnotatedElement element) {
//判斷方法上是否有RequestMapping.class
RequestMapping requestMapping = AnnotatedElementUtils.findMergedAnnotation(element, RequestMapping.class);
RequestCondition<?> condition = (element instanceof Class ?
getCustomTypeCondition((Class<?>) element) : getCustomMethodCondition((Method) element));
//創(chuàng)建RequestMappingInfo
return (requestMapping != null ? createRequestMappingInfo(requestMapping, condition) : null);
}
}
public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMapping implements InitializingBean {
//注冊
public void register(T mapping, Object handler, Method method) {
...
//把類名handler跟方法method包裝成一個HandlerMethod對象
HandlerMethod handlerMethod = createHandlerMethod(handler, method);
//把方法注解里的path跟mapping的關(guān)系保存下來痹愚,
//后面訪問的時候會先從httpRequest解析出path富岳,再根據(jù)path找到mapping
Set<String> directPaths = AbstractHandlerMethodMapping.this.getDirectPaths(mapping);
for (String path : directPaths) {
this.pathLookup.add(path, mapping);
}
//mapping是上面創(chuàng)建的RequestMappingInfo
this.registry.put(mapping,
new MappingRegistration<>(mapping, handlerMethod, directPaths, name, corsConfig != null));
}
}
1.4 注冊HandlerMapping
將HandlerMapping
注冊到DispatcherServlet
蛔糯。在第一次請求時進(jìn)行初始化時觸發(fā)
public class DispatcherServlet extends FrameworkServlet {
private void initHandlerMappings(ApplicationContext context) {
this.handlerMappings = null;
if (this.detectAllHandlerMappings) {
//從spring上下文里拿到所有實現(xiàn)了HandlerMapping的bean
Map<String, HandlerMapping> matchingBeans =
BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
if (!matchingBeans.isEmpty()) {
//綁定
this.handlerMappings = new ArrayList<>(matchingBeans.values());
// We keep HandlerMappings in sorted order.
AnnotationAwareOrderComparator.sort(this.handlerMappings);
}
}
}
}
1.5 注冊DispatcherServlet
將DispatcherServlet
注冊到Tomcat
的Servlet
容器里
public class ServletWebServerApplicationContext extends GenericWebApplicationContext
implements ConfigurableWebServerApplicationContext {
private void createWebServer() {
...
//創(chuàng)建webServer并添加下面的ServletContextInitializer匿名實現(xiàn)
this.webServer = factory.getWebServer(getSelfInitializer());
...
}
private org.springframework.boot.web.servlet.ServletContextInitializer getSelfInitializer() {
return this::selfInitialize;
}
private void selfInitialize(ServletContext servletContext) throws ServletException {
...
//找到所有實現(xiàn)了ServletContextInitializer接口的Spring bean
//這里拿到的是DispatcherServletRegistrationBean
//而DispatcherServletRegistrationBean里持有DispatcherServlet的Spring bean
for (ServletContextInitializer beans : getServletContextInitializerBeans()) {
//將DispatcherServlet注冊到Tomcat的Servlet容器中
beans.onStartup(servletContext);
}
}
}
@Configuration(proxyBeanMethods = false)
protected static class DispatcherServletRegistrationConfiguration {
@Bean(name = DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME)
public DispatcherServletRegistrationBean dispatcherServletRegistration(DispatcherServlet dispatcherServlet,
WebMvcProperties webMvcProperties, ObjectProvider<MultipartConfigElement> multipartConfig) {
//通過DispatcherServlet創(chuàng)建DispatcherServletRegistrationBean
DispatcherServletRegistrationBean registration = new DispatcherServletRegistrationBean(dispatcherServlet,
webMvcProperties.getServlet().getPath());
return registration;
}
}
public abstract class RegistrationBean implements ServletContextInitializer, Ordered {
@Override
public final void onStartup(ServletContext servletContext) throws ServletException {
//注冊DispatcherServlet。最終會注冊到StandardWrapper#setServlet
register(description, servletContext);
}
}
//Tomcat啟動時觸發(fā)
class TomcatStarter implements ServletContainerInitializer {
public void onStartup(Set<Class<?>> classes, ServletContext servletContext) throws ServletException {
//這里會拿到上面添加的匿名ServletContextInitializer
for (ServletContextInitializer initializer : this.initializers) {
initializer.onStartup(servletContext);
}
}
}
2. 訪問controller
2.1 請求進(jìn)入DispatcherServlet
-
Tomcat
從Servlet容器中取出DispatcherServlet
窖式,并將請求交給DispatcherServlet
public class DispatcherServlet extends FrameworkServlet {
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
//根據(jù)request的url跟http method從RequestMappingHandlerMapping.registry中獲取對應(yīng)的
//請求處理器
mappedHandler = getHandler(processedRequest);
...
//調(diào)用請求處理方法
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
}
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
if (this.handlerMappings != null) {
//遍歷大步驟1小步驟4時注冊進(jìn)來的handlerMappings
for (HandlerMapping mapping : this.handlerMappings) {
//根據(jù)request path查找handler
HandlerExecutionChain handler = mapping.getHandler(request);
if (handler != null) {
return handler;
}
}
}
return null;
}
}
2.2 查找請求處理方法
RequestMappingHandlerMapping
根據(jù)HttpServletRequest
查找請求處理器
public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMapping implements InitializingBean {
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
//解析出url的path
String lookupPath = initLookupPath(request);
this.mappingRegistry.acquireReadLock();
try {
//根據(jù)path找到對應(yīng)的HandlerMethod
HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
}
finally {
this.mappingRegistry.releaseReadLock();
}
}
protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
List<Match> matches = new ArrayList<>();
//通過lookupPath找到mapping
List<T> directPathMatches = this.mappingRegistry.getMappingsByDirectPath(lookupPath);
if (directPathMatches != null) {
//根據(jù)mapping從RequestMappingHandlerMapping#registry里
//找到對應(yīng)的RequestMappingInfo并包裝成Match對象蚁飒,放入matches中
addMatchingMappings(directPathMatches, matches, request);
}
if (!matches.isEmpty()) {
Match bestMatch = matches.get(0);
//從RequestMappingInfo獲取HandlerMethod
return bestMatch.getHandlerMethod();
}
}
private void addMatchingMappings(Collection<T> mappings, List<Match> matches, HttpServletRequest request) {
for (T mapping : mappings) {
T match = getMatchingMapping(mapping, request);
if (match != null) {
//根據(jù)mapping找到RequestMappingInfo并包裝成Match對象
matches.add(new Match(match, this.mappingRegistry.getRegistrations().get(mapping)));
}
}
}
public Map<T, MappingRegistration<T>> getRegistrations() {
//第一步里的registry
return this.registry;
}
}