How to get Spring to autowire integration test cla

2019-02-04 15:42发布

问题:

One of my integration tests uses multiple Spring context files. It seems that Spring only autowires in beans from the first context and not the second. Does anyone know what I am doing wrong or how to work around the problem?

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {
        "classpath:/META-INF/spring/applicationContext.xml",
        "classpath:/META-INF/spring/applicationContext-security.xml"})
@Configurable
public class UserDetailsServiceImplIntegrationTest {

    @Autowired
    UserDataOnDemand dod;
    // @Autowired does not work for this bean from applicationContext-security.xml
    UserDetailsService userDetailsService;

    @Before
    public void setup() {
        dod.init();
        // workaround for autowiring problem
        userDetailsService = (UserDetailsService)ctx.getBean("userDetailsService");
    }

    @Test
    public void testLoadUser() {
        UserDetails ud = userDetailsService.loadUserByUsername("david@somewhere.co.za");
        Assert.assertEquals("david@somewhere.co.za", ud.getUsername());
    }
}

I am using Spring 3.0.3.

Here is the stack trace when I uncomment the @Autowired line for UserDetailsService:

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'za.co.acme.app.security.UserDetailsServiceImplIntegrationTest': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: org.springframework.security.core.userdetails.UserDetailsService za.co.acme.app.security.UserDetailsServiceImplIntegrationTest.userDetailsService; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No matching bean of type [org.springframework.security.core.userdetails.UserDetailsService] 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.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:286)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1064)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireBeanProperties(AbstractAutowireCapableBeanFactory.java:374)
    at org.springframework.beans.factory.wiring.BeanConfigurerSupport.configureBean(BeanConfigurerSupport.java:140)
    at org.springframework.beans.factory.aspectj.AnnotationBeanConfigurerAspect.configureBean(AnnotationBeanConfigurerAspect.aj:59)
    at org.springframework.beans.factory.aspectj.AbstractDependencyInjectionAspect.ajc$afterReturning$org_springframework_beans_factory_aspectj_AbstractDependencyInjectionAspect$2$1ea6722c(AbstractDependencyInjectionAspect.aj:89)
    at za.co.acme.app.security.UserDetailsServiceImplIntegrationTest.(UserDetailsServiceImplIntegrationTest.java:25)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:39)
    at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:27)
    at java.lang.reflect.Constructor.newInstance(Constructor.java:513)
    at org.junit.runners.BlockJUnit4ClassRunner.createTest(BlockJUnit4ClassRunner.java:202)

The bean is definitely there since the "by name" lookup works, and it is of the correct type.

回答1:

A workaround is to create a new single configuration file (let's call it "test-configuration.xml") which is including the both applicationContext.xml and applicationContext-security.xml. Then you can use these configurations within your tests.

test-configuration.xml:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">

    <import resource="classpath:/META-INF/spring/applicationContext.xml"/>
    <import resource="classpath:/META-INF/spring/applicationContext-security.xml"/>
</beans>

UserDetailsServiceImplIntegrationTest.java:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("/test-configuration.xml")
@Configurable
public class UserDetailsServiceImplIntegrationTest {
...
}


回答2:

I have a similar setup and it's working fine for me.

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath:/applicationContext-struts.xml", "classpath:/applicationContext.xml" })
@TransactionConfiguration(transactionManager = "transactionManager", defaultRollback = true)
@Transactional
public abstract class BaseTests {

My guess is that the problem is in the setup of your project. Do you use Eclipse? Does it load the context files from the /bin folder or from a /src folder? Did you exclude "applicationContext-security.xml" from the build?



回答3:

I had the same problem, the solution fixed my problem is switch the bean expose through Interface. (i.e) Your reference Bean Type should be a Interface instead of its implementation class

In your case change the concrete Class UserDetailsService reference with its interface.

For Example:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {
        "classpath:/META-INF/spring/applicationContext.xml",
        "classpath:/META-INF/spring/applicationContext-security.xml"})
@Configurable
public class UserDetailsServiceImplIntegrationTest {

  //modified code  
  @Autowired
  IUserDetailsService userDetailsService;

    //your test cases follows

}

NOTE: I know this wont be a sensible solution, But just give a try, I worried lot because of this same error and finally changed my reference and it worked fine. Hope it would resolve your issue.



回答4:

You need to tell Spring to act on those annotations. In the relevant context file you should add the following:

<context:annotation-config/>

Now it'll scan for those annotations. See the Spring documentation on Annotation-based configuration

To limit the number of classes it needs to scan so you don't needlessly scan packages with no autowiring, add this:

<context:component-scan base-package="org.example"/>

See the docs for Auto-detecting components for more on that, as well as the XML namespaces you'll need to add to reference those tags.



回答5:

What is the name of the bean for userDetailsService in your xml? You might need to add a @Qualifier annotation with the bean name, and then putting a <qualifier> tag in the context.

Take a look at Spring's documentation on the topic.



回答6:

I have the same problems.It seems that there is a proxy UserDataOnDemand instead of the real UserDataOnDemand .