Spring基于注解的日志功能实现 (aop,aspectj和annotation)
自定义注解
注解相当于一种标记,在程序中加了注解就等于为程序打上了某种标记,没加,则等于没有某种标记,javac编译器,开发工具和其他程序可以用反射来了解你的类及各种元素上有无何种标记,看你有什么标记,就去干相应的事。标记可以加在包,类,字段,方法,方法的参数以及局部变量上。
java元注解介绍
Java中提供了四种元注解,专门负责注解其他的注解,分别如下:
@Retention元注解,表示需要在什么级别保存该注释信息(生命周期)。可选的RetentionPoicy参数包括:
RetentionPolicy.SOURCE: 停留在java源文件,编译器被丢掉
RetentionPolicy.CLASS:停留在class文件中,但会被VM丢弃(默认)
RetentionPolicy.RUNTIME:内存中的字节码,VM将在运行时也保留注解,因此可以通过反射机制读取注解的信息
@Target元注解,默认值为任何元素,表示该注解用于什么地方。可用的ElementType参数包括
ElementType.CONSTRUCTOR: 构造器声明
ElementType.FIELD: 成员变量、对象、属性(包括enum实例)
ElementType.LOCAL_VARIABLE: 局部变量声明
ElementType.METHOD: 方法声明
ElementType.PACKAGE: 包声明
ElementType.PARAMETER: 参数声明
ElementType.TYPE: 类、接口(包括注解类型)或enum声明
@Documented注解将注解包含在JavaDoc中
@Inheried注解允许子类继承父类中的注解
基本的注解演示
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface LogOut {
String module();
}
使用方法
@HystrixCommand(fallbackMethod = "fallBackMethod")
@GetMapping("test")
@LogOut(module = "模块")
public @ResponseBody String getTest(@RequestParam("msg") String msg){
int b=0;
System.out.println(100/b);
return "我是服务器一号!";
}
//当注解中只有valUe属性时,可简化为 @LogOut("我是value对应的值")
实现日志保存功能
-
自定义注解,配置保存需要的参数
@Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface LogOut { String module(); //模块名 String function(); //功能名 String value() default ""; //自定义内容 }
2.使用AspectJ切面切入
@Aspect //表示为切面类
@Component //交由spring去管理
@EnableAspectJAutoProxy(proxyTargetClass = true) //默认为false,true表示使用cglib代理,false表示jdk动态代理
public class LogAnnotationAspect {
private static Logger LOGGER = LoggerFactory.getLogger(LogAnnotationAspect.class);
//设置切入点(此处为使用LogOut注解的方法)
@Pointcut("@annotation(com.stoneppy.ec.annoation.LogOut)")
public void pointcutConfig(){}
//前置通知
@Before("pointcutConfig()")
public void before(JoinPoint joinpoint) {
LOGGER.info("前置通知---方法前执行" + joinpoint);
//伪代码,执行日志保存操作:logService.save(xxx)
//获取注解参数
Map<String,String> annotionMap = Maps.newHashMap();
Class targetClass = joinpoint.getTarget().getClass();
String methodName = joinpoint.getSignature().getName();
Object[] arguments = joinpoint.getArgs();
Method[] methods = targetClass.getMethods();
for(Method m:methods){
//找到切入点对应得方法
if(m.getName().equals(methodName)){
//拿到方法上的注解对象,获取参数
LogOut logOut= m.getAnnotation(LogOut.class);
//拿到方法上的请求url
GetMapping requestMapping= m.getAnnotation(GetMapping.class);
//获取参数
String url=requestMapping.value()[0];
String module=logOut.module();
String fun=logOut.function();
String desc=logOut.value();
LOGGER.info("接受到的参数:module-{},function-{},desc-{},url-{}",module,fun,desc,url);
//拿到参数后将数据保存到数据库......
// .... 此处保存代码省略
//
}
}
LOGGER.info("执行日志写入日到MySQL数据库成功");
}
//如有需要,可做操作
@After("pointcutConfig()")
public void after(JoinPoint joinpoint) {
//LOG.info("后置通知---方法后执行" + joinpoint);
}
//如有需要,可做操作
@AfterReturning("pointcutConfig()")
public void afterReturn(JoinPoint joinpoint) {
//LOG.info("后置返回通知---调用获得返回值后执行" + joinpoint);
}
//如有需要,可做操作
@AfterThrowing("pointcutConfig()")
public void afterThrows(JoinPoint joinpoint) {
//LOG.info("抛出异常后,执行" + joinpoint);
}
}
请求后的结果如下
成功获取参数。
@~~~~~~