简体   繁体   English

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

[英]Context confusion - Spring instantiating singleton beans twice

I'm doing my Spring configuration programatically. 我正在以编程方式进行Spring配置。 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正在生成我的单身豆子两次。

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\9.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\9.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$$84ad5432] 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

Singleton guarantees single instance inside Spring container. Singleton保证Spring容器内的单个实例。 That mean that you could have as many singleton instances as there are containers (even if they use the same JVM class loader). 这意味着您可以拥有与容器一样多的singleton实例(即使它们使用相同的JVM类加载器)。 See The singleton scope and this answer for Singleton Design Pattern vs. Singleton Bean 请参阅单例范围Singleton Design Pattern与Singleton 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 {}

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 如果在扩展的WebAppInitalizer中扩展AbstractAnnotationConfigDispatcherServletInitializer类确保

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM