前言
众说周知,aop是oop思想的延续,是为了我们更好的程序的开发, 更便于我们对技术及代码的维护。
今天就利用aop来做一个日志的记 录。废话不多说,上代码。
实例
entity
import lombok.Data;
import lombok.NoArgsConstructor;
import org.hibernate.annotations.GenericGenerator;
import javax.persistence.*;
import java.io.Serializable;
@Entity
@Table(name="tb_operate_record")
@Data
@NoArgsConstructor
public class TbOperateRecord implements Serializable {
private static final long serialVersionUID = 4907931420975674126L;
@Id
@GeneratedValue(generator ="paymentableGenerator")
@GenericGenerator(name ="paymentableGenerator", strategy = "uuid")
@Column(name= "log_record_id")
private String id; // 主键 采用UUID 策略目的是为了适配数据库(具体项目可以选择合适的生成策略)
@Column(name="input_Params")
private String inputParams; //方法输入的参数
@Column(name="user_name")
private String userName; //操作人
@Column(name="operate_description")
private String operateDescription ; //用户操作记录描述
@Column( name = "req_ip")
private String reqIp; //请求ip地址
@Column(name="req_url")
private String reqUrl; //请求reqURL
@Column(name = "take_time")
private String takeTime; //耗时
@Column(name="operate_time")
private String operateTime; //操作时间
@Column(name= "method_description")
private String methodDescription; //接口功能描述
@Column(name="method_name")
private String methodName; //方法名
切面类
@Component
@Aspect
public class LoggerRecord {
private final static Logger logger = LoggerFactory.getLogger(LoggerRecord.class);
private final SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); // 规定日期格式
@Autowired
private HttpServletRequest request;
@Autowired
private TbOperateRecordRepository tbOperateRecordRepository;
//定义切点
@Pointcut(value = "execution(* com.*.controller.*.*(..))")
public void PointCutJcLog(){
}
@Around(value = "PointCutJcLog()") //编写逻辑
public Object saveJcRecordLogger(ProceedingJoinPoint joinPoint)throws Throwable{
TbOperateRecord operateRecord = new TbOperateRecord(); //创建日志记录对象
long startTime = System.currentTimeMillis();
Object retVal = joinPoint.proceed();
long entTime = System.currentTimeMillis();
String consumeTime = formatDuring(entTime - startTime);
operateRecord.setTakeTime(consumeTime);
operateRecord.setOperateTime(df.format(new Date()));
//从session中取出用户及部门信息
getUserInfo(operateRecord); //获取用户信息方法
//创建日志操作对象
Class<?> target = joinPoint.getTarget().getClass();
getJcLoggerSource(operateRecord, target); //获取日志目录
MethodSignature signature =(MethodSignature)joinPoint.getSignature(); //从切面织入点处通过反射机制获取织入点处的方法
Method method = signature.getMethod();
operateRecord.setMethodName(method.getName());
getUsingAndInterfaceFunction(operateRecord, method); //获取用途及接口功能描述
Object[] args = joinPoint.getArgs();
getRequestAgrs(operateRecord, args);
String remoteAddr = request.getRemoteAddr();//获取请求地址
operateRecord.setReqIp(remoteAddr);
String url = request.getRequestURL().toString();
operateRecord.setReqUrl(url);
try {
tbOperateRecordRepository.save(operateRecord);
logger.info("操作日志记录成功");
} catch (Exception e) {
logger.error("操作日志记录新增失败",e.getMessage());
}
return retVal; // 使用 ProceedingJoinPoin 该类必须返回,即释放拦截的
}
//获取请求参数
private void getRequestAgrs(TbOperateRecord operateRecord, Object[] args) {
if(null!= args){
List<Map<String, Object>> paramsMap = new ArrayList<Map<String, Object>>();
HttpServletRequest request;
for (int i = 0; i <args.length ; i++) {
try{
request = (HttpServletRequest) args[i]; //过滤参数为接口类型的
//如果是HttpServletRequest则跳过
continue;
}catch (Exception e){
ConcurrentHashMap<String, Object> eachParam = new ConcurrentHashMap<>();
eachParam.put("第"+(i+1)+"参数",args[i]);
paramsMap.add(eachParam);
}
}
String content = JSON.toJSONString(paramsMap);
operateRecord.setInputParams(content);
}else{
operateRecord.setInputParams("该方法没有请求参数");
}
}
//记录信息的组装
private void getOperateDescription(TbOperateRecord operateRecord, String name) {
String description = "";
StringBuffer buffer = new StringBuffer("用户【");
description = buffer.append(operateRecord.getUserName() + "】,于【")
.append(operateRecord.getOperateTime() + "】,执行【")
.append(operateRecord.getMethodDescription()+ "】操作,请求接口【")
.append(name + "】,请求ip【")
.append(operateRecord.getReqIp() + "】,请求参数【")
.append(operateRecord.getInputParams() + "】").toString();
operateRecord.setOperateDescription(description);
}
//请求耗时计算
public static String formatDuring(long time) {
long minutes = (time % (1000 * 60 * 60)) / (1000 * 60);
long seconds = (time % (1000 * 60)) / 1000;
long subsec = time % 1000;
return minutes + "分钟," + seconds + "秒," + subsec + "毫秒 ";
}
// 用户信息的获取 (根据自己项目封装的工具类取值)
private void getUserInfo(TbOperateRecord operateRecord) {
String userName="";
try {
Session currentUser = Session.getCurrentUser(request.getSession());
UserInfo userinfo = currentUser.getUserinfo();
if(null !=userinfo){
userName = userinfo.getUserName();
}
} catch (Exception e) {
logger.error("未获取当前登录人相关信息",e.getMessage());
}
operateRecord.setUserName(userName);
}
Repository
import com.sanling.credit.wd.entity.TbOperateRecord;
import org.springframework.data.jpa.repository.JpaRepository;
public interface TbOperateRecordRepository extends JpaRepository<TbOperateRecord,String> {
}
注意
提高篇:
https://blog.csdn.net/WindwirdBird/article/details/110129276
切点的选择及正则的写法很重要,必须写对
execution使用
https://blog.csdn.net/WindwirdBird/article/details/110201365