简体   繁体   中英

“Autowire” fails after deploying spring modular application to tomcat

I need some help as I can not find the solution myself and google does not help either. I have a modular spring app, which I can run from command line using SpringBoot with no problem. I've created a war using gradle war command. I've installed Tomcat 8 on a server with PostgreSQL database and JRE 8. I've put the war into the webapps folder and my external conf files into the conf folder. All my other modules are present in the libs folder of the war file. When I run the Tomcat, I get the following error:

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'securityConfig': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'personDetailsService': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private eu.bato.anyoffice.serviceapi.service.PersonService eu.bato.anyoffice.frontend.config.PersonDetailsService.personService; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [eu.bato.anyoffice.serviceapi.service.PersonService] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}

Here is part of my main conf file:

@Configuration
@EnableAutoConfiguration
@ComponentScan(value = {"eu.bato.anyoffice"})
@EnableWebMvc
public class Application extends WebMvcConfigurerAdapter{

    @Autowired
    SchedulerService scheduler;

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

    @PostConstruct
    protected void startScheduler(){
        scheduler.start();
    }

    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/login").setViewName("login");
        registry.setOrder(Ordered.HIGHEST_PRECEDENCE);
    }

    @Autowired
    private MessageSource messageSource;

The security configuration that defines the PersonDetailsService bean:

@Configuration
@EnableWebMvcSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    private static final Logger log = LoggerFactory.getLogger(SecurityConfig.class);

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(personDetailsService()).passwordEncoder(new StandardPasswordEncoder());
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .addFilterBefore(authenticationFilter(), LogoutFilter.class)
                .csrf().disable()
                .authorizeRequests()
                ......
                .permitAll();
    }

    @Bean
    PersonDetailsService personDetailsService() {
        return new PersonDetailsService();
    }

    @Bean
    Filter authenticationFilter() {
        BasicAuthenticationFilter basicAuthFilter = new BasicAuthenticationFilter(customAuthenticationManager(), new BasicAuthenticationEntryPoint());
        return basicAuthFilter;
    }

    @Bean
    ProviderManager customAuthenticationManager() {
        List<AuthenticationProvider> providers = new LinkedList<>();
        providers.add(daoAuthPovider());
        ProviderManager authenticationManager = new ProviderManager(providers);
        authenticationManager.setEraseCredentialsAfterAuthentication(true);
        return authenticationManager;
    }

    @Bean
    DaoAuthenticationProvider daoAuthPovider() {
        DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
        provider.setUserDetailsService(personDetailsService());
        provider.setPasswordEncoder(new StandardPasswordEncoder());
        //TODO: add salt
        return provider;
    }

A part of the PersonDetailsService class:

public class PersonDetailsService implements UserDetailsService {

    private static final Logger log = LoggerFactory.getLogger(PersonDetailsService.class);

    private static final StandardPasswordEncoder encoder = new StandardPasswordEncoder();

    @Autowired
    private PersonService personService;

    @Autowired
    private Environment environment;

    @PostConstruct
    protected void initialize() {
    }

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        log.info("Authenticating: " + username);

PersonService interface is in package eu.bato.anyoffice.serviceapi.service and its implementation is in eu.bato.anyoffice.backend.service.impl and has tags @Service and @Transactional

I would be very grateful for any hints. I can provide any further logs and information.

The problem in your configuration is that PersonService is loaded by @ComponentScan declared in the Servlet Context .

In that way PersonService is not accessible in the Application Context where your SecurityConfig is loaded.

Beans in Servlet Context can reference beans in Application Context , but not vice versa!

So putting @ComponentScan in SecurityConfig allow that component to be reacheable.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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