1

je voudrais avoir un grain et un SubBean comme ceci:Get haricot mère dans le haricot prototype qui obtient injecté

@Scope(BeanDefinition.SCOPE_PROTOTYPE) 
@Component 
public class SubBean implements ApplicationContextAware{ 
    private Object parent; 
    public void setApplicationContext(ApplicationContext ctx){ 
    this.parent = doSomeMagicToGetMyParent(ctx); 
    } 
    public Object getParent(){ 
    return parent; 
    } 
} 

@Component 
public class SomeBean implements InitializingBean{ 
    @Resource 
    private SubBean sub; 

    public void afterPropertiesSet(){ 
    Assert.isTrue(this == sub.getParent()); 
    } 
} 

L'astuce que je veux atteindre est que le SubBean obtient automagiquement une référence au Bean il s'est injecté dedans. Étant donné que la portée de la sous-classe est prototype, elle sera injectée en tant que nouvelle instance dans chaque parent qui veut l'injecter.

Ma grande idée est d'exploiter ce modèle pour écrire un LoggerBean qui peut être injecté dans des beans normaux. Le sous-sol devrait fonctionner comme un enregistreur SLF4J.

Alors, est-ce que quelqu'un sait la magie pour faire ce travail? :)


EDIT: Je ai trouvé une solution pour faire cela avec un BeanPostProcessor personnalisé:

@Component 
public class DependencyInjectionAwareBeanPostProcessor implements BeanPostProcessor { 
    @Override 
    public Object postProcessBeforeInitialization(Object bean, String beanName) { 
    return bean; 
    } 
    @Override 
    public Object postProcessAfterInitialization(Object bean, String beanName) { 
    for (Field f : bean.getClass().getFields()) { 
     if (f.getType().isInstance(IDependencyInjectionAware.class)) { 
      ReflectionUtils.makeAccessible(f); 
      try { 
       IDependencyInjectionAware diAware = (IDependencyInjectionAware) f.get(bean); 
       diAware.injectedInto(bean); 
      } catch (IllegalArgumentException e) { 
       ReflectionUtils.handleReflectionException(e); 
      } catch (IllegalAccessException e) { 
       ReflectionUtils.handleReflectionException(e); 
      } 
     } 
    } 
    return bean; 
    } 
} 

Voici l'interface:

public interface IDependencyInjectionAware { 
    void injectedInto(Object parent); 
} 

Et ici un Bean en l'utilisant:

@Scope(BeanDefinition.SCOPE_PROTOTYPE) 
@Component 
public class SomeAwareBean implements IDependencyInjectionAware { 
    private Object parent; 
    public void injectedInto(Object parent){ 
    this.parent = parent; 
    } 
    public Object getParent(){ 
    return parent; 
    } 
} 

Voici un test avec un grain normal qui fonctionne parfaitement:

@Component 
public class UsingBean implements InitializingBean { 
    @Resource 
    private SomeAwareBean b; 
    public void afterPropertiesSet(){ 
    Assert.notNull(b); //works 
    Assert.isTrue(b.getParent() == this); //works 
    } 
} 

Bien que, lorsque vous utilisez la même chose avec une classe normale qui obtient les depedencies injectés par @Configurable, le test échoue:

@Configurable 
public class UsingPlainClass implements InitializingBean { 
    @Resource 
    private SomeAwareBean b; 
    public void afterPropertiesSet(){ 
    Assert.notNull(b); //works 
    Assert.isTrue(b.getParent() == this); //fails because null is returned 
    } 
} 

Cela semble donc m'avoir amené à une autre question: pourquoi mon BeanPostProcessor personnalisé ne fonctionnera-t-il pas sur des classes @Configurable? Peut-être que je dois recourir à AspectJ ... après tout


EDIT: Juste pour mettre à jour le statut. Je ne suis pas mettre en œuvre cette afterall parce que c'est overengineering ...

Répondre

0

Je trouve cela plus simple:

@Scope(BeanDefinition.SCOPE_PROTOTYPE) 
@Component 
public class SubBean implements ApplicationContextAware{ 
    private Object parent; 
    public void setApplicationContext(ApplicationContext ctx){ 
    ... 
    } 
    public Object getParent(){ 
    return parent; 
    } 
    //ADDED CODE 
    public void setParent(Object parent) { 
    this.parent = parent; 
    } 
    //END ADDED CODE 
} 

@Component 
public class SomeBean implements InitializingBean{ 

    private SubBean sub; 
    //ADDED CODE 
    @Resource 
    public void setSub(SubBean sub) { 
     this.sub = sub; 
     sub.setParent(this); 
    } 
    //END ADDED CODE 

    public void afterPropertiesSet(){ 
    Assert.isTrue(this == sub.getParent()); 
    } 
} 
0

Correction de plusieurs bugs avec la solution donnée par l'affiche originale:

import java.lang.reflect.Field; 

import org.apache.log4j.Logger; 
import org.springframework.beans.factory.config.BeanPostProcessor; 
import org.springframework.util.ReflectionUtils; 

public interface DependencyInjectionAware { 

void injectedInto(final Object bean, final String beanName); 

public static class DependencyInjectionAwareBeanPostProcessor implements 
     BeanPostProcessor { 

    private static final Logger logger = Logger.getLogger(DependencyInjectionAwareBeanPostProcessor.class); 

    @Override 
    public Object postProcessBeforeInitialization(final Object bean, 
      final String beanName) { 
     return bean; 
    } 

    @Override 
    public Object postProcessAfterInitialization(final Object bean, 
      final String beanName) { 
     for (final Field f : bean.getClass().getDeclaredFields()) { 
      logger.info("scanning field " + f.getName() + " of bean " + beanName + " (class= " + bean.getClass() + ")"); 

      if (DependencyInjectionAware.class.isAssignableFrom(f.getType())) { 
       ReflectionUtils.makeAccessible(f); 
       try { 
        final DependencyInjectionAware diAware = (DependencyInjectionAware) f.get(bean); 
        diAware.injectedInto(bean, beanName); 
       } catch (final IllegalArgumentException e) { 
        ReflectionUtils.handleReflectionException(e); 
       } catch (final IllegalAccessException e) { 
        ReflectionUtils.handleReflectionException(e); 
       }     
      } 
     } 
     return bean; 
    } 
} 

}