1

I have a base auto configuration class that uses generics to create the bean you want. But when I test having two configs that both extend that base config class, the second one never creates its beans.

I believe this is because the method name is the same on both, so Spring assumes that it's already created.

Is there a way to dynamically set the name based off the generic type? (or some other solution)

@RunWith( SpringJUnit4ClassRunner.class )
@ContextConfiguration( classes = { TestGenericBean.MyClientCreator.class, TestGenericBean.MyClientCreator2.class } )
public class TestGenericBean
{
    @Autowired
    private TestClient client;

    @Autowired
    private TestClient2 client2;

    public static class ClientConfig<T>
    {
        private Class<T> classCreator;

        public ClientConfig(Class<T> classCreator)
        {
            this.classCreator = classCreator;
        }

        /* ***** This base class's method is only called once for
         *       the first class (MyClientCreator) not for the
         *       second one (MyClientCreator2)
         */
        @Bean
        public T createClient(AsyncRestTemplate asyncRestTemplate) throws Exception
        {
            Constructor<T> constructor = classCreator.getConstructor(
                AsyncRestTemplate.class
            );

            return constructor.newInstance( asyncRestTemplate );
        }

        @Bean
        public AsyncRestTemplate asyncRestTemplate()
        {
            return new AsyncRestTemplate();
        }
    }

    @Configuration
    public static class MyClientCreator extends ClientConfig<TestClient>
    {
        public MyClientCreator()
        {
            super( TestClient.class );
        }
    }

    public static class TestClient
    {
        public AsyncRestTemplate asyncRestTemplate;

        public TestClient(AsyncRestTemplate asyncRestTemplate)
        {
            this.asyncRestTemplate = asyncRestTemplate;
        }
    }

    /* This is the second configuration class. This config's bean never gets created */
    @Configuration
    public static class MyClientCreator2 extends ClientConfig<TestClient2>
    {
        public MyClientCreator2()
        {
            super( TestClient2.class );
        }
    }

    public static class TestClient2
    {
        public AsyncRestTemplate asyncRestTemplate;

        public TestClient2(AsyncRestTemplate asyncRestTemplate)
        {
            this.asyncRestTemplate = asyncRestTemplate;
        }
    }

    @Test
    public void testBean()
    {
        System.out.print( client.asyncRestTemplate );
    }
}
Don Rhummy
  • 24,730
  • 42
  • 175
  • 330

1 Answers1

0

You can solve your problem by inheriting the createClient method, removing the @Bean annotation on your parent class and qualifying the name in the bean title:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = { TestGenericBean.MyClientCreator.class, TestGenericBean.MyClientCreator2.class })
public class TestGenericBean {

    // Need to remove the bean here so we don't get duplicate naming
    // @Bean
    public T createClient(AsyncRestTemplate asyncRestTemplate) throws Exception
    {
       ...
    }

    ...

    @Configuration
    public static class MyClientCreator extends ClientConfig<TestClient> {
        public MyClientCreator()
        {
            super( TestClient.class );
        }
        @Bean("testClient")
        @Override
        public TestClient createClient(AsyncRestTemplate asyncRestTemplate) throws Exception {
            return super.createClient(asyncRestTemplate);
        }
    }

    ...

    /*
     * This is the second configuration class. This config's bean never gets created
     */
    @Configuration
    public static class MyClientCreator2 extends ClientConfig<TestClient2> {
        public MyClientCreator2()
        {
            super( TestClient2.class );
        }
        @Bean("testClient2")
        @Override
        public TestClient2 createClient(AsyncRestTemplate asyncRestTemplate) throws Exception {
            return super.createClient(asyncRestTemplate);
        }
    }

    ...
}

However, ideally if you have control of the beans, you should be using the awesome Object Factory (and similar) methods for non-constructor/DI-friendly beans. Cheers!

Dovmo
  • 8,121
  • 3
  • 30
  • 44