環(huán)境:IDEA版本2017.3.1 x64, JDK1.8蒲赂, SpringBoot2.1.1州丹, Druid1.1.8醋安, mybatis1.3.2杂彭,Security5.1.2,thymeleaf3.0.11
思路總結(jié):首先在需要做日志記錄的方法中添加一個(gè)自定義注解吓揪,再去實(shí)現(xiàn)一個(gè)日志AOP類亲怠,AOP類把自定義注解設(shè)置為切點(diǎn),所以當(dāng)系統(tǒng)執(zhí)行某一個(gè)添加了自定義注解的方法時(shí)柠辞,AOP會(huì)自動(dòng)獲取該方法名稱以及用戶信息實(shí)現(xiàn)日志記錄团秽。
編寫自定義注解
/**
* 自定義注解類 定義controller方法的中文含義
* @Target({METHOD,TYPE}) 表示這個(gè)注解可以用用在類/接口上,還可以用在方法上
* @Retention(RetentionPolicy.RUNTIME) 表示這是一個(gè)運(yùn)行時(shí)注解叭首,即運(yùn)行起來(lái)之后习勤,才獲取注解中的相關(guān)信息,而不像基本注解如@Override 那種不用運(yùn)行焙格,在編譯時(shí)eclipse就可以進(jìn)行相關(guān)工作的編譯時(shí)注解图毕。
* @Inherited 表示這個(gè)注解可以被子類繼承
* @Documented 表示當(dāng)執(zhí)行javadoc的時(shí)候,本注解會(huì)生成相關(guān)文檔
*/
@Target({METHOD, TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Operation {
String value() default "";
}
日志AOP類會(huì)用到獲取IP地址的工具類眷唉,也用到j(luò)son工具類予颤,實(shí)現(xiàn)工具類如下
實(shí)現(xiàn)必要工具類
獲取ip地址工具類
/**
* 獲取用戶真實(shí)的ip地址
* @param request
* @return
*/
public class IpAdrressUtil {
public static String getIpAdrress(HttpServletRequest request) {
String ip = null;
//X-Forwarded-For:Squid 服務(wù)代理
String ipAddresses = request.getHeader("X-Forwarded-For");
String unknown = "unknown";
if (ipAddresses == null || ipAddresses.length() == 0 || unknown.equalsIgnoreCase(ipAddresses)) {
//Proxy-Client-IP:apache 服務(wù)代理
ipAddresses = request.getHeader("Proxy-Client-IP");
}
if (ipAddresses == null || ipAddresses.length() == 0 || unknown.equalsIgnoreCase(ipAddresses)) {
//WL-Proxy-Client-IP:weblogic 服務(wù)代理
ipAddresses = request.getHeader("WL-Proxy-Client-IP");
}
if (ipAddresses == null || ipAddresses.length() == 0 || unknown.equalsIgnoreCase(ipAddresses)) {
//HTTP_CLIENT_IP:有些代理服務(wù)器
ipAddresses = request.getHeader("HTTP_CLIENT_IP");
}
if (ipAddresses == null || ipAddresses.length() == 0 || unknown.equalsIgnoreCase(ipAddresses)) {
//X-Real-IP:nginx服務(wù)代理
ipAddresses = request.getHeader("X-Real-IP");
}
//有些網(wǎng)絡(luò)通過(guò)多層代理,那么獲取到的ip就會(huì)有多個(gè)冬阳,一般都是通過(guò)逗號(hào)(,)分割開來(lái)蛤虐,并且第一個(gè)ip為客戶端的真實(shí)IP
if (ipAddresses != null && ipAddresses.length() != 0) {
ip = ipAddresses.split(",")[0];
}
//還是不能獲取到,最后再通過(guò)request.getRemoteAddr();獲取
if (ip == null || ip.length() == 0 || unknown.equalsIgnoreCase(ipAddresses)) {
ip = request.getRemoteAddr();
}
return ip;
}
}
實(shí)現(xiàn)json工具類
public class JacksonUtil {
private final static ObjectMapper objectMapper = new ObjectMapper();
private JacksonUtil() {
}
public static ObjectMapper getInstance() {
return objectMapper;
}
/**
* javaBean肝陪、列表數(shù)組轉(zhuǎn)換為json字符串
*/
public static String obj2json(Object obj) throws Exception {
return objectMapper.writeValueAsString(obj);
}
/**
* json 轉(zhuǎn)JavaBean
*/
public static <T> T json2pojo(String jsonString, Class<T> clazz) throws Exception {
objectMapper.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);
return objectMapper.readValue(jsonString, clazz);
}
/**
* json字符串轉(zhuǎn)換為map
*/
public static <T> Map<String, Object> json2map(String jsonString) throws Exception {
ObjectMapper mapper = new ObjectMapper();
mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
return mapper.readValue(jsonString, Map.class);
}
}
實(shí)現(xiàn)日志AOP類
/**
* 系統(tǒng)日志:切面處理類
*/
@Aspect
@Component
public class SysLogAspect {
@Autowired
private SysLogService sysLogService;
//定義切點(diǎn) @Pointcut
//在注解的位置切入代碼
@Pointcut("@annotation(cn.springboot.util.Operation)")
public void logPoinCut() {
}
//切面 配置通知
@AfterReturning("logPoinCut()")
public void saveSysLog(JoinPoint joinPoint) {
//保存日志
SysLog sysLog = new SysLog();
//從切面織入點(diǎn)處通過(guò)反射機(jī)制獲取織入點(diǎn)處的方法
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
//獲取切入點(diǎn)所在的方法
Method method = signature.getMethod();
//獲取操作
Operation operation = method.getAnnotation(Operation.class);
if (operation != null) {
String value = operation.value();
sysLog.setOperation(value);//保存獲取的操作
}
//獲取請(qǐng)求的類名
String className = joinPoint.getTarget().getClass().getName();
//獲取請(qǐng)求的方法名
String methodName = method.getName();
sysLog.setMethod(className + "." + methodName);
//請(qǐng)求的參數(shù)
Object[] args = joinPoint.getArgs();
//將參數(shù)所在的數(shù)組轉(zhuǎn)換成json
String params = null;
try {
params = JacksonUtil.obj2json(args);
} catch (Exception e) {
e.printStackTrace();
}
sysLog.setParams(params);
//請(qǐng)求的時(shí)間
sysLog.setCreateDate(new Date());
//獲取用戶名
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (!(authentication instanceof AnonymousAuthenticationToken)) {
sysLog.setUsername(authentication.getName());
}
//獲取用戶ip地址
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes())
.getRequest();
sysLog.setIp(IpAdrressUtil.getIpAdrress(request));
//調(diào)用service保存SysLog實(shí)體類到數(shù)據(jù)庫(kù)
sysLogService.saveLog(sysLog);
}
}
最后驳庭,我是調(diào)用service保存SysLog實(shí)體類到數(shù)據(jù)庫(kù),你也可以直接輸出到控制臺(tái)氯窍,要保存到數(shù)據(jù)庫(kù)饲常,還要實(shí)現(xiàn)service類,mapper類和javeBean荞驴。
我就簡(jiǎn)單貼個(gè)javaBean不皆,其他類就不具體貼出了。
@Data
@NoArgsConstructor
@AllArgsConstructor
public class SysLog implements Serializable {
private Long id;
private String username; //用戶名
private String operation; //操作
private String method; //方法名
private String params; //參數(shù)
private String ip; //ip地址
private Date createDate; //操作時(shí)間
//創(chuàng)建getter和setter方法
}
javaBean中使用lombok的注解實(shí)現(xiàn)了get熊楼、set等方法霹娄。
以下是存進(jìn)數(shù)據(jù)庫(kù)的數(shù)據(jù),關(guān)于IP的問題是因?yàn)楸镜販y(cè)試鲫骗,部署在服務(wù)器上就會(huì)有正常的IP地址了犬耻。
更多Spring Boot整合可瀏覽此博客:malizhi.cn