5 Replies Latest reply on Oct 18, 2006 10:32 AM by alesj

    New feature proposal: byType injection of Spring beans

    c.vidal

      Hi guys,

      The current JBoss Spring integration seems to support only byName injection, it would be great if it could support also byType injection.

      ByType injection of dependencies is a very powerfull feature of Spring, and it would be great if it could be leveraged in the JBoss Spring integration. ByType injection could be attempted by the SpringInjectionSupport whenever the bean attribute of the Spring annotation is missing.

      @Spring(jndiName = "spring-pojo")
      private IntCreator intCreator;


      This feature is restricted to BeanFactory instances which are ListableBeanFactory though. In this case, the method to call is:

      Map ListableBeanFactory#getBeansOfType(Class type) throws BeansException;


      The only difficulty with this kind of injection is if more than one bean is of the expected type. The behavior to follow in that case is described here:
      http://static.springframework.org/spring/docs/2.0.x/reference/beans.html#beans-factory-autowire

      Is anybody else interested by that functionality ?

      Kind regards !


        • 1. Re: New feature proposal: byType injection of Spring beans
          alesj

          Yep - an interesting feature.
          Working on the same thing on Microcontainer. :-)

          There are some issues with Spring 2.0 compatibility - so I hope I'll find some time to implement this stuff.

          But it will definitely be a part of new JBoss deployers.

          • 2. Re: New feature proposal: byType injection of Spring beans
            c.vidal

            Great, glad to hear that :D

            • 3. Re: New feature proposal: byType injection of Spring beans
              c.vidal

              Hi Ales,

              Here is a quick and dirty patch. I just couldn't wait :) I didn't have the time to figure out how the jboss unit tests work though, so I didn't add any unit tests. If you could just explain that to me quickly, I'd be glad to add them.

              The code tries to do as much guess-work as possible and i'm not sure if it's the best way to do it. The algorithm used is explained in the code.

              Tell me if it suits you.

              Regards,

              Cédric

              Index: spring-int/src/main/org/jboss/spring/support/SpringInjectionSupport.java
              ===================================================================
              --- spring-int/src/main/org/jboss/spring/support/SpringInjectionSupport.java (revision 57255)
              +++ spring-int/src/main/org/jboss/spring/support/SpringInjectionSupport.java (working copy)
              @@ -26,6 +26,7 @@
               import org.jboss.logging.Logger;
               import org.jboss.naming.Util;
               import org.springframework.beans.factory.BeanFactory;
              +import org.springframework.beans.factory.ListableBeanFactory;
               import org.springframework.util.Assert;
              
               import java.lang.reflect.Field;
              @@ -111,30 +112,101 @@
               return m.getName().startsWith("set") && m.getParameterTypes().length == 1;
               }
              
              - private Object getObjectFromBeanFactory(Spring spring) throws Exception
              + private Object getObjectFromBeanFactory(Spring spring, String defaultBeanName, Class type) throws Exception
               {
               BeanFactory beanFactory = (BeanFactory) Util.lookup(spring.jndiName(), BeanFactory.class);
              - return beanFactory.getBean(spring.bean());
              + return getObjectFromBeanFactory(spring, beanFactory, defaultBeanName, type);
               }
              
              + private Object getObjectFromBeanFactory(Spring spring, BeanFactory beanFactory, String defaultBeanName, Class type) {
              + Object bean = null;
              +
              + if(spring.bean() != null && spring.bean().length() > 0) {
              +
              + /*
              + * If the bean name is specified in the annotation, then the bean
              + * must looked up by name whether the bean factory is listable or
              + * not.
              + */
              + bean = beanFactory.getBean(spring.bean());
              +
              + } else {
              + if (beanFactory instanceof ListableBeanFactory) {
              +
              + /*
              + * If no bean name is specified in the annotation but the bean
              + * factory is listable then the bean is looked up by type.
              + */
              + ListableBeanFactory listableBeanFactory = (ListableBeanFactory) beanFactory;
              + Map beans = listableBeanFactory.getBeansOfType(type);
              +
              + if(beans.size() > 1) {
              +
              + /*
              + * If there is a ambiguity, try with the default name
              + */
              + bean = beans.get(defaultBeanName);
              +
              + /*
              + * If no beans amongst the ones of the expected type has the
              + * default name then we can't do anything.
              + */
              + if(bean == null) {
              + throw new IllegalArgumentException("More than one bean is of type " + type);
              + }
              +
              + } else {
              + Iterator beansIterator = beans.values().iterator();
              + if(beansIterator.hasNext()) {
              + bean = beansIterator.next();
              + }
              + }
              +
              + } else {
              +
              + /*
              + * If no bean name is specified in the annotation and the bean
              + * factory is not listable then the bean can only be looked up
              + * by its default name.
              + */
              + bean = beanFactory.getBean(defaultBeanName);
              +
              + }
              + }
              + return bean;
              + }
              +
               private void injectToMethod(Object target, Method method, Spring spring) throws Exception
               {
              - Object bean = getObjectFromBeanFactory(spring);
              + String defaultBeanName = getDefaultBeanName(method);
              + Object bean = getObjectFromBeanFactory(spring, defaultBeanName, method.getReturnType());
               doAssert(bean, method.getParameterTypes()[0]);
               logInjection(spring, bean, target, method);
               method.setAccessible(true);
               method.invoke(target, bean);
               }
              
              - private void injectToField(Object target, Field field, Spring spring) throws Exception
              + private void injectToField(Object target, Field field, Spring spring) throws Exception
               {
              - Object bean = getObjectFromBeanFactory(spring);
              + String defaultBeanName = getDefaultBeanName(field);
              + Object bean = getObjectFromBeanFactory(spring, defaultBeanName, field.getType());
               doAssert(bean, field.getType());
               logInjection(spring, bean, target, field);
               field.setAccessible(true);
               field.set(target, bean);
               }
              
              + private String getDefaultBeanName(Method method) {
              + String name = method.getName().substring(3, 3).toLowerCase();
              + name += method.getName().substring(4);
              + return name;
              + }
              +
              + private String getDefaultBeanName(Field field) {
              + String name = field.getName();
              + return name;
              + }
              +
               private void doAssert(Object bean, Class expectedBeanClass)
               {
               Assert.isTrue(expectedBeanClass.isAssignableFrom(bean.getClass()),
              Index: spring-int/src/main/org/jboss/annotation/spring/Spring.java
              ===================================================================
              --- spring-int/src/main/org/jboss/annotation/spring/Spring.java (revision 57255)
              +++ spring-int/src/main/org/jboss/annotation/spring/Spring.java (working copy)
              @@ -38,6 +38,6 @@
              
               String jndiName();
              
              - String bean();
              + String bean() default "";
              
               }
              


              • 4. Re: New feature proposal: byType injection of Spring beans
                c.vidal

                Btw, the patch was made against trunk, relative to the jboss root directory.

                • 5. Re: New feature proposal: byType injection of Spring beans
                  alesj

                  SpringDeployer compatible with Spring2.0 final.
                  http://www.jboss.org/index.html?module=bb&op=viewtopic&p=3979096