java 上下文混淆 - Spring 两次实例化单例 bean

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/15060418/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-31 18:21:31  来源:igfitidea点击:

Context confusion - Spring instantiating singleton beans twice

javaspringservletsspring-mvc

提问by Sotirios Delimanolis

I'm doing my Spring configuration programatically. I'm not getting the injection results I'm expecting so I looked at logs and for some reason Spring is generating my singleton beans twice.

我正在以编程方式进行 Spring 配置。我没有得到我期望的注入结果,所以我查看了日志,出于某种原因,Spring 两次生成我的单例 bean。

I get this in Tomcat start-up logs

我在 Tomcat 启动日志中得到了这个

INFO: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@74b1128c: defining beans <LIST OF BEANS>

...little further

INFO: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@61de76d0: defining beans <SAME LIST OF BEANS>

Is this normal behavior?

这是正常行为吗?

This is my Application Context

这是我的应用程序上下文

@Configuration
@EnableTransactionManagement
@ComponentScan(basePackages = {"com.application.shiro",
                   "com.business.dao.impl", 
                   "com.business.services"})
public class AppRootContext {    
    /* Beans */
}

This is my Servlet Context

这是我的 Servlet 上下文

@Configuration
@EnableWebMvc
@ComponentScan(basePackages = {"com.application.controllers"})
public class AppServletContext extends WebMvcConfigurerAdapter {
    /* Beans */
}

And my Servlet Initializer class called BidAppInitializer

我的 Servlet Initializer 类称为 BidAppInitializer

@Override
public void onStartup(ServletContext container) throws ServletException {
    AnnotationConfigWebApplicationContext rootContext = new AnnotationConfigWebApplicationContext();
    rootContext.register(AppRootContext.class);

    container.addListener(new ContextLoaderListener(rootContext));

    AnnotationConfigWebApplicationContext servletContext = new AnnotationConfigWebApplicationContext();
    servletContext.register(AppServletContext.class);

    ServletRegistration.Dynamic appServlet = container.addServlet("bidapp", new DispatcherServlet(servletContext));
    appServlet.setLoadOnStartup(1);
    appServlet.addMapping("/");
}

This is my full Tomcat start-up log:

这是我完整的 Tomcat 启动日志:

Feb 26, 2013 11:32:21 PM org.apache.catalina.core.AprLifecycleListener init
INFO: The APR based Apache Tomcat Native library which allows optimal performance in production environments was not found on the java.library.path: C:\Program Files\Java\jre7\bin;C:\Windows\Sun\Java\bin;C:\Windows\system32;C:\Windows;C:\Program Files (x86)\NVIDIA Corporation\PhysX\Common;C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Windows\System32\WindowsPowerShell\v1.0\;C:\Program Files\MATLAB\R2010b\runtime\win64;C:\Program Files\MATLAB\R2010b\bin;C:\Users\Soto\Desktop\android-sdk-windows\tools;C:\Program Files (x86)\Common Files\Teleca Shared;C:\Program Files\TortoiseSVN\bin;C:\Program Files (x86)\QuickTime\QTSystem\;C:\Program Files\TortoiseHg\;C:\MinGW\bin;C:\Program Files\nodejs\; C:\PostgreSQL.2\bin\;C:\Program Files\MySQL\MySQL Server 5.5\bin;C:\MySQL\MySQL Server 5.5\bin;C:\Ruby193\bin;C:\Program Files (x86)\SSH Communications Security\SSH Secure Shell;C:\Users\Soto\Desktop\android-sdk-windows\tools;C:\MinGW\bin;C:\Users\Soto\AppData\Roaming\npm\;C:\apache-maven-3.0.4\bin;C:\Program Files\Java\jdk1.7.0_02\bin;C:\PostgreSQL.2\bin;.
Feb 26, 2013 11:32:21 PM org.apache.tomcat.util.digester.SetPropertiesRule begin
WARNING: [SetPropertiesRule]{Server/Service/Engine/Host/Context} Setting property 'source' to 'org.eclipse.jst.j2ee.server:BidApp' did not find a matching property.
Feb 26, 2013 11:32:21 PM org.apache.coyote.AbstractProtocol init
INFO: Initializing ProtocolHandler ["http-bio-8080"]
Feb 26, 2013 11:32:21 PM org.apache.coyote.AbstractProtocol init
INFO: Initializing ProtocolHandler ["ajp-bio-8009"]
Feb 26, 2013 11:32:21 PM org.apache.catalina.startup.Catalina load
INFO: Initialization processed in 824 ms
Feb 26, 2013 11:32:21 PM org.apache.catalina.core.StandardService startInternal
INFO: Starting service Catalina
Feb 26, 2013 11:32:21 PM org.apache.catalina.core.StandardEngine startInternal
INFO: Starting Servlet Engine: Apache Tomcat/7.0.22
Feb 26, 2013 11:32:29 PM org.apache.catalina.core.ApplicationContext log
INFO: Spring WebApplicationInitializers detected on classpath: [com.bidapp.bootstrap.BidAppInitializer@60515c64]
Feb 26, 2013 11:32:32 PM org.apache.catalina.core.ApplicationContext log
INFO: Initializing Spring root WebApplicationContext
Feb 26, 2013 11:32:32 PM org.springframework.web.context.ContextLoader initWebApplicationContext
INFO: Root WebApplicationContext: initialization started
Feb 26, 2013 11:32:32 PM org.springframework.context.support.AbstractApplicationContext prepareRefresh
INFO: Refreshing Root WebApplicationContext: startup date [Tue Feb 26 23:32:32 EST 2013]; root of context hierarchy
Feb 26, 2013 11:32:33 PM org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider registerDefaultFilters
INFO: JSR-330 'javax.inject.Named' annotation found and supported for component scanning
Feb 26, 2013 11:32:33 PM org.springframework.web.context.support.AnnotationConfigWebApplicationContext loadBeanDefinitions
INFO: Registering annotated classes: [class com.bidapp.bootstrap.AppRootContext]
Feb 26, 2013 11:32:33 PM org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider registerDefaultFilters
INFO: JSR-330 'javax.inject.Named' annotation found and supported for component scanning
Feb 26, 2013 11:32:36 PM org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor <init>
INFO: JSR-330 'javax.inject.Inject' annotation found and supported for autowiring

