How to Build a Robust Business Operation Log System with Spring AOP
This article explains how to design and implement a business operation logging feature in a Java Spring Boot application using AOP, covering requirement analysis, a flawed manual approach, the AOP‑based solution with custom annotations, code examples, testing tips, and a concise summary of the benefits.
Preface
The author shares a long‑awaited article about implementing a simple yet essential business operation log feature, describing the initial poor implementation and the motivation for a cleaner solution.
Requirement Description and Analysis
The client needs to record who performed which business operation, when it happened, the request and response payloads, and optionally provide a one‑click rollback.
System logs record program execution details for developers or ops, while operation logs record actual business actions for users or administrators.
Fields to store: operator, operation time, function name, log type, description, request payload, response payload.
A visual page for querying and tracing important operations.
Management functions to allow rollback of erroneous actions.
Bad Implementation Example
A naive approach adds logging code and exception handling in every controller method, leading to tight coupling and code duplication.
@RestController
@Slf4j
@BusLog(name = "Personnel Management")
@RequestMapping("/person")
public class PersonController2 {
@Autowired
private IPersonService personService;
@Autowired
private IBusLogService busLogService;
@PostMapping
public Person add(@RequestBody Person person) {
try {
Person result = this.personService.registe(person);
this.saveLog(person);
log.info("//add person completed");
} catch (Exception e) {
this.saveExceptionLog(e);
}
return result;
}
}This implementation couples logging with business logic and requires repetitive changes for each new endpoint.
Design Idea
Use Aspect‑Oriented Programming (AOP) to separate logging concerns from business code.
Define a custom @BusLog annotation with name and description attributes.
Annotate methods that need operation logging.
Create a pointcut and aspect that intercepts annotated methods, records annotation data, serializes method arguments, writes payloads to files, and persists a log record.
Spring AOP
AOP allows adding cross‑cutting behavior without modifying source code. Spring AOP is a concrete implementation that can intercept methods at any layer (controller, service, etc.) and provides fine‑grained advice types.
Filter and HandlerInterceptor
Filters depend on the servlet container and can only intercept at the request‑response entry point, while HandlerInterceptor works only in Spring MVC. Both lack the flexibility and granularity of AOP.
Comparison
Scope: Filter works only in servlet containers, Interceptor only in Spring MVC, while Spring AOP works anywhere a pointcut is defined. Granularity: Filter provides coarse‑grained request/response filtering, Interceptor adds pre‑handle/post‑handle hooks, and Spring AOP offers before, after, around, after‑returning, and after‑throwing advice, even allowing return‑value modification.
Implementation
Environment
JDK 1.8, IntelliJ IDEA 2020.1
Spring Boot 2.3.9.RELEASE
mybatis‑spring‑boot‑starter 2.1.4
Dependency
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>Table Design
create table if not exists bus_log (
id bigint auto_increment primary key,
bus_name varchar(100) comment 'business name',
bus_descrip varchar(255) comment 'description',
oper_person varchar(100) comment 'operator',
oper_time datetime comment 'operation time',
ip_from varchar(50) comment 'source ip',
param_file varchar(255) comment 'parameter file'
) comment 'business operation log' charset='utf8';Code
Define the annotation.
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface BusLog {
String name() default "";
String descrip() default "";
}Apply the annotation to controller methods.
@RestController
@Slf4j
@BusLog(name = "Personnel Management")
@RequestMapping("/person")
public class PersonController {
@Autowired
private IPersonService personService;
@PostMapping
@BusLog(descrip = "Add a person")
public Person add(@RequestBody Person person) {
Person result = personService.registe(person);
log.info("//add person completed");
return result;
}
// other CRUD methods annotated similarly
}Aspect that handles logging.
@Component
@Aspect
@Slf4j
public class BusLogAop implements Ordered {
@Autowired
private BusLogDao busLogDao;
@Pointcut("@annotation(com.fanfu.anno.BusLog)")
public void pointcut() {}
@Around("pointcut()")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
Object result = pjp.proceed();
// Retrieve annotation info, serialize arguments, write to file, insert log record
return result;
}
@Override
public int getOrder() { return 1; }
}Testing
Instead of Postman, the article recommends IntelliJ IDEA’s built‑in HTTP client (Test RESTful Web Service) for sending requests.
Additional screenshots show request execution and response verification.
Summary
The business operation log records function name, description, operator, timestamp, and parameter payloads; payloads are stored in files so they can be used for rollback when needed, keeping the core log table lightweight.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Java High-Performance Architecture
Sharing Java development articles and resources, including SSM architecture and the Spring ecosystem (Spring Boot, Spring Cloud, MyBatis, Dubbo, Docker), Zookeeper, Redis, architecture design, microservices, message queues, Git, etc.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
