Spring上下文与XML和注释配置加载两次 [英] Spring context loading twice with both xml and annotation configuration

查看:630
本文介绍了Spring上下文与XML和注释配置加载两次的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在Tomcat 7.0.34,春天3.2.3 Web应用程序,春季安全3.2.0.RC1和Spring 1.1的社会

由于某些原因,Spring上下文被加载两次。第一次加载完成后第二负载立即发生。下面的日志显示上下文加载器加载根WebApplicationContext。通常的一切进步,所有的RequstMappingHandlers正确注册。然后立即上下文被再次刷新

我读过的有这么几种解决方案,要确保你不加载的配置上下文加载器的一部分,DispatcherServlet的同时,并测试了这一点,但各种组合似乎并没有有固定的而我成为code盲目的。

我对所有这些测试已经把我推到既为容器和Spring组件,但在配置类注释唯一配置是pretty多副本,并从github上春节社会的例子粘贴。虽然我已经包括下面的SocialConfig.java细节,这个问题已经发生了,我实现社会春季之前,但无固定它,我无法继续前进。

此外,问题是present与混合XML(web.xml中,安全-APP-context.xml中)和注释配置。

我实施WebApplicationInitializer而不是一个web.xml

 公共类WebClientInitialiser实现WebApplicationInitializer {    公共无效onStartup(ServletContext的容器)抛出了ServletException {        //创建根Spring应用程序上下文
        AnnotationConfigWebApplicationContext appContext =新AnnotationConfigWebApplicationContext();        //管理的根应用程序上下文的生命周期
        appContext.setConfigLocation(com.mycompany.webclient.config);
        appContext.setServletContext(容器);        container.addListener(新的ContextLoaderListener(appContext));
        container.addListener(新MyCompanyContextListener());        container.addFilter(是springSecurityFilterChain,新的DelegatingFilterProxy(是springSecurityFilterChain))
            .addMappingForUrlPatterns(空,假,/ *);        //注册并映射调度的servlet
        动态调度= container.addServlet(调度员,新的DispatcherServlet(appContext));
        dispatcher.addMapping(/);
        dispatcher.setLoadOnStartup(1);
    }}

我MainConfig.java

  / **
 *应用程序主要配置类。
 *打开@Component扫描,负载外部化的应用性能
 *进口遗留的安全配置
 * /
@组态
@ComponentScan(basePackages =com.mycompany.webclient,excludeFilters = {@Filter(Configuration.class)})
@PropertySource(类路径:wc.properties)
@ImportResource(/ WEB-INF /春/ appServlet /安全应用-context.xml中)
公共类MainConfig {
    @豆
    公共PropertySourcesPlaceholderConfigurer propertyPlaceHolderConfigurer(){
        返回新PropertySourcesPlaceholderConfigurer();
    }}

我WebMvcConfig.java

  @Configuration
@EnableWebMvc
公共类WebMvcConfig扩展WebMvcConfigurerAdapter {    公共无效addResourceHandlers(ResourceHandlerRegistry注册表){
        。registry.addResourceHandler(/资源/ **)addResourceLocations(/资源/);
    }    @豆
    公众的MessageSource为messageSource(){
        ReloadableResourceBundleMessageSource为messageSource =新ReloadableResourceBundleMessageSource();
        messageSource.setBasename(/ WEB-INF /邮件/信息);
        返回为messageSource;
    }    @豆
    公共InternalResourceViewResolver这个视图解析器(){
        InternalResourceViewResolver这个视图解析器=新InternalResourceViewResolver这个();
        viewResolver.set preFIX(/ WEB-INF /视图/);
        viewResolver.setSuffix(JSP);
        返回的ViewResolver;
    }
}

我SocialConfig.java

  @Configuration
@EnableSocial
公共类SocialConfig实现SocialConfigurer {        私人SocialUserDAO socialUserDao;
        //
        // SocialConfigurer实现方法
        //        @覆盖
        公共无效addConnectionFactories(ConnectionFactoryConfigurer cfConfig,环境Env){
            字符串的clientId =XXXXXX;
            串clientSecret =XXXXX;
            cfConfig.addConnectionFactory(新FacebookConnectionFactory(客户端ID,clientSecret));
        }        @覆盖
        公共UserIdSource getUserIdSource(){
                返回新UserIdSource(){
                        @覆盖
                        公共字符串getUserId(){
                                验证验证= SecurityContextHolder.getContext()getAuthentication()。
                                如果(认证== NULL){
                                        抛出新IllegalStateException异常(无法获取ConnectionRepository:没有用户登录);
                                }
                                返回authentication.getName();
                        }
                };
        }        @覆盖
        公共UsersConnectionRepository getUsersConnectionRepository(ConnectionFactoryLocator connectionFactoryLocator){
                返回新HibernateUsersConnectionRepository(socialUserDao,connectionFactoryLocator,Encryptors.noOpText());
        }        //
        // API绑定豆
        //        @豆
        @Scope(值=请求,proxyMode = ScopedProxyMode.INTERFACES)
        市民正在使用Facebook(ConnectionRepository库){
                连接< Facebook和GT;连接= repository.findPrimaryConnection(Facebook.class);
                返回连接!= NULL? connection.getApi():空;
        }        @豆
        @Scope(值=请求,proxyMode = ScopedProxyMode.INTERFACES)
        公共Twitter的微博(ConnectionRepository库){
                连接< Twitter并GT;连接= repository.findPrimaryConnection(Twitter.class);
                返回连接!= NULL? connection.getApi():空;
        }        @豆
        @Scope(值=请求,proxyMode = ScopedProxyMode.INTERFACES)
        公共LinkedIn LinkedIn(ConnectionRepository库){
                连接< LinkedIn>连接= repository.findPrimaryConnection(LinkedIn.class);
                返回连接!= NULL? connection.getApi():空;
        }        //
        //网络控制器和过滤器bean
        //
        @豆
        公共ConnectController connectController(ConnectionFactoryLocator connectionFactoryLocator,ConnectionRepository connectionRepository){
                ConnectController connectController =新ConnectController(connectionFactoryLocator,connectionRepository);
                connectController.addInterceptor(新PostToWallAfterConnectInterceptor());
                connectController.addInterceptor(新TweetAfterConnectInterceptor());
                返回connectController;
        }        @豆
        公共ProviderSignInController providerSignInController(ConnectionFactoryLocator connectionFactoryLocator,UsersConnectionRepository usersConnectionRepository){
                返回新ProviderSignInController(connectionFactoryLocator,usersConnectionRepository,新SimpleSignInAdapter(新HttpSessionRequestCache()));
        }        @豆
        公共DisconnectController disconnectController(UsersConnectionRepository usersConnectionRepository,环境Env){
                返回新DisconnectController(usersConnectionRepository,env.getProperty(facebook.clientSecret));
        }        @豆
        公共ReconnectFilter apiExceptionHandler(UsersConnectionRepository usersConnectionRepository,UserIdSource userIdSource){
                返回新ReconnectFilter(usersConnectionRepository,userIdSource);
        }}

