2011-02-28 12 views
6
<beans default-autowire="byType /> 

significa que todos los campos de beans tendrán automáticamente dependencias inyectadas si no hay más de 1 bean con el tipo deseado.¿Pueden coexistir @Autowired y Autowire predeterminado?

La pregunta es cómo funciona esto cuando se utilizan anotaciones, y funciona en absoluto.

Mi prueba mostró que, incluso si uso

@Resource(name="someConcreteFoo") 
private Foo foo; 

el contexto intenta Autowire el campo según el tipo y falla si hay múltiples implementaciones de Foo. Entonces, por lo que veo, default-autowire no se mezcla con las anotaciones. No pude encontrar nada específico en la documentación.

Para ampliar la pregunta: ¿cómo se comporta la primavera con el cableado automático predeterminado cuando se usa solo xml. Es decir. si tiene <property>. ¿La inyección de propiedad anula el valor predeterminado (debería ser)?

Puedo hacer más pruebas, pero prefiero que el comportamiento sea confirmado por algunas citas. ¿Alguna idea?

+0

una nota particular no es útil: en casos similares que mirar el código fuente; toma menos tiempo que escribir y publicar una respuesta y me asegura que sé exactamente por qué/cómo. Desafortunadamente, no uso la primavera, así que no tengo una respuesta directa. – bestsss

+2

@bestsss Yo también lo hago. Y a pesar de que estoy bastante familiarizado con el código de la primavera (habiendo hecho esto en varias ocasiones), el mecanismo de autovínculo es un poco más difícil. Si tuviera un problema con esto, tal vez hubiera pasado algunas horas investigando. Actualmente es por curiosidad, así que solo estoy verificando si alguien ya lo sabe. – Bozho

+0

* un poco más difícil *; Supongo que (sin experiencia) punto de interrupción en un setter, estás seguro de que no llamas solo, examina la stacktrace. Paso hasta el código (o la falta de) vincular el recurso nombrado. Y felicitaciones por hacerlo usted mismo. Casi exclusivamente compilo las bibliotecas de terceros (de código abierto) de la fuente. – bestsss

Respuesta

1

Editar:

¿La inyección de la propiedad reemplazar el valor predeterminado (que debería ser).

Tienes razón. Si no desea que Spring inyecte la dependencia a un determinado campo de un bean, la anotación @Qualifier se puede usar para inyectar la dependencia deseada. Todavía estoy tratando de encontrar la documentación que confirmaría que — el más cercano que pude encontrar es un post en el foro de primavera override default-autowire setting with an annotation?

Editar: Aquí es otro post @Resource considered only after default-autowire="byName" que describe el uso de un nuevo InstantiationAwareBeanPostProcessor para cambiar el orden de cableado para tener @Resource en setter tiene prioridad sobre default-autowire.

0

Hasta donde yo sé, el atributo de autoarrendamiento predeterminado define el "modo de autoenvío" predeterminado para esos granos cableados en su configuración XML solamente. Entonces el autoenlace basado en anotaciones es independiente de eso. @Autowired es siempre por tipo, @Resource es siempre por nombre!
Consulte la sugerencia en la documentación de referencia de Spring 3.9.3:
"Si tiene la intención de expresar la inyección controlada por anotación por su nombre, no utilice principalmente @Autowired, incluso si es técnicamente capaz de hacer referencia a un nombre de bean a través de @Qualifier valores. En su lugar, use la anotación JSR-250 @Resource, que está semánticamente definida para identificar un componente objetivo específico por su nombre único, con el tipo declarado como irrelevante para el proceso de coincidencia ".

+1

sí, pero si usa ambas, no funciona. No es que espere que el Autowire predeterminado funcione para las anotaciones, pero ignora las anotaciones y falla. – Bozho

+0

Quiere decir que está recibiendo una excepción usando '@Resource (name =" someConcreteFoo ") Foo foo;' privado con 'default-autowire =" byType "' y con 'default-autowire =" byName "' la excepción no ocurre ? Esto sería un error en primavera, supongo. – Robin

+0

con cualquier cableado automático predeterminado, no solo porTipo. El punto es - no se puede tener predeterminado - autowire si quieres usar anotaciones. Quería asegurarme de que este es el caso – Bozho

3

Tuve una puñalada rápida para depurar este problema, y ​​creo que esto puede ser un error en la primavera. En mi opinión, el problema se deriva de la siguiente código en AbstractAutowireCapableBeanFactory