SDASD <--- Logged by my Root Context class constructor

Feb 26, 2013 11:33:49 PM org.springframework.context.support.AbstractApplicationContext$BeanPostProcessorChecker postProcessAfterInitialization
INFO: Bean 'appRootContext' of type [class com.bidapp.bootstrap.AppRootContext$$EnhancerByCGLIB$ad5432] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
Feb 26, 2013 11:33:52 PM org.springframework.context.support.AbstractApplicationContext$BeanPostProcessorChecker postProcessAfterInitialization
INFO: Bean 'proxyAsyncConfiguration' of type [class org.springframework.scheduling.annotation.ProxyAsyncConfiguration$$EnhancerByCGLIB$$f08c92c2] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
Feb 26, 2013 11:33:54 PM org.springframework.beans.factory.support.DefaultListableBeanFactory preInstantiateSingletons
INFO: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@6393d737: defining beans [org.springframework.context.annotation.internalConfigurationAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalRequiredAnnotationProcessor,org.springframework.context.annotation.internalCommonAnnotationProcessor,org.springframework.context.annotation.internalPersistenceAnnotationProcessor,appRootContext,org.springframework.context.annotation.ConfigurationClassPostProcessor$ImportAwareBeanPostProcessor#0,accountActivationDaoHibernateImpl,accountDao,auctionDaoHibernateImpl,categoryDao,accountService,emailService,org.springframework.aop.config.internalAutoProxyCreator,proxyTransactionManagementConfiguration,org.springframework.transaction.config.internalTransactionAdvisor,transactionAttributeSource,transactionInterceptor,proxyAsyncConfiguration,org.springframework.context.annotation.internalAsyncAnnotationProcessor,securityManager,credentialsMatcher,sessionFactory,transactionManager,hibernateRealm,lifecycleBeanPostProcessor,webTemplateResolver,emailTemplateResolver,templateEngine,shiroFilter,taskExecutor,dataSource,mailSender,viewResolver]; root of factory hierarchy
Feb 26, 2013 11:33:56 PM com.mchange.v2.log.MLog <clinit>
INFO: MLog clients using java 1.4+ standard logging.
Feb 26, 2013 11:33:56 PM com.mchange.v2.c3p0.C3P0Registry banner
INFO: Initializing c3p0-0.9.1 [built 16-January-2007 14:46:42; debug? true; trace: 10]
Feb 26, 2013 11:34:45 PM com.mchange.v2.c3p0.impl.AbstractPoolBackedDataSource getPoolManager
INFO: Initializing c3p0 pool... com.mchange.v2.c3p0.ComboPooledDataSource [ acquireIncrement -> 10, acquireRetryAttempts -> 30, acquireRetryDelay -> 1000, autoCommitOnClose -> false, automaticTestTable -> null, breakAfterAcquireFailure -> false, checkoutTimeout -> 0, connectionCustomizerClassName -> null, connectionTesterClassName -> com.mchange.v2.c3p0.impl.DefaultConnectionTester, dataSourceName -> 1hge15x8t76zmwq1uj2ed9|6cfb984, debugUnreturnedConnectionStackTraces -> false, description -> null, driverClass -> com.mysql.jdbc.Driver, factoryClassLocation -> null, forceIgnoreUnresolvedTransactions -> false, identityToken -> 1hge15x8t76zmwq1uj2ed9|6cfb984, idleConnectionTestPeriod -> 0, initialPoolSize -> 3, jdbcUrl -> jdbc:mysql://localhost:3306/bidapp, lastAcquisitionFailureDefaultUser -> null, maxAdministrativeTaskTime -> 0, maxConnectionAge -> 0, maxIdleTime -> 1800, maxIdleTimeExcessConnections -> 0, maxPoolSize -> 20, maxStatements -> 0, maxStatementsPerConnection -> 0, minPoolSize -> 5, numHelperThreads -> 3, numThreadsAwaitingCheckoutDefaultUser -> 0, preferredTestQuery -> null, properties -> {user=******, password=******}, propertyCycle -> 0, testConnectionOnCheckin -> false, testConnectionOnCheckout -> false, unreturnedConnectionTimeout -> 0, usesTraditionalReflectiveProxies -> false ]
Feb 26, 2013 11:35:33 PM org.springframework.orm.hibernate4.HibernateTransactionManager afterPropertiesSet
INFO: Using DataSource [com.mchange.v2.c3p0.ComboPooledDataSource [ acquireIncrement -> 10, acquireRetryAttempts -> 30, acquireRetryDelay -> 1000, autoCommitOnClose -> false, automaticTestTable -> null, breakAfterAcquireFailure -> false, checkoutTimeout -> 0, connectionCustomizerClassName -> null, connectionTesterClassName -> com.mchange.v2.c3p0.impl.DefaultConnectionTester, dataSourceName -> 1hge15x8t76zmwq1uj2ed9|6cfb984, debugUnreturnedConnectionStackTraces -> false, description -> null, driverClass -> com.mysql.jdbc.Driver, factoryClassLocation -> null, forceIgnoreUnresolvedTransactions -> false, identityToken -> 1hge15x8t76zmwq1uj2ed9|6cfb984, idleConnectionTestPeriod -> 0, initialPoolSize -> 3, jdbcUrl -> jdbc:mysql://localhost:3306/bidapp, lastAcquisitionFailureDefaultUser -> null, maxAdministrativeTaskTime -> 0, maxConnectionAge -> 0, maxIdleTime -> 1800, maxIdleTimeExcessConnections -> 0, maxPoolSize -> 20, maxStatements -> 0, maxStatementsPerConnection -> 0, minPoolSize -> 5, numHelperThreads -> 3, numThreadsAwaitingCheckoutDefaultUser -> 0, preferredTestQuery -> null, properties -> {user=******, password=******}, propertyCycle -> 0, testConnectionOnCheckin -> false, testConnectionOnCheckout -> false, unreturnedConnectionTimeout -> 0, usesTraditionalReflectiveProxies -> false ]] of Hibernate SessionFactory for HibernateTransactionManager
Feb 26, 2013 11:35:36 PM org.springframework.web.context.ContextLoader initWebApplicationContext
INFO: Root WebApplicationContext: initialization completed in 183726 ms
Feb 26, 2013 11:35:36 PM org.apache.catalina.core.ApplicationContext log
INFO: Initializing Spring FrameworkServlet 'bidapp'
Feb 26, 2013 11:35:36 PM org.springframework.web.servlet.FrameworkServlet initServletBean
INFO: FrameworkServlet 'bidapp': initialization started
Feb 26, 2013 11:35:36 PM org.springframework.context.support.AbstractApplicationContext prepareRefresh
INFO: Refreshing WebApplicationContext for namespace 'bidapp-servlet': startup date [Tue Feb 26 23:35:36 EST 2013]; parent: Root WebApplicationContext
Feb 26, 2013 11:35:36 PM org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider registerDefaultFilters
INFO: JSR-330 'javax.inject.Named' annotation found and supported for component scanning
Feb 26, 2013 11:35:36 PM org.springframework.web.context.support.AnnotationConfigWebApplicationContext loadBeanDefinitions
INFO: Registering annotated classes: [class com.bidapp.bootstrap.AppServletContext]
Feb 26, 2013 11:35:37 PM org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider registerDefaultFilters
INFO: JSR-330 'javax.inject.Named' annotation found and supported for component scanning
Feb 26, 2013 11:35:40 PM org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor <init>
INFO: JSR-330 'javax.inject.Inject' annotation found and supported for autowiring
Feb 26, 2013 11:35:42 PM org.springframework.beans.factory.support.DefaultListableBeanFactory preInstantiateSingletons
INFO: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@2c8ff601: defining beans [org.springframework.context.annotation.internalConfigurationAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalRequiredAnnotationProcessor,org.springframework.context.annotation.internalCommonAnnotationProcessor,org.springframework.context.annotation.internalPersistenceAnnotationProcessor,appServletContext,org.springframework.context.annotation.ConfigurationClassPostProcessor$ImportAwareBeanPostProcessor#0,accountController,auctionController,indexPagesController,delegatingWebMvcConfiguration,requestMappingHandlerMapping,viewControllerHandlerMapping,beanNameHandlerMapping,resourceHandlerMapping,defaultServletHandlerMapping,requestMappingHandlerAdapter,mvcConversionService,mvcValidator,httpRequestHandlerAdapter,simpleControllerHandlerAdapter,handlerExceptionResolver]; parent: org.springframework.beans.factory.support.DefaultListableBeanFactory@6393d737
Feb 26, 2013 11:35:52 PM org.springframework.web.servlet.handler.AbstractHandlerMethodMapping registerHandlerMethod
...Controller handler method mappings

回答by Petro Semeniuk

Singleton guarantees single instance inside Spring container. That mean that you could have as many singletoninstances as there are containers (even if they use the same JVM class loader). See The singleton scopeand this answer for Singleton Design Pattern vs. Singleton Bean

Singleton 保证 Spring 容器内的单个实例。这意味着您可以拥有与singleton容器一样多的实例(即使它们使用相同的 JVM 类加载器)。请参阅单例范围单例设计模式与单例 Bean 的答案

In your case you're instantiating two contexts - rootContext and servletContext and that were log statements are coming from.

在您的情况下,您正在实例化两个上下文 - rootContext 和 servletContext 并且它们是日志语句的来源。

If you want to have single instance of every singleton - you could merge these two contexts.

如果您想拥有每个单例的单个实例 - 您可以合并这两个上下文。



Small java class which illustrates such behavior(apologies for weird code formatting - wanted to make it more concise ):

说明这种行为的小型 java 类(对奇怪的代码格式表示歉意 - 想让它更简洁):

package singleton;

import java.util.UUID;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;

public class AppMain {

    public static void main(String[] args) {
        final AnnotationConfigApplicationContext rootContext = new AnnotationConfigApplicationContext();
        rootContext.register(AppRootContext.class);
        rootContext.refresh();

        final AnnotationConfigApplicationContext servletContext = new AnnotationConfigApplicationContext();
        servletContext.register(AppServletContext.class);
        servletContext.refresh();

        final Singleton one = (Singleton) rootContext.getBean("singleton");
        final Singleton andAnotherOne = (Singleton) servletContext.getBean("singleton");

        System.out.println("Singletons are " + (one.ID.equals(andAnotherOne.ID) ? "equal" : "different"));
    }
}

@Component
class Singleton {public String ID = UUID.randomUUID().toString();}

@Configuration
@ComponentScan(basePackages = { "singleton" })
class AppRootContext {}

@Configuration
@ComponentScan(basePackages = { "singleton" })
class AppServletContext {}

回答by Rahul Tokase

There are two types of context are there one is rootcontext and another is Web application context.

有两种类型的上下文,一种是 rootcontext,另一种是 Web 应用程序上下文。

If in WebAppInitalizer where you extends extends AbstractAnnotationConfigDispatcherServletInitializer class Make sure that

如果在扩展 AbstractAnnotationConfigDispatcherServletInitializer 类的 WebAppInitalizer 中,请确保

you override following methods and make sure that class definition are not repeated in the two method.

您覆盖以下方法并确保在两个方法中不重复类定义。

@Override protected Class[] getServletConfigClasses() { return new Class[] { MvcConfig.class}; }

@Override protected Class[] getServletConfigClasses() { return new Class[] { MvcConfig.class}; }

@Override
protected Class<?>[] getRootConfigClasses() {
    return new Class[] {RooTContextAppConfig.class };
}