本文这里环境是springboot 2.2.4.RELEASE。创建WebServer是在refresh方法的onRefresh方法中实现的。其也是refresh方法体系的一个重要步骤。
ServletWebServerApplicationContext的onRefresh方法。如下所示其首先调用父类的onRefresh方法初始化ThemeSource,然后调用createWebServer创建WebServer。
@Override protected void onRefresh() {
super.onRefresh(); try {
createWebServer(); } catch (Throwable ex) {
throw new ApplicationContextException("Unable to start web server", ex); } } //GenericWebApplicationContext @Override protected void onRefresh() {
this.themeSource = UiApplicationContextUtils.initThemeSource(this); }
【1】createWebServer
ServletWebServerApplicationContext的createWebServer方法如下。
private void createWebServer() {
WebServer webServer = this.webServer; // 获取的是GenericWebApplicationContext的servletContext ServletContext servletContext = getServletContext(); if (webServer == null && servletContext == null) {
// 本文环境获取的是tomcatServletWebServerFactory ServletWebServerFactory factory = getWebServerFactory(); this.webServer = factory.getWebServer(getSelfInitializer()); } else if (servletContext != null) {
try {
getSelfInitializer().onStartup(servletContext); } catch (ServletException ex) {
throw new ApplicationContextException("Cannot initialize servlet context", ex); } } initPropertySources(); }
关于initPropertySources();方法可以参考博文:Spring中refresh分析之onRefresh方法详解 。
① 获取WebServerFactory
如下所示,从容器中获取ServletWebServerFactory类型的bean,唯一一个,否则抛出异常。本文环境获取的是tomcatServletWebServerFactory。
protected ServletWebServerFactory getWebServerFactory() {
// Use bean names so that we don't consider the hierarchy String[] beanNames = getBeanFactory().getBeanNamesForType(ServletWebServerFactory.class); if (beanNames.length == 0) {
throw new ApplicationContextException("Unable to start ServletWebServerApplicationContext due to missing " + "ServletWebServerFactory bean."); } if (beanNames.length > 1) {
throw new ApplicationContextException("Unable to start ServletWebServerApplicationContext due to multiple " + "ServletWebServerFactory beans : " + StringUtils.arrayToCommaDelimitedString(beanNames)); } return getBeanFactory().getBean(beanNames[0], ServletWebServerFactory.class); }
② getSelfInitializer
ServletWebServerApplicationContext的getSelfInitializer方法,返回的是ServletContextInitializer。
private org.springframework.boot.web.servlet.ServletContextInitializer getSelfInitializer() {
return this::selfInitialize; }
看到this::selfInitialize是不是比较迷糊?典型的java8的lambda写法。我们看一下ServletContextInitializer 可能就明白了。
如下所示,其是一个函数式接口,只有一个onStartup方法。函数式接口(有且仅有一个抽象方法的接口)可以使用lambda式的写法。
@FunctionalInterface public interface ServletContextInitializer {
// 初始化过程中,使用给定的servlets、filters、listeners //context-params and attributes necessary配置ServletContext void onStartup(ServletContext servletContext) throws ServletException; }
this指的是AnnotationConfigServletWebServerApplicationContext,其继承于ServletWebServerApplicationContext
private void selfInitialize(ServletContext servletContext) throws ServletException {
prepareWebApplicationContext(servletContext); registerApplicationScope(servletContext); WebApplicationContextUtils.registerEnvironmentBeans(getBeanFactory(), servletContext); for (ServletContextInitializer beans : getServletContextInitializerBeans()) {
beans.onStartup(servletContext); } }
其实换成匿名类的写法则是:
new ServletContextInitializer() {
@Override public void onStartup(ServletContext servletContext) throws ServletException {
selfInitialize(servletContext); } };
【2】getWebServer
本文这里是TomcatServletWebServerFactory的getWebServer方法。
@Override public WebServer getWebServer(ServletContextInitializer... initializers) {
if (this.disableMBeanRegistry) {
// registry = new NoDescriptorRegistry(); Registry.disableRegistry(); } //实例化Tomcat Tomcat tomcat = new Tomcat(); //获取临时路径 C:\Users\12746\AppData\Local\Temp\tomcat..8188 File baseDir = (this.baseDirectory != null) ? this.baseDirectory : createTempDir("tomcat"); //设置基础路径 tomcat.setBaseDir(baseDir.getAbsolutePath()); //实例化Connector 并进行配置 Connector connector = new Connector(this.protocol); connector.setThrowOnFailure(true); //这里会实例化server service tomcat.getService().addConnector(connector); customizeConnector(connector); //对Connector做配置比如Protocol、URIEncoding tomcat.setConnector(connector); //这里会实例化Engine、Host tomcat.getHost().setAutoDeploy(false); configureEngine(tomcat.getEngine()); for (Connector additionalConnector : this.additionalTomcatConnectors) {
tomcat.getService().addConnector(additionalConnector); } prepareContext(tomcat.getHost(), initializers); return getTomcatWebServer(tomcat); }
getService
getService首先会触发getServer然后获取service。getServer如下所示会实例化Server并对其进行配置。
public Service getService() {
return getServer().findServices()[0]; } public Server getServer() {
if (server != null) {
return server; } System.setProperty("catalina.useNaming", "false"); // 实例化 server server = new StandardServer(); // 对basedir做处理 initBaseDir(); // Set configuration source ConfigFileLoader.setSource(new CatalinaBaseConfigurationSource(new File(basedir), null)); // 为server设置port和service server.setPort( -1 ); //实例化service Service service = new StandardService(); service.setName("Tomcat"); server.addService(service); return server; }
prepareContext
这里会实例化TomcatEmbeddedContext并对其进行配置。
protected void prepareContext(Host host, ServletContextInitializer[] initializers) {
File documentRoot = getValidDocumentRoot(); TomcatEmbeddedContext context = new TomcatEmbeddedContext(); if (documentRoot != null) {
context.setResources(new LoaderHidingResourceRoot(context)); } context.setName(getContextPath()); context.setDisplayName(getDisplayName()); context.setPath(getContextPath()); File docBase = (documentRoot != null) ? documentRoot : createTempDir("tomcat-docbase"); context.setDocBase(docBase.getAbsolutePath()); context.addLifecycleListener(new FixContextListener()); context.setParentClassLoader((this.resourceLoader != null) ? this.resourceLoader.getClassLoader() : ClassUtils.getDefaultClassLoader()); resetDefaultLocaleMapping(context); addLocaleMappings(context); context.setUseRelativeRedirects(false); try {
context.setCreateUploadTargets(true); } catch (NoSuchMethodError ex) {
// Tomcat is < 8.5.39. Continue. } configureTldSkipPatterns(context); WebappLoader loader = new WebappLoader(context.getParentClassLoader()); loader.setLoaderClass(TomcatEmbeddedWebappClassLoader.class.getName()); loader.setDelegate(true); context.setLoader(loader); if (isRegisterDefaultServlet()) {
addDefaultServlet(context); } if (shouldRegisterJspServlet()) {
addJspServlet(context); addJasperInitializer(context); } context.addLifecycleListener(new StaticResourceConfigurer(context)); ServletContextInitializer[] initializersToUse = mergeInitializers(initializers); host.addChild(context); configureContext(context, initializersToUse); postProcessContext(context); }
getTomcatWebServer
这个方法很简单,只是直接实例化了TomcatWebServer返回。其构造方法触发了initialize,这会引起后续一系列动作,包括tomcat.start。
protected TomcatWebServer getTomcatWebServer(Tomcat tomcat) {
return new TomcatWebServer(tomcat, getPort() >= 0); } public TomcatWebServer(Tomcat tomcat, boolean autoStart) {
Assert.notNull(tomcat, "Tomcat Server must not be null"); this.tomcat = tomcat; this.autoStart = autoStart; initialize(); }
【3】selfInitialize
获取到TomcatWebServer后,就触发了selfInitialize方法。这里servletContext其实是获取了ApplicationContext的一个门面/外观–ApplicationContextCade。
// ServletWebServerApplicationContext private void selfInitialize(ServletContext servletContext) throws ServletException {
prepareWebApplicationContext(servletContext); registerApplicationScope(servletContext); WebApplicationContextUtils.registerEnvironmentBeans(getBeanFactory(), servletContext); for (ServletContextInitializer beans : getServletContextInitializerBeans()) {
beans.onStartup(servletContext); } }
① prepareWebApplicationContext
ServletWebServerApplicationContext的prepareWebApplicationContext方法如下所示,简单来讲就是为servletContext设置根容器属性并为当前应用上下文ApplicationContext设置servletContext引用。
protected void prepareWebApplicationContext(ServletContext servletContext) {
//尝试从servletContext中获取rootContext Object rootContext = servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE); if (rootContext != null) {
if (rootContext == this) {
throw new IllegalStateException( "Cannot initialize context because there is already a root application context present - " + "check whether you have multiple ServletContextInitializers!"); } return; } Log logger = LogFactory.getLog(ContextLoader.class); // 这个日志是不是很熟悉?! servletContext.log("Initializing Spring embedded WebApplicationContext"); try {
//向servletContext设置属性 ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this); if (logger.isDebugEnabled()) {
logger.debug("Published root WebApplicationContext as ServletContext attribute with name [" + WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE + "]"); } // 为ApplicationContext设置servletContext引用 setServletContext(servletContext); if (logger.isInfoEnabled()) {
long elapsedTime = System.currentTimeMillis() - getStartupDate(); logger.info("Root WebApplicationContext: initialization completed in " + elapsedTime + " ms"); } } catch (RuntimeException | Error ex) {
logger.error("Context initialization failed", ex); servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, ex); throw ex; } }
② registerApplicationScope
ServletWebServerApplicationContext的registerApplicationScope方法如下所示,简单来讲就是(扩展)注册scope-application。这里会实例化一个ServletContextScope (包装了servletContext),然后注册到BeanFactory中并为servletContext设置属性。
private void registerApplicationScope(ServletContext servletContext) {
ServletContextScope appScope = new ServletContextScope(servletContext); // application getBeanFactory().registerScope(WebApplicationContext.SCOPE_APPLICATION, appScope); // Register as ServletContext attribute, for ContextCleanupListener to detect it. servletContext.setAttribute(ServletContextScope.class.getName(), appScope); }
我们在Spring中refresh分析之postProcessBeanFactory方法详解提到了request-RequestScope,session–SessionScope的注册,本文这里注册了application-ServletContextScope注册。
③ registerEnvironmentBeans
WebApplicationContextUtils的registerEnvironmentBeans方法。
public static void registerEnvironmentBeans(ConfigurableListableBeanFactory bf, @Nullable ServletContext sc) {
registerEnvironmentBeans(bf, sc, null); } public static void registerEnvironmentBeans(ConfigurableListableBeanFactory bf, @Nullable ServletContext servletContext, @Nullable ServletConfig servletConfig) {
//将servletContext作为单例注册容器 if (servletContext != null && !bf.containsBean(WebApplicationContext.SERVLET_CONTEXT_BEAN_NAME)) {
bf.registerSingleton(WebApplicationContext.SERVLET_CONTEXT_BEAN_NAME, servletContext); } // 将servletConfig 作为单例注册容器本文这里没有触发 if (servletConfig != null && !bf.containsBean(ConfigurableWebApplicationContext.SERVLET_CONFIG_BEAN_NAME)) {
bf.registerSingleton(ConfigurableWebApplicationContext.SERVLET_CONFIG_BEAN_NAME, servletConfig); } // String CONTEXT_PARAMETERS_BEAN_NAME = "contextParameters"; if (!bf.containsBean(WebApplicationContext.CONTEXT_PARAMETERS_BEAN_NAME)) {
Map<String, String> parameterMap = new HashMap<>(); if (servletContext != null) {
// 获取servletContextd的初始化参数 Enumeration<?> paramNameEnum = servletContext.getInitParameterNames(); while (paramNameEnum.hasMoreElements()) {
String paramName = (String) paramNameEnum.nextElement(); parameterMap.put(paramName, servletContext.getInitParameter(paramName)); } } // 本文这里servletConfig 为null if (servletConfig != null) {
// // 获取servletConfig的初始化参数 Enumeration<?> paramNameEnum = servletConfig.getInitParameterNames(); while (paramNameEnum.hasMoreElements()) {
String paramName = (String) paramNameEnum.nextElement(); parameterMap.put(paramName, servletConfig.getInitParameter(paramName)); } } // 将contextParameters作为单例注册到容器 bf.registerSingleton(WebApplicationContext.CONTEXT_PARAMETERS_BEAN_NAME, Collections.unmodifiableMap(parameterMap)); } // String CONTEXT_ATTRIBUTES_BEAN_NAME = "contextAttributes"; if (!bf.containsBean(WebApplicationContext.CONTEXT_ATTRIBUTES_BEAN_NAME)) {
Map<String, Object> attributeMap = new HashMap<>(); if (servletContext != null) {
Enumeration<?> attrNameEnum = servletContext.getAttributeNames(); while (attrNameEnum.hasMoreElements()) {
String attrName = (String) attrNameEnum.nextElement(); attributeMap.put(attrName, servletContext.getAttribute(attrName)); } } // 将contextAttributes作为单例注册到容器 bf.registerSingleton(WebApplicationContext.CONTEXT_ATTRIBUTES_BEAN_NAME, Collections.unmodifiableMap(attributeMap)); } }
④ 触发ServletContextInitializer的onStartup
如下所示,这里会获取ServletContextInitializer的所有实例,遍历触发其onStartup方法。
for (ServletContextInitializer beans : getServletContextInitializerBeans()) {
beans.onStartup(servletContext); }
发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/201654.html原文链接:https://javaforall.net
