1

Tomcat 7.0.34、Spring 3.2.3、Spring Security 3.2.0.RC1、および Spring Social 1.1 に Web アプリケーションがあります。

何らかの理由で、Spring コンテキストが 2 回読み込まれています。2 番目のロードは、最初のロードが終了した直後に発生しています。以下のログは、Context Loader がルート WebApplicationContext をロードしていることを示しています。すべてが正常に進行し、すべての RequstMappingHandlers が正しく登録されています。その後すぐに、コンテキストが再び更新されます。

Context Loader と DispatcherServlet の一部として同時に構成をロードしないようにすることについて、SO でいくつかのソリューションを読み、これのさまざまな組み合わせをテストしましたが、それは修正されていないようです。 mもコードブラインドになります。

これに関する私のすべてのテストでは、コンテナとSpringコンポーネントの両方のアノテーションのみの構成に私を駆り立てましたが、構成クラスはgithubのSpring Socialの例からほとんどコピーして貼り付けています. 以下に SocialConfig.java の詳細を含めましたが、この問題は Spring Social を実装する前から発生していましたが、修正しないと先に進むことができません。

また、問題はハイブリッド xml (web.xml、security-app-context.xml) と注釈構成にも存在していました。

web.xml ではなく、WebApplicationInitializer を実装しています

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

}

私の 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();
    }

}

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

マイ 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);
        }

}

ヘルプ、コメント、ポインタは大歓迎です。

このログ出力は、各コンテキスト リフレッシュの開始時に 2 回繰り返されます。

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]
4

1 に答える 1

4

ContextLoaderListenerとの両方に同じコンテキストを渡しているDispatcherServletため、構成のロードが 2 回トリガーされます。

すべての汎用 Bean (サービスなど) をロードするためのインスタンスと、Web 関連のものをロードするAnnotationConfigWebApplicationContextためのインスタンスの 2 つの別個のインスタンスが必要です。ContextLoaderListenerDispatcherServlet

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

}

このようなもの。また、アノテーション付きのクラスcontextConfigLocationを登録するだけで設定する必要はありません。@Configurationまた、設定はServletContextSpringによってすでに行われているため、その必要はありません。

設定に関するメモ。PropertySourcesPlaceHolderConfigurerはデフォルトで有効になっているため、MainConfigクラスで再度登録する必要はありません。

考慮すべきもう 1 つのことは、アプリケーションがおそらく失敗する (つまり、@Controllersもう動作しない) ことです。これは、すべてがルート アプリケーション コンテキスト内にあるのに対し@Controllers、DispatcherServlet によってロードされる必要があるためです。これを修正するには、クラスでスキャンを除外し、クラスでスキャンを有効にする必要@ControllerがありMainConfigます。@ControllerWebMvcConfig

于 2013-11-25T11:14:27.920 に答える