1.自定义注解
package cn.wwkj.pms.basis.annotation
;
import cn.wwkj.pms.basis.entity.vo.EnumLogCode
;
import java.lang.annotation.*
;
/**
* 日志注解 日志记录注解使用方法@SysLogAnno
(value
= "添加年份",operateType
= EnumLogCode .SYS
)
* @author GaoDongYang
* @date 2020/8/12 14:33
*/
@Documented
@Target
({ElementType.METHOD
})
@Retention
(RetentionPolicy.RUNTIME
)
public @interface SysLogAnno
{
/**
* 操作内容
* @return String
*/
String value
() default
"";
/**
* 日志code类型
* @return EnumLogCode
*/
EnumLogCode operateType
() default EnumLogCode.SYS
;
}
@Target({ElementType.METHOD}) 注解 表明作用在方法上的注解 ElementType 是一个枚举类型的常量提供了一个简单的分类:表示你自定义的注解可能出现在Java程序中的语法位置,常用的有METHOD、PARAMETER 可用位置和类型有:
package java.lang.annotation
;
/**
1. The constants of this enumerated
type provide a simple classification of the
2. syntactic locations where annotations may appear
in a Java program. These
3. constants are used
in {@link Target java.lang.annotation.Target
}
4. meta-annotations to specify where it is legal to
write annotations of a
5. given type.
6. @author Joshua Bloch
7. @since 1.5
8. @jls 9.6.4.1 @Target
9. @jls 4.1 The Kinds of Types and Values
*/
public enum ElementType
{
/** 类, 接口
(包括注释类型
), 或 枚举 声明 */
TYPE,
/** 字段声明(包括枚举常量) */
FIELD,
/** 方法声明
(Method declaration
) */
METHOD,
/** 正式的参数声明 */
PARAMETER,
/** 构造函数声明 */
CONSTRUCTOR,
/** 局部变量声明 */
LOCAL_VARIABLE,
/** 注释类型声明 */
ANNOTATION_TYPE,
/** 包声明 */
PACKAGE,
/**
* 类型参数声明
*
* @since 1.8
*/
TYPE_PARAMETER,
/**
* 使用的类型
*
* @since 1.8
*/
TYPE_USE
}
@Retention({RetentionPolicy.Runtime}) 注解 RetentionPolicy这个枚举类型的常量描述保留注释的各种策略,它们与元注释(@Retention)一起指定注释要保留多长时间,一般Runtime
package java.lang.annotation
;
/**
1. Annotation retention policy. The constants of this enumerated
type
2. describe the various policies
for retaining annotations. They are used
3.
in conjunction with the
{@link Retention
} meta-annotation
type to specify
4. how long annotations are to be retained.
5. 6. @author Joshua Bloch
7. @since 1.5
*/
public enum RetentionPolicy
{
/**
* 注释只在源代码级别保留,编译时被忽略
*/
SOURCE,
/**
* 注释将被编译器在类文件中记录
* 但在运行时不需要JVM保留。这是默认的
* 行为.
*/
CLASS,
/**
*注释将被编译器记录在类文件中
*在运行时保留VM,因此可以反读。
* @see java.lang.reflect.AnnotatedElement
*/
RUNTIME
}
@Documented注解 就是一个声明
Documented注解表明这个注释是由 javadoc记录的,在默认情况下也有类似的记录工具。 如果一个类型声明被注释了文档化,它的注释成为公共API的一部分。
2.引入aop依赖
pom文件中
<!--spring切面aop依赖--
>
<dependency
>
<groupId
>org.springframework.boot
</groupId
>
<artifactId
>spring-boot-starter-aop
</artifactId
>
</dependency
>
3.自定义切面类
package cn.wwkj.pms.basis.aspect
;
import cn.wwkj.pms.basis.annotation.SysLogAnno
;
import cn.wwkj.pms.basis.entity.bo.Log
;
import cn.wwkj.pms.basis.entity.vo.EnumLogCode
;
import cn.wwkj.pms.basis.service.SysLogService
;
import com.fasterxml.jackson.databind.ObjectMapper
;
import lombok.extern.slf4j.Slf4j
;
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.core.annotation.Order
;
import org.springframework.stereotype.Component
;
import java.lang.reflect.Method
;
/**
* 日志切面
* @author gaodongyang
* @date 2020/8/12 14:37
**/
@Aspect
@Component
@Order
(0
)
@Slf4j
public class LogAspect
{
private final SysLogService sysLogService
;
private final ObjectMapper objectMapper
;
/**
* 构造注入
* @param sysLogService 日志
* @param objectMapper json序列化映射器
*/
public LogAspect
(SysLogService sysLogService, ObjectMapper objectMapper
) {
this.sysLogService
= sysLogService
;
this.objectMapper
= objectMapper
;
}
/**
* 设置切点 对有@SysLogAnno注释的方法生效
* @author gaodongyang
* @date 2020/8/12 14:42
**/
@Pointcut
("@annotation(cn.wwkj.pms.basis.annotation.SysLogAnno)")
public void logPointCut
() {
}
/**
* 环绕通知
* @author gaodongyang
* @date 2020/8/12 16:47
* @param point 连接点
* @return Object 结果
**/
@Around
("logPointCut()")
public Object around
(ProceedingJoinPoint point
) throws Throwable
{
log.info
("环绕通知开始。。。。。");
//日志
Log sysLog
= new Log
();
Object result
= null
;
try
{
// 执行方法
result
= point.proceed
();
sysLog.setRet
(0
);
}catch
(Exception e
){
log.error
(e.getMessage
(),e
);
sysLog.setRet
(1
)
.setRetStr
(e.getMessage
());
}finally
{
//从切面织入点处通过反射机制获取织入点处的方法
MethodSignature signature
= (MethodSignature
) point.getSignature
();
//获取切入点所在的方法
Method method
= signature.getMethod
();
// 获得注解 操作
SysLogAnno monitorLog
= method.getAnnotation
(SysLogAnno.class
);
// 获得code
EnumLogCode operateType
= monitorLog.operateType
();
// 获得操作内容
String value
= monitorLog.value
();
System.out.print
(result
);
sysLog.setCont1
(value
);
//请求的参数
Object
[] args
= point.getArgs
();
//将参数所在的数组转换成json
StringBuilder params
= new StringBuilder
();
for
(Object o
: args
){
params.append
(objectMapper.writerWithDefaultPrettyPrinter
().writeValueAsString
(o
));
}
sysLog.setRemark1
(params.toString
());
//添加日志
sysLogService.add
(operateType,sysLog
);
log.info
("环绕通知结束。。。。。");
}
return result
;
}
}
4.日志有关数据库,这里只写插入方法
日志插入service层的add方法,主要就是在本方法上加上**@Transactional(propagation = Propagation.NOT_SUPPORTED)**事务,防止在其他方法上加日志注解时,因其方法内部的错误导致事务回归,日志也回滚,不能在其他方法执行失败时把日志插入数据库中。
/**
* 添加日志
* @param logParam 日志字段实体
* @author GaoDongYang, GongLiHai
* @date 2020/8/11 10:38
**/
@Override
@Transactional
(propagation
= Propagation.NOT_SUPPORTED
)
public void addLog
(Log logParam
) {
//初始化日志对象,并设置设备信息,用户信息,ip等内容
SysLog sysLog
= new SysLog
(logParam
)
.setDevice
(getDevice
(request.getHeader
("User-Agent")))
.setUserDept
(CurrentUserService.currentUserDetails
())
.setIp
(((WebAuthenticationDetails) SecurityContextHolder.getContext().getAuthentication().getDetails()).getRemoteAddress
())
.setInParam
(getFmtInParam
(logParam.getInParam
()));
sysLogMapper.insert
(sysLog
);
}
5.使用时,只需加上注解
/**
* 添加要素详情
* @param elementDetail 要添加的要素详情信息
* @param year 年份
* @return 返回添加结果
* @author WeiJian
**/
@RequestMapping
("/addElementDetail")
@SysLogAnno
(value
= "添加年份",operateType
= EnumLogCode .SYS
)
public Result
<?
> addElementDetail
(@Valid ElementDetail elementDetail, @CurrentYear int year
) {
elementDetail.setYear
(year
);
return ResultUtil.updateResult
(elementDetailService.addElementDetail
(elementDetail
));
}
6.我定义的注解中operateType = EnumLogCode .SYS时枚举类型
也可不用枚举,传字符串什么的都一样
package cn.wwkj.pms.basis.entity.vo
;
/**
* 日志 code name 枚举类
* @author gaodongyang
* @date 2020/8/11 10:00
**/
@SuppressWarnings
("unused")
public enum EnumLogCode
{
//code数据库映射
SYS
("001",
"系统日志"),
SYS_LOGIN
("001001",
"登录日志");
private String name
;
private String code
;
EnumLogCode
(String code, String name
){
this.name
= name
;
this.code
= code
;
}
public String getName
() {
return name
;
}
public String getCode
() {
return code
;
}
}