Strange behaviour of @Configuration with @Configur

2019-04-17 10:45发布

问题:

I've been using XML based configuration for a while - we've got a Vaadin application with Spring used as DI, but we are not interested in DispacherServlet - only root context, which we use to inject global (not user owned dependencies).

The way it works
I've defined root-context.xml file with content:

<context:annotation-config />
<context:spring-configured />
<context:load-time-weaver />
<context:component-scan base-package="com.example" />

And my web.xml has in it:

<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>/WEB-INF/spring/root-context.xml</param-value>
</context-param>
<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

Then, some of my classes are defined with @Component annotation and others with @Configurable (the latter mostly belong to user session, so require DI for each instance created with new keyword).

I've got context.xml file with line:

<Loader delegate="false" loaderClass="org.springframework.instrument.classloading.tomcat.TomcatInstrumentableClassLoader" />

And spring-instrument-tomcat-3.2.1.RELEASE.jar in Tomcat's lib directory.
All the dependencies are injected (with @Autowire) to my @Configurable classes correctly.

The way it doesn't work
Recently I've tried to get rid of root-context.xml and move context initialisation to Java @Configuration class.
I've created a class as follows:

@Configuration
@EnableSpringConfigured
@EnableLoadTimeWeaving
@ComponentScan("com.example")
public class BeansConfiguration
{
}

In addition I changed web.xml entries:

<context-param>
    <param-name>contextClass</param-name>
    <param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext</param-value>
</context-param>
<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>com.example.spring.BeansConfiguration</param-value>
</context-param>
<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

Unfortunately, strange things started to happen. Let me show you an example. Simplifying my class structure looks as follows:

@Component
public class ComponentA
{
}

@Configurable
public class BeanB
{
    @Autowired
    private ComponentA componentA;
}

@Configurable
public class BeanC
{
    @Autowired
    private ComponentA componentA;

    private BeanB beanB;

    public BeanC(BeanB beanB)
    {
        this.beanB = beanB;
    }
}

@Configurable
public class Application
{
    @Autowired
    private ComponentA componentA;

    public Application()
    {
    }

    public void init()
    {
        BeanC beanC = new BeanC(new BeanB());
    }
}

With the XML setup, when it does work, ComponentA is correctly injected by Spring into all my @Configurable objects. Strangely, with annotation-only configuration BeanC doesn't get the ComponentA injected (it's always null), howewer BeanB and Application do get that!

Have you got any ideas why would it happen? As soon as I comment out lines in web.xml to go back to my previous (XML-based) configuration all starts to work.

My happy guess is that XML Spring entries register something more under the cover than their annotation based counterparts. I've spend half a day trying to find out, what could that be, but I gave up. I would appreciate any suggestions.