How to Implement Custom Logging with Spring AOP and Annotations
This guide explains how to create a custom Spring AOP logging solution by defining a @SystemCrmlog annotation, extracting its parameters via pointcuts, and persisting detailed log entries—including user, method, parameters, and execution results—to a database using aspect advice methods.
Introduction
When building a Spring project, logging requirements often exceed what simple AOP can capture; basic logs may only record table names or operation types. To store richer information, a custom annotation is introduced, allowing developers to embed desired log details and retrieve them through pointcuts for database insertion.
Spring AOP Basics
Reference: https://blog.csdn.net/yjt520557/article/details/84833508
Key terminology:
Aspect : Implemented via a regular class annotated with @Aspect (AspectJ style) or a generic class.
Joinpoint : Represents a method execution point.
Advice : Action executed at a specific joinpoint (e.g., @Around, @Before, @After).
Pointcut : Defines one or more methods that trigger advice; Spring uses AspectJ pointcut syntax by default.
Advice Types
@Before : Executes before a joinpoint; cannot prevent execution unless an exception is thrown.
@AfterReturning : Executes after successful completion of a joinpoint.
@AfterThrowing : Executes when a method throws an exception.
@After : Executes after a joinpoint regardless of outcome.
@Around : Wraps a joinpoint, allowing custom behavior before and after method invocation, and can control whether the original method proceeds.
Spring AOP Configuration Styles
XML style: Declares AOP support in XML.
AspectJ style: Uses annotation‑based configuration.
Creating a Custom Annotation
Define an annotation to hold log description and table name:
package com.ywj.log;
import java.lang.annotation.*;
/**
* @interface SystemCrmlog
* Custom annotation for AOP logging
*/
@Target({ElementType.PARAMETER, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface SystemCrmlog {
String description() default "";
String tableName() default "";
}Aspect Class – Extracting Annotation Data
The aspect intercepts methods annotated with @SystemCrmlog, gathers request, user, and method details, builds a CrmLogMessage object, and persists it via Sys_logDao:
package com.ywj.log;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import java.text.SimpleDateFormat;
import java.util.*;
@Aspect
@Component
public class SystemLogAspect {
@Pointcut("@annotation(com.ywj.log.SystemCrmlog)")
public void crmAspect() {}
@AfterThrowing(value="crmAspect()", throwing="ex")
public void afterThrowingMethod(JoinPoint joinPoint, Exception ex) throws Exception {
HttpServletRequest request = getHttpServletRequest();
WebUtil webUtil = new WebUtil();
Map<String, Object> user = webUtil.getUser(request);
CrmLogMessage log = new CrmLogMessage();
String context = getServiceMthodDescription(joinPoint);
String usr_name = "";
String rolename = "";
if (user != null) {
usr_name = user.get("usr_name").toString();
rolename = user.get("rolename").toString();
}
log.setUserName(usr_name);
log.setUserRole(rolename);
log.setContent(usr_name + context);
log.setRemarks(getServiceMthodParams(joinPoint));
log.setTableName(getServiceMthodTableName(joinPoint));
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
log.setDateTime(sdf.format(new Date()));
log.setIp(request.getRemoteAddr());
log.setRequestUrl(request.getRequestURI());
log.setResult("执行失败");
log.setExString(ex.getMessage());
new Sys_logDao().addSys_log(log);
}
@AfterReturning(value="crmAspect()", returning="returnValue")
public void doCrmLog(JoinPoint joinPoint, Object returnValue) throws Exception {
HttpServletRequest request = getHttpServletRequest();
WebUtil webUtil = new WebUtil();
Map<String, Object> user = webUtil.getUser(request);
CrmLogMessage log = new CrmLogMessage();
String context = getServiceMthodDescription(joinPoint);
String usr_name = "";
String rolename = "";
if (user != null) {
usr_name = user.get("usr_name").toString();
rolename = user.get("rolename").toString();
}
log.setUserName(usr_name);
log.setUserRole(rolename);
log.setContent(usr_name + context);
log.setRemarks(getServiceMthodParams(joinPoint));
log.setTableName(getServiceMthodTableName(joinPoint));
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
log.setDateTime(sdf.format(new Date()));
log.setIp(request.getRemoteAddr());
log.setRequestUrl(request.getRequestURI());
// Determine result based on returnValue type
if (returnValue != null) {
if (returnValue instanceof List) {
log.setResult(((List<?>) returnValue).size() > 0 ? "执行成功" : "执行成功");
} else if (returnValue instanceof Boolean) {
log.setResult(((Boolean) returnValue) ? "执行成功" : "执行失败");
} else if (returnValue instanceof Integer) {
log.setResult(((Integer) returnValue) > 0 ? "执行成功" : "执行失败");
} else {
log.setResult("执行成功");
}
}
new Sys_logDao().addSys_log(log);
}
private String getServiceMthodDescription(JoinPoint joinPoint) throws Exception {
String targetName = joinPoint.getTarget().getClass().getName();
String methodName = joinPoint.getSignature().getName();
Object[] arguments = joinPoint.getArgs();
Class<?> targetClass = Class.forName(targetName);
for (Method method : targetClass.getMethods()) {
if (method.getName().equals(methodName) && method.getParameterTypes().length == arguments.length) {
return method.getAnnotation(SystemCrmlog.class).description();
}
}
return "";
}
private String getServiceMthodTableName(JoinPoint joinPoint) throws Exception {
String targetName = joinPoint.getTarget().getClass().getName();
String methodName = joinPoint.getSignature().getName();
Object[] arguments = joinPoint.getArgs();
Class<?> targetClass = Class.forName(targetName);
for (Method method : targetClass.getMethods()) {
if (method.getName().equals(methodName) && method.getParameterTypes().length == arguments.length) {
return method.getAnnotation(SystemCrmlog.class).tableName();
}
}
return "";
}
private String getServiceMthodParams(JoinPoint joinPoint) throws Exception {
Object[] arguments = joinPoint.getArgs();
return new com.fasterxml.jackson.databind.ObjectMapper().writeValueAsString(arguments);
}
public HttpServletRequest getHttpServletRequest() {
return ((org.springframework.web.context.request.ServletRequestAttributes)org.springframework.web.context.request.RequestContextHolder.getRequestAttributes()).getRequest();
}
}Log Entity Class
Represents a database record for each logged operation:
package com.ywj.log;
public class CrmLogMessage {
private Integer logid;
private String UserName;
private String UserRole;
private String Content;
private String Remarks;
private String TableName;
private String DateTime;
private String resultValue;
private String ip;
private String requestUrl;
private String result;
private String ExString;
// getters, setters, constructors, toString omitted for brevity
}Utility Class for Session User
package com.ywj.log;
import javax.servlet.http.HttpServletRequest;
import java.util.Map;
public class WebUtil {
public Map<String, Object> getUser(HttpServletRequest request) {
if (request == null) return null;
Object user = request.getSession().getAttribute(Constans.USER_KEY);
return (Map<String, Object>) user;
}
}Spring Configuration
<aop:aspectj-autoproxy proxy-target-class="true"/>
<context:component-scan base-package="com.ywj.log"/>
<context:component-scan base-package="com.*.*.biz.impl"/>Usage Example
Annotate any service method that requires logging:
@SystemCrmlog(description = "Performed login operation", tableName = Constans.USER_TABLENAME)
public void login(...) { ... }After execution, a record similar to the following is stored in the database:
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 Interview Crash Guide
Dedicated to sharing Java interview Q&A; follow and reply "java" to receive a free premium Java interview guide.
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.
