Spring 使用新构造函数初始化自动连线bean时会发生什么?
我过去用过弹簧。我搬到了另一个团队,在那里我开始熟悉代码库。我找到了下面的代码,并试图理解它是如何工作的,以及spring是如何在这种情况下注入自连线对象的。从我对Spring的基本知识来看,这绝对不是正确的方法。但令人惊讶的是,这段代码已经在生产中很长时间了,并且没有发现任何问题Spring 使用新构造函数初始化自动连线bean时会发生什么?,spring,spring-mvc,autowired,Spring,Spring Mvc,Autowired,我过去用过弹簧。我搬到了另一个团队,在那里我开始熟悉代码库。我找到了下面的代码,并试图理解它是如何工作的,以及spring是如何在这种情况下注入自连线对象的。从我对Spring的基本知识来看,这绝对不是正确的方法。但令人惊讶的是,这段代码已经在生产中很长时间了,并且没有发现任何问题 @Controller @RequestMapping("/start") public class AController implements Runnable, InitializingBean {
@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)本地主机:8080/project/start应该做什么。没有定义GET或POST方法
问题2)在注释的第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方法。在后一种情况下会发生什么?我们是否真的要停止已启动的服务?我想我们不会停止服务
我读过。这是非常有用的。但是它没有回答我的问题。我将尝试具体回答这些问题,因为代码的目的很难理解(至少对我来说) 问题1)我不清楚该代码试图实现什么。正如您所注意到的,它不是一个控制器,我怀疑它以这种方式注册的唯一原因是它可以自动扫描(这也可以通过使用
@controller
来完成)。这只是一种预感,我不太理解它的用途
问题2)答案是将创建两个实例,一个通过new
,另一个作为bean。在Spring中运行时,字段的最终值是bean,因为依赖项注入发生在构造之后。通常,当该类被设想在Spring之外使用时(例如,单元测试),就可以使用默认值初始化该字段
Q3)stop()
将在新实例上调用,而不是在bean上调用。服务bean被停止是因为在该行上直接调用注入的bean,但我猜下一个将是NPE,因为通过new
创建的目标对象上没有调用AfterPropertieSet
。日志中没有显示NPE的唯一原因是下面的异常被吞没了。线程
变量未初始化且保持为空
希望这能有所帮助,此代码在许多方面都有缺陷
@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
}
}
现在rest控制器所做的就是切换“活动”字段的值。好处:
- 每个班级只做一件事
- 你总是知道你有多少线程
- 您发布的代码非常奇怪
Q1)localhost:8080/project/start应该做什么。那里
未定义GET或POST方法
我认为
localhost:8080/project/start
将返回404错误(请求的资源不可用)。因为AController
中没有映射的方法<代码>@RequestMapping类级别上的注释不足以向控制器发出请求。必须有一个映射方法
但无论如何服务都会启动。因为AController
实现了初始化bean
。方法afterPropertieSet()
将在创建控制器并初始化所有字段后由Spring调用
问题2)在注释的第1行中,StartServiceImpl是自动连接的,并且
用“新”构建。那么这里发生了什么。这个容器有问题吗
注入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) {
}
}
}
另一个奇怪的片段。在创建AController
类的实例时,Java将创建StartServiceImpl
的新实例。但在这之后,Spring将把它自己的实例(声明为组件)分配给这个字段。并且对第一个实例(由构造函数创建)的引用将丢失
Q3)同样在注释的第2行中,调用停止,调用打开的停止
应用程序上下文或新对象中的bean被创建并
调用stop方法。在后一种情况下会发生什么?是
我们是否真的停止了已启动的服务?我想是的
不停止服务
实际上,服务将停止。因为调用了service.shutdownRequested()代码>。但是AController
bean中的线程将继续工作<代码>新建AController().stop()代码>将调用刚创建的实例的方法,但不会调用控制器的方法(由Spring创建的实例)
这段代码完全错误地使用了Spring框架。使用新的AController().stop()
有什么意义。我不明白。仅就测试而言,感谢SIS Q2最佳实践?一般来说,现场自动布线不被视为良好实践,而首选构造函数注入。唯一相对可接受的情况是在没有构造函数的测试类中。