第二章-集成spring

本章主要介绍了spring REST服务以及与JMS、RabbitMQ的集成。

构建一个REST服务

常用的几个请求方式注解。
Alt text
案例:
Alt text

允许XML输出

[允许XML输出]
1
@RequestMapping(path="/design", produces={"application/json", "text/xml"})

url传参

[url传参]
1
2
3
4
5
6
7
8
@GetMapping("/{id}")
public Taco tacoById(@PathVariable("id") Long id) {
Optional<Taco> optTaco = tacoRepo.findById(id);
if (optTaco.isPresent()) {
return optTaco.get();
}
return null;
}

url返回404

[返回404]
1
2
3
4
5
6
7
8
@GetMapping("/{id}")
public ResponseEntity<Taco> tacoById(@PathVariable("id") Long id) {
Optional<Taco> optTaco = tacoRepo.findById(id);
if (optTaco.isPresent()) {
return new ResponseEntity<>(optTaco.get(), HttpStatus.OK);
}
return new ResponseEntity<>(null, HttpStatus.NOT_FOUND);
}

使用REST服务

使用RestTemplate使用REST端点

Alt text

配置RestTemplate

[spring配置类]
1
2
3
4
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}

获得资源(Getting resouces)

[查询:getForObject()]
1
2
3
4
5
6
public Ingredient getIngredientById(String ingredientId) {
Map<String,String> urlVariables = new HashMap<>();
urlVariables.put("id", ingredientId);
return rest.getForObject("http://localhost:8080/ingredients/{id}",
Ingredient.class, urlVariables);
}
[查询:URI+getForObject()]
1
2
3
4
5
6
7
8
public Ingredient getIngredientById(String ingredientId) {
Map<String,String> urlVariables = new HashMap<>();
urlVariables.put("id", ingredientId);
URI url = UriComponentsBuilder
.fromHttpUrl("http://localhost:8080/ingredients/{id}")
.build(urlVariables);
return rest.getForObject(url, Ingredient.class);
}
[查询:getForEntity()]
1
2
3
4
5
6
7
8
public Ingredient getIngredientById(String ingredientId) {
ResponseEntity<Ingredient> responseEntity =
rest.getForEntity("http://localhost:8080/ingredients/{id}",
Ingredient.class, ingredientId);
log.info("Fetched time: " +
responseEntity.getHeaders().getDate());
return responseEntity.getBody();
}

更新资源(Putting resouces)

[新增或编辑]
1
2
3
4
5
public void updateIngredient(Ingredient ingredient) {
rest.put("http://localhost:8080/ingredients/{id}",
ingredient,
ingredient.getId());
}

删除资源(DELETEing resources)

[删除]
1
2
3
4
public void deleteIngredient(Ingredient ingredient) {
rest.delete("http://localhost:8080/ingredients/{id}",
ingredient.getId());
}

发布资源(POSTing resource data)

[发布资源]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 方式一
public Ingredient createIngredient(Ingredient ingredient) {
return rest.postForObject("http://localhost:8080/ingredients",
ingredient,
Ingredient.class);
}
// 方式二
public URI createIngredient(Ingredient ingredient) {
return rest.postForLocation("http://localhost:8080/ingredients",
ingredient);
}
// 方式三
public Ingredient createIngredient(Ingredient ingredient) {
ResponseEntity<Ingredient> responseEntity =
rest.postForEntity("http://localhost:8080/ingredients",
ingredient,
Ingredient.class);
log.info("New resource created at " +
responseEntity.getHeaders().getLocation());
return responseEntity.getBody();
}

异步发送消息

使用JMS发送消息

安装JMS

  1. 添加依赖

    [pom.xml]
    1
    2
    3
    4
    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-activemq</artifactId>
    </dependency>
  2. 修改spring配置文件

    [spring.yml]
    1
    2
    3
    4
    5
    spring:
    activemq:
    broker-url: tcp://activemq.tacocloud.com
    user: tacoweb
    password: l3tm31n

