3

我需要一个带有一些内部字段的服务(单例拟合),比如挂起的线程列表(是的,所有内容都写成线程安全的)问题是,如果我使用@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);
   }
}

monitor在我得到的 getList 调用期间调试变量

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)
4

3 回答 3

15

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

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

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

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

为什么 Spring 不能使用相同的类和委托 via super?我想为简单起见:首先创建“原始”bean,然后在后处理期间应用 AOP 代理。

于 2012-07-20T16:44:28.023 回答
4

“在调用 getSomething() 之前,这个字段肯定会被某人填充”

被某人?不,Spring bean 工厂。如果您不配置它,则不会填充任何内容。

并不是每个 bean 都需要在 Spring 的控制之下。听起来您想要一个List客户端可以以线程安全的方式添加和删除项目。如果是这样,请删除@Autowired注释,创建一个新的List,并公开添加和删除的方法。

我会推荐一个来自新并发集合的列表。

于 2012-07-20T14:08:02.977 回答
2

CGLIB 将代理受保护的 getter。

所以你可以拥有:

@Autowired
private Monitor monitor;

protected Monitor getMonitor() { return monitor; }

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

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

于 2014-11-14T13:05:05.377 回答