javascript
SpringMVC源代码学习(一)从HTttpServletBean到DispatcherServlet
以下內容基于書:《看透SpringMVC-源代碼分析與實踐》基本照搬。。。用于自己查閱備忘。
觀察DispatcherServlet繼承樹?
?
做Java?web的應該都知道GenericServlet到HttpServlet的關系,它們都是 javax.servlet;包下的。?
從HttpServletBean開始就是由spring定義的了。
我們知道HttpServlet有一個無參的init()方法交由子類實現,用于類的初始化,那我們從HTttpServletBean中的init()方法開始分析。
@Override public final void init() throws ServletException {//log ...// Set bean properties from init parameters.try {PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);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 Exception ...// Let subclasses do whatever initialization they like.initServletBean();//log ... }首先將Servlet中配置的參數使用BeanWrapper設置到DispatcherServlet的相關屬性,然后調用模版方法initServletBean,
子類,也就是FrameworkServlet通過定義自己的initServletBean定義了自己的初始化內容。
FrameworkServlet的initServletBean
@Override protected final void initServletBean() throws ServletException {//log ...long startTime = System.currentTimeMillis();try {this.webApplicationContext = initWebApplicationContext();initFrameworkServlet();}//catch ...//log ... }過濾掉日志與catch代碼,主要代碼有兩句,分別說一下:
1、FrameworkServlet的initFrameworkServlet方法。?
類似于HTttpServletBean的initServletBean,這個方法沒有內容,是供給子類調用的模版方法。
2、initWebApplicationContext方法,詳細代碼如下:
protected WebApplicationContext initWebApplicationContext() {WebApplicationContext rootContext =WebApplicationContextUtils.getWebApplicationContext(getServletContext());WebApplicationContext wac = null;if (this.webApplicationContext != null) {// A context instance was injected at construction time -> use itwac = 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, etcif (cwac.getParent() == null) {// The context instance was injected without an explicit parent -> set// the root application context (if any; may be null) as the parentcwac.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 idwac = findWebApplicationContext();}if (wac == null) {// No context instance is defined for this servlet -> create a local onewac = 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.onRefresh(wac);}if (this.publishContext) {// Publish the context as a servlet context attribute.String attrName = getServletContextAttributeName();getServletContext().setAttribute(attrName, wac);if (this.logger.isDebugEnabled()) {this.logger.debug("Published WebApplicationContext of servlet '" + getServletName() +"' as ServletContext attribute with name [" + attrName + "]");}}return wac; }其中有三步,1、第一步,判斷是不是在構造方法中傳遞webApplicationContext參數,ServletContext.addServlet方式注冊Servlet?
2、第二步,判斷是不是通過web.xml配置文件傳入webApplicationContext參數?
3、第三步,在前面兩部探索后都沒有找到webApplicationContext的情況下自己創建一個。?
這個過程由createWebApplicationContext方法來做,它內部又調用了configureAndRefreshWebApplicationContext方法完成,createWebApplicationContext代碼如下:
在其中我們看到的contextConfigLocation就是spring的配置文件地址,默認是WEB-INFO/[ServletName]-Servlet.xml。
再看一下configureAndRefreshWebApplicationContext的代碼:
protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) {if (ObjectUtils.identityToString(wac).equals(wac.getId())) {// The application context id is still set to its original default value// -> assign a more useful id based on available informationif (this.contextId != null) {wac.setId(this.contextId);}else {// Generate default id...wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +ObjectUtils.getDisplayString(getServletContext().getContextPath()) + "/" + getServletName());}}wac.setServletContext(getServletContext());wac.setServletConfig(getServletConfig());wac.setNamespace(getNamespace());wac.addApplicationListener(new SourceFilteringListener(wac, new ContextRefreshListener()));// The wac environment's #initPropertySources will be called in any case when the context// is refreshed; do it eagerly here to ensure servlet property sources are in place for// use in any post-processing or initialization that occurs below prior to #refreshConfigurableEnvironment env = wac.getEnvironment();if (env instanceof ConfigurableWebEnvironment) {((ConfigurableWebEnvironment) env).initPropertySources(getServletContext(), getServletConfig());}postProcessWebApplicationContext(wac);applyInitializers(wac);wac.refresh(); }有一句要注意一下:
wac.addApplicationListener(new SourceFilteringListener(wac, new ContextRefreshListener()));ContextRefreshListener是FrameworkServlet的內部類,監聽ContextRefreshedEvent事件,當接收到消息時調用FrameworkServlet的onApplicationEvent方法,在onApplicationEvent中會調用一次onRefresh方法,將refreshEventReceived標志設置為true,表示refresh過。
private class ContextRefreshListener implements ApplicationListener<ContextRefreshedEvent> {@Overridepublic void onApplicationEvent(ContextRefreshedEvent event) {FrameworkServlet.this.onApplicationEvent(event);} } public void onApplicationEvent(ContextRefreshedEvent event) {this.refreshEventReceived = true;onRefresh(event.getApplicationContext()); }注意看之前的initWebApplicationContext方法,方法較后的位置會判斷根據refreshEventReceived標志來判斷是否要運行onRefresh,?
第一種方法已經調用了configureAndRefreshWebApplicationContext,就是已經refresh過了,第三種方法也是,所以只有第二種需要重新運行它。?
DispatcherServlet就是通過重寫onRefresh模版方法實現初始化的。
下面我們看DispatcherServlet中重寫的onRefresh方法代碼,如下:
@Override protected void onRefresh(ApplicationContext context) {initStrategies(context); }再看initStrategies
protected void initStrategies(ApplicationContext context) {initMultipartResolver(context);initLocaleResolver(context);initThemeResolver(context);initHandlerMappings(context);initHandlerAdapters(context);initHandlerExceptionResolvers(context);initRequestToViewNameTranslator(context);initViewResolvers(context);initFlashMapManager(context); }任意取一個,如initLocaleResolver,其他8種也是類似的。
private void initLocaleResolver(ApplicationContext context) {try {this.localeResolver = context.getBean(LOCALE_RESOLVER_BEAN_NAME, LocaleResolver.class);if (logger.isDebugEnabled()) {logger.debug("Using LocaleResolver [" + this.localeResolver + "]");}}catch (NoSuchBeanDefinitionException ex) {// We need to use the default.this.localeResolver = getDefaultStrategy(context, LocaleResolver.class);if (logger.isDebugEnabled()) {logger.debug("Unable to locate LocaleResolver with name '" + LOCALE_RESOLVER_BEAN_NAME +"': using default [" + this.localeResolver + "]");}} }我們可以看到它調用了getDefaultStrategy方法,getDefaultStrategy又調用了getDefaultStrategies方法,兩個一起列出來
protected <T> T getDefaultStrategy(ApplicationContext context, Class<T> strategyInterface) {List<T> strategies = getDefaultStrategies(context, strategyInterface);if (strategies.size() != 1) {throw new BeanInitializationException("DispatcherServlet needs exactly 1 strategy for interface [" + strategyInterface.getName() + "]");}return strategies.get(0); }protected <T> List<T> getDefaultStrategies(ApplicationContext context, Class<T> strategyInterface) {String key = strategyInterface.getName();String value = defaultStrategies.getProperty(key);if (value != null) {String[] classNames = StringUtils.commaDelimitedListToStringArray(value);List<T> strategies = new ArrayList<T>(classNames.length);for (String className : classNames) {try {Class<?> clazz = ClassUtils.forName(className, DispatcherServlet.class.getClassLoader());Object strategy = createDefaultStrategy(context, clazz);strategies.add((T) strategy);}catch (ClassNotFoundException ex) {throw new BeanInitializationException("Could not find DispatcherServlet's default strategy class [" + className +"] for interface [" + key + "]", ex);}catch (LinkageError err) {throw new BeanInitializationException("Error loading DispatcherServlet's default strategy class [" + className +"] for interface [" + key + "]: problem with class file or dependent class", err);}}return strategies;}else {return new LinkedList<T>();}分析getDefaultStragedies,可以發現,數據轉換的關鍵在于傳入key,根據defaultStrategies.getProperty(key); 得到value,在ide里可以看出來defaultStrategies是一個靜態變量,類型是properties,它通過一個靜態塊進行初始化,代碼如下:
private static final String DEFAULT_STRATEGIES_PATH = "DispatcherServlet.properties"; private static final Properties defaultStrategies; static {// Load default strategy implementations from properties file.// This is currently strictly internal and not meant to be customized// by application developers.try {ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, DispatcherServlet.class);defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);}catch (IOException ex) {throw new IllegalStateException("Could not load 'DispatcherServlet.properties': " + ex.getMessage());} }這里可以看出來這個變量通過讀取DEFAULT_STRATEGIES_PATH路徑下property文件來進行值得初始化,DEFAULT_STRATEGIES_PATH得代碼我也順便加在上面了,它在spring-beans-4.2.5.RELEASE-sources.jar里,位置就在org.springframework.web.servlet.DispatcherServlet.properties,內容如下:
# Default implementation classes for DispatcherServlet's strategy interfaces. # Used as fallback when no matching beans are found in the DispatcherServlet context. # Not meant to be customized by application developers.org.springframework.web.servlet.LocaleResolver=org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolverorg.springframework.web.servlet.ThemeResolver=org.springframework.web.servlet.theme.FixedThemeResolverorg.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMappingorg.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapterorg.springframework.web.servlet.HandlerExceptionResolver=org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerExceptionResolver,\org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver,\org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolverorg.springframework.web.servlet.RequestToViewNameTranslator=org.springframework.web.servlet.view.DefaultRequestToViewNameTranslatororg.springframework.web.servlet.ViewResolver=org.springframework.web.servlet.view.InternalResourceViewResolverorg.springframework.web.servlet.FlashMapManager=org.springframework.web.servlet.support.SessionFlashMapManager默認配置在相應類型沒有配置時才會使用,如當使用< mvc:annotation-driven />后,并不會全部使用默認配置,因為它配置了HandlerMapping\HandlerAdapter和Handler-ExceptionResolver。?
那總的來說,?
HttpServletBean直接繼承自Java的HttpServlet,作用是將Servlet中配置的參數設置到相應的屬性;FrameworkServlet初始化了WebApplicationContext,DispatcherServlet初始化了自身的9個組件。?
FrameworkServlet初始化WebApplicationContext一共有三種方式,過程中使用了Servlet中配置的一些參數。
轉載于:https://www.cnblogs.com/fupengpeng/p/7382634.html
總結
以上是生活随笔為你收集整理的SpringMVC源代码学习(一)从HTttpServletBean到DispatcherServlet的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 文档模式引起的浏览器兼容问题
- 下一篇: CSU 1328 近似回文词【最长回文字