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.

Java Captain
Java Captain
Java Captain
Using Custom Annotations, Abstract Classes, and Interfaces in Spring Backend Development

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<Boolean> 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<Worker, String> implements IWorkerService {}

public interface WorkerService extends IWorkerService {}

public interface IWorkerService extends BaseService<Worker, String> {}

public abstract class BaseServiceImpl<T, ID> implements BaseService<T, ID> {}

MySQL Example

@Service
public class StudyServiceImpl extends ServiceImpl<StudyMapper, Study> implements StudyService {}

public interface StudyService extends IService<Study> {}

public class ServiceImpl<M extends BaseMapper<T>, T> implements IService<T> {}

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<PersonMapper, Person> {
    @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.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

BackendJavaaopspringCustom AnnotationInterfaceAbstract Class
Java Captain
Written by

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.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.