Spring源碼解析之SpringMVC源碼解析(一)


Spring源碼解析之SpringMVC源碼解析(一)


一、源碼閱讀環境

用idea快速創建一個springboot 項目 ,在項目添加一個controller 方法,這裡就不寫創建過程了。主要需要以下

啟動類

<code>@SpringBootApplication
public class MVCApplication {
public static void main (String args[]){
SpringApplication.run(MVCApplication.class, args);
}
}
/<code>

Controller

<code>@Controller
public class MVCDemoController {

@RequestMapping(value = "/testMVC",method = RequestMethod.GET )
public String testMVC(){
return "test";
}
}
/<code>

二、源碼分析

SpringMVC自動配置

我們知道在SpringBoot中使用SpringMVC的時候是不需要像傳統Spring中配置web.xml和配置文件等等的。那麼大家知道這是為什麼嗎

答案就在這個類WebMvcAutoConfiguration裡面 以下代碼為了方便閱讀只保留了部分源碼

<code>@Configuration
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass({ Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class })
@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)
@AutoConfigureAfter({ DispatcherServletAutoConfiguration.class,
TaskExecutionAutoConfiguration.class, ValidationAutoConfiguration.class })
public class WebMvcAutoConfiguration {

public static final String DEFAULT_PREFIX = "";

public static final String DEFAULT_SUFFIX = "";

private static final String[] SERVLET_LOCATIONS = { "/" };

@Bean
@ConditionalOnMissingBean(HiddenHttpMethodFilter.class)
@ConditionalOnProperty(prefix = "spring.mvc.hiddenmethod.filter", name = "enabled", matchIfMissing = true)
public OrderedHiddenHttpMethodFilter hiddenHttpMethodFilter() {
return new OrderedHiddenHttpMethodFilter();
}

@Bean
@ConditionalOnMissingBean(FormContentFilter.class)
@ConditionalOnProperty(prefix = "spring.mvc.formcontent.filter", name = "enabled", matchIfMissing = true)
public OrderedFormContentFilter formContentFilter() {
return new OrderedFormContentFilter();
}

// Defined as a nested config to ensure WebMvcConfigurer is not read when not
// on the classpath
@Configuration
@Import(EnableWebMvcConfiguration.class)
@EnableConfigurationProperties({ WebMvcProperties.class, ResourceProperties.class })
@Order(0)
public static class WebMvcAutoConfigurationAdapter
implements WebMvcConfigurer, ResourceLoaderAware {

private static final Log logger = LogFactory.getLog(WebMvcConfigurer.class);

private final ResourceProperties resourceProperties;

private final WebMvcProperties mvcProperties;

private final ListableBeanFactory beanFactory;

private final ObjectProvider<httpmessageconverters> messageConvertersProvider;

final ResourceHandlerRegistrationCustomizer resourceHandlerRegistrationCustomizer;


private ResourceLoader resourceLoader;

public WebMvcAutoConfigurationAdapter(ResourceProperties resourceProperties,
WebMvcProperties mvcProperties, ListableBeanFactory beanFactory,
ObjectProvider<httpmessageconverters> messageConvertersProvider,
ObjectProvider<resourcehandlerregistrationcustomizer> resourceHandlerRegistrationCustomizerProvider) {
this.resourceProperties = resourceProperties;
this.mvcProperties = mvcProperties;
this.beanFactory = beanFactory;
this.messageConvertersProvider = messageConvertersProvider;
this.resourceHandlerRegistrationCustomizer = resourceHandlerRegistrationCustomizerProvider
.getIfAvailable();
}

@Bean
@ConditionalOnMissingBean
public InternalResourceViewResolver defaultViewResolver() {
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setPrefix(this.mvcProperties.getView().getPrefix());
resolver.setSuffix(this.mvcProperties.getView().getSuffix());
return resolver;
}

@Bean
@ConditionalOnBean(View.class)
@ConditionalOnMissingBean
public BeanNameViewResolver beanNameViewResolver() {
BeanNameViewResolver resolver = new BeanNameViewResolver();
resolver.setOrder(Ordered.LOWEST_PRECEDENCE - 10);
return resolver;
}

@Bean
@ConditionalOnBean(ViewResolver.class)
@ConditionalOnMissingBean(name = "viewResolver", value = ContentNegotiatingViewResolver.class)
public ContentNegotiatingViewResolver viewResolver(BeanFactory beanFactory) {
ContentNegotiatingViewResolver resolver = new ContentNegotiatingViewResolver();
resolver.setContentNegotiationManager(
beanFactory.getBean(ContentNegotiationManager.class));
// ContentNegotiatingViewResolver uses all the other view resolvers to locate
// a view so it should have a high precedence
resolver.setOrder(Ordered.HIGHEST_PRECEDENCE);
return resolver;
}

@Bean
@ConditionalOnMissingBean
@ConditionalOnProperty(prefix = "spring.mvc", name = "locale")
public LocaleResolver localeResolver() {

if (this.mvcProperties
.getLocaleResolver() == WebMvcProperties.LocaleResolver.FIXED) {
return new FixedLocaleResolver(this.mvcProperties.getLocale());
}
AcceptHeaderLocaleResolver localeResolver = new AcceptHeaderLocaleResolver();
localeResolver.setDefaultLocale(this.mvcProperties.getLocale());
return localeResolver;
}

@Bean
public WelcomePageHandlerMapping welcomePageHandlerMapping(
ApplicationContext applicationContext) {
return new WelcomePageHandlerMapping(
new TemplateAvailabilityProviders(applicationContext),
applicationContext, getWelcomePage(),
this.mvcProperties.getStaticPathPattern());
}

@Bean
@ConditionalOnMissingBean({ RequestContextListener.class,
RequestContextFilter.class })
@ConditionalOnMissingFilterBean(RequestContextFilter.class)
public static RequestContextFilter requestContextFilter() {
return new OrderedRequestContextFilter();
}

@Configuration
@ConditionalOnProperty(value = "spring.mvc.favicon.enabled", matchIfMissing = true)
public static class FaviconConfiguration implements ResourceLoaderAware {

private final ResourceProperties resourceProperties;

private ResourceLoader resourceLoader;

public FaviconConfiguration(ResourceProperties resourceProperties) {
this.resourceProperties = resourceProperties;
}

@Override
public void setResourceLoader(ResourceLoader resourceLoader) {
this.resourceLoader = resourceLoader;
}

@Bean
public SimpleUrlHandlerMapping faviconHandlerMapping() {
SimpleUrlHandlerMapping mapping = new SimpleUrlHandlerMapping();
mapping.setOrder(Ordered.HIGHEST_PRECEDENCE + 1);
mapping.setUrlMap(Collections.singletonMap("**/favicon.ico",
faviconRequestHandler()));
return mapping;

}

@Bean
public ResourceHttpRequestHandler faviconRequestHandler() {
ResourceHttpRequestHandler requestHandler = new ResourceHttpRequestHandler();
requestHandler.setLocations(resolveFaviconLocations());
return requestHandler;
}

}

}

/**
* Configuration equivalent to {@code @EnableWebMvc}.
*/
@Configuration
public static class EnableWebMvcConfiguration extends DelegatingWebMvcConfiguration {

private final WebMvcProperties mvcProperties;

private final ListableBeanFactory beanFactory;

private final WebMvcRegistrations mvcRegistrations;

public EnableWebMvcConfiguration(
ObjectProvider<webmvcproperties> mvcPropertiesProvider,
ObjectProvider<webmvcregistrations> mvcRegistrationsProvider,
ListableBeanFactory beanFactory) {
this.mvcProperties = mvcPropertiesProvider.getIfAvailable();
this.mvcRegistrations = mvcRegistrationsProvider.getIfUnique();
this.beanFactory = beanFactory;
}

@Bean
@Override
public RequestMappingHandlerAdapter requestMappingHandlerAdapter() {
RequestMappingHandlerAdapter adapter = super.requestMappingHandlerAdapter();
adapter.setIgnoreDefaultModelOnRedirect(this.mvcProperties == null
|| this.mvcProperties.isIgnoreDefaultModelOnRedirect());
return adapter;
}
@Bean
@Primary
@Override
public RequestMappingHandlerMapping requestMappingHandlerMapping() {
// Must be @Primary for MvcUriComponentsBuilder to work
return super.requestMappingHandlerMapping();
}

@Bean
@Override
public FormattingConversionService mvcConversionService() {
WebConversionService conversionService = new WebConversionService(
this.mvcProperties.getDateFormat());
addFormatters(conversionService);
return conversionService;
}

@Bean
@Override
public Validator mvcValidator() {
if (!ClassUtils.isPresent("javax.validation.Validator",
getClass().getClassLoader())) {
return super.mvcValidator();
}
return ValidatorAdapter.get(getApplicationContext(), getValidator());
}

@Bean
@Override
public ContentNegotiationManager mvcContentNegotiationManager() {
ContentNegotiationManager manager = super.mvcContentNegotiationManager();
List<contentnegotiationstrategy> strategies = manager.getStrategies();
ListIterator<contentnegotiationstrategy> iterator = strategies.listIterator();
while (iterator.hasNext()) {
ContentNegotiationStrategy strategy = iterator.next();
if (strategy instanceof PathExtensionContentNegotiationStrategy) {
iterator.set(new OptionalPathExtensionContentNegotiationStrategy(
strategy));
}
}
return manager;
}

}

@Configuration
@ConditionalOnEnabledResourceChain
static class ResourceChainCustomizerConfiguration {

@Bean
public ResourceChainResourceHandlerRegistrationCustomizer resourceHandlerRegistrationCustomizer() {
return new ResourceChainResourceHandlerRegistrationCustomizer();
}
}

\tinterface ResourceHandlerRegistrationCustomizer {

\t\tvoid customize(ResourceHandlerRegistration registration);


\t}
/<contentnegotiationstrategy>/<contentnegotiationstrategy>/<webmvcregistrations>/<webmvcproperties>/<resourcehandlerregistrationcustomizer>/<httpmessageconverters>/<httpmessageconverters>/<code>

仔細看這個類,你就會發現這些自動注入的一些類都是之前需要我們在xml文件中配置的,現在SpringBoot幫我們做了這個操作。這就是大名鼎鼎的約定大於配置

初始化DispatcherServlet

DispatcherServlet是一個實現了Servlet接口的類,Servlet的初始化階段會調用它的init()方法,而DispatcherServlet的方法是繼承自父類HttpServletBean的

<code>public final void init() throws ServletException {

// Set bean properties from init parameters.
//處理init-param參數,但是SpringBoot中默認是沒有的
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.
// 初始化Servlet,往下看
initServletBean();
}
/<code>
<code>/** 

* Overridden method of {@link HttpServletBean}, invoked after any bean properties
* have been set. Creates this servlet's WebApplicationContext.
*/
@Override
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 {
//初始化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");
}
}
protected WebApplicationContext initWebApplicationContext() {
//獲取AnnotationConfigServletWebServerApplicationContext類型的web容器
\t\tWebApplicationContext rootContext =
\t\t\t\tWebApplicationContextUtils.getWebApplicationContext(getServletContext());
\t\tWebApplicationContext wac = null;

\t\tif (this.webApplicationContext != null) {
\t\t\t// A context instance was injected at construction time -> use it
\t\t\twac = this.webApplicationContext;
\t\t\tif (wac instanceof ConfigurableWebApplicationContext) {
\t\t\t\tConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
\t\t\t\tif (!cwac.isActive()) {
\t\t\t\t\t// The context has not yet been refreshed -> provide services such as
\t\t\t\t\t// setting the parent context, setting the application context id, etc

\t\t\t\t\tif (cwac.getParent() == null) {
\t\t\t\t\t\t// The context instance was injected without an explicit parent -> set
\t\t\t\t\t\t// the root application context (if any; may be null) as the parent
\t\t\t\t\t\tcwac.setParent(rootContext);
\t\t\t\t\t}
\t\t\t\t\tconfigureAndRefreshWebApplicationContext(cwac);
\t\t\t\t}
\t\t\t}
\t\t}
\t\tif (wac == null) {
\t\t\t// No context instance was injected at construction time -> see if one
\t\t\t// has been registered in the servlet context. If one exists, it is assumed
\t\t\t// that the parent context (if any) has already been set and that the
\t\t\t// user has performed any initialization such as setting the context id
\t\t\twac = findWebApplicationContext();
\t\t}
\t\tif (wac == null) {
\t\t\t// No context instance is defined for this servlet -> create a local one
\t\t\twac = createWebApplicationContext(rootContext);
\t\t}

\t\tif (!this.refreshEventReceived) {
\t\t\t// Either the context is not a ConfigurableApplicationContext with refresh
\t\t\t// support or the context injected at construction time had already been
\t\t\t// refreshed -> trigger initial onRefresh manually here.
\t\t\tsynchronized (this.onRefreshMonitor) {
// 刷新應用上下文,這裡是重點,接著往下看
\t\t\t\tonRefresh(wac);
\t\t\t}
\t\t}

\t\tif (this.publishContext) {
\t\t\t// Publish the context as a servlet context attribute.
// 推送事件通知
\t\t\tString attrName = getServletContextAttributeName();
\t\t\tgetServletContext().setAttribute(attrName, wac);
\t\t}

\t\treturn wac;
\t}
/<code>