/** 
* Populate the bean instance in the given BeanWrapper with the property values 
* from the bean definition. 
* @param beanName the name of the bean 
* @param mbd the bean definition for the bean 
* @param bw BeanWrapper with bean instance 
*/ 
protected void populateBean(String beanName, AbstractBeanDefinition mbd, BeanWrapper bw) { 
    PropertyValues pvs = mbd.getPropertyValues(); 

    if (bw == null) { 
     if (!pvs.isEmpty()) { 
      throw new BeanCreationException(
        mbd.getResourceDescription(), beanName, "Cannot apply property values to null instance"); 
     } 
     else { 
      // Skip property population phase for null instance. 
      return; 
     } 
    } 

    // Give any InstantiationAwareBeanPostProcessors the opportunity to modify the 
    // state of the bean before properties are set. This can be used, for example, 
    // to support styles of field injection. 
    boolean continueWithPropertyPopulation = true; 

    if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) { 
     for (BeanPostProcessor bp : getBeanPostProcessors()) { 
      if (bp instanceof InstantiationAwareBeanPostProcessor) { 
       InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp; 
       if (!ibp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) { 
        continueWithPropertyPopulation = false; 
        break; 
       } 
      } 
     } 
    } 

    if (!continueWithPropertyPopulation) { 
     return; 
    } 

    if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_NAME || 
      mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_TYPE) { 
     MutablePropertyValues newPvs = new MutablePropertyValues(pvs); 

     // Add property values based on autowire by name if applicable. 
     if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_NAME) { 
      autowireByName(beanName, mbd, bw, newPvs); 
     } 

     // Add property values based on autowire by type if applicable. 
     if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_TYPE) { 
      autowireByType(beanName, mbd, bw, newPvs); 
     } 

     pvs = newPvs; 
    } 

    boolean hasInstAwareBpps = hasInstantiationAwareBeanPostProcessors(); 
    boolean needsDepCheck = (mbd.getDependencyCheck() != RootBeanDefinition.DEPENDENCY_CHECK_NONE); 

    if (hasInstAwareBpps || needsDepCheck) { 
     PropertyDescriptor[] filteredPds = filterPropertyDescriptorsForDependencyCheck(bw); 
     if (hasInstAwareBpps) { 
      for (BeanPostProcessor bp : getBeanPostProcessors()) { 
       if (bp instanceof InstantiationAwareBeanPostProcessor) { 
        InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp; 
        pvs = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName); 
        if (pvs == null) { 
         return; 
        } 
       } 
      } 
     } 
     if (needsDepCheck) { 
      checkDependencies(beanName, mbd, filteredPds, pvs); 
     } 
    } 

    applyPropertyValues(beanName, mbd, bw, pvs); 
} 

Personalmente creo que el orden de aplicación de la autowiring y la InstantiationAwareBeanPostProcessor es incorrecto, ya que la anotación @Resource sólo se aplica en los postProcessPropertyValues, así que después de el autoencendido (cuando ya ha fallado el autoenvío).

Ahora no sé si habría un impacto al cambiar el orden de las invocaciones, de modo que las anotaciones de @Resource se resuelven antes del autoenvío, pero esto puede ser algo que plantee como un error/solución (lo usé de la siguiente manera de cargar mi contexto de aplicación de prueba para solucionar este problema):

ApplicationContext ctx = new ClassPathXmlApplicationContext("test/appctx.xml") { 
     protected org.springframework.beans.factory.support.DefaultListableBeanFactory createBeanFactory() { 
      return new DefaultListableBeanFactory(getInternalParentBeanFactory()) { 
       protected void populateBean(String beanName, org.springframework.beans.factory.support.AbstractBeanDefinition mbd, org.springframework.beans.BeanWrapper bw) { 
        PropertyValues pvs = mbd.getPropertyValues(); 

        if (bw == null) { 
         if (!pvs.isEmpty()) { 
          throw new BeanCreationException(
            mbd.getResourceDescription(), beanName, "Cannot apply property values to null instance"); 
         } 
         else { 
          // Skip property population phase for null instance. 
          return; 
         } 
        } 

        // Give any InstantiationAwareBeanPostProcessors the opportunity to modify the 
        // state of the bean before properties are set. This can be used, for example, 
        // to support styles of field injection. 
        boolean continueWithPropertyPopulation = true; 

        if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) { 
         for (BeanPostProcessor bp : getBeanPostProcessors()) { 
          if (bp instanceof InstantiationAwareBeanPostProcessor) { 
           InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp; 
           if (!ibp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) { 
            continueWithPropertyPopulation = false; 
            break; 
           } 
          } 
         } 
        } 

        if (!continueWithPropertyPopulation) { 
         return; 
        } 

        boolean hasInstAwareBpps = hasInstantiationAwareBeanPostProcessors(); 
        boolean needsDepCheck = (mbd.getDependencyCheck() != RootBeanDefinition.DEPENDENCY_CHECK_NONE); 

        if (hasInstAwareBpps || needsDepCheck) { 
         PropertyDescriptor[] filteredPds = filterPropertyDescriptorsForDependencyCheck(bw); 
         if (hasInstAwareBpps) { 
          for (BeanPostProcessor bp : getBeanPostProcessors()) { 
           if (bp instanceof InstantiationAwareBeanPostProcessor) { 
            InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp; 
            pvs = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName); 
            if (pvs == null) { 
             return; 
            } 
           } 
          } 
         } 
         if (needsDepCheck) { 
          checkDependencies(beanName, mbd, filteredPds, pvs); 
         } 
        } 

        if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_NAME || 
          mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_TYPE) { 
         MutablePropertyValues newPvs = new MutablePropertyValues(pvs); 

         // Add property values based on autowire by name if applicable. 
         if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_NAME) { 
          autowireByName(beanName, mbd, bw, newPvs); 
         } 

         // Add property values based on autowire by type if applicable. 
         if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_TYPE) { 
          autowireByType(beanName, mbd, bw, newPvs); 
         } 

         pvs = newPvs; 
        } 

        applyPropertyValues(beanName, mbd, bw, pvs); 
       } 
      }; 
     } 
    }; 

Espero que ayude

Cuestiones relacionadas