使用新构造函数初始化自动装配的 bean 时会发生什么?

What happens when autowired bean is initialized with new constructor?

我以前用过Spring。我搬到了另一个团队,在那里我熟悉了代码库。我找到了以下代码并试图了解它是如何工作的以及 spring 如何在这种情况下注入自动装配的对象。从我Spring的基础来看,这绝对不是正确的做法。但令人惊讶的是,这段代码已经投入生产很长时间了,没有发现任何问题。

@Controller
@RequestMapping("/start")
public class AController implements Runnable, InitializingBean {

    @Autowired
    private StartServiceImpl service = new StartServiceImpl(); // 1

    Thread thread;

    public void run() {

        service.start();
    }

    public void stop()  {
            try {
                thread.join(); 
            } catch (InterruptedException e) {
            }
        }
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        thread = new Thread(this);
        thread.setPriority(Thread.MAX_PRIORITY);
        thread.start();
    }
}

@Component
public class StartServiceImpl {
    //methods
}

Q1) localhost:8080/project/start 预期做什么。没有定义 GET 或 POST 方法。

Q2) 在注释行 1 中,StartServiceImpl 是自动装配的,并且是用 "new" 构造的。那么这里发生了什么。容器注入bean还是只实例化一个对象

 @Controller
    @RequestMapping("/stop")

public class BController {

    @Autowired
    private StartServiceImpl service = new StartServiceImpl();

    @RequestMapping(value = "**", method = RequestMethod.GET)
    public void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        try {

            service.shutdownRequested();
            new AController().stop(); // 2
        } catch (Exception e) {
        }
    }
}

Q3) 再次在注释行 2 中调用 stop,在应用程序上下文中的 bean 上调用 stop,或者创建一个新对象并调用 stop 方法。在后一种情况下会发生什么?我们是否真的停止了启动或未启动的服务?我想我们不会停止服务。

我已阅读this post。这非常有用。但它没有回答我的问题。

我会尽量具体地回答问题,因为代码的目的很难理解(至少对我而言)。

Q1) 我不清楚这段代码试图实现什么。正如您所注意到的,它不是一个控制器,我怀疑它以这种方式注册的唯一原因是它会被自动扫描(这也可以通过使用 @Controller 来完成)。这只是一个预感,不太明白它的用途

Q2) 答案是将创建两个实例,一个通过 new,另一个作为 bean。当运行 in Spring时,该字段的最终值为bean,因为依赖注入发生在构建之后。通常,当 class 被设想在 Spring 之外使用(例如单元测试)时,会执行此操作,以便可以使用默认值初始化该字段。

Q3) stop() 将在新实例上调用,而不是在 bean 上调用。由于在该行上方直接调用注入的 bean,服务 bean 已停止,但我猜下一个将是 NPE,因为未在通过 new 创建的目标对象上调用 afterPropertiesSet .这在日志中没有显示 NPE 的唯一原因是因为下面吞下了异常。 thread 变量未初始化并保持为 NULL。

希望这对您有所帮助,

这段代码在很多层面上都有缺陷。

  1. 自从 Java 5 以来,手动启动线程是一种反模式。它很乱而且太低级了。应使用 ExecutorServices。
  2. 作为 Runnable 的 Rest 控制器?这是一个可怕的问题。
  3. 服务是通过 new 创建的,但随后被自动装配的依赖覆盖?卧槽!
  4. 等等

我会一直保持线程 运行,scheduling the task with the @Scheduled annotation 并使用控制器切换一个标志,该标志决定线程是否实际执行某些操作,例如

@Service
class StartService{
   private boolean active;
   public void setActive(boolean active){this.active=active;}


   @Scheduled(fixedRate=5000)
   public void doStuff(){
      if(!active)return;
      // do actual stuff here
   }

}

现在所有其他控制器所做的就是切换 "active" 字段的值。好处:

  • 每个class只做一件事
  • 你总是知道你有多少线程

你发的代码很奇怪。

Q1 ) What does localhost:8080/project/start is expected to do. There is NO GET or POST methods defined.

我认为localhost:8080/project/start会return404错误(请求的资源不可用)。因为 AController 中没有映射方法。 @RequestMapping class 级别的注释不足以向控制器发出请求。有必须映射的方法。

但无论如何都会启动服务。因为 AController 实现了 InitializingBean。创建控制器并初始化所有字段后,Spring 将调用方法 afterPropertiesSet()

Q2) on the commented line 1, StartServiceImpl is both autowired and constructed with "new". so what happens here. does the container inject bean or just an object is instantiated.

另一个奇怪的片段。 Java 将在创建 AController class 的实例时创建 StartServiceImpl 的新实例。但在那之后,Spring 会将它自己的实例(声明为组件)分配给该字段。并且对第一个实例(由构造函数创建)的引用将丢失。

Q3) Again in commented line 2, does calling stop, calls the stop on the bean in the appication context or a new object gets created and the stop method is called. what would happen in the latter case? Are we really stopping the service that was started or not? I think we are not stopping the service

实际上服务将被停止。因为调用了 service.shutdownRequested();。但是 AController bean 中的线程将继续工作。 new AController().stop(); 将调用刚刚创建的实例的方法,但不会调用控制器的方法(由 Spring 创建的实例)。


此代码完全错误地使用了 Spring 框架。