springboot自定义日志注解的实现
前言
在之前的日志记录的写法中,我们大多是写一个工具类,在这个类里面定义日志保存的方法,然后再controller中执行请求的时候调用即可,虽然调用仅仅一行代码,但是不够友好;所有可以写一个类似于@Controller等的注解,在需要保存日志的方法上面加上一个注解,这样不用在每个都写一端代码;话不多说上代码
1、首先一个log的实体类,这个无关紧要
package com.sysmg.system.domain; import java.io.Serializable; import java.util.Date; import javax.persistence.Column; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.Table; import javax.persistence.Transient; import com.sysmg.common.annotation.ExportConfig; @Table(name = "t_log") public class SysLog implements Serializable { private static final long serialVersionUID = -8878596941954995444L; @Id @GeneratedValue(generator = "JDBC") @Column(name = "ID") private Long id; @Column(name = "USERNAME") @ExportConfig(value = "操作用户") private String username; @Column(name = "OPERATION") @ExportConfig(value = "描述") private String operation; @Column(name = "TIME") @ExportConfig(value = "耗时(毫秒)") private Long time; @Column(name = "METHOD") @ExportConfig(value = "操作方法") private String method; @Column(name = "PARAMS") @ExportConfig(value = "参数") private String params; @Column(name = "IP") @ExportConfig(value = "IP地址") private String ip; @Column(name = "CREATE_TIME") @ExportConfig(value = "操作时间", convert = "c:com.sysmg.common.util.poi.convert.TimeConvert") private Date createTime; @Column(name = "LOCATION") @ExportConfig(value = "地点") private String location; // 用于搜索条件中的时间字段 @Transient private String timeField; /** * @return ID */ public Long getId() { return id; } /** * @param id */ public void setId(Long id) { this.id = id; } /** * @return USERNAME */ public String getUsername() { return username; } /** * @param username */ public void setUsername(String username) { this.username = username == null ? null : username.trim(); } /** * @return OPERATION */ public String getOperation() { return operation; } /** * @param operation */ public void setOperation(String operation) { this.operation = operation == null ? null : operation.trim(); } /** * @return TIME */ public Long getTime() { return time; } /** * @param time */ public void setTime(Long time) { this.time = time; } /** * @return METHOD */ public String getMethod() { return method; } /** * @param method */ public void setMethod(String method) { this.method = method == null ? null : method.trim(); } /** * @return PARAMS */ public String getParams() { return params; } /** * @param params */ public void setParams(String params) { this.params = params == null ? null : params.trim(); } /** * @return IP */ public String getIp() { return ip; } /** * @param ip */ public void setIp(String ip) { this.ip = ip == null ? null : ip.trim(); } /** * @return CREATE_TIME */ public Date getCreateTime() { return createTime; } /** * @param createTime */ public void setCreateTime(Date createTime) { this.createTime = createTime; } public String getLocation() { return location; } public void setLocation(String location) { this.location = location; } public String getTimeField() { return timeField; } public void setTimeField(String timeField) { this.timeField = timeField; } }
2、定义一个注解接口
package com.sysmg.common.annotation; 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 Log { String value() default ""; }
@Target(ElementType.METHOD)代表是方法上的注解,当然也可以是类注解,字段注解等
@Target(ElementType.TYPE) //接口、类、枚举、注解
@Target(ElementType.FIELD) //字段、枚举的常量
@Target(ElementType.METHOD) //方法
@Target(ElementType.PARAMETER) //方法参数
@Target(ElementType.CONSTRUCTOR) //构造函数
@Target(ElementType.LOCAL_VARIABLE)//局部变量
@Target(ElementType.ANNOTATION_TYPE)//注解
@Target(ElementType.PACKAGE) ///包
@Retention(RetentionPolicy.RUNTIME)代表注解会被jvm保留,这个参数有三种
RetentionPolicy.SOURCE —— 这种类型的Annotations只在源代码级别保留,编译时就会被忽略
RetentionPolicy.CLASS —— 这种类型的Annotations编译时被保留,在class文件中存在,但JVM将会忽略
RetentionPolicy.RUNTIME —— 这种类型的Annotations将被JVM保留,所以他们能在运行时被JVM或其他使用反射机制的代码所读取和使用。
一般默认第三种
当然也可以写
@Documented和@Order(优先级 数字越小优先级越高)
@Documented 注解表明这个注解应该被 javadoc工具记录. 默认情况下,javadoc是不包括注解的. 但如果声明注解时指定了 @Documented,则它会被 javadoc 之类的工具处理, 所以注解类型信息也会被包括在生成的文档中。
@Order标记定义了组件的加载顺序,这个标记包含一个value属性。属性接受整形值。如:1,2 等等。值越小拥有越高的优先级。Ordered.HIGHEST_PRECEDENCE这个属性值是最高优先级的属性,它的值是-2147483648,对应的最低属性值是Ordered.LOWEST_PRECEDENCE,它的值是2147483647。
String value() default ""
这个代表是要传递的参数,类似
@Autowired(required=true) public @interface Autowired { /** * Declares whether the annotated dependency is required. * <p>Defaults to {@code true}. */ boolean required() default true; }
springmvc项目还需要开启切面编程
<aop:aspectj-autoproxy proxy-target-class="true"/>
springboot默认是开启的
3、定义注解的实现类,也就是这个注解要做什么事
package com.sysmg.common.aspect; import java.lang.reflect.Method; import java.util.Date; import javax.servlet.http.HttpServletRequest; import org.apache.shiro.SecurityUtils; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; import org.aspectj.lang.reflect.MethodSignature; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.LocalVariableTableParameterNameDiscoverer; import org.springframework.stereotype.Component; import com.fasterxml.jackson.databind.ObjectMapper; import com.sysmg.common.annotation.Log; import com.sysmg.common.util.AddressUtilsBak; import com.sysmg.common.util.HttpContextUtils; import com.sysmg.common.util.IPUtils; import com.sysmg.system.domain.SysLog; import com.sysmg.system.domain.User; import com.sysmg.system.service.LogService; @Aspect @Component public class LogAspect { @Autowired private LogService logService; @Autowired ObjectMapper mapper; @Pointcut("@annotation(com.sysmg.common.annotation.Log)") public void pointcut() { } @Around("pointcut()") public Object around(ProceedingJoinPoint point) { Object result = null; long beginTime = System.currentTimeMillis(); try { result = point.proceed(); } catch (Throwable e) { e.printStackTrace(); } long time = System.currentTimeMillis() - beginTime; saveLog(point, time); return result; } private void saveLog(ProceedingJoinPoint joinPoint, long time) { User user = (User) SecurityUtils.getSubject().getPrincipal(); MethodSignature signature = (MethodSignature) joinPoint.getSignature(); Method method = signature.getMethod(); SysLog log = new SysLog(); Log logAnnotation = method.getAnnotation(Log.class); if (logAnnotation != null) { log.setOperation(logAnnotation.value()); } String className = joinPoint.getTarget().getClass().getName(); String methodName = signature.getName(); log.setMethod(className + "." + methodName + "()"); Object[] args = joinPoint.getArgs(); LocalVariableTableParameterNameDiscoverer u = new LocalVariableTableParameterNameDiscoverer(); String[] paramNames = u.getParameterNames(method); if (args != null && paramNames != null) { String params = ""; for (int i = 0; i < args.length; i++) { params += " " + paramNames[i] + ": " + args[i]; } log.setParams(params); } HttpServletRequest request = HttpContextUtils.getHttpServletRequest(); log.setIp(IPUtils.getIpAddr(request)); log.setUsername(user.getUsername()); log.setTime(time); log.setCreateTime(new Date()); log.setLocation(AddressUtilsBak.getRealAddressByIP(log.getIp(), mapper)); this.logService.save(log); } }
这里的实现类中日志添加的方法中使用的淘宝的获取ip服务,后续会加上去,其实这里面可以随便写一个实现方法,因为我是留工具备份,所以记录的较多
具体的@Aspect、@Pointcut、@Around、@Before、@After等aop相关的注解和参数需要自己去巩固一下知识
4、然后就可以在你想要记录日志的地方使用即可
package com.sysmg.controller; import java.util.List; import java.util.Map; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; import com.github.pagehelper.PageHelper; import com.github.pagehelper.PageInfo; import com.sysmg.common.annotation.Log; import com.sysmg.common.domain.QueryRequest; import com.sysmg.common.domain.ResponseBo; import com.sysmg.common.util.FileUtils; @Controller public class TestController{ @Log("规则") @RequestMapping("test") public String index() { return "test"; } }
大概这样就可以使用了,目前里面缺少aop相关知识的介绍以及获取访问ip的工具类,不过跟本课题关系不大,后续会更上去
到此这篇关于springboot自定义日志注解的实现的文章就介绍到这了,更多相关springboot自定义日志注解内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!