首先是几个概念:连接点(Joinpoint)、切点(Pointcut)、增强(Advice)、切面(Aspect)
另外也要使用到注解。
需求:通过注解定义LogEnable。然后程序运行能够识别定义了LogEnable注解的方法记录日志。
1.定义注解
package cn.duanjt.util; import java.lang.annotation.*; /** * 记录日志的注解类 * @author 段江涛 * @date 2018-11-08 */ @Target(ElementType.METHOD)//表示用于标识方法 @Retention(RetentionPolicy.RUNTIME)//表示运行时保留 public @interface LogEnable { /** * 主要是标志日志的描述信息 * @return */ String note() default ""; }
2.定义需要监听的类和方法
package cn.duanjt.service; import org.springframework.stereotype.Service; import cn.duanjt.Pojo.Student; import cn.duanjt.util.LogEnable; @Service public class StudentService { //定义注解,然后描述当前方法的作用 @LogEnable(note="获取学生信息") public Student getStudent(int id) { if (id == 0) { throw new RuntimeException("编码不能为0"); } Student stu = new Student(); stu.setId(id); stu.setName("zhangsan"); stu.setAddr("重庆"); return stu; } //未定义注解,将不会被监听 public int getId(int id) { return id + 1; } }
3.定义切面,记录日志
package cn.duanjt.util; import org.apache.commons.lang3.StringUtils; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.reflect.MethodSignature; import org.springframework.stereotype.Component; @Aspect @Component public class LogHelper { // 没有单独定义切点,直接在环绕方法里面处理[包cn.duanjt.service下面的所有类下面的所有方法,同时包含LogEnable注解的将被监听] @Around("execution(* cn.duanjt.service.*.*(..)) && @annotation(LogEnable)") public Object around(ProceedingJoinPoint point) { MethodSignature signature = (MethodSignature) point.getSignature(); long time = System.currentTimeMillis(); //记录开始时间 String className = point.getTarget().getClass().getName(); // 类名 String method = className + "." + signature.getName(); //方法名 Object[] args = point.getArgs(); // 参数 LogEnable logEnable= signature.getMethod().getAnnotation(LogEnable.class); String logNote=logEnable.note(); //日志信息 try { Object result = point.proceed(); System.out.println("方法名:" + method); System.out.println("参数:" + StringUtils.join(args)); System.out.println("返回值:" + result.toString()); System.out.println("日志功能:" + logNote); System.out.println("耗时:" + (System.currentTimeMillis() - time) + "毫秒"); System.out.println("-----------------------"); return result; } catch (Exception e) { System.out.println("方法名1:" + method); System.out.println("参数:" + StringUtils.join(args)); System.out.println("日志功能:" + logNote); System.out.println("异常信息:" + e.getMessage()); System.out.println("耗时:" + (System.currentTimeMillis() - time) + "毫秒"); System.out.println("-----------------------"); return null; } catch (Throwable e) { return null; } } }
4.在主程序上要加上注解@EnableAspectJAutoProxy。我这里使用的是springboot,如下:
package cn.duanjt; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.EnableAspectJAutoProxy; @SpringBootApplication @ComponentScan("cn.duanjt") @EnableAspectJAutoProxy //表示启用AOP public class ServerDemoApplication { public static void main(String[] args) { SpringApplication.run(ServerDemoApplication.class, args); } }
最后,运行结果如下:
方法名:cn.duanjt.service.StudentService.getStudent 参数:1 返回值:Student [id=1, name=zhangsan, addr=重庆] 日志功能:获取学生信息 耗时:0毫秒 -----------------------
注意:
1. @EnableAspectJAutoProxy用于开启全局的AOP
2. LogHelper类上面的@Aspect和@Component是必须的,前者用于标注是切面,后者用于将对象注入到spring容器
3. 切面表达式@Around("execution(* cn.duanjt.service.*.*(..)) && @annotation(LogEnable)").一定需要execution。详细的可以下去再了解