使用JmsTemplate发送消息

JmsTemplate有几个用于发送消息的方法,包括以下方法:

[JmsTemplate核心方法]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// Send raw messages
void send(MessageCreator messageCreator) throws JmsException;
void send(Destination destination, MessageCreator messageCreator)
throws JmsException;
void send(String destinationName, MessageCreator messageCreator)
throws JmsException;
// Send messages converted from objects
void convertAndSend(Object message) throws JmsException;
void convertAndSend(Destination destination, Object message)
throws JmsException;
void convertAndSend(String destinationName, Object message)
throws JmsException;
// Send messages converted from objects with post-processing
void convertAndSend(Object message,
MessagePostProcessor postProcessor) throws JmsException;
void convertAndSend(Destination destination, Object message,
MessagePostProcessor postProcessor) throws JmsException;
void convertAndSend(String destinationName, Object message,
MessagePostProcessor postProcessor) throws JmsException;
编写发送方法
[send()]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@Service
public class JmsOrderMessagingService implements OrderMessagingService {
private JmsTemplate jms;
@Autowired
public JmsOrderMessagingService(JmsTemplate jms) {
this.jms = jms;
}
/**@Override
public void sendOrder(Order order) {
jms.send(new MessageCreator() {
@Override
public Message createMessage(Session session)
throws JMSException {
return session.createObjectMessage(order);
}
}
);
}*/
// 简化版
@Override
public void sendOrder(Order order) {
jms.send(session -> session.createObjectMessage(order));
}
}
指定发送目的地
[配置目的地]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
// 1.spring.yml文件
spring:
jms:
template:
default-destination: tacocloud.order.queue

// 2.配置类
@Bean
public Destination orderQueue() {
return new ActiveMQQueue("tacocloud.order.queue");
}

// 3.注入到service类,JmsOrderMessagingService
private Destination orderQueue;
@Autowired
public JmsOrderMessagingService(JmsTemplate jms,
Destination orderQueue) {
this.jms = jms;
this.orderQueue = orderQueue;
}
...
@Override
public void sendOrder(Order order) {
jms.send(
orderQueue,
session -> session.createObjectMessage(order));
}

// 4.使用类似这样的目标对象指定目标,使您有机会配置目标,而不仅仅是目标名称。
@Override
public void sendOrder(Order order) {
jms.send(
"tacocloud.order.queue",
session -> session.createObjectMessage(order));
}
发送前对消息进行转换
[代码块]
1
2
3
4
@Override
public void sendOrder(Order order) {
jms.convertAndSend("tacocloud.order.queue", order);
}
配置消息转换器
[代码块]
1
2
3
4
5
public interface MessageConverter {
Message toMessage(Object object, Session session)
throws JMSException, MessageConversionException;
Object fromMessage(Message message);
}

Alt text

[MappingJackson2MessageConverter]
1
2
3
4
5
6
7
@Bean
public MappingJackson2MessageConverter messageConverter() {
MappingJackson2MessageConverter messageConverter =
new MappingJackson2MessageConverter();
messageConverter.setTypeIdPropertyName("_typeId");
return messageConverter;
}
后期处理消息

在消息中添加一个自定义头。

[代码块]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
jms.send("tacocloud.order.queue",
session -> {
Message message = session.createObjectMessage(order);
message.setStringProperty("X_ORDER_SOURCE", "WEB");
});
// 案例
jms.convertAndSend("tacocloud.order.queue", order, new MessagePostProcessor() {
@Override
public Message postProcessMessage(Message message) throws JMSException {
message.setStringProperty("X_ORDER_SOURCE", "WEB");
return message;
}
});
// 案例-优化
jms.convertAndSend("tacocloud.order.queue", order,
message -> {
message.setStringProperty("X_ORDER_SOURCE", "WEB");
return message;
});
// 特殊情况
@GetMapping("/convertAndSend/order")
public String convertAndSendOrder() {
Order order = buildOrder();
jms.convertAndSend("tacocloud.order.queue", order,
this::addOrderSource);
return "Convert and sent order";
}
private Message addOrderSource(Message message) throws JMSException {
message.setStringProperty("X_ORDER_SOURCE", "WEB");
return message;
}

