spring beanFactory 与 tomcat启动

  前面我们已经了解到Spring IoC容器初始化是调用AbstractApplicationContext.refresh()方法

 1     public void refresh() throws BeansException, IllegalStateException {
 2         synchronized (this.startupShutdownMonitor) {
 3             // Prepare this context for refreshing.
 4             prepareRefresh();
 5 
 6             // Tell the subclass to refresh the internal bean factory.
 7             ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
 8 
 9             // Prepare the bean factory for use in this context.
10             prepareBeanFactory(beanFactory);
11 
12             try {
13                 // Allows post-processing of the bean factory in context subclasses.
14                 postProcessBeanFactory(beanFactory);
15 
16                 // Invoke factory processors registered as beans in the context.
17                 invokeBeanFactoryPostProcessors(beanFactory);
18 
19                 // Register bean processors that intercept bean creation.
20                 registerBeanPostProcessors(beanFactory);
21 
22                 // Initialize message source for this context.
23                 initMessageSource();
24 
25                 // Initialize event multicaster for this context.
26                 initApplicationEventMulticaster();
27 
28                 // Initialize other special beans in specific context subclasses.
29                 onRefresh();
30 
31                 // Check for listener beans and register them.
32                 registerListeners();
33 
34                 // Instantiate all remaining (non-lazy-init) singletons.
35                 finishBeanFactoryInitialization(beanFactory);
36 
37                 // Last step: publish corresponding event.
38                 finishRefresh();
39             }
40 
41             catch (BeansException ex) {
42                 if (logger.isWarnEnabled()) {
43                     logger.warn("Exception encountered during context initialization - " +
44                             "cancelling refresh attempt: " + ex);
45                 }
46 
47                 // Destroy already created singletons to avoid dangling resources.
48                 destroyBeans();
49 
50                 // Reset 'active' flag.
51                 cancelRefresh(ex);
52 
53                 // Propagate exception to caller.
54                 throw ex;
55             }
56         }
57     }

那么,spring是在什么时候初始化BeanFactory容器呢?

  一,当使用tomcat时,在web.xml中会配置servlet

1 <servlet>
2         <servlet-name>service</servlet-name>
3         <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
4         <load-on-startup>1</load-on-startup>
5     </servlet>

 其中 tomcat启动时加载web.xml中servlet,

 

对spring DispatcherServlet来说,类层次如下

在HttpServletBean.init()方法中

 1     @Override
 2     public final void init() throws ServletException {
 3         if (logger.isDebugEnabled()) {
 4             logger.debug("Initializing servlet '" + getServletName() + "'");
 5         }
 6 
 7         // Set bean properties from init parameters.
 8         try {
 9             PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
10             BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
11             ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
12             bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
13             initBeanWrapper(bw);
14             bw.setPropertyValues(pvs, true);
15         }
16         catch (BeansException ex) {
17             logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);
18             throw ex;
19         }
20 
21         // Let subclasses do whatever initialization they like.
22         initServletBean();
23 
24         if (logger.isDebugEnabled()) {
25             logger.debug("Servlet '" + getServletName() + "' configured successfully");
26         }
27     }

