版权声明:本文为优快云博主「雨剑yyy」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.youkuaiyun.com/csdn_20150804/article/details/103943435
文章目录
基于springboot2.1.4;
springmvc执行,核心逻辑是在DispatchServlet中,其中的若干属性,初始值都是null,如下:
package org.springframework.web.servlet;
public class DispatcherServlet extends FrameworkServlet {
......
protected void initStrategies(ApplicationContext context) {
initMultipartResolver(context);
initLocaleResolver(context);
initThemeResolver(context);
//主要分析这个
initHandlerMappings(context);
initHandlerAdapters(context);
initHandlerExceptionResolvers(context);
initRequestToViewNameTranslator(context);
initViewResolvers(context);
initFlashMapManager(context);
}
......
}
上篇文章中,DispatchServlet在执行相关逻辑时,都是直接使用了上边这些属性,本文分析下这些属性是何时在哪里被初始化的,并以handlerMappings为例,着重分析。
DispatchServlet类是个servlet,包含init方法,tomcat容器在启动时,会默认执行init方法,而DispatchServlet的父类HttpServletBean实现了Servlet接口的init方法。因此我们从HttpServletBean#init() 方法开始。
1.HttpServletBean#init()
package org.springframework.web.servlet;
public abstract class HttpServletBean extends HttpServlet implements
EnvironmentCapable, EnvironmentAware {
......
public final void init() throws ServletException {
// Set bean properties from init parameters.
PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
if (!pvs.isEmpty()) {
try {
BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
initBeanWrapper(bw);
bw.setPropertyValues(pvs, true);
}
catch (BeansException ex) {
if (logger.isErrorEnabled()) {
logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);
}
throw ex;
}
}
// Let subclasses do whatever initialization they like.
//此方法是个空方法,目的是留给子类自己去实现一些初始化动作
initServletBean();
}
......
}
上面方法,其中关键方法是最后一行 initServletBean();这是个空方法,实现在其子类FrameworkServlet中实现,FrameworkServlet是DispatchServlet的直接父类。继续看下initServletBean的实现,
package org.springframework.web.servlet;
@SuppressWarnings("serial")
public abstract class FrameworkServlet extends HttpServletBean
implements ApplicationContextAware {
......
protected final void initServletBean() throws ServletException {
getServletContext().log("Initializing Spring " + getClass().getSimpleName() + " '" + getServletName() + "'");
if (logger.isInfoEnabled()) {
logger.info("Initializing Servlet '" + getServletName() + "'");
}
long startTime = System.currentTimeMillis();
try {
//初始化springmvc web容器
this.webApplicationContext = initWebApplicationContext();
//
initFrameworkServlet();
}
catch (ServletException | RuntimeException ex) {
logger.error("Context initialization failed", ex);
throw ex;
}
if (logger.isDebugEnabled()) {
String value = this.enableLoggingRequestDetails ?
"shown which may lead to unsafe logging of potentially sensitive data" :
"masked to prevent unsafe logging of potentially sensitive data";
logger.debug("enableLoggingRequestDetails='" + this.enableLoggingRequestDetails +
"': request parameters and headers will be " + value);
}
if (logger.isInfoEnabled()) {
logger.info("Completed initialization in " + (System.currentTimeMillis() - startTime) + " ms");
}
}
......
}
其中initWebApplicationContext()表示初始化web容器,进入,
package org.springframework.web.servlet;
@SuppressWarnings("serial")
public abstract class FrameworkServlet extends HttpServletBean
implements ApplicationContextAware {
......
protected WebApplicationContext initWebApplicationContext() {
//得到根容器,也就是springmvc的父容器,spring容器ApplicationContext
WebApplicationContext rootContext =
WebApplicationContextUtils.getWebApplicationContext(getServletContext());
WebApplicationContext wac = null;
if (this.webApplicationContext != null) {
// A context instance was injected at construction time -> use it
wac = this.webApplicationContext;
if (wac instanceof ConfigurableWebApplicationContext) {
ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
if (!cwac.isActive()) {
// The context has not yet been refreshed -> provide services such as
// setting the parent context, setting the application context id, etc
if (cwac.getParent() == null) {
// The context instance was injected without an explicit parent -> set
// the root application context (if any; may be null) as the parent
cwac.setParent(rootContext);
}
configureAndRefreshWebApplicationContext(cwac);
}
}
}
if (wac == null) {
// No context instance was injected at construction time -> see if one
// has been registered in the servlet context. If one exists, it is assumed
// that the parent context (if any) has already been set and that the
// user has performed any initialization such as setting the context id
wac = findWebApplicationContext();
}
if (wac == null) {
// No context instance is defined for this servlet -> create a local one
wac = createWebApplicationContext(rootContext);
}
if (!this.refreshEventReceived) {
// Either the context is not a ConfigurableApplicationContext with refresh
// support or the context injected at construction time had already been
// refreshed -> trigger initial onRefresh manually here.
//刷新容器
synchronized (this.onRefreshMonitor) {
onRefresh(wac);
}
}
if (this.publishContext) {
// Publish the context as a servlet context attribute.
String attrName = getServletContextAttributeName();
getServletContext().setAttribute(attrName, wac);
}
return wac;
}
......
}
从上面方法可以看出,web容器实际上是spring容器的子容器,web容器内部包含了父容器的引用,然后再包含自己特有的一些内容,这些内容在onRefresh(wac);方法中初始化,继续进入onRefresh方法,
2.DispatcherServlet#onRefresh(ApplicationContext context)
package org.springframework.web.servlet;
public class DispatcherServlet extends FrameworkServlet {
......
protected void onRefresh(ApplicationContext context) {
initStrategies(context);
}
protected void initStrategies(ApplicationContext context) {
initMultipartResolver(context);
initLocaleResolver(context);
initThemeResolver(context);
//初始化HandlerMappings
initHandlerMappings(context);
initHandlerAdapters(context);
initHandlerExceptionResolvers(context);
initRequestToViewNameTranslator(context);
initViewResolvers(context);
initFlashMapManager(context);
}
......
}
这里使用了策略模式,其中initHandlerMappings(context);
就是用来初始化handlerMappings属性的。
package org.springframework.web.servlet;
public class DispatcherServlet extends FrameworkServlet {
......
private void initHandlerMappings(ApplicationContext context) {
this.handlerMappings = null;
if (this.detectAllHandlerMappings) {//默认是true,扫描所有
// 从ApplicationContext中,找类型是HandlerMapping的bean
Map<String, HandlerMapping> matchingBeans =
BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
if (!matchingBeans.isEmpty()) {
//初始化handlerMappings属性
this.handlerMappings = new ArrayList<>(matchingBeans.values());
// We keep HandlerMappings in sorted order.
AnnotationAwareOrderComparator.sort(this.handlerMappings);
}
}
else {
try {
HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);
this.handlerMappings = Collections.singletonList(hm);
}
catch (NoSuchBeanDefinitionException ex) {
// Ignore, we'll add a default HandlerMapping later.
}
}
// Ensure we have at least one HandlerMapping, by registering
// a default HandlerMapping if no other mappings are found.
if (this.handlerMappings == null) {
//如果容器中一个handlerMapping都没有,则给一个默认的,就是去DispatcherServlet.properties配置文件中读取
this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
if (logger.isTraceEnabled()) {
logger.trace("No HandlerMappings declared for servlet '" + getServletName() +
"': using default strategies from DispatcherServlet.properties");
}
}
}
......
}
上述方法中,handlerMappings是从上下文容器中直接get出来的,如果get不到,会使用默认的配置:其实从上述方法中的log信息中可以看出,有个叫DispatcherServlet.properties
的配置文件。
打开看下:
org.springframework.web.servlet.LocaleResolver=org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver
org.springframework.web.servlet.ThemeResolver=org.springframework.web.servlet.theme.FixedThemeResolver
org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping
org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\
org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter
org.springframework.web.servlet.HandlerExceptionResolver=org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver,\
org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver,\
org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver
org.springframework.web.servlet.RequestToViewNameTranslator=org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator
org.springframework.web.servlet.ViewResolver=org.springframework.web.servlet.view.InternalResourceViewResolver
org.springframework.web.servlet.FlashMapManager=org.springframework.web.servlet.support.SessionFlashMapManager
从配置文件中我们可以发现,所有初始化所需要的内容都在这里了,其中包括HandlerMapping,共有两个。
但是,如果容器中有,就不会使用默认的,那么容器中是何时放进去的呢,
我们就直接分析springboot中的初始化逻辑。
3.WebMvcAutoConfiguration初始化配置
我们知道springboot在启动的时候,默认会加载spring.factories文件中的配置类,在spring-boot-autoconfigure.jar包下的spring.factories
文件中,有如下内容:
org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration,
就是这个WebMvcAutoConfiguration
,初始化了所有webmvc的配置,在这里面可以找到此配置类中还有个EnableWebMvcConfiguration
内部类,可以发现他继承了WebMvcConfigurationSuppor
t,所有HandlerMappings都能在这俩配置类中找到(代码比较多,就不贴出来了)。
4.RequestMappingHandlerMapping初始化
这里看下一个比较重要的HandlerMapping,RequestMappingHandlerMapping 的初始化代码,RequestMappingHandlerMapping专门用来处理@Controller修饰的bean,这种方式也是我们开发中最常用的。
package org.springframework.web.servlet.config.annotation;
public class WebMvcConfigurationSupport implements ApplicationContextAware,
ServletContextAware {
@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());
PathMatchConfigurer configurer = getPathMatchConfigurer();
Boolean useSuffixPatternMatch = configurer.isUseSuffixPatternMatch();
if (useSuffixPatternMatch != null) {
mapping.setUseSuffixPatternMatch(useSuffixPatternMatch);
}
Boolean useRegisteredSuffixPatternMatch = configurer.isUseRegisteredSuffixPatternMatch();
if (useRegisteredSuffixPatternMatch != null) {
mapping.setUseRegisteredSuffixPatternMatch(useRegisteredSuffixPatternMatch);
}
Boolean useTrailingSlashMatch = configurer.isUseTrailingSlashMatch();
if (useTrailingSlashMatch != null) {
mapping.setUseTrailingSlashMatch(useTrailingSlashMatch);
}
UrlPathHelper pathHelper = configurer.getUrlPathHelper();
if (pathHelper != null) {
mapping.setUrlPathHelper(pathHelper);
}
PathMatcher pathMatcher = configurer.getPathMatcher();
if (pathMatcher != null) {
mapping.setPathMatcher(pathMatcher);
}
Map<String, Predicate<Class<?>>> pathPrefixes = configurer.getPathPrefixes();
if (pathPrefixes != null) {
mapping.setPathPrefixes(pathPrefixes);
}
return mapping;
}
protected RequestMappingHandlerMapping createRequestMappingHandlerMapping() {
return new RequestMappingHandlerMapping();
}
}
上面这段代码,是告诉spring容器实例化一个RequestMappingHandlerMapping,并且设置了一些属性值。
而RequestMappingHandlerMapping类还实现了接口InitializingBean,所以在实例化后,会执行afterPropertiesSet()方法:
public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMapping
implements MatchableHandlerMapping, EmbeddedValueResolverAware {
......
public void afterPropertiesSet() {
//初始化RequestMappingInfo的config
this.config = new RequestMappingInfo.BuilderConfiguration();
this.config.setUrlPathHelper(getUrlPathHelper());
this.config.setPathMatcher(getPathMatcher());
this.config.setSuffixPatternMatch(this.useSuffixPatternMatch);
this.config.setTrailingSlashMatch(this.useTrailingSlashMatch);
this.config.setRegisteredSuffixPatternMatch(this.useRegisteredSuffixPatternMatch);
this.config.setContentNegotiationManager(getContentNegotiationManager());
//调用父类方法
super.afterPropertiesSet();
}
......
打开父类方法:
public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMapping
implements InitializingBean {
......
public void afterPropertiesSet() {
initHandlerMethods();
}
protected void initHandlerMethods() {
//从spring容器中拿到所有bean,然后遍历
for (String beanName : getCandidateBeanNames()) {
if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
//扫描所有handler方法,就是controller层的方法
processCandidateBean(beanName);
}
}
//记录个日志
handlerMethodsInitialized(getHandlerMethods());
}
......
}
上述方法中,spring拿到容器中的诉苦有bean,然后遍历处理,让我们看下如何处理的,
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) {
// An unresolvable bean type, probably from a lazy bean - let's ignore it.
if (logger.isTraceEnabled()) {
logger.trace("Could not resolve type for bean '" + beanName + "'", ex);
}
}
if (beanType != null && isHandler(beanType)) {
//探测寻找Handler方法
detectHandlerMethods(beanName);
}
}
protected abstract boolean isHandler(Class<?> var1);
......
}
上述代码逻辑就是判断一个bean是不是Handler(就是所谓的controller),如果是,那么就继续扫描Handler中的方法,并注册;其中判断isHandler(beanType)用来判断是否是handler。
因为我们这里分析的是RequestMappingHandlerMapping,所以isHandler方法在此类中实现:
public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMapping
implements MatchableHandlerMapping, EmbeddedValueResolverAware {
......
protected boolean isHandler(Class<?> beanType) {
return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) ||
AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class));
}
......
}
就是判断bean上是否有@Controller注解或者@RequestMapping注解。
下面继续看下spring如何探测handler方法然后如何注册的,
public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMapping
implements InitializingBean {
......
protected void detectHandlerMethods(Object handler) {
//handler对象就是我们定义的一个Controller
Class<?> handlerType = (handler instanceof String ?
obtainApplicationContext().getType((String) handler) : handler.getClass());
if (handlerType != null) {
Class<?> userType = ClassUtils.getUserClass(handlerType);
//扫描到Controller中的所有方法,key是方法名,value是路径名
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);
}
});
if (logger.isTraceEnabled()) {
logger.trace(formatMappings(userType, methods));
}
//
methods.forEach((method, mapping) -> {
//如果有代理,这里要找到被代理后的方法
Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
//注册Method
registerHandlerMethod(handler, invocableMethod, mapping);
});
}
}
......
}
上述代码逻辑,首先拿到Controller中的所有方法以及方法上的路径,如果方法有被代理,要使用被代理后的方法;然后遍历进行注册;
下面看下注册的逻辑:
public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMapping
implements InitializingBean {
private final AbstractHandlerMethodMapping<T>.MappingRegistry mappingRegistry = new AbstractHandlerMethodMapping.MappingRegistry();
......
protected void registerHandlerMethod(Object handler, Method method, T mapping) {
this.mappingRegistry.register(mapping, handler, method);
}
......
}
其中,MappingRegistry是AbstractHandlerMethodMapping的内部类,里面维护了一堆map,
public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMapping
implements InitializingBean {
......
class MappingRegistry {
private final Map<T, MappingRegistration<T>> registry = new HashMap<>();
private final Map<T, HandlerMethod> mappingLookup = new LinkedHashMap<>();
private final MultiValueMap<String, T> urlLookup = new LinkedMultiValueMap<>();
private final Map<String, List<HandlerMethod>> nameLookup = new ConcurrentHashMap<>();
private final Map<HandlerMethod, CorsConfiguration> corsLookup = new ConcurrentHashMap<>();
private final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
......
}
......
}
注册就是往这些map中存东西:
public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMapping
implements InitializingBean {
......
class MappingRegistry {
......
public void register(T mapping, Object handler, Method method) {
this.readWriteLock.writeLock().lock();
try {
HandlerMethod handlerMethod = createHandlerMethod(handler, method);
assertUniqueMethodMapping(handlerMethod, mapping);
//存储请求路径(包括请求方式)和请求方法的映射关系
this.mappingLookup.put(mapping, handlerMethod);
List<String> directUrls = getDirectUrls(mapping);
for (String url : directUrls) {
//存储url和请RequestMapping注解中的信息映射
this.urlLookup.add(url, mapping);
}
String name = null;
if (getNamingStrategy() != null) {
name = getNamingStrategy().getName(handlerMethod, mapping);
addMappingName(name, handlerMethod);
}
CorsConfiguration corsConfig = initCorsConfiguration(handler, method, mapping);
if (corsConfig != null) {
this.corsLookup.put(handlerMethod, corsConfig);
}
this.registry.put(mapping, new MappingRegistration<>(mapping, handlerMethod, directUrls, name));
}
finally {
this.readWriteLock.writeLock().unlock();
}
}
......
}
}
说下参数:
mapping:是方法上的路径信息(@RequestMpping注解中的信息),包括请求方式GET/POST等;
handler:就是Controller对象;
method:Controller中的方法;
注册的过程就是MappingRegistry存请求路径处理方法等相关的映射关系,而这些东西最终会在DispatcherServlet处理前端请求时使用到,也就是上篇文章中,我们分析的内容。
本文先分析到这里,其他初始化内容也是和HandlerMapping一样的,具体代码就不再展示了,直接在配置类中搜索相关内容即可。