日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 前端技术 > javascript >内容正文

javascript

SpringMVC源代码学习(一)从HTttpServletBean到DispatcherServlet

發(fā)布時(shí)間:2025/3/15 javascript 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 SpringMVC源代码学习(一)从HTttpServletBean到DispatcherServlet 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

以下內(nèi)容基于書:《看透SpringMVC-源代碼分析與實(shí)踐》基本照搬。。。用于自己查閱備忘。

觀察DispatcherServlet繼承樹?
?
做Java?web的應(yīng)該都知道GenericServlet到HttpServlet的關(guān)系,它們都是 javax.servlet;包下的。?
從HttpServletBean開始就是由spring定義的了。

我們知道HttpServlet有一個(gè)無參的init()方法交由子類實(shí)現(xiàn),用于類的初始化,那我們從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中配置的參數(shù)使用BeanWrapper設(shè)置到DispatcherServlet的相關(guān)屬性,然后調(diào)用模版方法initServletBean,

子類,也就是FrameworkServlet通過定義自己的initServletBean定義了自己的初始化內(nèi)容。

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,這個(gè)方法沒有內(nèi)容,是供給子類調(diào)用的模版方法。

2、initWebApplicationContext方法,詳細(xì)代碼如下:

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、第一步,判斷是不是在構(gòu)造方法中傳遞webApplicationContext參數(shù),ServletContext.addServlet方式注冊(cè)Servlet?
2、第二步,判斷是不是通過web.xml配置文件傳入webApplicationContext參數(shù)?
3、第三步,在前面兩部探索后都沒有找到webApplicationContext的情況下自己創(chuàng)建一個(gè)。?
這個(gè)過程由createWebApplicationContext方法來做,它內(nèi)部又調(diào)用了configureAndRefreshWebApplicationContext方法完成,createWebApplicationContext代碼如下:

protected WebApplicationContext createWebApplicationContext(ApplicationContext parent) {Class<?> contextClass = getContextClass();if (this.logger.isDebugEnabled()) {this.logger.debug("Servlet with name '" + getServletName() +"' will try to create custom WebApplicationContext context of class '" +contextClass.getName() + "'" + ", using parent context [" + parent + "]");}if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {throw new ApplicationContextException("Fatal initialization error in servlet with name '" + getServletName() +"': custom WebApplicationContext class [" + contextClass.getName() +"] is not of type ConfigurableWebApplicationContext");}ConfigurableWebApplicationContext wac =(ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);wac.setEnvironment(getEnvironment());wac.setParent(parent);wac.setConfigLocation(getContextConfigLocation());configureAndRefreshWebApplicationContext(wac);return wac; }

在其中我們看到的contextConfigLocation就是spring的配置文件地址,默認(rèn)是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的內(nèi)部類,監(jiān)聽ContextRefreshedEvent事件,當(dāng)接收到消息時(shí)調(diào)用FrameworkServlet的onApplicationEvent方法,在onApplicationEvent中會(huì)調(diào)用一次onRefresh方法,將refreshEventReceived標(biāo)志設(shè)置為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方法,方法較后的位置會(huì)判斷根據(jù)refreshEventReceived標(biāo)志來判斷是否要運(yùn)行onRefresh,?
第一種方法已經(jīng)調(diào)用了configureAndRefreshWebApplicationContext,就是已經(jīng)refresh過了,第三種方法也是,所以只有第二種需要重新運(yùn)行它。?
DispatcherServlet就是通過重寫onRefresh模版方法實(shí)現(xiàn)初始化的。

下面我們看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); }

任意取一個(gè),如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 + "]");}} }

我們可以看到它調(diào)用了getDefaultStrategy方法,getDefaultStrategy又調(diào)用了getDefaultStrategies方法,兩個(gè)一起列出來

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,可以發(fā)現(xiàn),數(shù)據(jù)轉(zhuǎn)換的關(guān)鍵在于傳入key,根據(jù)defaultStrategies.getProperty(key); 得到value,在ide里可以看出來defaultStrategies是一個(gè)靜態(tài)變量,類型是properties,它通過一個(gè)靜態(tài)塊進(jìn)行初始化,代碼如下:

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());} }

這里可以看出來這個(gè)變量通過讀取DEFAULT_STRATEGIES_PATH路徑下property文件來進(jìn)行值得初始化,DEFAULT_STRATEGIES_PATH得代碼我也順便加在上面了,它在spring-beans-4.2.5.RELEASE-sources.jar里,位置就在org.springframework.web.servlet.DispatcherServlet.properties,內(nèi)容如下:

# 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

默認(rèn)配置在相應(yīng)類型沒有配置時(shí)才會(huì)使用,如當(dāng)使用< mvc:annotation-driven />后,并不會(huì)全部使用默認(rèn)配置,因?yàn)樗渲昧薍andlerMapping\HandlerAdapter和Handler-ExceptionResolver。?
那總的來說,?
HttpServletBean直接繼承自Java的HttpServlet,作用是將Servlet中配置的參數(shù)設(shè)置到相應(yīng)的屬性;FrameworkServlet初始化了WebApplicationContext,DispatcherServlet初始化了自身的9個(gè)組件。?
FrameworkServlet初始化WebApplicationContext一共有三種方式,過程中使用了Servlet中配置的一些參數(shù)。

轉(zhuǎn)載于:https://www.cnblogs.com/fupengpeng/p/7382634.html

總結(jié)

以上是生活随笔為你收集整理的SpringMVC源代码学习(一)从HTttpServletBean到DispatcherServlet的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。