简体   繁体   English

Spring上下文加载两次xml和annotation配置

[英]Spring context loading twice with both xml and annotation configuration

I have a web application on Tomcat 7.0.34, Spring 3.2.3, Spring Security 3.2.0.RC1 and Spring Social 1.1. 我有一个关于Tomcat 7.0.34,Spring 3.2.3,Spring Security 3.2.0.RC1和Spring Social 1.1的Web应用程序。

For some reason the Spring context is being loaded twice. 由于某种原因,Spring上下文被加载了两次。 The second load is happening immediately after the first load has finished. 第一次加载完成后立即发生第二次加载。 The log below shows the Context Loader loading the Root WebApplicationContext. 下面的日志显示了加载Root WebApplicationContext的Context Loader。 Everything progresses normally and all the RequstMappingHandlers are registering correctly. 一切正常进行,所有RequstMappingHandler都正确注册。 Then immediately the context is refreshed again. 然后立即再次刷新上下文。

I've read several solutions on SO about ensuring you don't load the configuration as part of the Context Loader and the DispatcherServlet at the same time and have tested various combinations of this but that doesn't seem to have fixed it and I'm becoming code blind as well. 我已经阅读了几个关于SO的解决方案,确保你不要同时加载配置作为Context Loader和DispatcherServlet的一部分,并测试了它的各种组合,但似乎没有修复它,我'我也成了代码盲。

All my testing on this has pushed me to an annotation only configuration both for the container and Spring components but the config classes are pretty much copy and paste from the Spring Social examples on github. 我对此的所有测试都将我推向了仅用于容器和Spring组件的注释配置,但是配置类几乎是从github上的Spring Social示例中复制和粘贴的。 Although I've included the SocialConfig.java details below, this problem has been happening before I implemented Spring Social but I can't move on without fixing it. 虽然我已经在下面列出了SocialConfig.java的详细信息,但是在我实现Spring Social之前就已经出现了这个问题,但是如果不修复它就无法继续。

Also, the problem was present with a hybrid xml (web.xml, security-app-context.xml) and annotation configuration. 此外,问题出现在混合xml(web.xml,security-app-context.xml)和注释配置中。

I'm implementing WebApplicationInitializer rather than having a web.xml 我正在实现WebApplicationInitializer而不是web.xml

public class WebClientInitialiser implements WebApplicationInitializer {

    public void onStartup(ServletContext container) throws ServletException {

        // Create the 'root' Spring application context
        AnnotationConfigWebApplicationContext appContext = new AnnotationConfigWebApplicationContext();  

        // Manage the lifecycle of the root application context
        appContext.setConfigLocation("com.mycompany.webclient.config");
        appContext.setServletContext(container);

        container.addListener(new ContextLoaderListener(appContext)); 
        container.addListener(new MyCompanyContextListener());

        container.addFilter("springSecurityFilterChain", new DelegatingFilterProxy("springSecurityFilterChain"))
            .addMappingForUrlPatterns(null, false, "/*");

        // Register and map the dispatcher servlet
        Dynamic dispatcher = container.addServlet("dispatcher", new DispatcherServlet(appContext));
        dispatcher.addMapping("/");
        dispatcher.setLoadOnStartup(1);
    }

}

My MainConfig.java 我的MainConfig.java

/**
 * Main configuration class for the application.
 * Turns on @Component scanning, loads externalized application properties
 * and imports legacy security configuration
 */
@Configuration
@ComponentScan(basePackages = "com.mycompany.webclient", excludeFilters = { @Filter(Configuration.class) })
@PropertySource("classpath:wc.properties")
@ImportResource("/WEB-INF/spring/appServlet/security-app-context.xml")
public class MainConfig {


    @Bean
    public PropertySourcesPlaceholderConfigurer propertyPlaceHolderConfigurer() {
        return new PropertySourcesPlaceholderConfigurer();
    }

}

