Announcement Announcement Module
Collapse
No announcement yet.
Precedence of XML vs. Annotation configuration Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Precedence of XML vs. Annotation configuration

    Hello,

    I want to migrate XML based configured beans to the new 2.5 annotation style. I have problems to change beans, which have configured simple parameters like boolean, int, String.

    Is it possible to combine/mix xml and annotations? I would like to autowire references to other beans _and_ configure primitive types within the xml file.

    But when I combine them, only the xml configuration is used. The annotations are completley ignored in this case.

    Is it a wrong configuration of mine or isn't it possible to mix the configuration idioms?

    Does it mean, that whenever there is (and even if there is just one) a simple value set in the configuration, I have to use xml configuration?

    Thank you,
    Timo Meinen

  • #2
    Hi Timo. You can certainly mix the two approaches to injection. It should work without any special effort but it would help to see what you are doing. Can you post your config file and also the source for the class containing the fields that you want half-autowired and half-manually-wired?

    Here are some suggestions:

    If you have default-autowire turned on, and you're defining beans explicitly (rather than using <context:component-scan> to discover beans), then you can set exactly the primitive properties in the beans. Should work.

    If you're using the @Autowire mechanism, and again not using <context:component-scan>, once again just define your beans and their primitive-valued properties explicitly. Your @Autowire should go only on the fields you want to autowire, of course. In this case make sure you have <context:annotation-config> in there so that annotation-based autowiring is activated. (You'll need to make sure you declare the context namespace on your beans element as well.)

    Comment


    • #3
      Thank you for your reply. It is exactly the fact, that I have <context:component-scan> enabled _and_ the classes annotated with e.g. Component.

      So, did I understand it correct, that I can configure the bean in the xml, don't annotate the class, but annotate the setter-methods with Autowired?

      That would minimize the <property... ref...> entries in the xml files, at least.

      I will search for example code out of the project tomorrow.

      Thank you
      Timo Meinen

      Comment


      • #4
        I tested it and it doesn't work. :-( Here is the sample code:

        Code:
        <bean id="message" class="Message">
        <!-- 
          <property name="messageDao" ref="messageDao" />
         -->
        </bean>
        The xml configuration is working. To test the autowire annotation, I commented out the dao-reference, as you can see.

        Code:
        public class Message {
        
          private MessageDao messageDao;
          
          @PostConstruct
          public void afterPropertiesSet() throws Exception {
            Assert.notNull(messageDao, "messageDao must be set");
          }
        
          @Autowired
          public void setMessageDao(MessageDao messageDao) {
            this.messageDao = messageDao;
          }
        }
        This mix, doesn't work. I get this error:

        Code:
        Invocation of init method failed; nested exception is java.lang.IllegalArgumentException: messageDao must be set
        The sample code works also, if I completely remove the xml configuration and add an Component-Annotation to the class. Then, the Autowired annotation works like a charme.

        Any ideas?

        Timo

        Comment


        • #5
          Hi Timo. I assume then that you will add a primitive property to Message as well?

          So <context:component-scan> only scans @Component stereotypes, such as @Component itself, @Controller, @Service, @Repository and AspectJ's @Aspect. Since Message doesn't have that, it won't be picked up by a component scan, which is fine because you're wanting to define Message explicitly in the XML anyway so you can inject a primitive property (if I understand your goal correctly). So set @Autowired on the setter as you're doing (or you can even put it on the field--either way) and then add the <context:annotation-config/> element to your app context file. Again don't put the component scan in there unless you're using it for the DAO (in which case you can use it in conjunction with the @Repository annotation on the DAO class). Hopefully that will get you up and running.

          Comment


          • #6
            Dear Willie,

            you understood my goal absolutely correct. In the real application I want to configure a simple property, as well. But even in this sample configuration, the autowiring _doesn't_ work. It only works, if I completely remove the xml configuration and add the Component annotation to the message class (and activate component-scan).

            It seems, that xml configuration "overwrites" the annotation based configuration and if a xml conf is set, the annotations are ignored.

            By the way: The annotation PostConstruct always works, even if it's just a xml configuration.

            Timo
            Last edited by timomeinen; Jul 9th, 2008, 02:09 AM.

            Comment


            • #7
              If you want to use annotation configuration and NOT want to use component scanning you shoudn't use the component-scan tag. If you do this you will end up with 2 instances of your bean (or 1 if the name is the same 1 overriding the other), one from xml one from the component scanning.

              So instead use this.

              Code:
              <context:annotation-config />
              
              // your xml declarations go here

              Comment


              • #8
                Hey Timo. I think Marten and I are telling you the same thing. (Which makes me feel good since he's got about 5,000 posts to his credit.)

                If you want to inject the primitive property explicitly, then you must (at least as far as I'm aware) define the bean in the XML, rather than automagically discover it with @Component + <context:component-scan>. But once you remove <context:component-scan> (and also @Component), you have to have some way to tell Spring to autowire any @Autowired-marked dependencies. While <context:component-scan> does in fact tell Spring to do that, it's not the right mechanism here since the point of that scan is to automagically discover components. (The reason <context:component-scan> activates autowiring is that it's reasonable to assume autowiring in this context; there's no other way for you to inject dependencies into scanned components since they never appear in your app context file.)

                So there has to be some other way to activate autowiring. And that's what the <context:annotation-config/> element is doing. It basically tells Spring to honor @Autowired annotations. My guess is that you have forgotten to add <context:annotation-config/> to your config file and that's why your autowiring isn't working. Try it out and let us know. :-D
                Last edited by wwheeler; Jul 9th, 2008, 03:02 AM.

                Comment


                • #9
                  I get your point and I think, that I understand the dependencies between autowiring and component-scan. But, I have to disappoint you, because this is on the top of my applicationcontext.xml:

                  Code:
                  <!-- Annotation-based configuration --> 
                  	<context:annotation-config/>
                  	<context:component-scan base-package="de.timomeinen"/>
                  Because it's a migration of an established Spring-2.0 application to 2.5, and we hoped to reduce "static" configurations in the xml files massively, I have to realize, that a complete transition from xml to annotation conifguration is not possible.

                  My solution is, to change the beans with simple configuration (I mean conifgurations without property-values pairs) into an annotation based configuration. Whenever a bean has other configurations than property-ref pairs, I keep the old xml conifguration, because it works. ;-)

                  In summary the new annotation based configuration is a little disappointing, because it can only handle simple configurations. I found another problem with referenced beans, which are produced by a factory. In this case, the bean is not found via autowiring. Even not, if I set the bean name explicitly with the Qualifier annotation. But that is another issue.

                  Thank you very much for your help and let me know, if there are changes in the spring configuration via annotation.

                  Best regards,
                  Timo
                  timomeinen (at) gmail.com

                  Comment


                  • #10
                    Hi Timo. Let me write the code for it and post it. Give me a few minutes here...

                    Comment


                    • #11
                      OK, here it is:

                      Code:
                      <?xml version="1.0" encoding="UTF-8"?>
                      <beans xmlns="http://www.springframework.org/schema/beans"
                      	xmlns:context="http://www.springframework.org/schema/context"
                      	xmlns:p="http://www.springframework.org/schema/p"
                      	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-2.5.xsd
                      		http://www.springframework.org/schema/context
                      		http://www.springframework.org/schema/context/spring-context-2.5.xsd">
                      
                      	<context:annotation-config/>
                      	
                      	<bean id="dao" class="example.Dao"/>
                      	
                      	<!-- dao is autowired into service, but maxClients isn't -->
                      	<bean id="service" class="example.Service" p:maxClients="2000"/>
                      	
                      </beans>
                      and a couple of Java classes:

                      Code:
                      package example;
                      
                      public class Dao { }
                      Code:
                      package example;
                      
                      import org.springframework.beans.factory.annotation.Autowired;
                      
                      public class Service {
                      	private Dao dao;
                      	private int maxClients;
                      	
                      	@Autowired
                      	public void setDao(Dao dao) {
                      		System.out.println("Setting dao=" + dao);
                      		this.dao = dao;
                      	}
                      	
                      	public void setMaxClients(int maxClients) {
                      		System.out.println("Setting maxClients=" + maxClients);
                      		this.maxClients = maxClients;
                      	}
                      }
                      When I load up the context I get the following on the console:

                      Code:
                      INFO  DispatcherServlet - FrameworkServlet 'contact': initialization started
                      INFO  XmlWebApplicationContext - Refreshing [email protected]3ef810: display name [WebApplicationContext for namespace 'contact-servlet']; startup date [Wed Jul 09 02:42:32 MST 2008]; root of context hierarchy
                      INFO  XmlBeanDefinitionReader - Loading XML bean definitions from ServletContext resource [/WEB-INF/contact/beans-servlet.xml]
                      INFO  XmlWebApplicationContext - Bean factory for application context [[email protected]3ef810]: org.springframework.beans.factory.support.DefaultListableBeanFactory@11c2b67
                      INFO  DefaultListableBeanFactory - Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@11c2b67: defining beans [org.springframework.context.annotation.internalPersistenceAnnotationProcessor,org.springframework.context.annotation.internalCommonAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalRequiredAnnotationProcessor,dao,service]; root of factory hierarchy
                      Setting dao=example.Dao@c5e9c
                      Setting maxClients=2000
                      INFO  DispatcherServlet - FrameworkServlet 'contact': initialization completed in 891 ms
                      Hope that addresses what you're trying to do.

                      Comment


                      • #12
                        Dear Willie,

                        thank you very much for your work. I can't believe it works for you. This is exactly, what I want to do, but for me it don't work.

                        Beside this bean I have several other beans, which are autodetected via <context:component-scan>. Perhaps the problem is, that I have this feature set. Does the code still work for you, if you add <context:component-scan>?

                        Thank you
                        Timo

                        Comment


                        • #13
                          component-scan works ONLY with autowiring so if you have a @Repository bean it will only work with autowiring, configuration in XML for the same bean is ignored (well not exactly it will result in another bean instance).

                          Also when you have component-scan you don't need the annotation-config, that is automatically implied.

                          Comment


                          • #14
                            Hi Marten,

                            thanks for clarifying the component-scan issue. But how do you configure the bean properties, which cannot be autowired? Or does component-scan with autowiring just work for beans, which has only autowire-able references?

                            Comment


                            • #15
                              Timo, you lucky guy. I think I have something for you. :-)

                              I didn't realize that you were stuck with the component-scan for other beans. Now I see why that kept popping up in your examples.

                              So here's what you can do. Let's say your current application context file is called app-context.xml, and you are forced to keep component-scan in there. Simply add an import there, like this:

                              Code:
                              	<import resource="example-context.xml"/>
                              (OK, I don't know if one line of code justified using code tags, but Marten used the Force on me.)

                              OK, now that you have that, create the example-context.xml file, which is just the app context I gave above. Here it is again just to avoid confusion:

                              Code:
                              <?xml version="1.0" encoding="UTF-8"?>
                              <beans xmlns="http://www.springframework.org/schema/beans"
                              	xmlns:context="http://www.springframework.org/schema/context"
                              	xmlns:p="http://www.springframework.org/schema/p"
                              	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-2.5.xsd
                              		http://www.springframework.org/schema/context
                              		http://www.springframework.org/schema/context/spring-context-2.5.xsd">
                              	
                              	<context:annotation-config/>
                              	
                              	<bean id="dao" class="example.Dao"/>
                              	
                              	<!-- dao is autowired into service, but maxClients isn't -->
                              	<bean id="service" class="example.Service" p:maxClients="2000"/>
                              </beans>
                              Basically this will make the example beans visible to your current application context, and yet it will allow you to isolate those so that you can define them without component scanning.

                              Let me know.

                              Comment

                              Working...
                              X