由一个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 ...
随机推荐
- java基础之 GC
Java程序员在编码过程中通常不需要考虑内存问题,JVM经过高度优化的GC机制大部分情况下都能够很好地处理堆(Heap)的清理问题.以至于许多Java程序员认为,我只需要关心何时创建对象,而回收对象, ...
- VS2012 调试时 局部变量显示不全的问题解决
在工程上右键,打开属性页,配置属性——C/C++——优化,将优化改为“已禁用/Od“
- MAC上显示隐藏文件夹
第一步:打开「终端」应用程序. 第二步:输入如下命令: defaults write com.apple.finder AppleShowAllFiles -boolean true ; killal ...
- CSS之立体球体
<!DOCTYPE html><html><head> <title>球体</title> <meta charset="u ...
- C++虚函数和虚函数表
前导 在上面的博文中描述了基类中存在虚函数时,基类和派生类中虚函数表的结构. 在派生类也定义了虚函数时,函数表又是怎样的结构呢? 先看下面的示例代码: #include <iostream> ...
- SQLserver技巧
(1) SQL标记 连接连个表然后用 DATA COMPAREDATA进行区分select 'DATA ' ,'列名1','列名2','列名3' from 表 union select 'COM ...
- 第二篇——The communication during software engineering.
I've learned a lot in my software engineering class about how a program comes out.That's also a esse ...
- Guid和Sequence做主键的比较
记得A项目组是一个物流管理系统,后台采用了Oracle数据库.在系统中的核心表托运单表中,关于主键采用何种数据类型,是 sequence 还是用GUID , 大家起了争论. 从网络搜索得到的结论看,一 ...
- SQL Server简单语句/待整理
数据库对象:表Table,视图View,存储过程Stored Procedure,触发器Trigger 关系:1关系=1二维表,1关系有1关系名.1关系=1表对象 属性/字段: 二维表中垂直方向的列 ...
- 360个人图书馆 轻松解除网页防复制 (转自老D)
360个人图书馆会自动采集一些比较好的文章,我的博客文章也被采集过几篇,用过360个人图书馆的人都知道要复制别人的文章需要先收藏到自己的图书馆才可以复制,没有账号右键复制它会直接弹出一个提示登录框.不 ...