My WebMvcConfig.java 我的WebMvcConfig.java

@Configuration
@EnableWebMvc
public class WebMvcConfig extends WebMvcConfigurerAdapter {

    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/resources/**").addResourceLocations("/resources/");
    }

    @Bean
    public MessageSource messageSource() {
        ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
        messageSource.setBasename("/WEB-INF/messages/messages");
        return messageSource;
    }

    @Bean
    public InternalResourceViewResolver viewResolver() {
        InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
        viewResolver.setPrefix("/WEB-INF/views/");
        viewResolver.setSuffix(".jsp");
        return viewResolver;
    }
}

My SocialConfig.java 我的SocialConfig.java

@Configuration
@EnableSocial
public class SocialConfig implements SocialConfigurer {

        private SocialUserDAO socialUserDao;
        //
        // SocialConfigurer implementation methods
        //

        @Override
        public void addConnectionFactories(ConnectionFactoryConfigurer cfConfig, Environment env) {
            String clientId="XXXXXX";
            String clientSecret="XXXXX";
            cfConfig.addConnectionFactory(new FacebookConnectionFactory(clientId, clientSecret));
        }

        @Override
        public UserIdSource getUserIdSource() {
                return new UserIdSource() {                        
                        @Override
                        public String getUserId() {
                                Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
                                if (authentication == null) {
                                        throw new IllegalStateException("Unable to get a ConnectionRepository: no user signed in");
                                }
                                return authentication.getName();
                        }
                };
        }

        @Override
        public UsersConnectionRepository getUsersConnectionRepository(ConnectionFactoryLocator connectionFactoryLocator) {
                return new HibernateUsersConnectionRepository(socialUserDao, connectionFactoryLocator, Encryptors.noOpText());
        }

        //
        // API Binding Beans
        //

        @Bean
        @Scope(value="request", proxyMode=ScopedProxyMode.INTERFACES)
        public Facebook facebook(ConnectionRepository repository) {
                Connection<Facebook> connection = repository.findPrimaryConnection(Facebook.class);
                return connection != null ? connection.getApi() : null;
        }

        @Bean
        @Scope(value="request", proxyMode=ScopedProxyMode.INTERFACES)
        public Twitter twitter(ConnectionRepository repository) {
                Connection<Twitter> connection = repository.findPrimaryConnection(Twitter.class);
                return connection != null ? connection.getApi() : null;
        }

        @Bean
        @Scope(value="request", proxyMode=ScopedProxyMode.INTERFACES)
        public LinkedIn linkedin(ConnectionRepository repository) {
                Connection<LinkedIn> connection = repository.findPrimaryConnection(LinkedIn.class);
                return connection != null ? connection.getApi() : null;
        }

        //
        // Web Controller and Filter Beans
        //
        @Bean
        public ConnectController connectController(ConnectionFactoryLocator connectionFactoryLocator, ConnectionRepository connectionRepository) {
                ConnectController connectController = new ConnectController(connectionFactoryLocator, connectionRepository);
                connectController.addInterceptor(new PostToWallAfterConnectInterceptor());
                connectController.addInterceptor(new TweetAfterConnectInterceptor());
                return connectController;
        }

        @Bean
        public ProviderSignInController providerSignInController(ConnectionFactoryLocator connectionFactoryLocator, UsersConnectionRepository usersConnectionRepository) {
                return new ProviderSignInController(connectionFactoryLocator, usersConnectionRepository, new SimpleSignInAdapter(new HttpSessionRequestCache()));
        }

        @Bean
        public DisconnectController disconnectController(UsersConnectionRepository usersConnectionRepository, Environment env) {
                return new DisconnectController(usersConnectionRepository, env.getProperty("facebook.clientSecret"));
        }

        @Bean
        public ReconnectFilter apiExceptionHandler(UsersConnectionRepository usersConnectionRepository, UserIdSource userIdSource) {
                return new ReconnectFilter(usersConnectionRepository, userIdSource);
        }

}

Any help, comments, pointers is greatly appreciated. 任何帮助,评论,指针都非常感谢。

This log output is repeated twice at the start of each context refresh: 在每次上下文刷新开始时,此日志输出重复两次:

org.springframework.web.context.ContextLoader- Root WebApplicationContext: initialization started
org.springframework.web.context.support.AnnotationConfigWebApplicationContext- Refreshing Root WebApplicationContext: startup date [Mon Nov 25 22:43:39 GMT 2013]; root of context hierarchy
org.springframework.context.annotation.ClassPathBeanDefinitionScanner- JSR-330 'javax.inject.Named' annotation found and supported for component scanning
org.springframework.web.context.support.AnnotationConfigWebApplicationContext- Registering annotated classes: [class com.mycompany.webclient.config.WebMvcConfig,class com.mycompany.webclient.config.SocialConfig]

You are passing the same context to both the ContextLoaderListener and DispatcherServlet and hence this will trigger loading the configuration twice. 您将相同的上下文传递给ContextLoaderListenerDispatcherServlet ,因此这将触发加载配置两次。

You should have 2 seperate AnnotationConfigWebApplicationContext instances one for the ContextLoaderListener loading all your generic beans (services etc.) and one for the DispatcherServlet loading the web related things. 你应该有2个单独的AnnotationConfigWebApplicationContext实例,一个用于加载所有通用bean(服务等)的ContextLoaderListener ,另一个用于加载web相关内容的DispatcherServlet

public class WebClientInitialiser implements WebApplicationInitializer {

    public void onStartup(ServletContext container) throws ServletException {

        // Create the 'root' Spring application context
        AnnotationConfigWebApplicationContext rootContext = new AnnotationConfigWebApplicationContext();  
        rootContext.register(MainConfig.class);
        // Manage the lifecycle of the root application context

        container.addListener(new ContextLoaderListener(rootContext)); 
        container.addListener(new MyCompanyContextListener());

        container.addFilter("springSecurityFilterChain", new DelegatingFilterProxy("springSecurityFilterChain"))
            .addMappingForUrlPatterns(null, false, "/*");

        AnnotationConfigWebApplicationContext dispatcherContext = new AnnotationConfigWebApplicationContext();  
        dispatcherContext.register(WebMvcConfig.class, SocialConfig.class);

        // Register and map the dispatcher servlet
        Dynamic dispatcher = container.addServlet("dispatcher", new DispatcherServlet(dispatcherContext));
        dispatcher.addMapping("/");
        dispatcher.setLoadOnStartup(1);
    }

}

Something like this. 像这样的东西。 Also you don't need to set the contextConfigLocation simply register the @Configuration annotated classes. 此外,您不需要设置contextConfigLocation只需注册@Configuration注释的类。 Also setting the ServletContext is already done by Spring so no need for that to. 同样设置ServletContext已经由Spring完成,所以不需要这样做。

A note on your configuration, the PropertySourcesPlaceHolderConfigurer is enabled by default so no need to register that again in in MainConfig class. 关于您的配置的注释,默认情况下启用PropertySourcesPlaceHolderConfigurer ,因此无需再在MainConfig类中注册它。

Another thing to take into account is that now probably your application fails (ie your @Controllers don't work anymore). 另一件需要考虑的事情是,现在可能你的应用程序失败了(即你的@Controllers不再工作)。 This is due to the fact that everything is inside the root application context whereas @Controllers should be loaded by the DispatcherServlet. 这是因为所有内容都在根应用程序上下文中,而@Controllers应该由DispatcherServlet加载。 To fix this you need to exclude @Controller scanning in your MainConfig and enable @Controller scanning on the WebMvcConfig class. 要解决此问题,您需要在MainConfig排除@Controller扫描并在WebMvcConfig类上启用@Controller扫描。

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

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