Using Custom Annotations, Abstract Classes, and Interfaces in Spring Backend Development
This article demonstrates how to create and apply custom Spring annotations for AOP logging, implements the corresponding aspect, and discusses the strategic use of abstract classes and interfaces to separate business logic from data access in both MongoDB and MySQL contexts.
6. Custom Annotations
Spring custom annotations allow flexible AOP operations during project development, especially when placed on interface methods. The example shows a global request‑operation log persistence using a custom annotation.
6.1 Define Annotation
The annotation definition includes retention, target, and inherited settings, along with attributes for a track ID and an operation enum.
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Inherited
public @interface OperateLog {
/**
* Track ID
*/
String trackId() default "";
/**
* Specific operation behavior
*/
OperationEnum operation();
}The OperationEnum must be prepared beforehand to describe concrete actions.
@Getter
@RequiredArgsConstructor
public enum OperationEnum {
XX_MODULE_ADD("xx模块", "新增xx"),
XX_MODULE_UPDATE("xx模块", "修改xx");
private final String module;
private final String detail;
}6.2 Aspect Implementation
The aspect determines when the annotated method is intercepted; for logging, @AfterReturning is typical, while authentication may use @Before . The following aspect extracts annotation parameters, builds a log entity, and persists it.
@Aspect
@Component
public class OperateLogAOP {
@Resource
private OperationLogService operationLogService;
/**
* Executes after the method returns, recording the operation log.
*/
@AfterReturning(value = "@annotation(operateLog)")
public void operateLogAopMethod(JoinPoint joinPoint, OperateLog operateLog) {
String trackId = operateLog.trackId();
Assert.hasText(trackId, "trackId param error!");
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
Object[] args = joinPoint.getArgs();
String businessLogId = (String) AopUtils.getFieldValue(args, methodSignature, trackId);
String module = operateLog.operation().getModule();
String detail = operateLog.operation().getDetail();
HttpServletRequest request = ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest();
OperationLog operationLog = OperationLog.builder()
.trackId(businessLogId)
.module(module)
.detail(detail)
.ip(IpUtil.getUserIp(request))
.createTime(new Date())
.operatorUuid(UserDataBuilder.get().getUserUuid())
.operatorName(UserDataBuilder.get().getUserName())
.build();
operationLogService.save(operationLog);
}
}6.3 Business Usage
After defining the annotation and aspect, place the annotation on controller methods to automatically record logs.
@PostMapping("update")
@OperateLog(trackId = "studyDTO.id", operation = OperationEnum.XX_MODULE_UPDATE)
public BaseResponse
updateStudy(@RequestBody StudyDTO studyDTO) {
return ResultUtils.success(studyService.updateStudy(studyDTO));
}7. Abstract Classes and Interfaces
Using abstract classes and interfaces correctly reduces coupling and improves code reuse. Abstract classes share common features, while interfaces decouple contracts from implementations.
7.1 Isolate Business Layer from ORM Layer
MongoDB Example
@Service
public class WorkerServiceImpl extends AbstractWorkerServiceImpl implements WorkerService {}
public abstract class AbstractWorkerServiceImpl extends BaseServiceImpl
implements IWorkerService {}
public interface WorkerService extends IWorkerService {}
public interface IWorkerService extends BaseService
{}
public abstract class BaseServiceImpl
implements BaseService
{}MySQL Example
@Service
public class StudyServiceImpl extends ServiceImpl
implements StudyService {}
public interface StudyService extends IService
{}
public class ServiceImpl
, T> implements IService
{}7.2 Isolate Subsystem Business Implementation
The Facade pattern provides a simple entry point for subsystems, hiding complexity.
/** Only cares about data, essentially database operations */
@Service
public class PersonService extends ServiceImpl
{
@Resource
private PersonMapper mapper;
// other DB statements
}
/** Only cares about business, referenced by controller */
@Service
public class PersonFacade {
@Resource
private PersonService service;
// business logic methods
}This separation clarifies responsibilities between data handling and business processing.
7.3 Choice Comparison
In practice, choose either abstract classes or interfaces for isolating business from data; the combination best reflects Java's inheritance, encapsulation, and polymorphism principles.
Article Summary
This second article in the development‑tips series introduces practical techniques for custom Spring annotations, AOP logging, and the disciplined use of abstract classes and interfaces to structure backend services, with concrete code examples for both MongoDB and MySQL.
Java Captain
Focused on Java technologies: SSM, the Spring ecosystem, microservices, MySQL, MyCat, clustering, distributed systems, middleware, Linux, networking, multithreading; occasionally covers DevOps tools like Jenkins, Nexus, Docker, ELK; shares practical tech insights and is dedicated to full‑stack Java development.
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.