接收JMS消息

接收JMS TEMPLATE

核心方法:

[代码块]
1
2
3
4
5
6
Message receive() throws JmsException;
Message receive(Destination destination) throws JmsException;
Message receive(String destinationName) throws JmsException;
Object receiveAndConvert() throws JmsException;
Object receiveAndConvert(Destination destination) throws JmsException;
Object receiveAndConvert(String destinationName) throws JmsException;

案例:

[代码块]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// 案例一
@Component
public class JmsOrderReceiver implements OrderReceiver {
private JmsTemplate jms;
private MessageConverter converter;
@Autowired
public JmsOrderReceiver(JmsTemplate jms, MessageConverter converter) {
this.jms = jms;
this.converter = converter;
}
public Order receiveOrder() {
Message message = jms.receive("tacocloud.order.queue");
return (Order) converter.fromMessage(message);
}
}
// 案例二
@Component
public class JmsOrderReceiver implements OrderReceiver {
private JmsTemplate jms;
@Autowired
public JmsOrderReceiver(JmsTemplate jms) {
this.jms = jms;
}
public Order receiveOrder() {
return (Order) jms.receiveAndConvert("tacocloud.order.queue");
}
}
定义消息监听器
[代码块]
1
2
3
4
5
6
7
8
9
10
11
12
@Component
public class OrderListener {
private KitchenUI ui;
@Autowired
public OrderListener(KitchenUI ui) {
this.ui = ui;
}
@JmsListener(destination = "tacocloud.order.queue")
public void receiveOrder(Order order) {
ui.displayOrder(order);
}
}

使用RabbitMQ and AMQP

Alt text

向Spring添加RabbitMQ

  1. 添加RabbitMQ依赖

    [代码块]
    1
    2
    3
    4
    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-amqp</artifactId>
    </dependency>
  2. 添加RabbitMQ配置文件
    Alt text

    [代码块]
    1
    2
    3
    4
    5
    6
    7
    spring:
    profiles: prod
    rabbitmq:
    host: rabbit.tacocloud.com
    port: 5673
    username: tacoweb
    password: l3tm31n
使用RabbitTemplate发送消息

RabbitTemplate几个核心方法:

[代码块]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// Send raw messages
void send(Message message) throws AmqpException;
void send(String routingKey, Message message) throws AmqpException;
void send(String exchange, String routingKey, Message message)
throws AmqpException;
// Send messages converted from objects
void convertAndSend(Object message) throws AmqpException;
void convertAndSend(String routingKey, Object message)
throws AmqpException;
void convertAndSend(String exchange, String routingKey,
Object message) throws AmqpException;
// Send messages converted from objects with post-processing
void convertAndSend(Object message, MessagePostProcessor mPP)
throws AmqpException;
void convertAndSend(String routingKey, Object message,
MessagePostProcessor messagePostProcessor)
throws AmqpException;
void convertAndSend(String exchange, String routingKey,
Object message,
MessagePostProcessor messagePostProcessor)
throws AmqpException;

案例:

