接到一个需求,记录对后台控制系统的所有增、删、改操作,将员工号与ip等相关信息记录下来。
分析下来,可在service层方法上添加切面,通过AOP来拦截记录控制信息。因为后台开发历史太久,经手人太多(屎堆),增删改操作命名不规范,故不适合用切点表达式来做切点。故采用自定义注解方法,在需要记录的操作上添加注解就可以了。
1.切面
LOGGER为公司日志组件,可用合适日志工具替换。
request正常获取的条件是,该切点的方法只能被http请求调用。如果job或者远程服务的方法调用是无法获取request的。
@Aspect
@Component
public class LogServiceAspect {
private static final SnfLogger LOGGER = SnfLoggerFactory.getLogger(LogServiceAspect.class);
private static final String SPLIT_STR = ";";
private static final String JOIN_STR = "#";
/**
* 该切面用于service层,获取service的入参、出参等用于打印日志。
* 注意—当调用非http请求调用方法时,无法获取上下文的request。
* 所以当该service方法有被非http请求调用时,不宜使用该切面
* @param joinPoint
* @return
*/
@Around(value = "@annotation(com.suning.springaop.LogAnnotation)")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
Object result = null;
String ip =StringUtils.EMPTY;
String employeeId =StringUtils.EMPTY;
String function =StringUtils.EMPTY;//功能说明,通过解析注解信息获取
String classAndMethod =StringUtils.EMPTY;
String params =StringUtils.EMPTY;//入参
try {
//获取requeset
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
HttpServletRequest request = ((ServletRequestAttributes) requestAttributes).getRequest();
//获取员工号
employeeId = request.getRemoteUser();
// 获取ip
ip = GetClientIPUtil.getClientIP(IpUtils.getIpAddr(request));
//获取类、方法名
String className = joinPoint.getSignature().getDeclaringTypeName();
String methodName = joinPoint.getSignature().getName();
classAndMethod = className + SPLIT_STR + methodName;
//获取注解内容
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
Method targetMethod = methodSignature.getMethod();
Method realMethod = joinPoint.getTarget().getClass().getDeclaredMethod(methodName, targetMethod.getParameterTypes());
LogAnnotation logAnnotation = AnnotationUtils.findAnnotation(realMethod, LogAnnotation.class);
//注解——功能描述
String detail = logAnnotation.detail();
//注解——功能模块
MoudleEnum moudleEnum = logAnnotation.moudle();
function = moudleEnum.toString() + SPLIT_STR + detail;
//获取入参
List<Object> args = Arrays.asList(joinPoint.getArgs());
for (int i = 0; i < args.size(); i++) {
params += JSONObject.toJSONString(args.get(i)) + JOIN_STR;
}
params = StringUtils.isBlank(params) ? params : params.substring(params.length() - 1);
} catch (Exception e) {
LOGGER.warn("LogAspException:", e);
} finally {
result = joinPoint.proceed();
LOGGER.warn("{};{};{};{};{};{};", new Object[]{ip, employeeId, function, classAndMethod, params, result});
}
return result;
}
}
2. 注解
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 自定义日志增强注解
* 在需要进行审计的方法上添加该注解,可在注解内部填写合适的描述信息
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface LogAnnotation {
String detail() default "...";
MoudleEnum moudle() default MoudleEnum.DEFAULT ;
}
3.后台功能枚举类
import org.apache.commons.lang3.StringUtils;
/**
* @Description 功能模块定义,枚举类。用于注解中描述方法所属功能模块
*
*/
public enum MoudleEnum {
MENU("menu","菜单模块"),
ADDRESS("address","地址管理"),
DEFAULT("default","未定义");
private MoudleEnum(String code,String msg) {
this.code = code;
this.msg = msg;
}
private String msg;
private String code;
public String getCode() {
return code;
}
public String getMsg() {
return msg;
}
/**
* todo 该方法写的较差,后面改掉
* @param code
* @return 返回中文模块名称
*/
public static String getMsgByCode(String code){
if (StringUtils.isBlank(code)) {
return StringUtils.EMPTY;
}
for (MoudleEnum msiConstellationEnum : MoudleEnum.values()) {
if (msiConstellationEnum.getCode().equals(code)) {
return msiConstellationEnum.getMsg();
}
}
return StringUtils.EMPTY;
}
@Override
public String toString() {
return this.getCode()+";"+this.getMsg();
}
}
4. aop配置
5.使用
6.效果
懒得截图,后面补