@Target
@Retention
@Documented
@Inherited
/**
* Operate Log 的自定义注解
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface OpLog {
/**
* 业务类型,如新增、删除、修改
* @return
*/
public OpType opType();
/**
* 业务对象名称,如订单、库存、价格
* @return
*/
public String opItem();
/**
* 业务对象编号表达式,描述了如何获取订单号的表达式
* @return
*/
public String opItemIdExpression();
}
/**
* OpLog的切面处理类,用于通过注解获取日志信息,进行日志记录
* @author Hollis
*/
@Aspect
@Component
public class OpLogAspect {
private static final Logger LOGGER = LoggerFactory.getLogger(OpLogAspect.class);
@Autowired
HttpServletRequest request;
@Around("@annotation(com.hollis.annotation.OpLog)")
public Object log(ProceedingJoinPoint pjp) throws Exception {
Method method = ((MethodSignature)pjp.getSignature()).getMethod();
OpLog opLog = method.getAnnotation(OpLog.class);
Object response = null;
try {
// 目标方法执行
response = pjp.proceed();
} catch (Throwable throwable) {
throw new Exception(throwable);
}
if (StringUtils.isNotEmpty(opLog.opItemIdExpression())) {
SpelExpressionParser parser = new SpelExpressionParser();
Expression expression = parser.parseExpression(opLog.opItemIdExpression());
EvaluationContext context = new StandardEvaluationContext();
// 获取参数值
Object[] args = pjp.getArgs();
// 获取运行时参数的名称
LocalVariableTableParameterNameDiscoverer discoverer
= new LocalVariableTableParameterNameDiscoverer();
String[] parameterNames = discoverer.getParameterNames(method);
// 将参数绑定到context中
if (parameterNames != null) {
for (int i = 0; i < parameterNames.length; i++) {
context.setVariable(parameterNames[i], args[i]);
}
}
// 将方法的resp当做变量放到context中,变量名称为该类名转化为小写字母开头的驼峰形式
if (response != null) {
context.setVariable(
CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_CAMEL, response.getClass().getSimpleName()),
response);
}
// 解析表达式,获取结果
String itemId = String.valueOf(expression.getValue(context));
// 执行日志记录
handle(opLog.opType(), opLog.opItem(), itemId);
}
return response;
}
private void handle(OpType opType, String opItem, String opItemId) {
// 通过日志打印输出
LOGGER.info("opType = " + opType.name() +",opItem = " +opItem + ",opItemId = " +opItemId);
}
}
-
1、使用@Around注解来指定对标注了OpLog的方法设置切面。 -
2、使用Spel的相关方法,通过指定的表示,从对应的参数中获取到目标对象的唯一性标识。 -
3、再方法执行成功后,输出日志。
@RequestMapping(method = {RequestMethod.GET, RequestMethod.POST})
@OpLog(opType = OpType.QUERY, opItem = "order", opItemIdExpression = "#id")
public @ResponseBody
HashMap view(@RequestParam(name = "id") String id)
throws Exception {
}
#id
指定即可。
@RequestMapping(method = {RequestMethod.GET, RequestMethod.POST})
@OpLog(opType = OpType.QUERY, opItem = "order", opItemIdExpression = "#orderVo.id")
public @ResponseBody
HashMap update(OrderVO orderVo)
throws Exception {
}
@RequestMapping(method = {RequestMethod.GET, RequestMethod.POST})
@OpLog(opType = OpType.QUERY, opItem = "order", opItemIdExpression = "#insertResult.id")
public @ResponseBody
InsertResult insert(OrderVO orderVo)
throws Exception {
return orderDao.insert(orderVo);
}
public class User {
private String idempotentNo;
@NotNull(
message = "userName can't be null"
)
private String userName;
}
/**
* 参数校验工具
* @author Hollis
*/
public class BeanValidator {
private static Validator validator = Validation.byProvider(HibernateValidator.class).configure().failFast(true)
.buildValidatorFactory().getValidator();
/**
* @param object object
* @param groups groups
*/
public static void validateObject(Object object, Class<?>... groups) throws ValidationException {
Set<ConstraintViolation<Object>> constraintViolations = validator.validate(object, groups);
if (constraintViolations.stream().findFirst().isPresent()) {
throw new ValidationException(constraintViolations.stream().findFirst().get().getMessage());
}
}
}
/**
* facade接口注解, 用于统一对facade进行参数校验及异常捕获
* <pre>
* 注意,使用该注解需要注意,该方法的返回值必须是BaseResponse的子类
* </pre>
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Facade {
}
/**
* Facade的切面处理类,统一统计进行参数校验及异常捕获
* @author Hollis
*/
@Aspect
@Component
public class FacadeAspect {
private static final Logger LOGGER = LoggerFactory.getLogger(FacadeAspect.class);
@Autowired
HttpServletRequest request;
@Around("@annotation(com.hollis.annotation.Facade)")
public Object facade(ProceedingJoinPoint pjp) throws Exception {
Method method = ((MethodSignature)pjp.getSignature()).getMethod();
Object[] args = pjp.getArgs();
Class returnType = ((MethodSignature)pjp.getSignature()).getMethod().getReturnType();
//循环遍历所有参数,进行参数校验
for (Object parameter : args) {
try {
BeanValidator.validateObject(parameter);
} catch (ValidationException e) {
return getFailedResponse(returnType, e);
}
}
try {
// 目标方法执行
Object response = pjp.proceed();
return response;
} catch (Throwable throwable) {
return getFailedResponse(returnType, throwable);
}
}
/**
* 定义并返回一个通用的失败响应
*/
private Object getFailedResponse(Class returnType, Throwable throwable)
throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
//如果返回值的类型为BaseResponse 的子类,则创建一个通用的失败响应
if (returnType.getDeclaredConstructor().newInstance() instanceof BaseResponse) {
BaseResponse response = (BaseResponse)returnType.getDeclaredConstructor().newInstance();
response.setSuccess(false);
response.setResponseMessage(throwable.toString());
response.setResponseCode(GlobalConstant.BIZ_ERROR);
return response;
}
LOGGER.error(
"failed to getFailedResponse , returnType (" + returnType + ") is not instanceof BaseResponse");
return null;
}
}
@Facade
public TestResponse query(User user) {
}
往期推荐
一个技术总监的忠告:精通那么多技术,你为何还是受不到重用?
大厂的 404 页面都长啥样?看到最后一个,我笑了...
重磅:Swagger3.0 官方 starter 诞生了,其它的都可以扔了~
直面Java第329期:哪个命令可以监控虚拟机各种运行状态信息?
深入并发第013期:拓展synchronized——锁优化
本文分享自微信公众号 - Hollis(hollischuang)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。
来源:oschina
链接:https://my.oschina.net/u/3991012/blog/4710843