在FrameWorkServlet.initServletBean()方法和initWebApplicationContext()中,用到了refresh创建BeanFactory

 1     @Override
 2     protected final void initServletBean() throws ServletException {
 3         getServletContext().log("Initializing Spring FrameworkServlet '" + getServletName() + "'");
 4         if (this.logger.isInfoEnabled()) {
 5             this.logger.info("FrameworkServlet '" + getServletName() + "': initialization started");
 6         }
 7         long startTime = System.currentTimeMillis();
 8 
 9         try {
10             this.webApplicationContext = initWebApplicationContext();
11             initFrameworkServlet();
12         }
13         catch (ServletException ex) {
14             this.logger.error("Context initialization failed", ex);
15             throw ex;
16         }
17         catch (RuntimeException ex) {
18             this.logger.error("Context initialization failed", ex);
19             throw ex;
20         }
21 
22         if (this.logger.isInfoEnabled()) {
23             long elapsedTime = System.currentTimeMillis() - startTime;
24             this.logger.info("FrameworkServlet '" + getServletName() + "': initialization completed in " +
25                     elapsedTime + " ms");
26         }
27     }
28 
29     /**
30      * Initialize and publish the WebApplicationContext for this servlet.
31      * <p>Delegates to {@link #createWebApplicationContext} for actual creation
32      * of the context. Can be overridden in subclasses.
33      * @return the WebApplicationContext instance
34      * @see #FrameworkServlet(WebApplicationContext)
35      * @see #setContextClass
36      * @see #setContextConfigLocation
37      */
38     protected WebApplicationContext initWebApplicationContext() {
39         WebApplicationContext rootContext =
40                 WebApplicationContextUtils.getWebApplicationContext(getServletContext());
41         WebApplicationContext wac = null;
42 
43         if (this.webApplicationContext != null) {
44             // A context instance was injected at construction time -> use it
45             wac = this.webApplicationContext;
46             if (wac instanceof ConfigurableWebApplicationContext) {
47                 ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
48                 if (!cwac.isActive()) {
49                     // The context has not yet been refreshed -> provide services such as
50                     // setting the parent context, setting the application context id, etc
51                     if (cwac.getParent() == null) {
52                         // The context instance was injected without an explicit parent -> set
53                         // the root application context (if any; may be null) as the parent
54                         cwac.setParent(rootContext);
55                     }
56                     configureAndRefreshWebApplicationContext(cwac);
57                 }
58             }
59         }
60         if (wac == null) {
61             // No context instance was injected at construction time -> see if one
62             // has been registered in the servlet context. If one exists, it is assumed
63             // that the parent context (if any) has already been set and that the
64             // user has performed any initialization such as setting the context id
65             wac = findWebApplicationContext();
66         }
67         if (wac == null) {
68             // No context instance is defined for this servlet -> create a local one
69             wac = createWebApplicationContext(rootContext);
70         }
71 
72         if (!this.refreshEventReceived) {
73             // Either the context is not a ConfigurableApplicationContext with refresh
74             // support or the context injected at construction time had already been
75             // refreshed -> trigger initial onRefresh manually here.
76             onRefresh(wac);
77         }
78 
79         if (this.publishContext) {
80             // Publish the context as a servlet context attribute.
81             String attrName = getServletContextAttributeName();
82             getServletContext().setAttribute(attrName, wac);
83             if (this.logger.isDebugEnabled()) {
84                 this.logger.debug("Published WebApplicationContext of servlet '" + getServletName() +
85                         "' as ServletContext attribute with name [" + attrName + "]");
86             }
87         }
88 
89         return wac;
90     }

当不配置在web.xml中时,只有当调用到相应servlet时,才会加载servlet

  二, 当使用spring-boot时,

1 public class HelloApplication {
2 
3     public static void main(String[] args) {
4         SpringApplication.run(HelloApplication.class, args);
5     }
6 
7 }
 1     public ConfigurableApplicationContext run(String... args) {
 2         StopWatch stopWatch = new StopWatch();
 3         stopWatch.start();
 4         ConfigurableApplicationContext context = null;
 5         configureHeadlessProperty();
 6         SpringApplicationRunListeners listeners = getRunListeners(args);
 7         listeners.started();
 8         try {
 9             ApplicationArguments applicationArguments = new DefaultApplicationArguments(
10                     args);
11             context = createAndRefreshContext(listeners, applicationArguments);
12             afterRefresh(context, applicationArguments);
13             listeners.finished(context, null);
14             stopWatch.stop();
15             if (this.logStartupInfo) {
16                 new StartupInfoLogger(this.mainApplicationClass)
17                         .logStarted(getApplicationLog(), stopWatch);
18             }
19             return context;
20         }
21         catch (Throwable ex) {
22             handleRunFailure(context, listeners, ex);
23             throw new IllegalStateException(ex);
24         }
25     }

