由一个RABBITMQ监听器死循环引出的SPRING中BEAN和MAPPER接口的注入问题
技术交流群:233513714 1 @Slf4j
2 @RestController
3 @Component
4 public class VouchersReceiverController implements MessageListener {
5
6 @Autowired
7 private VouchersService vouchersService;
8
9 String MerchantCode = PropertyReader.getValue("MerchantCode");
10
11 /**
12 * 启动监听
13 */
14 public void ReceiverVouchersStart() {
15 new ClassPathXmlApplicationContext("spring/rabbitmq-consumer-resources.xml");17 }
18
19 /**
20 * 监听MQ消息队列
21 * @param message
22 */
23 public void onMessage(Message message) {
24 String phoneNo = "";
25 String orderNo = "";
26 String faceValue = "";
27 String voucherNo = "";
28 try {
29 String str = new String(message.getBody());
30 log.info("监听MQ中的信息{}" + str);
31 VouchersResponse vouchers = JsonUtil.fromJson(str, VouchersResponse.class);
32 if ("000000".equals(vouchers.getRESPONSECODE())) {
33 List<VouchersResponseResult> result = vouchers.getRESULTMAP().getRESULT();
34 for (VouchersResponseResult list : result) {
35 phoneNo = list.getLoginId();
36 orderNo = list.getAcceptTransSeqNo().substring(0, 20);
37 faceValue = list.getRebateAmt();
38 voucherNo = list.getVoucherNo();
39 }
40 }
41 } catch (Exception e) {
42 log.info("监听出现异常{}" + e);
43 }
44 VoucherReqInfoObj obj = new VoucherReqInfoObj();
45 obj.setProductNo(phoneNo);
46 obj.setOrderTotalAmount(faceValue);
47 obj.setMerchantCode(MerchantCode);
48 obj.setOrderChannel("08");
49 TInsuranceUserTicket tInsuranceUserTicket = new TInsuranceUserTicket();
50 tInsuranceUserTicket.setOrderNo(orderNo);
51 tInsuranceUserTicket.setVoucherUseStatus("1");
52 log.info("状态修改参数:{}" + tInsuranceUserTicket);
53 vouchersService.updateUserTicketStatus(tInsuranceUserTicket);
54 log.info("状态修改完成");
55 }
56 }
以上代码就是我们要展开讨论的部分。当代码发到服务器上我们向MQ中推一条消息后就会收到一个回调,消费这个回调信息的地方就在onMessage方法中,这就是这个MQ监听的工作流程。正常情况下当监听到消息后会消费下一条消息,如果没有消息则不会再进行消费。然而上面的代码会出现一个很奇怪的问题,当一条消息进来之后会出现重复消费的现象,查看服务日志之后发现程序在走到53行时不再向下执行而是又返回到28行执行,起初考虑是不是因为For循环造成了死循环,但是这个疑问很快被打消,如果是在For循环出现了死循环程序会打不到第30行的日志。
通过在网上谷歌,我将catch的位置进行了调整,代码如下:
1 /**
2 * 监听MQ消息队列
3 * @param message
4 */
5 public void onMessage(Message message) {
6 String phoneNo = "";
7 String orderNo = "";
8 String faceValue = "";
9 String voucherNo = "";
10 try {
11 String str = new String(message.getBody());
12 log.info("监听MQ中的信息{}" + str);
13 VouchersResponse vouchers = JsonUtil.fromJson(str, VouchersResponse.class);
14 if ("000000".equals(vouchers.getRESPONSECODE())) {
15 List<VouchersResponseResult> result = vouchers.getRESULTMAP().getRESULT();
16 for (VouchersResponseResult list : result) {
17 phoneNo = list.getLoginId();
18 orderNo = list.getAcceptTransSeqNo().substring(0, 20);
19 faceValue = list.getRebateAmt();
20 voucherNo = list.getVoucherNo();
21 }
22 }
23 VoucherReqInfoObj obj = new VoucherReqInfoObj();
24 obj.setProductNo(phoneNo);
25 obj.setOrderTotalAmount(faceValue);
26 obj.setMerchantCode(MerchantCode);
27 obj.setOrderChannel("08");
28 TInsuranceUserTicket tInsuranceUserTicket = new TInsuranceUserTicket();
29 tInsuranceUserTicket.setOrderNo(orderNo);
30 tInsuranceUserTicket.setVoucherUseStatus("1");
31 log.info("状态修改参数:{}" + tInsuranceUserTicket);
32 vouchersService.updateUserTicketStatus(tInsuranceUserTicket);
33 log.info("状态修改完成");
34 } catch (Exception e) {
35 log.info("监听出现异常{}" + e);
36 }
37 }
以上就是调整后的代码,死循环的问题终于被解决了。但是新的问题出现了,程序在走到32行时就会报出NullPointerException,没猜错这个是因为没有注入VouchersService造成的,然后在选择注入方式的时候发现RabbitMQ已经整合到spring中了。所以只能通过spring注入VouchersService注入了,经过试用发现构造方法的方式注入会出问题,所以最后选择了属性注入的方式。Controller层、Service层、配置文件依次如下:
Controller层:
1 @Slf4j
2 @RestController
3 @Component
4 public class VouchersReceiverController implements MessageListener {
5
6 private VouchersService vouchersService;
7
8 public VouchersService getVouchersService() {
9 return vouchersService;
10 }
11
12 public void setVouchersService(VouchersService vouchersService) {
13 this.vouchersService = vouchersService;
14 }
15
16 String MerchantCode = PropertyReader.getValue("MerchantCode");
17
18 /**
19 * 启动监听
20 */
21 public void ReceiverVouchersStart() {
22 new ClassPathXmlApplicationContext("spring/rabbitmq-consumer-resources.xml");
23 log.info("保险前置开启对代金券营销平台的MQ监听");
24 }
25
26 /**
27 * 监听MQ消息队列
28 * @param message
29 */
30 public void onMessage(Message message) {
31 String phoneNo = "";
32 String orderNo = "";
33 String faceValue = "";
34 String voucherNo = "";
35 try {
36 String str = new String(message.getBody());
37 log.info("监听MQ中的信息{}" + str);
38 VouchersResponse vouchers = JsonUtil.fromJson(str, VouchersResponse.class);
39 if ("000000".equals(vouchers.getRESPONSECODE())) {
40 List<VouchersResponseResult> result = vouchers.getRESULTMAP().getRESULT();
41 for (VouchersResponseResult list : result) {
42 phoneNo = list.getLoginId();
43 orderNo = list.getAcceptTransSeqNo().substring(0, 20);
44 faceValue = list.getRebateAmt();
45 voucherNo = list.getVoucherNo();
46 }
47 }
48 VoucherReqInfoObj obj = new VoucherReqInfoObj();
49 obj.setProductNo(phoneNo);
50 obj.setOrderTotalAmount(faceValue);
51 obj.setMerchantCode(MerchantCode);
52 obj.setOrderChannel("08");
53 TInsuranceUserTicket tInsuranceUserTicket = new TInsuranceUserTicket();
54 tInsuranceUserTicket.setOrderNo(orderNo);
55 tInsuranceUserTicket.setVoucherUseStatus("1");
56 log.info("状态修改参数:{}" + tInsuranceUserTicket);
57 getVouchersService().updateUserTicketStatus(tInsuranceUserTicket);
58 log.info("状态修改完成");
59 } catch (Exception e) {
60 log.info("监听出现异常{}" + e);
61 }
62 }
63 }
Service层:
1 @Service
2 @Slf4j
3 @Component
4 public class VouchersService {
5 @Autowired
6 private TInsuranceUserTicketMapper tInsuranceUserTicketMapper;
7
8 public void updateUserTicketStatus(TInsuranceUserTicket tInsuranceUserTicket) {
9 tInsuranceUserTicket.setUpdatedTime(new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(new Date()));
10 log.info("代金券使用状态修改参数{}" + tInsuranceUserTicket);
11 try {
12 tInsuranceUserTicketMapper.updateUserTicketStatus(tInsuranceUserTicket);
13 log.info("代金券使用状态已经修改{}" + tInsuranceUserTicket);
14 } catch (Exception e) {
15 log.info("代金券使用状态修改出现异常{}" + e);
16 }
17 }
18 }
配置文件(rabbitmq-consumer-resources.xml):
1 <?xml version="1.0" encoding="UTF-8"?>
2 <beans xmlns="http://www.springframework.org/schema/beans"
3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4 xmlns:rabbit="http://www.springframework.org/schema/rabbit"
5 xmlns:context="http://www.springframework.org/schema/context"
6 xsi:schemaLocation="http://www.springframework.org/schema/beans
7 http://www.springframework.org/schema/beans/spring-beans.xsd
8 http://www.springframework.org/schema/rabbit
9 http://www.springframework.org/schema/rabbit/spring-rabbit-1.3.xsd
10 http://www.springframework.org/schema/context
11 http://www.springframework.org/schema/context/spring-context.xsd">
12
13 <context:property-placeholder location="classpath:/properties/test.properties"/>
14
15 <!-- 配置连接工厂 -->
16 <rabbit:connection-factory id="connectionFactory"
17 host="${rabbit.host}"
18 port="${rabbit.port}"
19 username="${rabbit.consumer.username}"
20 password="${rabbit.consumer.password}"
21 virtual-host="${rabbit.vhost}"
22 connection-factory="refConnectionFactory"/>
23
24 <!-- 配置心跳、超时、自动恢复-->
25 <bean id="refConnectionFactory" class="com.rabbitmq.client.ConnectionFactory">
26 <property name="requestedHeartbeat" value="240"></property>
27 <property name="connectionTimeout" value="10000"></property>
28 <property name="automaticRecoveryEnabled" value="true"></property>
29 </bean>
30
31 <!-- 配置消费者监听器,指定队列名及监听类 -->
32 <rabbit:listener-container connection-factory="connectionFactory" prefetch="1" concurrency="10" acknowledge="auto">
33 <rabbit:listener queue-names="${rabbit.queueName.reply}" ref="vouchersReceiverController"/>
34 </rabbit:listener-container>
35
36 <!-- 监听类 -->
37 <bean id="vouchersReceiverController" class="com.bestpay.insurance.dal.controller.vouchers.VouchersReceiverController">
38 <!--注入Service-->
39 <property name="vouchersService" ref="vouchersService"/>
40 </bean>
41
42 <bean id="vouchersService" class="com.bestpay.insurance.service.vouchers.VouchersService"/>
43 </beans>
经过了一番这样配置之后项目再一次启动起来,心中窃喜应该不会再有什么问题了吧。果然Service层可以注入进来了,并且程序顺利的走入到了Service层的updateUserTicketStatus方法中。问题又一次出现了Service层中第10行的日志可以被打出来,但是之后又会报出NullPointerException的异常,这回问题显而易见,应该是TInsuranceUserTicketMapper接口没有被注入进来,考虑到mapper接口的注入方式是和一般的bean注入是不一样的,所以经过了一番谷歌发现的确是不一样的。以下给出调整后的Service层(Service注入mapper的方式依然采用属性注入)、Mapper接口、调整后的配置文件(rabbitmq-consumer-resources.xml):
Service层:
1 @Service
2 @Slf4j
3 @Component
4 public class VouchersService {
5
6 private TInsuranceVouchersMapper tInsuranceVouchersMapper;
7
8 private TInsuranceUserTicketMapper tInsuranceUserTicketMapper;
9
10 public TInsuranceVouchersMapper getTInsuranceVouchersMapper() {
11 return tInsuranceVouchersMapper;
12 }
13
14 public void setTInsuranceVouchersMapper(TInsuranceVouchersMapper tInsuranceVouchersMapper) {
15 this.tInsuranceVouchersMapper = tInsuranceVouchersMapper;
16 }
17
18 public TInsuranceUserTicketMapper getTInsuranceUserTicketMapper() {
19 return tInsuranceUserTicketMapper;
20 }
21
22 public void setTInsuranceUserTicketMapper(TInsuranceUserTicketMapper tInsuranceUserTicketMapper) {
23 this.tInsuranceUserTicketMapper = tInsuranceUserTicketMapper;
24 }
25
26 public void updateUserTicketStatus(TInsuranceUserTicket tInsuranceUserTicket) {
27 tInsuranceUserTicket.setUpdatedTime(new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(new Date()));
28 log.info("代金券使用状态修改参数{}" + tInsuranceUserTicket);
29 try {
30 getTInsuranceUserTicketMapper().updateUserTicketStatus(tInsuranceUserTicket);
31 log.info("代金券使用状态已经修改{}" + tInsuranceUserTicket);
32 } catch (Exception e) {
33 log.info("代金券使用状态修改出现异常{}" + e);
34 }
35 }
36 }
Mapper接口:
@Component
public interface TInsuranceUserTicketMapper {
int updateUserTicketStatus(TInsuranceUserTicket record);
}
配置文件(rabbitmq-consumer-resources.xml):
1 <?xml version="1.0" encoding="UTF-8"?>
2 <beans xmlns="http://www.springframework.org/schema/beans"
3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4 xmlns:rabbit="http://www.springframework.org/schema/rabbit"
5 xmlns:context="http://www.springframework.org/schema/context"
6 xsi:schemaLocation="http://www.springframework.org/schema/beans
7 http://www.springframework.org/schema/beans/spring-beans.xsd
8 http://www.springframework.org/schema/rabbit
9 http://www.springframework.org/schema/rabbit/spring-rabbit-1.3.xsd
10 http://www.springframework.org/schema/context
11 http://www.springframework.org/schema/context/spring-context.xsd">
12
13 <context:property-placeholder location="classpath:/properties/test.properties"/>
14
15 <import resource="classpath:spring/spring-datasource.xml"/>
16
17 <!-- 配置连接工厂 -->
18 <rabbit:connection-factory id="connectionFactory"
19 host="${rabbit.host}"
20 port="${rabbit.port}"
21 username="${rabbit.consumer.username}"
22 password="${rabbit.consumer.password}"
23 virtual-host="${rabbit.vhost}"
24 connection-factory="refConnectionFactory"/>
25
26 <!-- 配置心跳、超时、自动恢复-->
27 <bean id="refConnectionFactory" class="com.rabbitmq.client.ConnectionFactory">
28 <property name="requestedHeartbeat" value="240"></property>
29 <property name="connectionTimeout" value="10000"></property>
30 <property name="automaticRecoveryEnabled" value="true"></property>
31 </bean>
32
33 <!-- 配置消费者监听器,指定队列名及监听类 -->
34 <rabbit:listener-container connection-factory="connectionFactory" prefetch="1" concurrency="10" acknowledge="auto">
35 <rabbit:listener queue-names="${rabbit.queueName.reply}" ref="vouchersReceiverController"/>
36 </rabbit:listener-container>
37
38 <!-- 监听类 -->
39 <bean id="vouchersReceiverController" class="com.bestpay.insurance.dal.controller.vouchers.VouchersReceiverController">
40 <!--注入Service-->
41 <property name="vouchersService" ref="vouchersService"/>
42 </bean>
43
44 <bean id="vouchersService" class="com.bestpay.insurance.service.vouchers.VouchersService">
45 <!--注入Mapper-->
46 <property name="TInsuranceUserTicketMapper" ref="tInsuranceUserTicketMapper"/>
47 </bean>
48
49 <bean id="tInsuranceUserTicketMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
50 <property name="mapperInterface" value="com.bestpay.insurance.dal.mapper.TInsuranceUserTicketMapper"/>
51 <property name="sqlSessionFactory" ref="sqlSessionFactory"/>
52 </bean>
53
54 <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
55 <property name="dataSource" ref="anteaterDs"/>
56 <property name="configLocation" value="classpath:spring/mybatis.xml"/>
57 <property name="mapperLocations">
58 <list>
59 <value>classpath:mapper/*Mapper.xml</value>
60 </list>
61 </property>
62 </bean>
63
64 <bean id="anteaterDs" class="org.apache.commons.dbcp.BasicDataSource">
65 <property name="driverClassName" value="${jdbc.driverClassName}"/>
66 <property name="url" value="${jdbc.url}"/>
67 <property name="username" value="${jdbc.username}"/>
68 <property name="password" value="${jdbc.password}"/>
69 <property name="initialSize" value="${jdbc.initialSize}"/>
70 <property name="minIdle" value="${jdbc.minIdle}"/>
71 <property name="maxIdle" value="${jdbc.maxIdle}"/>
72 <property name="maxActive" value="${jdbc.maxActive}"/>
73 <property name="maxWait" value="${jdbc.maxWait}"/>
74 <property name="testOnBorrow" value="${jdbc.testOnBorrow}"/>
75 <property name="testWhileIdle" value="${jdbc.testWhileIdle}"/>
76 <property name="timeBetweenEvictionRunsMillis" value="${jdbc.timeBetweenEvictionRunsMillis}"/>
77 <property name="numTestsPerEvictionRun" value="${jdbc.numTestsPerEvictionRun}"/>
78 <property name="minEvictableIdleTimeMillis" value="${jdbc.minEvictableIdleTimeMillis}"/>
79 <property name="validationQuery" value="SELECT 1 FROM DUAL"/>
80 <property name="removeAbandonedTimeout" value="60"/>
81 <property name="removeAbandoned" value="true"/>
82 </bean>
83 </beans>
通过以上修改之后,再启动项目之后发现问题已经解决。这个问题解决之后回过头来细细想了一下为什么监听的方法中不能注入Service,从而导致了一个死循环的假象,这个问题需要后续不断深入的学习去探索问题的原因,但是还有一个问题可以道出问题的原委,就是监听后不能注入。因为RabbitMQ被整合到了spring中,所以项目在启动的时候会自动的注入RabbitMQ相关的东西,而如果采用@Autowired方式调用Service就会导致注入不进来,所以要在项目启动的同时注入Service和Mapper,这样就不会出现空指针的问题了。
由一个RABBITMQ监听器死循环引出的SPRING中BEAN和MAPPER接口的注入问题的更多相关文章
- 深究Spring中Bean的生命周期
前言 这其实是一道面试题,是我在面试百度的时候被问到的,当时没有答出来(因为自己真的很菜),后来在网上寻找答案,看到也是一头雾水,直到看到了<Spring in action>这本书,书上 ...
- Spring中Bean的作用域、生命周期
Bean的作用域.生命周期 Bean的作用域 Spring 3中为Bean定义了5中作用域,分别为singleton(单例).protot ...
- Spring中Bean的实例化
Spring中Bean的实例化 在介绍Bean的三种实例化的方式之前,我们首先需要介绍一下什么是Bean,以及Bean的配置方式. 如果 ...
- 轻松了解Spring中的控制反转和依赖注入(二)
紧接上一篇文章<轻松了解Spring中的控制反转和依赖注入>讲解了SpringIOC和DI的基本概念,这篇文章我们模拟一下SpringIOC的工作机制,使我们更加深刻的理解其中的工作. 类 ...
- Spring中Bean的命名问题(id和name区别)及ref和idref之间的区别
Spring中Bean的命名 1.每个Bean可以有一个id属性,并可以根据该id在IoC容器中查找该Bean,该id属性值必须在IoC容器中唯一: 2.可以不指定id属性,只指定全限定类名,如: & ...
- (转)Spring中Bean的命名问题(id和name区别)及ref和idref之间的区别
Spring中Bean的命名 1.每个Bean可以有一个id属性,并可以根据该id在IoC容器中查找该Bean,该id属性值必须在IoC容器中唯一: 2.可以不指定id属性,只指定全限定类名,如: & ...
- JAVA面试题:Spring中bean的生命周期
Spring 中bean 的生命周期短暂吗? 在spring中,从BeanFactory或ApplicationContext取得的实例为Singleton,也就是预设为每一个Bean的别名只能维持一 ...
- Spring中Bean的命名问题及ref和idref之间的区别
一直在用Spring,其实对其了解甚少,刚去了解了一下Spring中Bean的命名问题以及ref和idref之间的区别,略作记录,以备后查. Spring中Bean的命名 1.每个Bean可以有一个i ...
- Spring中bean的注入方式
首先,要学习Spring中的Bean的注入方式,就要先了解什么是依赖注入.依赖注入是指:让调用类对某一接口的实现类的实现类的依赖关系由第三方注入,以此来消除调用类对某一接口实现类的依赖. Spring ...
随机推荐
- Manjaro mirror in china
1, mirrot file: /etc/pacman.d/mirrorlist Server = http://mirrors.ustc.edu.cn/manjaro/stable/$repo/$a ...
- INSERT INTO 语句的语法错误【 OLE报错,office终端执行SQL没有问题】
表名,字段在代码执行之前一定要进行" [ 字段.表名 ] "中括号包裹,不然会报INSERT INTO 语句的语法错误! office终端没有报错的原因,应该是office在执行之 ...
- 数据结构-图-Java实现:有向图 图存储(邻接矩阵),最小生成树,广度深度遍历,图的连通性,最短路径1
import java.util.ArrayList; import java.util.List; // 模块E public class AdjMatrixGraph<E> { pro ...
- VS2010默认属性文件配置
问题: 在VS2010中,同一个解决方案下有多个项目,都需要使用某一个库. 如果项目比较多,或者编译链接环境属性变动频繁,分别对项目进行配置就很麻烦. 解决: 在VS的配置文件中统一配置属性: 我的配 ...
- Android 图片浏览器 从原来位置放大至全屏显示
android 图片浏览器 特点: 1.从网络加载图片,只需要传图片地址数组即可 2.点击图片,从原来位置放大至全屏 3.支持手势操作 4.完全自定义布局 项目源码请到GitHub下载:https:/ ...
- Python学习路程day20
本节内容: 项目:开发一个简单的BBS论坛 需求: 整体参考“抽屉新热榜” + “虎嗅网” 实现不同论坛版块 帖子列表展示 帖子评论数.点赞数展示 在线用户展示 允许登录用户发贴.评论.点赞 允许上传 ...
- static成员变量与返回对象的引用
(1)用static修饰类成员变量(属性),表明该变量是静态的,无论创建多少对象,都只创建一个一个静态属性副本,也就是对象们共享同一个静态属性,这个方法常用的一个用途就是用来计算程序调用了多少次这个类 ...
- c# txt文件的读写
在公司实习,任务不太重,总结一下c#关于txt文件的读写,以便以后有用到的时候可以查看一下.如果有写得不完整的地方还请补充 说明:本人C#水平可能初级都谈不上,高手轻喷,参考:http://www.c ...
- iOS—Mask属性的使用
Mask属性介绍 Mask平时用的最多的是masksToBounds 吧. 其实除此以外Mask使用场景很多,看完之后你会发现好真是好用的不要不要的... 先来了解下Mask属性到底是什么? Mask ...
- linux动态时钟探索
在早期的linux内核版本的时间概念都是由周期时钟提供的.虽然比较有效,但是,对于关注能耗电量的系统上,就不能满足长时间休眠的需求,因为周期系统要求必须在一定的频率下,周期性的处于活动状态.因此,li ...