spring单例bean字段未填充

时间:2012-07-20 13:59:57

标签: java spring

我需要一个服务(单身适合)和一些内部字段,比如挂起的线程列表(是的,所有内容都写成线程安全的)问题是,如果我@autowire这个bean,字段似乎是空。调试我看到代理正确绑定到实例(字段CGLIB$CALLBACK_X正确链接到填充的bean),填充字段,但它提供的字段为空。

以下代码行概括了我正在谈论的内容。

@Service
public class myService{

   @Autowired
   private Monitor monitor;

   public List getSomething(){
       return monitor.getList();
   }
}


@Service
public class myStatefulService{

   //This field will be populated for sure by someone before getSomething() is called
   private List list;

   public synchronized List getSomething(){
       return this.list;
   }

   //Called by other services that self inject this bean 
   public synchronized void addToList(Object o){
      this.list.add(o);
   }
}

在getList调用期间调试变量monitor

monitor => instance of correct class
 fields:
   CGLIB$BOUND => true
   CGLIB$CALLBACK_0.advised => proxyFactory (correct)
   CGLIB$CALLBACK_1.target (reference to the correct instance of myStatefulService class)
        fields:
          list => [.........] (correctly populated)
   CGLIB$CALLBACK_2 ..... 
   ......
   ......
   ......
   list => [] (the list that would be populated is empty instead)

3 个答案:

答案 0 :(得分:12)

你好奇还是有一些真正的问题?不过这是一个解释。

当使用CGLIB代理类时,Spring将创建一个名为myService$EnhancerByCGLIB的子类。这个增强的类将覆盖一些(如果不是全部)商业方法,以便在实际代码中应用横切关注点。

这真是一个惊喜。这个额外的子类不会调用基类的super方法。相反,它会创建myService的第二个实例并委托给它。这意味着你现在有两个对象:你的真实对象和指向(包装)它的CGLIB增强对象。

增强类只是一个虚拟代理。它仍然具有与基类相同的字段(从中继承)但不使用它们。当您在addToList()对象上调用myService$EnhancerByCGLIB时,它将首先应用一些AOP逻辑,调用addToList() myService(它包装)并在返回时应用剩余的AOP逻辑。永远不会触及myService$EnhancerByCGLIB.list字段。

为什么Spring不能通过super使用同一个类和委托?我想简单一点:首先创建“ raw ”bean,然后在后期处理过程中应用AOP代理。

答案 1 :(得分:4)

“在调用getSomething()”

之前,某个人肯定会填充此字段

有人吗?不,春豆厂。如果您不配置它,则不会填充任何内容。

并非每个bean都需要受Spring控制。听起来你想要一个List客户端可以以线程安全的方式添加和删除项目。如果确实如此,请删除@Autowired注释,创建新的List,并公开要添加和删除的方法。

我建议使用新的并发集合中的List。

答案 2 :(得分:1)

CGLIB将代理受保护的getter。

所以你可以:

@Autowired
private Monitor monitor;

protected Monitor getMonitor() { return monitor; }

public List getSomething(){
    return getMonitor().getList();
}

getMonitor()将代理在另一个已注入监视器的实例上调用getMonitor()。