首先是幾個概念:連接點(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。詳細的可以下去再了解