任何帮助,评论,指针是极大的AP preciated。

本日志输出在各方面刷新开始重复两次:

  org.springframework.web.context.ContextLoader-根的WebApplicationContext:初始化启动
org.springframework.web.context.support.AnnotationConfigWebApplicationContext-清爽根的WebApplicationContext:启动日期[周一11月25日22时43分39秒GMT 2014]。上下文结构的根
org.springframework.context.annotation.ClassPathBeanDefinitionScanner- JSR-330javax.inject.Named注释发现支持组件扫描
org.springframework.web.context.support.AnnotationConfigWebApplicationContext-注册注释类:类com.mycompany.webclient.config.WebMvcConfig,类com.mycompany.webclient.config.SocialConfig]


解决方案

您是路过同样的情况下,以两个的ContextLoadListener DispatcherServlet的,因此这将触发加载配置两次。

您应该有2个独立的 AnnotationConfigWebApplicationContext 实例一为的ContextLoadListener 加载所有的通用豆(服务等)一个用于 DispatcherServlet的加载网页相关的东西。

 公共类WebClientInitialiser实现WebApplicationInitializer {    公共无效onStartup(ServletContext的容器)抛出了ServletException {        //创建根Spring应用程序上下文
        AnnotationConfigWebApplicationContext rootContext =新AnnotationConfigWebApplicationContext();
        rootContext.register(MainConfig.class);
        //管理的根应用程序上下文的生命周期        container.addListener(新的ContextLoaderListener(rootContext));
        container.addListener(新MyCompanyContextListener());        container.addFilter(是springSecurityFilterChain,新的DelegatingFilterProxy(是springSecurityFilterChain))
            .addMappingForUrlPatterns(空,假,/ *);        AnnotationConfigWebApplicationContext dispatcherContext =新AnnotationConfigWebApplicationContext();
        dispatcherContext.register(WebMvcConfig.class,SocialConfig.class);        //注册并映射调度的servlet
        动态调度= container.addServlet(调度员,新的DispatcherServlet(dispatcherContext));
        dispatcher.addMapping(/);
        dispatcher.setLoadOnStartup(1);
    }}

这样的事情。你也不必设置 contextConfigLocation的只需注册 @Configuration 注释类。还设置的ServletContext 已经由Spring所以没有必要为实现这一目标进行。

您的配置的说明中, PropertySourcesPlaceHolderConfigurer 默认情况下启用,因此无需注册一遍在 MainConfig 类。

要考虑的另一件事情是,现在大概您的应用程序失败(即你的 @Controllers 不工作了)。这是由于这样的事实,即一切而 @Controllers 根应用上下文内应由DispatcherServlet的加载。为了解决这个问题,你需要排除在你的 MainConfig 并启用 @Controller < @Controller 扫描/ code>扫描上的 WebMvcConfig 类。

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.

For some reason the Spring context is being loaded twice. The second load is happening immediately after the first load has finished. The log below shows the Context Loader loading the Root WebApplicationContext. Everything progresses normally and all the RequstMappingHandlers are registering correctly. 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.

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. 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.

Also, the problem was present with a hybrid xml (web.xml, security-app-context.xml) and annotation configuration.

I'm implementing WebApplicationInitializer rather than having a 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

/**
 * 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

@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

@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.

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.

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. Also setting the ServletContext is already done by Spring so no need for that to.

A note on your configuration, the PropertySourcesPlaceHolderConfigurer is enabled by default so no need to register that again in in MainConfig class.

Another thing to take into account is that now probably your application fails (i.e. your @Controllers don't work anymore). This is due to the fact that everything is inside the root application context whereas @Controllers should be loaded by the DispatcherServlet. To fix this you need to exclude @Controller scanning in your MainConfig and enable @Controller scanning on the WebMvcConfig class.

这篇关于Spring上下文与XML和注释配置加载两次的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