Most of the time, dependency injection is the first thing that comes to mind whenever the concept of inversion of control is brought up. For this article, I’d like to shed some light on method invocation as well, which is the concept being heavily utilized by Spring Integration.
在大多数情况下,每当提出控制反转的概念时,首先就会想到依赖注入。 对于本文,我也想对方法调用进行一些说明,这是Spring Integration大量使用的概念。
类型级耦合和系统级耦合 (Type-Level Coupling and System-Level Coupling)
Before diving into the code, let’s first talk about the types of coupling inversion of control solves: type-level coupling and system-level coupling.
在深入研究代码之前,让我们首先讨论控制解决方案的耦合反转类型:类型级耦合和系统级耦合。
Type-level coupling is probably the most understood — it’s coupling between types, and it’s solved by using dependency injection. I bet most of you know this already, but let me include an example below demonstrating the concept for clarity.
类型级别的耦合可能是最被理解的-它是类型之间的耦合,可以通过使用依赖注入来解决。 我敢打赌,你们大多数人已经知道了这一点,但让我在下面提供一个示例,以说明其概念。
Suppose we have an OrderService
class, and it’s being instantiated in other classes.
假设我们有一个OrderService
类,并且正在其他类中对其进行实例化。
Code with type-level coupling (in this case, unambiguous type coupling):
具有类型级别耦合的代码(在这种情况下,是明确的类型耦合):
class OrderService {
// a change in OrderService constructor will affect
// all other classes that instantiates OrderService.
// A problem that Dependency Injection solves.
public OrderService() {
}
}
class OrderController() {
private OrderService orderService;
public OrderController() {
this.orderService = new OrderService();
}
}
class ReportController() {
private OrderService orderService;
public ReportController() {
this.orderService = new OrderService();
}
}
A change in the constructor of the OrderService
will affect every other class that instantiates OrderService
.
OrderService
的构造函数的更改将影响实例化OrderService
所有其他类。
With dependency injection — where the creation of concrete instances is deferred to the framework that’s achieved by creating a bean of OrderService
and autowiring using either XML or Java DSL — this won’t be a problem.
通过依赖注入-具体实例的创建将推迟到通过创建OrderService
Bean并使用XML或Java DSL自动装配实现的框架上,这将不是问题。
@Service
class OrderService {
// Dependency Injection: Solution to Type Level Coupling
}
@RestController
class OrderController() {
@Autowired
private OrderService orderService;
}
@RestController
class ReportController() {
@Autowired
private OrderService orderService;
}
方法调用 (Method Invocation)
So much for dependency injection — let’s now dive into method invocation, which solves system-level coupling.
依赖注入的内容已经非常多了-现在让我们深入探讨方法调用,它解决了系统级耦合。
Imagine we’re dealing with multiple systems connected to each other using our previous example of the OrderService
class:
想象一下,使用前面的OrderService
类示例来处理多个相互连接的系统:
@Service
class WarehouseService {
public void updateInventory(...) {
...
}
}
@Service
class OrderService {
// warehouseService is coupled to OrderService (System Level Coupling)
// if for some reason warehouseService is unavailable, we won't be able to place an order
@Autowired
private WarehouseService warehouseService;
@Transactional
public void placeOrder(...) {
...
warehouseService.updateInventory(...);
}
}
Given the above, if for some reason the warehouseService
is unavailable, then we won’t be able to place an order. This might be the current business rule, but what if the business decided to at least put the order’s on hold whenever the warehouse service is unavailable?
鉴于上述情况,如果出于某些原因warehouseService
不可用,那么我们将无法下订单。 这可能是当前的业务规则,但是如果业务决定至少在仓库服务不可用时暂停订单,该怎么办?
This is where Spring Integration’s method invocation comes in handy by resolving the system-level coupling.
通过解决系统级耦合,Spring Integration的方法调用就可以派上用场了。
I’ll be demonstrating a simple solution (for demo purposes) wherein the Order
would be successfully placed on the orderQueue
, regardless if the warehouseService
is available or not.
我将演示一个简单的解决方案(出于演示目的),其中Order
将成功放置在orderQueue
,而不管warehouseService
是否可用。
package com.orderservice;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.integration.annotation.*;
import org.springframework.integration.channel.DirectChannel;
import org.springframework.integration.jms.*;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.messaging.*;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.jms.ConnectionFactory;
@SpringBootApplication
public class Main {
@Autowired
private ConnectionFactory connectionFactory;
public static void main(String[] args) {
ApplicationContext context = SpringApplication.run(Main.class, args);
OrderService orderService = context.getBean("orderService", OrderService.class);
orderService.placeOrder("Order is Placed on Queue");
}
@Bean
public MessageChannel inputChannel() {
return new DirectChannel();
}
@Bean
@ServiceActivator(inputChannel = "inputChannel")
public MessageHandler jsmOutboundAdapter() {
JmsTemplate template = new DynamicJmsTemplate();
template.setConnectionFactory(this.connectionFactory);
JmsSendingMessageHandler handler = new JmsSendingMessageHandler(template);
handler.setDestinationName("orderQueue");
return handler;
}
}
@Service
class OrderService {
@Autowired
private WarehouseService warehouseService;
@Transactional
public void placeOrder(String message) {
warehouseService.updateInventory(message);
}
}
@MessageEndpoint
class WarehouseService {
@Publisher(channel = "inputChannel")
public String updateInventory(String message) {
return message;
}
}
As we can see here, we’re still using the warehouseService
, but now even when the warehouse service is unavailable, we’d still be able to process the order once the warehouse service becomes available again. Running the code above will place an order to the orderQueue
.
正如我们在这里看到的那样,我们仍在使用warehouseService
,但是现在即使仓库服务不可用,一旦仓库服务再次可用,我们仍然能够处理订单。 运行上面的代码将向orderQueue
。
One message enqueued! You might’ve noticed that the number of consumers is 0
. It’s because we haven’t yet created the warehouseService
itself, and given this scenario, we can conclude that the OrderService
works despite being unaware of the warehouseService
.
一条消息入队! 您可能已经注意到,消费者数量为0
。 这是因为我们尚未创建warehouseService
本身,并且在这种情况下,我们可以得出结论,尽管不知道warehouseService
也可以使用OrderService
。
实施“ warehouseService” (Implementing the ‘warehouseService’)
We now have resolved the system-level coupling we were previously using with Spring Integration, which heavily utilizes method invocation. If you’re still here, though, then join me implementing the warehouseService
.
现在,我们已经解决了以前与Spring Integration一起使用的系统级耦合,该耦合大量利用了方法调用。 但是,如果您仍然在这里,请与我一起实现warehouseService
。
package com.warehouseservice;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.PropertySource;
import org.springframework.integration.annotation.*;
import org.springframework.integration.channel.DirectChannel;
import org.springframework.integration.jms.*;
import org.springframework.jms.listener.SimpleMessageListenerContainer;
import org.springframework.messaging.MessageChannel;
import org.springframework.transaction.annotation.Transactional;
import javax.jms.ConnectionFactory;
@SpringBootApplication
@PropertySource(value="application-dev.properties") // unnecessary if on separate project, demo purposes
public class Main {
public static void main(String[] args) {
SpringApplication.run(Main.class, args);
}
@Bean
public JmsInboundGateway jmsInboundGateway(ConnectionFactory connectionFactory) {
JmsInboundGateway gateway = new JmsInboundGateway(
simpleMessageListenerContainer(connectionFactory),
channelPublishingJmsMessageListener());
gateway.setRequestChannel(inputChannel());
return gateway;
}
@Bean
public SimpleMessageListenerContainer simpleMessageListenerContainer(
ConnectionFactory connectionFactory) {
SimpleMessageListenerContainer container =
new SimpleMessageListenerContainer();
container.setConnectionFactory(connectionFactory);
container.setDestinationName("orderQueue");
return container;
}
@Bean
public ChannelPublishingJmsMessageListener channelPublishingJmsMessageListener() {
ChannelPublishingJmsMessageListener channelPublishingJmsMessageListener =
new ChannelPublishingJmsMessageListener();
channelPublishingJmsMessageListener.setExpectReply(true);
return channelPublishingJmsMessageListener;
}
@Bean
public MessageChannel inputChannel() {
return new DirectChannel();
}
}
@MessageEndpoint
class WarehouseService {
@ServiceActivator(inputChannel = "inputChannel")
@Transactional
public void updateInventory(String message) {
System.out.println(message);
}
}
Let’s now run the warehouseService
we’ve created and check the ActiveMQ.
现在让我们来运行warehouseService
我们已经创建并检查ActiveMQ的。
We have one consumer, one message enqueued, and one message dequeued.
我们有一个使用者,一条消息入队,一条消息出队。
Once again, we’ve seen the beauty of Spring Integration’s method invocation, allowing us to declaratively mark updateInventory
with @ServiceActivator
to process the message from orderQueue
.
再次,我们看到了Spring Integration方法调用的美妙之处,它允许我们使用@ServiceActivator
声明性地标记updateInventory
以处理来自orderQueue
的消息。
结论 (Conclusion)
I hope you’ve enjoyed this article.
希望您喜欢这篇文章。
Happy coding!
编码愉快!
翻译自: https://medium.com/better-programming/spring-boot-integration-inversion-of-control-d65c1a1dcf2a