zoukankan      html  css  js  c++  java
  • 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>
  • 相关阅读:
    Python 企业面试题集锦之Python基础
    Jmeter 线程之间传递参数
    Jmeter 5.1参数化csv引入文件
    Jmeter 5.1命令行执行bat文件
    Idea JAVA項目的导入JAR包和导出jar包
    charles 设置弱网测试
    Jmeter_Beanshell 返回值中提取参数值
    在Notepad++里配置python环境
    python json格式参数遍历所有key、value 及替换key对于的value
    python 使用yaml模块
  • 原文地址:https://www.cnblogs.com/toUpdating/p/9846873.html
Copyright © 2011-2022 走看看