[1.代码块]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Service
public class RabbitOrderMessagingService
implements OrderMessagingService {
private RabbitTemplate rabbit;
@Autowired
public RabbitOrderMessagingService(RabbitTemplate rabbit) {
this.rabbit = rabbit;
}
public void sendOrder(Order order) {
MessageConverter converter = rabbit.getMessageConverter();
MessageProperties props = new MessageProperties();
Message message = converter.toMessage(order, props);
rabbit.send("tacocloud.order", message);
}
// 消息转换
public void sendOrder(Order order) {
rabbit.convertAndSend("tacocloud.order", order);
}
}
[2.spring.yml文件]
1
2
3
4
5
spring:
rabbitmq:
template:
exchange: tacocloud.orders
routing-key: kitchens.central
配置消息转换器
[spring配置类]
1
2
3
4
@Bean
public MessageConverter messageConverter() {
return new Jackson2JsonMessageConverter();
}
设置消息属性
[代码块]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public void sendOrder(Order order) {
MessageConverter converter = rabbit.getMessageConverter();
MessageProperties props = new MessageProperties();
props.setHeader("X_ORDER_SOURCE", "WEB");
Message message = converter.toMessage(order, props);
rabbit.send("tacocloud.order", message);
}

// 方式二
@Override
public void sendOrder(Order order) {
rabbit.convertAndSend("tacocloud.order.queue", order,
new MessagePostProcessor() {
@Override
public Message postProcessMessage(Message message)
throws AmqpException {
MessageProperties props = message.getMessageProperties();
props.setHeader("X_ORDER_SOURCE", "WEB");
return message;
}
});
}

从RabbitMQ接收消息

几个关键方法:

[代码块]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// Receive messages
Message receive() throws AmqpException;
Message receive(String queueName) throws AmqpException;
Message receive(long timeoutMillis) throws AmqpException;
Message receive(String queueName, long timeoutMillis) throws AmqpException;
// Receive objects converted from messages
Object receiveAndConvert() throws AmqpException;
Object receiveAndConvert(String queueName) throws AmqpException;
Object receiveAndConvert(long timeoutMillis) throws AmqpException;
Object receiveAndConvert(String queueName, long timeoutMillis) throws
AmqpException;
// Receive type-safe objects converted from messages
<T> T receiveAndConvert(ParameterizedTypeReference<T> type) throws
AmqpException;
<T> T receiveAndConvert(String queueName, ParameterizedTypeReference<T> type)
throws AmqpException;
<T> T receiveAndConvert(long timeoutMillis, ParameterizedTypeReference<T>
type) throws AmqpException;
<T> T receiveAndConvert(String queueName, long timeoutMillis,
ParameterizedTypeReference<T> type)
throws AmqpException;

案例:

[1.代码块]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
@Component
public class RabbitOrderReceiver {
private RabbitTemplate rabbit;
private MessageConverter converter;
@Autowired
public RabbitOrderReceiver(RabbitTemplate rabbit) {
this.rabbit = rabbit;
this.converter = rabbit.getMessageConverter();
}
public Order receiveOrder() {
Message message = rabbit.receive("tacocloud.orders");
return message != null
? (Order) converter.fromMessage(message)
: null;
}
// 1.延迟30000毫秒接收
public Order receiveOrder() {
Message message = rabbit.receive("tacocloud.order.queue", 30000);
return message != null
? (Order) converter.fromMessage(message)
: null;
}
// 2-1消息转换器接收
public Order receiveOrder() {
return (Order) rabbit.receiveAndConvert("tacocloud.order.queue");
}
// 2-2.消息转换器接收
public Order receiveOrder() {
return rabbit.receiveAndConvert("tacocloud.order.queue",
new ParameterizedTypeReference<Order>() {});
}
}
[2.spring.yml配置文件]
1
2
3
4
spring:
rabbitmq:
template:
receive-timeout: 30000

使用侦听器处理RABBITMQ消息

[代码块]
1
2
3
4
5
6
7
8
9
10
11
12
@Component
public class OrderListener {
private KitchenUI ui;
@Autowired
public OrderListener(KitchenUI ui) {
this.ui = ui;
}
@RabbitListener(queues = "tacocloud.order.queue")
public void receiveOrder(Order order) {
ui.displayOrder(order);
}
}

消息传递与Kafka(略)
Alt text