2013-02-25 54 views
8

我以编程方式执行我的Spring配置。我没有得到我期待的注射结果,所以我看着日志,由于某种原因,Spring正在生成我的单身豆两次。上下文混淆 - Spring实例化单例bean两次

我在Tomcat中获得此启动日志

INFO: Pre-instantiating singletons in org.s[email protected]74b1128c: defining beans <LIST OF BEANS> 

...little further 

INFO: Pre-instantiating singletons in org.s[email protected]61de76d0: defining beans <SAME LIST OF BEANS> 

这是正常的行为呢?

这是我的应用程序上下文

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

这是我的servlet上下文

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

而我叫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("/"); 
} 

这是我完全的Tomcat的Servlet初始化器类启动日志:

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: [[email protected]] 
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.s[email protected]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.s[email protected]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.s[email protected]6393d737 
Feb 26, 2013 11:35:52 PM org.springframework.web.servlet.handler.AbstractHandlerMethodMapping registerHandlerMethod 
...Controller handler method mappings 
+1

看起来好像是你的tomcat日志豆的这些名单实际上并没有豆完全相同的名单,特别是类看起来你正在创建即auctionController只实例化一次。 – 2013-02-27 05:29:38

+0

你是对的。 DERP。 – 2013-02-27 15:04:51

回答

13

Singleton确保Spring容器内有单个实例。这意味着您可以拥有与容器一样多的实例(即使它们使用相同的JVM类加载器)。见The singleton scope和这个答案Singleton Design Pattern vs. Singleton Bean

在你的情况下,你正在实例化两个上下文 - rootContext和servletContext,这是日志语句来自。

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


这说明这种行为小型的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 {} 
+1

我想补充的是,容器基本上类加载器实例,所以辛格尔顿得到尊重*一个ClassLoader内* – 2013-02-27 08:42:52

+0

这是一般真实的,但在这种情况下,两个容器在同一线程使用默认的构造函数实例,并使用相同的类加载器('Thread.getContextClassLoader()')。这种行为是从'DefaultResourceLoader'继承的。也就是说,即使Spring容器共享同一个类加载器,你也会得到多个spring singleton实例。 – 2013-02-27 09:52:13

+0

对不起,我不清楚。 GoF Singleton的实现将是ClassLoader中所有容器的单例,但是你是对的,你可以为每个容器使用singleton * scope *获得一个对象。对不起。 – 2013-02-27 10:19:17

0

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

如果WebAppInitalizer你那里延伸扩展AbstractAnnotationConfigDispatcherServletInitializer类确保

你重写下面的方法,并确保类定义并不在这两个方法重复。

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

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