- 确保框架中引入了aspectj 相关jar包
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>${spring-version}</version>
</dependency>
- 确保编写的@Aspect能被spring扫码到
<context:component-scan base-package="xxxxxx">
</context:component-scan>
- 编写切面类
@Aspect
@Component
public class LogAspect {
private Logger logger = LoggerFactory.getLogger(getClass());
/** 计算操作消耗时间 */
private static final ThreadLocal<Long> TIME_THREADLOCAL = new NamedThreadLocal<Long>("Cost Time");
/**
* 处理请求前执行
*/
@Before(value = "@annotation(controllerLog)")
public void boBefore(JoinPoint joinPoint, Log controllerLog) {
TIME_THREADLOCAL.set(System.currentTimeMillis());
}
/**
* 处理完请求后执行
*
* @param joinPoint 切点
*/
@AfterReturning(pointcut = "@annotation(controllerLog)", returning = "jsonResult")
public void doAfterReturning(JoinPoint joinPoint, Log controllerLog, Object jsonResult) {
handleLog(joinPoint, controllerLog, null, jsonResult);
}
protected void handleLog(final JoinPoint joinPoint, Log controllerLog, final Exception e, Object jsonResult) {
try {
HttpServletRequest request = ServletUtils.getRequest();
String requestUrl = StringUtils.substring(request.getRequestURI(), 0, 255);
String ip = CommonUtils.getIp(request);//增加操作人的IP地址
String params = argsArrayToString(joinPoint.getArgs(), null);
long time = System.currentTimeMillis() - TIME_THREADLOCAL.get();
logger.info("params={} time={} ip={} requestUrl={}", params, time, ip, requestUrl);
logger.info("jsonResult={}", JSON.toJSONString(jsonResult));
} catch (Exception exp) {
// 记录本地异常日志
logger.error("异常信息:{}", exp.getMessage());
exp.printStackTrace();
} finally {
TIME_THREADLOCAL.remove();
}
}
/**
* 参数拼装
*/
private String argsArrayToString(Object[] paramsArray, String[] excludeParamNames)
{
String params = "";
if (paramsArray != null && paramsArray.length > 0)
{
for (Object o : paramsArray)
{
if (o != null && !isFilterObject(o))
{
try
{
String jsonObj = JSON.toJSONString(o);
params += jsonObj.toString() + " ";
}
catch (Exception e)
{
}
}
}
}
return params.trim();
}
public boolean isFilterObject(final Object o)
{
Class<?> clazz = o.getClass();
if (clazz.isArray())
{
return clazz.getComponentType().isAssignableFrom(MultipartFile.class);
}
else if (Collection.class.isAssignableFrom(clazz))
{
Collection collection = (Collection) o;
for (Object value : collection)
{
return value instanceof MultipartFile;
}
}
else if (Map.class.isAssignableFrom(clazz))
{
Map map = (Map) o;
for (Object value : map.entrySet())
{
Map.Entry entry = (Map.Entry) value;
return entry.getValue() instanceof MultipartFile;
}
}
return o instanceof MultipartFile || o instanceof HttpServletRequest || o instanceof HttpServletResponse
|| o instanceof BindingResult;
}
}
- 切面注解类
@Target({ ElementType.PARAMETER, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Log
{
/**
* 模块
*/
public String title() default "";
/**
* 功能
*/
public FunctionTypeEnum businessType() default FunctionTypeEnum.QUERY;
/**
* 是否保存请求的参数
*/
public boolean isSaveRequestData() default true;
/**
* 是否保存响应的参数
*/
public boolean isSaveResponseData() default true;
/**
* 排除指定的请求参数
*/
public String[] excludeParamNames() default {};
}
- 在对应的Controller上增加注解
@Log(title = "用户信息查询", businessType = BusinessType.Query)
@RequestMapping(value="getUsers", method=RequestMethod.POST)
public @ResponseBody PageData queryUsersPage(PageForm form, User user) throws Exception{
PageData users = this.userService.selectUsersForPage(form, user);
return users;
}
总结
切面是Spring中一种比较常用的操作,面向切面编程,最大的好处的对业务代码无入侵。当然程序中增加一个@Log 注解是方便要给指定的请求增加日志,不是所有请求。可以指定一些业务参数,实际情况根据自身业务情况来看。不加@Log也是可以的,那就切面到所有Controller,编写好你想要切面的目的表达式即可。