最近接触一个新项目,想要在一些发送、接收消息中间件的消息时,把关键的数据记录到数据库中,方便快速定位线上问题。
目前想到两种实现的方式:
一种是写一个service,另一种就是用 @EventListener。
程序中,业务处理更为重要,不希望在调用记录数据的service过程中,遇到异常后,还会影响正常的业务逻辑。所以更偏向于使用 @EventListener 进行处理。
整体代码结构
- controller
- StudentController (入口)
- entites
- Record (Student操作时,会往Record对应的表插入记录)
- Student (操作表)
- listener
- StudentEventListener (监听器/观察者)
- repository
- RecordRepository
- StudentRepository
- service
- impl
- StudentServiceImpl
- StudentService
- impl
- 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表
业务逻辑数据插入成功
记录数据插入失败,并不影响业务逻辑