我需要在所有jms侦听器上应用一些预检查和常见步骤,例如根据模式验证原始消息(JSON模式)。示例 -
@Component
public class MyService {
@JmsListener(destination = "myDestination")
public void processOrder(Order order) { ... }
}
现在,在spring将队列中的Message转换为Order之前,我需要执行以下操作 -
Spring JMS架构是否提供了注入上述需求的任何方法? 我知道AOP跨越了大脑,但我不确定它是否适用于@JmsListener。
答案 0 :(得分:1)
一种相当简单的技术是在侦听器容器工厂上将autoStartup
设置为false
。
然后,使用JmsListenerEndpointRegistry
bean获取侦听器容器。
然后getMessageListener()
,将其包装在AOP代理和setMessageListener()
中。
然后启动容器。
可能有一种更优雅的方式,但我认为你必须深入了解监听器创建代码的内容,这是非常复杂的。
修改强>
Spring Boot示例:
@SpringBootApplication
public class So49682934Application {
private final Logger logger = LoggerFactory.getLogger(getClass());
public static void main(String[] args) {
SpringApplication.run(So49682934Application.class, args);
}
@JmsListener(id = "listener1", destination = "so49682934")
public void listen(Foo foo) {
logger.info(foo.toString());
}
@Bean
public ApplicationRunner runner(JmsListenerEndpointRegistry registry, JmsTemplate template) {
return args -> {
DefaultMessageListenerContainer container =
(DefaultMessageListenerContainer) registry.getListenerContainer("listener1");
Object listener = container.getMessageListener();
ProxyFactory pf = new ProxyFactory(listener);
NameMatchMethodPointcutAdvisor advisor = new NameMatchMethodPointcutAdvisor(new MyJmsInterceptor());
advisor.addMethodName("onMessage");
pf.addAdvisor(advisor);
container.setMessageListener(pf.getProxy());
registry.start();
Thread.sleep(5_000);
Foo foo = new Foo("baz");
template.convertAndSend("so49682934", foo);
};
}
@Bean
public MessageConverter converter() {
MappingJackson2MessageConverter converter = new MappingJackson2MessageConverter();
converter.setTargetType(MessageType.TEXT);
converter.setTypeIdPropertyName("typeId");
return converter;
}
public static class MyJmsInterceptor implements MethodInterceptor {
private final Logger logger = LoggerFactory.getLogger(getClass());
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
Message message = (Message) invocation.getArguments()[0];
logger.info(message.toString());
// validate
return invocation.proceed();
}
}
public static class Foo {
private String bar;
public Foo() {
super();
}
public Foo(String bar) {
this.bar = bar;
}
public String getBar() {
return this.bar;
}
public void setBar(String bar) {
this.bar = bar;
}
@Override
public String toString() {
return "Foo [bar=" + this.bar + "]";
}
}
}
和
spring.jms.listener.auto-startup=false
和
m2018-04-06 11:42:04.859 INFO 59745 --- [enerContainer-1] e.So49682934Application $ MyJmsInterceptor:ActiveMQTextMessage {commandId = 5,responseRequired = true,messageId = ID:gollum.local-60138-1523029319662 -4:2:1:1:1,originalDestination = null,originalTransactionId = null,producerId = ID:gollum.local-60138-1523029319662-4:2:1:1,destination = queue:// so49682934,transactionId = null ,expiration = 0,timestamp = 1523029324849,arrival = 0,brokerInTime = 1523029324849,brokerOutTime = 1523029324853,correlationId = null,replyTo = null,persistent = true,type = null,priority = 4,groupID = null,groupSequence = 0,targetConsumerId = null,compressed = false,userID = null,content = null,marshalledProperties = null,dataStructure = null,redeliveryCounter = 0,size = 1050,properties = {typeId = com.example.So49682934Application $ Foo},readOnlyProperties = true,readOnlyBody = true,droppable = false,jmsXGroupFirstForConsumer = false,text = {" bar":" baz"}}
2018-04-06 11:42:04.882 INFO 59745 --- [enerContainer-1] ication $$ EnhancerBySpringCGLIB $$ e29327b8:Foo [bar = baz]
<强> EDIT2 强>
以下是如何通过基础设施来实现的......
@SpringBootApplication
@EnableJms
public class So496829341Application {
private final Logger logger = LoggerFactory.getLogger(getClass());
public static void main(String[] args) {
SpringApplication.run(So496829341Application.class, args);
}
@JmsListener(id = "listen1", destination="so496829341")
public void listen(Foo foo) {
logger.info(foo.toString());
}
@Bean
public ApplicationRunner runner(JmsTemplate template) {
return args -> {
Thread.sleep(5_000);
template.convertAndSend("so496829341", new Foo("baz"));
};
}
@Bean
public MessageConverter converter() {
MappingJackson2MessageConverter converter = new MappingJackson2MessageConverter();
converter.setTargetType(MessageType.TEXT);
converter.setTypeIdPropertyName("typeId");
return converter;
}
@Bean(JmsListenerConfigUtils.JMS_LISTENER_ANNOTATION_PROCESSOR_BEAN_NAME)
public static JmsListenerAnnotationBeanPostProcessor bpp() {
return new JmsListenerAnnotationBeanPostProcessor() {
@Override
protected MethodJmsListenerEndpoint createMethodJmsListenerEndpoint() {
return new MethodJmsListenerEndpoint() {
@Override
protected MessagingMessageListenerAdapter createMessageListener(
MessageListenerContainer container) {
MessagingMessageListenerAdapter listener = super.createMessageListener(container);
ProxyFactory pf = new ProxyFactory(listener);
pf.setProxyTargetClass(true);
NameMatchMethodPointcutAdvisor advisor = new NameMatchMethodPointcutAdvisor(new MyJmsInterceptor());
advisor.addMethodName("onMessage");
pf.addAdvisor(advisor);
return (MessagingMessageListenerAdapter) pf.getProxy();
}
};
}
};
}
public static class MyJmsInterceptor implements MethodInterceptor {
private final Logger logger = LoggerFactory.getLogger(getClass());
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
Message message = (Message) invocation.getArguments()[0];
logger.info(message.toString());
// validate
return invocation.proceed();
}
}
public static class Foo {
private String bar;
public Foo() {
super();
}
public Foo(String bar) {
this.bar = bar;
}
public String getBar() {
return this.bar;
}
public void setBar(String bar) {
this.bar = bar;
}
@Override
public String toString() {
return "Foo [bar=" + this.bar + "]";
}
}
}
注意:BPP必须是静态的,并且需要@EnableJms
,因为此BPP的存在会禁用启动。
2018-04-06 13:44:41.607 INFO 82669 --- [enerContainer-1] .So496829341Application $ MyJmsInterceptor:ActiveMQTextMessage {commandId = 5,responseRequired = true,messageId = ID:gollum.local-63685-1523036676402- 4:2:1:1:1,originalDestination = null,originalTransactionId = null,producerId = ID:gollum.local-63685-1523036676402-4:2:1:1,destination = queue:// so496829341,transactionId = null, expiration = 0,timestamp = 1523036681598,arrival = 0,brokerInTime = 1523036681598,brokerOutTime = 1523036681602,correlationId = null,replyTo = null,persistent = true,type = null,priority = 4,groupID = null,groupSequence = 0,targetConsumerId = null,compressed = false,userID = null,content = null,marshalledProperties = null,dataStructure = null,redeliveryCounter = 0,size = 1050,properties = {typeId = com.example.So496829341Application $ Foo},readOnlyProperties = true,readOnlyBody = true,droppable = false,jmsXGroupFirstForConsumer = false,text = {&#34; bar&#34;:&#34; baz&#34;}}
2018-04-06 13:44:41.634 INFO 82669 --- [enerContainer-1] ication $$ EnhancerBySpringCGLIB $$ 9ff4b13f:Foo [bar = baz]
<强> EDIT3 强>
避免AOP ......
@SpringBootApplication
@EnableJms
public class So496829341Application {
private final Logger logger = LoggerFactory.getLogger(getClass());
public static void main(String[] args) {
SpringApplication.run(So496829341Application.class, args);
}
@JmsListener(id = "listen1", destination="so496829341")
public void listen(Foo foo) {
logger.info(foo.toString());
}
@Bean
public ApplicationRunner runner(JmsTemplate template) {
return args -> {
Thread.sleep(5_000);
template.convertAndSend("so496829341", new Foo("baz"));
};
}
@Bean
public MessageConverter converter() {
MappingJackson2MessageConverter converter = new MappingJackson2MessageConverter();
converter.setTargetType(MessageType.TEXT);
converter.setTypeIdPropertyName("typeId");
return converter;
}
@Bean(JmsListenerConfigUtils.JMS_LISTENER_ANNOTATION_PROCESSOR_BEAN_NAME)
public static JmsListenerAnnotationBeanPostProcessor bpp() {
return new JmsListenerAnnotationBeanPostProcessor() {
@Override
protected MethodJmsListenerEndpoint createMethodJmsListenerEndpoint() {
return new MethodJmsListenerEndpoint() {
@Override
protected MessagingMessageListenerAdapter createMessageListener(
MessageListenerContainer container) {
final MessagingMessageListenerAdapter listener = super.createMessageListener(container);
return new MessagingMessageListenerAdapter() {
@Override
public void onMessage(Message jmsMessage, Session session) throws JMSException {
logger.info(jmsMessage.toString());
// validate
listener.onMessage(jmsMessage, session);
}
};
}
};
}
};
}
public static class Foo {
private String bar;
public Foo() {
super();
}
public Foo(String bar) {
this.bar = bar;
}
public String getBar() {
return this.bar;
}
public void setBar(String bar) {
this.bar = bar;
}
@Override
public String toString() {
return "Foo [bar=" + this.bar + "]";
}
}
}
<强> EDIT4 强>
要访问侦听器方法上的其他注释,可以完成,但需要反射才能获得对Method
的引用...
@JmsListener(id = "listen1", destination="so496829341")
@Schema("foo.bar")
public void listen(Foo foo) {
logger.info(foo.toString());
}
@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Schema {
String value();
}
@Bean(JmsListenerConfigUtils.JMS_LISTENER_ANNOTATION_PROCESSOR_BEAN_NAME)
public static JmsListenerAnnotationBeanPostProcessor bpp() {
return new JmsListenerAnnotationBeanPostProcessor() {
@Override
protected MethodJmsListenerEndpoint createMethodJmsListenerEndpoint() {
return new MethodJmsListenerEndpoint() {
@Override
protected MessagingMessageListenerAdapter createMessageListener(
MessageListenerContainer container) {
final MessagingMessageListenerAdapter listener = super.createMessageListener(container);
InvocableHandlerMethod handlerMethod =
(InvocableHandlerMethod) new DirectFieldAccessor(listener)
.getPropertyValue("handlerMethod");
final Schema schema = AnnotationUtils.getAnnotation(handlerMethod.getMethod(), Schema.class);
return new MessagingMessageListenerAdapter() {
@Override
public void onMessage(Message jmsMessage, Session session) throws JMSException {
logger.info(jmsMessage.toString());
logger.info(schema.value());
// validate
listener.onMessage(jmsMessage, session);
}
};
}
};
}
};
}