Spring Boot + EventListener 实现观察者模式

最近接触一个新项目,想要在一些发送、接收消息中间件的消息时,把关键的数据记录到数据库中,方便快速定位线上问题。

目前想到两种实现的方式:
一种是写一个service,另一种就是用 @EventListener
程序中,业务处理更为重要,不希望在调用记录数据的service过程中,遇到异常后,还会影响正常的业务逻辑。所以更偏向于使用 @EventListener 进行处理。

整体代码结构

  • controller
    • StudentController (入口)
  • entites
    • Record (Student操作时,会往Record对应的表插入记录)
    • Student (操作表)
  • listener
    • StudentEventListener (监听器/观察者)
  • repository
    • RecordRepository
    • StudentRepository
  • service
    • impl
      • StudentServiceImpl
    • StudentService
  • ObserveApplication (启动类)

pom

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
	<groupId>org.projectlombok</groupId>
	<artifactId>lombok</artifactId>
	<optional>true</optional>
</dependency>
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-data-jpa</artifactId>
	<version>2.2.6.RELEASE</version>
</dependency>
<dependency>
	<groupId>mysql</groupId>
	<artifactId>mysql-connector-java</artifactId>
	<scope>runtime</scope>
</dependency>

application.yml

server:
  port: 8989

spring:
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://localhost:3306/springstudy?serverTimezone=UTC
    username: root
    password: root

jpa:
  properties:
    hibernate:
      hbm2ddl:
        auto: create
      dialect: org.hibernate.dialect.MySQL5InnoDBDialect
      format_sql: true
  show-sql: true

entites

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

import javax.persistence.*;

@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
@Entity
@Table(name = "t_record")
public class Record {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;

    @Column(name = "record")
    private String record;
}
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

import javax.persistence.*;

@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
@Entity
@Table(name = "t_student")
public class Student {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;

    @Column(name = "NAME")
    private String name;

    @Column(name = "AGE")
    private Integer age;

    @Column(name = "EMAIL")
    private String email;

    @Column(name = "ADDRESS")
    private String address;
}

repository

import com.dwayne.entites.Record;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface RecordRepository extends JpaRepository<Record, Integer> {
}
import com.dwayne.entites.Student;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface StudentRepository extends JpaRepository<Student, Integer> {
}

service & serviceImpl

import com.dwayne.entites.Student;

public interface StudentService {
    Student saveStudent(Student student);
}
import com.dwayne.entites.Record;
import com.dwayne.entites.Student;
import com.dwayne.repository.StudentRepository;
import com.dwayne.service.StudentService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Service;

@Service
public class StudentServiceImpl implements StudentService {

    @Autowired
    private StudentRepository studentRepository;

    @Autowired
    private ApplicationContext applicationContext;

    @Override
    public Student saveStudent(Student student) {
        student = studentRepository.save(student);
        
		// 通过ApplicationContext发布事件
        applicationContext.publishEvent(new Record()
                .builder()
                .record(student.toString())
                .build());

        return student;
    }
}

controller

import com.dwayne.entites.Student;
import com.dwayne.service.StudentService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class StudentController {

    @Autowired
    private StudentService studentService;

    @PostMapping("student")
    public Student saveStudent(Student student) {
        return studentService.saveStudent(student);
    }
}

EventListener

import com.dwayne.entites.Record;
import com.dwayne.repository.RecordRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

@Component
@Transactional
public class StudentEventListener {

    @Autowired
    private RecordRepository recordRepository;

    @EventListener
    public void handleStudentEvent(Record record) throws Exception {
        // 在save student的操作流程中,将student的信息备份到record表中
        System.out.println("Loading...");
        recordRepository.save(record);
        System.out.println("End: " + record.toString());
    }
}

正常流程

调用方法

在这里插入图片描述

结果

在这里插入图片描述在这里插入图片描述
在这里插入图片描述

EventListener中抛Exception

import com.dwayne.entites.Record;
import com.dwayne.repository.RecordRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

@Component
@Transactional
public class StudentEventListener {

    @Autowired
    private RecordRepository recordRepository;

    @EventListener
    public void handleStudentEvent(Record record) throws Exception {
        System.out.println("Loading...");
        recordRepository.save(record);
        System.out.println("End: " + record.toString());
        // 抛出exception
        int i = 1/ 0;
    }
}

结果

在这里插入图片描述

student表

业务逻辑数据插入成功
在这里插入图片描述
记录数据插入失败,并不影响业务逻辑
在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值