java 自定义注解+AOP实现日志记录
1、自定义注解
注解:参考我的这篇博客:https://blog.csdn.net/xiaoao20080/article/details/89632015
package com.xxx.controller.framework.auth;
import java.lang.annotation.*;
@Target({ ElementType.PARAMETER, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ControllerAop {
String url() default "";
/** 动作的名称 */
String action() default "";
}
2、通过AOP+反射
AOP:利用一种称为"横切"的技术,剖解开封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用模块,并将其命名为"Aspect",即切面。所谓"切面",简单说就是那些与业务无关,却为业务模块所共同调用的逻辑或责任封装起来,便于减少系统的重复代码,降低模块之间的耦合度,并有利于未来的可操作性和可维护性。
AOP相关概念
切面(Aspect):一个关注点的模块化,这个关注点实现可能另外横切多个对象。事务管理是J2EE应用中一个很好的横切关注点例子。方面用Spring的 Advisor或拦截器实现。
连接点(Joinpoint): 程序执行过程中明确的点,如方法的调用或特定的异常被抛出。
通知(Advice): 在特定的连接点,AOP框架执行的动作。各种类型的通知包括“around”、“before”和“throws”通知。通知类型将在下面讨论。许多AOP框架包括Spring都是以拦截器做通知模型,维护一个“围绕”连接点的拦截器链。Spring中定义了四个advice: BeforeAdvice, AfterAdvice, ThrowAdvice和DynamicIntroductionAdvice
切入点(Pointcut): 指定一个通知将被引发的一系列连接点的集合。AOP框架必须允许开发者指定切入点:例如,使用正则表达式。 Spring定义了Pointcut接口,用来组合MethodMatcher和ClassFilter,可以通过名字很清楚的理解, MethodMatcher是用来检查目标类的方法是否可以被应用此通知,而ClassFilter是用来检查Pointcut是否应该应用到目标类上。
通知(advice)
所谓通知指的就是指拦截到连接点之后要执行的代码,通知分为前置、后置、异常、最终、环绕通知五类:
Before(前) org.apringframework.aop.MethodBeforeAdvice
After-returning(返回后) org.springframework.aop.AfterReturningAdvice
After-throwing(抛出后) org.springframework.aop.ThrowsAdvice
Introduction(引入) org.springframework.aop.IntroductionInterceptor
Arround(环绕) org.aopaliance.intercept.MethodInterceptor
package com.xxx.controller.framework.auth;
import java.lang.reflect.Method;
import java.util.Date;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.xxx.controller.base.controller.BaseController;
import com.xxx.model.common.system.user.MipUser;
import com.xxx.model.common.system.userlog.SysUserlog;
import com.xxx.service.SysUserlogService;
import com.xxx.service.UserService;
import com.xxx.utils.OrgUtil;
import com.xxx.utils.Tools;
import com.xxx.utils.common.IdUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
/**
* 操作日志记录处理
*
* @author xiaoao
*/
@Aspect
@Component
public class ControllerAopOperator extends BaseController{
@Autowired
private SysUserlogService sysUserlogService;
HttpServletRequest request = null;
//声明AOP切入点,凡是使用了XXXOperateLog的方法均被拦截
@Pointcut("@annotation(com.xxx.controller.framework.auth.ControllerAop)")
public void log() {
// System.out.println("我是一个切入点");
}
/**
* 前置通知 用于拦截Controller层记录用户的操作
*
* @param joinPoint 切点
*/
@AfterReturning(pointcut = "log()")
public void doBefore(JoinPoint joinPoint)
{
handleLog(joinPoint, null);
}
@AfterThrowing(value = "log()", throwing = "e")
public void doAfter(JoinPoint joinPoint, Exception e)
{
handleLog(joinPoint, e);
}
private void handleLog(JoinPoint joinPoint, Exception e)
{
try
{
// 获得注解
ControllerAop controllerAop = giveController(joinPoint);
if (controllerAop== null)
{
return;
}
System.out.println(controllerAop.action()+">>>>>>>>>>>>>>>>>>>>>>>");
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes())
.getRequest();
// *========数据库日志=========*//
SysUserlog sysUserlog = new SysUserlog();
// 请求的IP
sysUserlog.setUserName(Tools.getUserName());
// sysUserlog.setUserName("sysadmin");
sysUserlog.setLoginTime(new Date());
String clientIp = getIpAddr(request);
sysUserlog.setLoginIp(clientIp);
sysUserlog.setUrl(request.getRequestURI());
sysUserlog.setRequestParam(JSON.toJSONString(request.getParameterMap()));
// 处理设置注解上的参数
getControllerMethodDescription(controllerAop,sysUserlog,request);
// 保存数据库
sysUserlogService.insertUserlog(sysUserlog);
}
catch (Exception ex)
{
// 记录本地异常日志
ex.printStackTrace();
}
}
/**
* 是否存在注解,如果存在就记录日志
*
* @param joinPoint
* @param //controllerAop
* @return
* @throws Exception
*/
private static ControllerAop giveController(JoinPoint joinPoint) throws Exception
{
Signature signature = joinPoint.getSignature();
MethodSignature methodSignature = (MethodSignature) signature;
Method method = methodSignature.getMethod();
if (method != null)
{
return method.getAnnotation(ControllerAop.class);
}
return null;
}
/**
* 获取注解中对方法的描述信息 用于Controller层注解
*
* @param /joinPoint 切点
* @return 方法描述
* @throws Exception
*/
public static void getControllerMethodDescription(ControllerAop controllerAop, SysUserlog sysUserlog,
HttpServletRequest request) throws Exception
{
// 设置action动作
sysUserlog.setAction(controllerAop.action());
}
/**
* 获取请求的参数,放到log中
*
* @param sysUserlog
* @param request
*/
@SuppressWarnings("all")
private static void setRequestValue(SysUserlog sysUserlog, HttpServletRequest request)
{
if (sysUserlog == null){
sysUserlog = new SysUserlog();
Map map = request.getParameterMap();
String params = JSONObject.toJSONString(map);
sysUserlog.setRequestParam(params);
}
}
}
IpUtils类
package com.xxxx.utils.logs.utils;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import javax.servlet.http.HttpServletRequest;
/**
* @version: V1.0
* @author: fendo
* @className: IpUtils
* @packageName: com.xxxx.logs.utils
* @description: IP工具类
* @data: 2018-05-31 10:48
**/
public class IpUtils {
/**
* 获取客户端IP地址
* @param request
* @return
*/
public static String getRemoteAddr(HttpServletRequest request) {
if (request == null) {
return "unknown";
}
String ip = request.getHeader("X-Forwarded-For");
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("Proxy-Client-IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("WL-Proxy-Client-IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("X-Real-IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr();
}
return StringUtils.split(ObjectUtils.toString(ip), ",")[0];
}
}
UserAgentUtils类
package com.xxxx.utils.logs.utils;
import eu.bitwalker.useragentutils.Browser;
import eu.bitwalker.useragentutils.DeviceType;
import eu.bitwalker.useragentutils.UserAgent;
import javax.servlet.http.HttpServletRequest;
/**
* @version: V1.0
* @author: fendo
* @className: UserAgentUtils
* @packageName: com.xxxx.logs.utils
* @description: 用户代理
* @data: 2018-05-31 10:49
**/
public class UserAgentUtils {
/**
* 获取用户代理对象
* @param request
* @return
*/
public static UserAgent getUserAgent(HttpServletRequest request){
return UserAgent.parseUserAgentString(request.getHeader("User-Agent"));
}
/**
* 获取设备类型
* @param request
* @return
*/
public static DeviceType getDeviceType(HttpServletRequest request){
return getUserAgent(request).getOperatingSystem().getDeviceType();
}
/**
* 是否是PC
* @param request
* @return
*/
public static boolean isComputer(HttpServletRequest request){
return DeviceType.COMPUTER.equals(getDeviceType(request));
}
/**
* 是否是手机
* @param request
* @return
*/
public static boolean isMobile(HttpServletRequest request){
return DeviceType.MOBILE.equals(getDeviceType(request));
}
/**
* 是否是平板
* @param request
* @return
*/
public static boolean isTablet(HttpServletRequest request){
return DeviceType.TABLET.equals(getDeviceType(request));
}
/**
* 是否是手机和平板
* @param request
* @return
*/
public static boolean isMobileOrTablet(HttpServletRequest request){
DeviceType deviceType = getDeviceType(request);
return DeviceType.MOBILE.equals(deviceType) || DeviceType.TABLET.equals(deviceType);
}
/**
* 获取浏览类型
* @param request
* @return
*/
public static Browser getBrowser(HttpServletRequest request){
return getUserAgent(request).getBrowser();
}
/**
* 是否IE版本是否小于等于IE8
* @param request
* @return
*/
public static boolean isLteIE8(HttpServletRequest request){
Browser browser = getBrowser(request);
return Browser.IE5.equals(browser) || Browser.IE6.equals(browser)
|| Browser.IE7.equals(browser) || Browser.IE8.equals(browser);
}
}
使用示例
/**
* @Description 查询日志列表
* @param //分页参数
* @return
*/
@ControllerAop(action="查询日志列表")
@RequestMapping("/getSyslogList")
@ResponseBody
public JSONObject getDepartList(String username,String logintime,String endTime,@RequestParam(defaultValue = "1") Integer
currentPage, @RequestParam(defaultValue =
"10") Integer size) throws Exception{
return sysUserlogService.userlogList(username,logintime,endTime,currentPage,size);
}
参考博客:1、https://blog.csdn.net/u011781521/article/details/80528049
2、https://blog.csdn.net/sinat_27143551/article/details/79794241
PS:自定义注解+AOP还可以用来实现权限控制,可以在请求方法上加上自定义权限注解,然后在切面类里添加业务逻辑。可参考:https://blog.csdn.net/qq_24689877/article/details/85143929