4

I recently came back to a Spring project I'd been working on and I've run into issues when starting up the app. This question is probably a duplicate, but I haven't been able to find an answer.

Here's a snippet from my original SecurityConfig.java:

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired private UserService userService;

    /**
     * Global security config to set the user details service etc.
     * @param auth authentication manager
     * @throws Exception
     */
    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
        auth
                .userDetailsService(userService)
                .passwordEncoder(passwordEncoder());
    }

The UserService object implements UserDetailsService. This gave the error on startup:

Caused by: org.springframework.beans.factory.BeanCreationException: Could not autowire field: private com.clubmate.web.service.UserService com.clubmate.web.config.SecurityConfig.userService; nested exception is java.lang.IllegalArgumentException: Can not set com.clubmate.web.service.UserService field com.clubmate.web.config.SecurityConfig.userService to com.sun.proxy.$Proxy62
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:573)
    at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:88)
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:331)
    ... 58 more
Caused by: java.lang.IllegalArgumentException: Can not set com.clubmate.web.service.UserService field com.clubmate.web.config.SecurityConfig.userService to com.sun.proxy.$Proxy62
    at sun.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:167)
    at sun.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:171)
    at sun.reflect.UnsafeObjectFieldAccessorImpl.set(UnsafeObjectFieldAccessorImpl.java:81)
    at java.lang.reflect.Field.set(Field.java:764)
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:569)
    ... 60 more

From my reading, this is because I'm autowiring a concrete class instead of the UserDetailsService interface, but this is strange to me, because when I worked on this project before, autowiring the concrete class worked fine.

I gave in and just changed it to autowire the UserDetailsService interface in SecurityConfig.java.

I'm not sure whether this is related or not, but now on startup I get the below error, for any of my other bean objects (@Services etc.) that have @Autowire private UserService userService.

Caused by: org.springframework.beans.factory.BeanCreationException: Could not autowire field: private com.clubmate.web.service.UserService com.clubmate.web.service.PageService.userService; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [com.clubmate.web.service.UserService] 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)}
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:573)
    at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:88)
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:331)
    ... 58 more
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [com.clubmate.web.service.UserService] 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)}
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoSuchBeanDefinitionException(DefaultListableBeanFactory.java:1373)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1119)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1014)
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:545)
    ... 60 more

Any help greatly appreciated. This has been driving me nuts for hours.

Update

More info: I have another config class (AppConfig.java) with the following:

@Configuration
@EnableWebMvc
@ComponentScan("com.clubmate.web")
@PropertySource(value = { "classpath:application.properties" })
@Import({
        SecurityConfig.class,
        CacheConfig.class,
        DatabaseConfig.class,
        CronConfig.class
})
public class AppConfig extends WebMvcConfigurerAdapter {
    // ...
}

I also have two app initializer classes, one for MVC which is:

public class ServletInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {

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

    @Override
    public Class<?>[] getServletConfigClasses() {
        return new Class[] {
                StartupHousekeeping.class,
                ShutdownHousekeeping.class
        };
    }

    @Override
    public String[] getServletMappings() {
        return new String[] {
                "/"
        };
    }

}

...and another for Security which is:

public class AppInitializer extends AbstractSecurityWebApplicationInitializer {

    private static Logger log = LogManager.getLogger(AppInitializer.class);

    @Override
    protected void beforeSpringSecurityFilterChain(ServletContext context) {
        // load multipart filter before other filters are created
        // see: http://docs.spring.io/spring-security/site/docs/4.0.1.RELEASE/reference/htmlsingle/#csrf-multipart
        MultipartFilter multipartFilter = new MultipartFilter();
        multipartFilter.setMultipartResolverBeanName("multipartResolver");
        insertFilters(context, multipartFilter);
    }

}

Could these be conflicting somehow?

Update 2

Screenshot of where the exception is being thrown: https://i.stack.imgur.com/tcE5S.jpg

var1 is the SecurityConfig class, var2 is a Proxy object, which should instead be my UserService class.

Kieran
  • 314
  • 1
  • 4
  • 18
  • Maybe you have the same problem http://stackoverflow.com/questions/35344135/custom-userdetailsservice-it-seems-to-be-not-autowired/35350968#35350968 – Ali Dehghani Feb 20 '16 at 16:35
  • That's for the suggestion. I've updated my OP with more info. I think you might be on to something...? – Kieran Feb 20 '16 at 16:46
  • Remove `@ComponentScan("com.clubmate.web")` from `SecurityConfig` – Ali Dehghani Feb 20 '16 at 16:54
  • Yep, I had tried that, but same error. – Kieran Feb 20 '16 at 16:56
  • You probably need to enable CGLIB-style proxy generation. Add this to your configuration class: `@EnableAspectJAutoProxy(proxyTargetClass=true)` – fateddy Feb 20 '16 at 18:55
  • I'm not using Aspect classes, though, @fateddy, does this matter? Either way, the same error is still being thrown along with others (i.e. NoClassDefFoundError, because I'm not including the org.aspectj library) – Kieran Feb 20 '16 at 19:01
  • Just updated my OP. This is hardly anything to do with switching to Java 8? – Kieran Feb 20 '16 at 19:06
  • You are using proxied objects (or spring does this for you). I guess the autowiring would work if you autowire the `UserDetailsService` (the interface instead of your concrete `UserService` implementation) - because by default the interface is proxied - or enable CGLIB proxying (to proxy the target class). – fateddy Feb 20 '16 at 19:08
  • So like I said in my OP, changing `SecurityConfig` to autowire the interface instead DOES get rid of the error, but I then get the other error mentioned in my OP, where none of my other Spring components can find my `UserService`. Maybe this is a case of what @AliDehghani mentioned? – Kieran Feb 20 '16 at 19:15
  • 1
    It might have something to do with context hierachies. Enabling CGLIB proxying as suggested should solve the issue. – fateddy Feb 20 '16 at 19:30
  • Yes, that has indeed worked, after adding the org.aspectj libraries. Thanks so much for your help. If you want to put it in an answer, I'll mark it as accepted. – Kieran Feb 20 '16 at 19:48
  • Thanks, added as answer – fateddy Feb 23 '16 at 14:12

2 Answers2

5

The autowiring fails because by default Spring creates proxies using JDK-dynamic proxies (which proxies the target class by implementing its interface(s)). CGLIB-based proxies on the other hand are subclasses of the target class.

See: What is the difference between JDK dynamic proxy and CGLib?

To enable CGLIB based proxying annotate one of your @Configuration classes with @EnableAspectJAutoProxy(proxyTargetClass=true):

@Configuration
@EnableAspectJAutoProxy(proxyTargetClass=true)
public class AppConfig {
    ...
}

Note that as of version 3.2 CGLIB is repackaged and included in the spring-core JAR. Therefore it will work right out of the box.

Community
  • 1
  • 1
fateddy
  • 6,887
  • 3
  • 22
  • 26
2

Thanks to @fateddy in the comments, the solution was to add

@EnableAspectJAutoProxy(proxyTargetClass=true)

to my AppConfig class, and then import the org.aspectj libraries to my project.

Kieran
  • 314
  • 1
  • 4
  • 18