繼續往下看DispatcherServlet中onRefresh

<code>/**
* This implementation calls {@link #initStrategies}.
*/

@Override
protected void onRefresh(ApplicationContext context) {
initStrategies(context);
}

/**
* Initialize the strategy objects that this servlet uses.
*

May be overridden in subclasses in order to initialize further strategy objects.
*/
protected void initStrategies(ApplicationContext context) {
initMultipartResolver(context);
initLocaleResolver(context);
initThemeResolver(context);
//初始化HandlerMappings
initHandlerMappings(context);
//初始化HandlerAdapters
initHandlerAdapters(context);
initHandlerExceptionResolvers(context);
initRequestToViewNameTranslator(context);
initViewResolvers(context);
initFlashMapManager(context);
}

/<code>

可以看到上方這一塊代碼都是一些常用組件的初始化,初始化的邏輯都比較簡單,隨意挑取2個看一下

<code>private void initHandlerMappings(ApplicationContext context) {
this.handlerMappings = null;

if (this.detectAllHandlerMappings) {
// Find all HandlerMappings in the ApplicationContext, including ancestor contexts.
Map<string> 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);
}
}
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) {
this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
if (logger.isTraceEnabled()) {
logger.trace("No HandlerMappings declared for servlet '" + getServletName() +
"': using default strategies from DispatcherServlet.properties");
}
}
}

