例如 用户注册之后需要计算用户的邀请关系,递归操作。如果注册的时候包含多步验证,生成基本初始化数据,这时候我们通过mq发送消息来处理这个邀请关系,会出现一个问题,就是用户还没注册数据还没入库,邀请关系就开始执行,但是查不到数据,导致出错。
@TransactionalEventListener 可以实现事务的监听,可以在提交之后再进行操作。
——————————————
1.实体类
@Data
public class Customer {
private Integer id;
private String name;
}
2.监听的对象
import lombok.Getter;
import org.springframework.context.ApplicationEvent;
/**
* fileName:RegCustomerEvent
* description:
* author: LJV
* createTime:2022/8/19 14:41
* version:1.0.0
*/
@Getter
public class RegCustomerEvent extends ApplicationEvent {
private Customer customer;
/***
* @description 构造函数,用于设置消息体属性内容
* @author 朱孝恒(javazhuxiaoheng @ 163.com)
* @date 2022-03-17
* @param customer 推送对象
*/
public RegCustomerEvent(Customer customer) {
super(customer);
this.customer = customer;
}
}
3.Spring事务事件发送, 用于调用ApplicationEventPublisher发布事件
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;
/**
* fileName:TranscationEventPublisher
* description:Spring事务事件发送, 用于调用ApplicationEventPublisher发布事件
* author: LJV
* createTime:2022/8/19 14:58
* version:1.0.0
*/
@Service
public class TranscationEventPublisher {
/**
* Spring事件发布器对象
*/
@Autowired
private ApplicationEventPublisher applicationEventPublisher;
/***
* @description 发布数据生成事件
* @author
* @date 2022-03-17
* @param customer
*/
public void publishCustomerEvent(Customer customer) {
applicationEventPublisher.publishEvent(new RegCustomerEvent(customer));
}
}
4.消息事件监听器
package com.ljv.chat.event_;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import org.springframework.transaction.event.TransactionPhase;
import org.springframework.transaction.event.TransactionalEventListener;
/**
* fileName:TranscationMessageListener
* description: 消息事件监听器
* author: LJV
* createTime:2022/8/19 15:04
* version:1.0.0
*/
@Component
@Slf4j
public class TranscationMessageListener {
/***
* @description 本地事务监听事件,事务完成后,发布一条推送订单给司机的MQTT消息
* @author 朱孝恒(javazhuxiaoheng @ 163.com)
* @date 2022-03-17
* @param regCustomerEvent 消息事件主题
*/
@Async //如果使用该异步注解,则需要 @EnableAsync在主类
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT, fallbackExecution = true)
public void pushCustomer(RegCustomerEvent regCustomerEvent) {
log.info("freightSendEvent: {}", regCustomerEvent);
//业务逻辑
System.out.println("---事件开始执行---");
}
}
5.工具类
package com.ljv.chat.event_;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.stereotype.Component;
/**
* spring工具类 方便在非spring管理环境中获取bean
*
* @author hupengnan
*/
@Component
public final class SpringUtils implements BeanFactoryPostProcessor
{
/** Spring应用上下文环境 */
private static ConfigurableListableBeanFactory beanFactory;
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException
{
SpringUtils.beanFactory = beanFactory;
}
/**
* 获取对象
*
* @param name
* @return Object 一个以所给名字注册的bean的实例
* @throws BeansException
*
*/
@SuppressWarnings("unchecked")
public static <T> T getBean(String name) throws BeansException
{
return (T) beanFactory.getBean(name);
}
/**
* 获取类型为requiredType的对象
*
* @param clz
* @return
* @throws BeansException
*
*/
public static <T> T getBean(Class<T> clz) throws BeansException
{
T result = (T) beanFactory.getBean(clz);
return result;
}
/**
* 如果BeanFactory包含一个与所给名称匹配的bean定义,则返回true
*
* @param name
* @return boolean
*/
public static boolean containsBean(String name)
{
return beanFactory.containsBean(name);
}
/**
* 判断以给定名字注册的bean定义是一个singleton还是一个prototype。 如果与给定名字相应的bean定义没有被找到,将会抛出一个异常(NoSuchBeanDefinitionException)
*
* @param name
* @return boolean
* @throws NoSuchBeanDefinitionException
*
*/
public static boolean isSingleton(String name) throws NoSuchBeanDefinitionException
{
return beanFactory.isSingleton(name);
}
/**
* @param name
* @return Class 注册对象的类型
* @throws NoSuchBeanDefinitionException
*
*/
public static Class<?> getType(String name) throws NoSuchBeanDefinitionException
{
return beanFactory.getType(name);
}
/**
* 如果给定的bean名字在bean定义中有别名,则返回这些别名
*
* @param name
* @return
* @throws NoSuchBeanDefinitionException
*
*/
public static String[] getAliases(String name) throws NoSuchBeanDefinitionException
{
return beanFactory.getAliases(name);
}
}
6.服务层接口
package com.ljv.chat.event_.service;
/**
* fileName:Customer
* description:
* author: LJV
* createTime:2022/8/19 16:36
* version:1.0.0
*/
public interface CustomerService {
void getCustomer();
}
7.服务层实现类
package com.ljv.chat.event_.service;
import com.ljv.chat.event_.Customer;
import com.ljv.chat.event_.SpringUtils;
import com.ljv.chat.event_.TranscationEventPublisher;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.concurrent.TimeUnit;
/**
* fileName:CustomerServiceImpl
* description:
* author: LJV
* createTime:2022/8/19 16:38
* version:1.0.0
*/
@Service
@Slf4j
public class CustomerServiceImpl implements CustomerService {
@Override
@Transactional
public void getCustomer() {
log.info("---业务开始执行---");
Customer customer = new Customer();
customer.setId(1);
customer.setName("qweqwe");
log.info("aaa");
SpringUtils.getBean(TranscationEventPublisher.class).publishCustomerEvent(customer);
log.info("ccc");
// Thread.sleep(1000);
try {
TimeUnit.SECONDS.sleep(10);//秒
} catch (InterruptedException e) {
e.printStackTrace();
}
log.info("---业务执行结束---");
}
}
8.控制层 进行测试
package com.ljv.chat.event_;
import com.ljv.chat.event_.service.CustomerService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.concurrent.TimeUnit;
/**
* fileName:Test
* description:
* author: LJV
* createTime:2022/8/19 15:09
* version:1.0.0
*/
@RestController
@RequestMapping("customer")
@Slf4j
public class TestController {
@Autowired
private CustomerService customerService;
@GetMapping("getCustomer")
public void getCustomer() throws InterruptedException {
customerService.getCustomer();
}
}