获取被注入的原型bean中的父bean

发布于 2024-09-29 00:27:02 字数 2984 浏览 5 评论 0原文

我想要一个 Bean 和一个 SubBean,如下所示:

@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());
  }
}

我想要实现的技巧是,SubBean 自动获取对其注入的 Bean 的引用。因为子 bean 的范围是原型,所以它将作为新实例注入到每个希望注入它的父级中。

我的主要想法是利用这种模式来编写一个可以注入到普通 bean 中的 LoggerBean。子 bean 应该像 SLF4J Logger 一样工作。

那么有人知道让这项工作发挥作用的魔力吗? :)


编辑:我找到了一个使用自定义 BeanPostProcessor 执行此操作的解决方案:

@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;
  }
}

这是接口:

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

这里是使用它的 Bean:

@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;
  }
}

这里是使用正常 Bean 进行的测试,效果很好:

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

不过,当与通过 @Configurable 注入依赖项的普通类使用相同的方法时,测试失败:

@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
  }
}

所以这似乎让我想到了另一个问题:为什么我的自定义 BeanPostProcessor 不能在 @Configurable 类上运行?也许我毕竟不得不求助于 AspectJ...


编辑:只是为了更新状态。我最终没有实现这个,因为这是过度设计......

I would like to have a Bean and a SubBean like this:

@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());
  }
}

The trick I want to achieve is, that the SubBean automagically gets a reference to the Bean it got injected into. Because the scope of the subbean is prototype, it will get injected as a new instance in every parent that wants it to get injected.

My big idea is to exploit this pattern to write a LoggerBean which can be injected into normal beans. The subbean should work just like a SLF4J Logger.

So does anyone know the magic to make this work? :)


EDIT: I've found a solution to do this with a custom BeanPostProcessor:

@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;
  }
}

Here is the Interface:

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

And here a Bean using it:

@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;
  }
}

Here a test with a normal Bean which works perfectly:

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

Though, when using the same with a normal class which gets the depedencies injected via @Configurable, the test fails:

@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
  }
}

So this seems to have gotten me to another question: Why won't my custom BeanPostProcessor run on a @Configurable classes? Maybe I have to resort to AspectJ afterall...


EDIT: Just to update the status. I did not implement this afterall because this is overengineering...

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

内心荒芜 2024-10-06 00:27:02

我发现这个更简单:

@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());
  }
}

I find this simpler:

@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());
  }
}
颜漓半夏 2024-10-06 00:27:02

使用原始海报给出的解决方案修复了几个错误:

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;
    }
}

}

Fixed several bugs with the solution given by the original poster:

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;
    }
}

}

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文