/**
* Initialize the HandlerAdapters used by this class.
*

If no HandlerAdapter beans are defined in the BeanFactory for this namespace,
* we default to SimpleControllerHandlerAdapter.
*/
private void initHandlerAdapters(ApplicationContext context) {
this.handlerAdapters = null;

if (this.detectAllHandlerAdapters) {
// Find all HandlerAdapters in the ApplicationContext, including ancestor contexts.
Map<string> matchingBeans =
BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerAdapter.class, true, false);
if (!matchingBeans.isEmpty()) {
this.handlerAdapters = new ArrayList<>(matchingBeans.values());
// We keep HandlerAdapters in sorted order.
AnnotationAwareOrderComparator.sort(this.handlerAdapters);
}
}
else {
try {
HandlerAdapter ha = context.getBean(HANDLER_ADAPTER_BEAN_NAME, HandlerAdapter.class);
this.handlerAdapters = Collections.singletonList(ha);
}
catch (NoSuchBeanDefinitionException ex) {
// Ignore, we'll add a default HandlerAdapter later.
}
}

// Ensure we have at least some HandlerAdapters, by registering
// default HandlerAdapters if no other adapters are found.
if (this.handlerAdapters == null) {
this.handlerAdapters = getDefaultStrategies(context, HandlerAdapter.class);


if (logger.isTraceEnabled()) {
logger.trace("No HandlerAdapters declared for servlet '" + getServletName() +
"': using default strategies from DispatcherServlet.properties");
}
}
}
/<string>

/<string>/<code>

至此,DispatcherServlet的初始化就完成了移步


分享到:


相關文章: