0

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.

Piotr Górny
  • 61
  • 1
  • 6
  • Real packages for your `@ComponentScan` value and `BeanC`? – chrylis -cautiouslyoptimistic- Nov 25 '13 at 13:09
  • You don't seem to have @Autowired on the constructor of BeanC. Have you tried that? It might also be worth making it @Autowired(required = true) to ensure that you see errors when it fails to inject. – Steve Nov 25 '13 at 13:13
  • 1
    loadtime-weaving and `@Configuration` can be a pain to get working. As soon as you reference a class and that class is loaded **BEFORE** loadtime-weaving is active there is no way the class can be enhanced anymore. The @Configuration classes are laoded, then scanned, so if one of those references `BeanC` you are screwed. With XML the loading of the classes is delayed (because they aren't part of a classfile). – M. Deinum Nov 25 '13 at 13:35
  • @chrylis packages passed to component scan include my whole code base, so that's not a problem, but thanks. – Piotr Górny Nov 26 '13 at 08:21
  • @Steve I don't want BeanC to get BeanB via autowiring. I pass it manually. Or am I missing something? – Piotr Górny Nov 26 '13 at 08:22
  • @PiotrGórny Are you using AspectJ, Spring Proxy AOP, or Spring LTW? – chrylis -cautiouslyoptimistic- Nov 26 '13 at 08:24
  • @M.Deinum I'm not sure if I understood you correctly - did you mean `@Configurable` classes? I'd thought that whenever I instantiate a class, which is `@Configurable` it will have `@Autowire` dependencies injected live, into just created instance... – Piotr Górny Nov 26 '13 at 08:24
  • @chrylis That's a really good question :-) I don't know to be honest. I don't set anything on `@EnableLoadTimeWeaving` nor ``, so please tell me :-) – Piotr Górny Nov 26 '13 at 08:26
  • @PiotrGórny Disassemble your `BeanC` constructor and see if there's a lot of `ajc` around. If not, you may not have had it compiled with the AspectJ compiler, and the difference may be one of the edge cases with AOP defaults between XML and JavaConfig. – chrylis -cautiouslyoptimistic- Nov 26 '13 at 09:22
  • 1
    @PiotrGórny This will only work if classes aren't already loaded **BEFORE** loadtime weaving kicks in. So if one of your `@Configuration` classes references `BeanC` that will not be available for loadtime weaving anymore because it is already loaded. This is one of the drawbacks/problems with the @Configuration approach (classes might get loaded eagerly). – M. Deinum Nov 26 '13 at 09:57
  • @chrylis he is using loadtime weaving NOT compile timeweaving so taking a look at the code will not show anything. – M. Deinum Nov 26 '13 at 09:58
  • To answer another question you are using LTW because that is what you are enabling with `@EnableLoadTimeWeaving` (or the xml variant of that), would be pretty useless if `@EnableLoadTimeWeaving` would create AOP proxies. (Might be that something else triggers that like a `` without `mode="aspectj"`. – M. Deinum Nov 26 '13 at 10:00
  • @M.Deinum I was wanting to confirm that the load-time weaving was being applied in the XML case. `@Configurable` can get quite weird with it. – chrylis -cautiouslyoptimistic- Nov 26 '13 at 10:14

0 Answers0