Best Practices for Java API Design, Controller Conventions, and AOP Using a Unified ResultBean
The article explains why a unified response format (ResultBean) is essential for Java Spring APIs, lists common interface design mistakes, provides concrete coding guidelines for controllers, and demonstrates how to use AOP for logging and exception handling with clear code examples.
In Java backend development, defining a consistent API contract is crucial for readability, maintainability, and enabling cross‑cutting concerns such as AOP. The author first enumerates typical problems like inconsistent return types, missing failure handling, irrelevant parameters, overly complex inputs, and not returning required data.
To solve these issues, a unified wrapper class ResultBean<T> is introduced. The class contains fields msg , code , and data , along with constants for SUCCESS, FAIL, NO_LOGIN, and NO_PERMISSION. Example definition:
@Data
public class ResultBean
implements Serializable {
private static final long serialVersionUID = 1L;
public static final int SUCCESS = 0;
public static final int FAIL = 1;
public static final int NO_LOGIN = -1;
public static final int NO_PERMISSION = 2;
private String msg = "success";
private int code = SUCCESS;
private T data;
public ResultBean() {}
public ResultBean(T data) { this.data = data; }
public ResultBean(Throwable e) { this.msg = e.toString(); this.code = FAIL; }
}Controller methods must return ResultBean (or PageResultBean for pagination) and avoid exposing raw Map , JSON strings, or framework objects such as HttpServletRequest . Parameters should contain only business‑related data; contextual information like language or user ID should be obtained from the session or ThreadLocal.
The article also provides a complete AOP implementation that logs execution time and uniformly handles exceptions. Key parts of the AOP class:
public class ControllerAOP {
private static final Logger logger = LoggerFactory.getLogger(ControllerAOP.class);
public Object handlerControllerMethod(ProceedingJoinPoint pjp) {
long start = System.currentTimeMillis();
ResultBean
result;
try {
result = (ResultBean
) pjp.proceed();
logger.info(pjp.getSignature() + " use time:" + (System.currentTimeMillis() - start));
} catch (Throwable e) {
result = handlerException(pjp, e);
}
return result;
}
private ResultBean
handlerException(ProceedingJoinPoint pjp, Throwable e) {
ResultBean
result = new ResultBean();
if (e instanceof CheckException) {
result.setMsg(e.getLocalizedMessage());
result.setCode(ResultBean.FAIL);
} else if (e instanceof UnloginException) {
result.setMsg("Unlogin");
result.setCode(ResultBean.NO_LOGIN);
} else {
logger.error(pjp.getSignature() + " error", e);
result.setMsg(e.toString());
result.setCode(ResultBean.FAIL);
}
return result;
}
}The corresponding Spring XML configuration registers the aspect:
<aop:aspectj-autoproxy/>
<bean id="controllerAop" class="xxx.common.aop.ControllerAOP"/>
<aop:config>
<aop:aspect id="myAop" ref="controllerAop">
<aop:pointcut id="target" expression="execution(public xxx.common.beans.ResultBean *(..))"/>
<aop:around method="handlerControllerMethod" pointcut-ref="target"/>
</aop:aspect>
</aop:config>By adhering to these conventions, developers achieve a clean, uniform API surface, simplify AOP usage, and reduce repetitive refactoring caused by inconsistent interface designs.
Java Architect Essentials
Committed to sharing quality articles and tutorials to help Java programmers progress from junior to mid-level to senior architect. We curate high-quality learning resources, interview questions, videos, and projects from across the internet to help you systematically improve your Java architecture skills. Follow and reply '1024' to get Java programming resources. Learn together, grow together.
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.