创建bean容器

 1     private ConfigurableApplicationContext createAndRefreshContext(
 2             SpringApplicationRunListeners listeners,
 3             ApplicationArguments applicationArguments) {
 4         ConfigurableApplicationContext context;
 5         // Create and configure the environment
 6         ConfigurableEnvironment environment = getOrCreateEnvironment();
 7         configureEnvironment(environment, applicationArguments.getSourceArgs());
 8         listeners.environmentPrepared(environment);
 9         if (isWebEnvironment(environment) && !this.webEnvironment) {
10             environment = convertToStandardEnvironment(environment);
11         }
12 
13         if (this.bannerMode != Banner.Mode.OFF) {
14             printBanner(environment);
15         }
16 
17         // Create, load, refresh and run the ApplicationContext
18         context = createApplicationContext();
19         context.setEnvironment(environment);
20         postProcessApplicationContext(context);
21         applyInitializers(context);
22         listeners.contextPrepared(context);
23         if (this.logStartupInfo) {
24             logStartupInfo(context.getParent() == null);
25             logStartupProfileInfo(context);
26         }
27 
28         // Add boot specific singleton beans
29         context.getBeanFactory().registerSingleton("springApplicationArguments",
30                 applicationArguments);
31 
32         // Load the sources
33         Set<Object> sources = getSources();
34         Assert.notEmpty(sources, "Sources must not be empty");
35         load(context, sources.toArray(new Object[sources.size()]));
36         listeners.contextLoaded(context);
37 
38         // Refresh the context
39         refresh(context);
40         if (this.registerShutdownHook) {
41             try {
42                 context.registerShutdownHook();
43             }
44             catch (AccessControlException ex) {
45                 // Not allowed in some environments.
46             }
47         }
48         return context;
49     }

 这时候,项目可以不是web servlet项目,不依赖tomcat,一个纯的spring bean容器,通过dubbo netty实现调用

销毁

Servlet销毁前会调用destroy()方法,tomcat如下情况会调用destroy()

  1)  修改 Servlet,触发重新编译加载,调用栈如下

 1     public Servlet getServlet() throws ServletException {
 2         /*
 3          * DCL on 'reload' requires that 'reload' be volatile
 4          * (this also forces a read memory barrier, ensuring the new servlet
 5          * object is read consistently).
 6          *
 7          * When running in non development mode with a checkInterval it is
 8          * possible (see BZ 62603) for a race condition to cause failures
 9          * if a Servlet or tag is reloaded while a compile check is running
10          */
11         if (getReloadInternal()) {
12             synchronized (this) {
13                 // Synchronizing on jsw enables simultaneous loading
14                 // of different pages, but not the same page.
15                 if (getReloadInternal()) {
16                     // This is to maintain the original protocol.
17                     destroy();
18 
19                     final Servlet servlet;
20 
21                     try {
22                         InstanceManager instanceManager = InstanceManagerFactory.getInstanceManager(config);
23                         servlet = (Servlet) instanceManager.newInstance(ctxt.getFQCN(), ctxt.getJspLoader());
24                     } catch (Exception e) {
25                         Throwable t = ExceptionUtils
26                                 .unwrapInvocationTargetException(e);
27                         ExceptionUtils.handleThrowable(t);
28                         throw new JasperException(t);
29                     }
30 
31                     servlet.init(config);
32 
33                     if (!firstTime) {
34                         ctxt.getRuntimeContext().incrementJspReloadCount();
35                     }
36 
37                     theServlet = servlet;
38                     reload = false;
39                     // Volatile 'reload' forces in order write of 'theServlet' and new servlet object
40                 }
41             }
42         }
43         return theServlet;
44     }
JspServletWrapper.getServlet
  • when the container shuts down or the application shuts down;
  • when the container decides that there is a shortage of memory;
  • when this servlet hasn't got a request in a long time.

 Servlet destory()

 1 /**
 2      * Called by the servlet container to indicate to a servlet that the servlet
 3      * is being taken out of service. This method is only called once all
 4      * threads within the servlet's <code>service</code> method have exited or
 5      * after a timeout period has passed. After the servlet container calls this
 6      * method, it will not call the <code>service</code> method again on this
 7      * servlet.
 8      * 
 9      * <p>
10      * This method gives the servlet an opportunity to clean up any resources
11      * that are being held (for example, memory, file handles, threads) and make
12      * sure that any persistent state is synchronized with the servlet's current
13      * state in memory.
14      */
15     public void destroy();

注:还可以通过ContextLoaderListener方式实现spring ioc启动

<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring.xml</param-value>
</context-param>
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
原文地址:https://www.cnblogs.com/toUpdating/p/9846873.html