1通过注解的方式实现

  1. <dependency>
  2. <groupId>org.springframework.boot</groupId>
  3. <artifactId>spring-boot-starter-aop</artifactId>
  4. </dependency>

1.1定义注解

  1. /**
  2. * 自定义操作日志注解
  3. * @author wu
  4. */
  5. @Target(ElementType.METHOD) //注解放置的目标位置,METHOD是可注解在方法级别上
  6. @Retention(RetentionPolicy.RUNTIME) //注解在哪个阶段执行
  7. @Documented
  8. public @interface LogAction {
  9. String operModul() default ""; // 操作模块
  10. String operType() default ""; // 操作类型
  11. String operDesc() default ""; // 操作说明
  12. }

1.2创建切面类记录操作日志

  1. /**
  2. * 切面处理类,操作日志异常日志记录处理
  3. *
  4. * @author wu
  5. * @date 2019/03/21
  6. */
  7. @Aspect // 表示这是一个切面
  8. @Component
  9. public class OperLogAspect {
  10. /**
  11. * 操作版本号
  12. * <p>
  13. * 项目启动时从命令行传入,例如:java -jar xxx.war --version=201902
  14. * </p>
  15. */
  16. @Value("${version}")
  17. private String operVer;
  18. @Autowired
  19. private OperationLogService operationLogService;
  20. @Autowired
  21. private ExceptionLogService exceptionLogService;
  22. /**
  23. * 设置操作日志切入点 记录操作日志 在注解的位置切入代码 此处可以直接写注解的名称
  24. */
  25. @Pointcut("@annotation(com.hyd.zcar.cms.common.utils.annotation.LogAction)")
  26. public void operLogPoinCut() {
  27. }
  28. /**
  29. * 设置操作异常切入点记录异常日志 扫描所有controller包下操作 通过扫描的方式
  30. */
  31. @Pointcut("execution(* com.hyd.zcar.cms.controller..*.*(..))")
  32. public void operExceptionLogPoinCut() {
  33. }
  34. @Before(value = "operLogPoinCut()") // 通过注解的方式
  35. public void saveOperLog(JoinPoint joinPoint) {
  36. Signature signature = joinPoint.getSignature();
  37. System.out.println(signature);
  38. List<Object> list = Arrays.asList(joinPoint.getArgs()); // 获取到的参数
  39. System.out.println(list);
  40. }
  41. // 也可以不写operLogPoinCut 方法,直接这样写也可以
  42. @Before(value = "@annotation(LogAction)")
  43. public void saveOperLog(JoinPoint joinPoint) {
  44. Signature signature = joinPoint.getSignature();
  45. System.out.println(signature);
  46. List<Object> list = Arrays.asList(joinPoint.getArgs());
  47. System.out.println(list);
  48. }
  49. ==================几种Aop的方式==============
  50. /**
  51. * 前置通知
  52. *
  53. * @param joinPoint 包含了目标方法的关键信息
  54. * @Before 注解标识这是一个前置通知,既在目标方法执行之前执行,注解中免去要填入切点
  55. */
  56. @Before(value = "@annotation(LogAction)")
  57. public void before(JoinPoint joinPoint) {
  58. Signature signature = joinPoint.getSignature();
  59. String name = signature.getName();
  60. System.out.println(name + "方法开始执行了。。。");
  61. }
  62. /**
  63. * 后置通知
  64. *
  65. * @param joinPoint 包含了目标方法的关键信息
  66. * @After 注解标识这是一个后置通知,既在目标方法执行之后执行,注解中免去要填入切点
  67. */
  68. @After(value = "@annotation(LogAction)")
  69. public void after(JoinPoint joinPoint) {
  70. Signature signature = joinPoint.getSignature();
  71. String name = signature.getName();
  72. System.out.println(name + "方法执行结束了。。。");
  73. }
  74. /**
  75. * 返回通知
  76. *
  77. * @param joinPoint 包含了目标方法的关键信息
  78. * @AfterReturning 注解标识这是一个返回通知,既有目标方法有返回值的时候才会触发,
  79. * 该注解中returning属性标识目标方法返回值的变量名,这个需要和参数一一对应,
  80. * 注意,目标方法的返回值类型和这里方法的返回值参数可以为object
  81. */
  82. @AfterReturning(value = "@annotation(LogAction)", returning = "r")
  83. public void afterReturning(JoinPoint joinPoint, Object r) {
  84. Signature signature = joinPoint.getSignature();
  85. String name = signature.getName();
  86. System.out.println(name + "方法返回" + r);
  87. }
  88. /**
  89. * 异常通知
  90. *
  91. * @param joinPoint 包含了目标方法的关键信息
  92. * @param e 目标方法所抛出的异常,注意,这个参数必须是目标方法所抛出的异常,
  93. * 或者所抛出异常的异常父类,只有这样,才会捕获,如果想拦截所有,
  94. * 参数类型声明为Exception
  95. * @AfterThrowing 注解标识这是一个异常通知,改方法会繁华一个异常e,
  96. */
  97. @AfterThrowing(value = "@annotation(LogAction)", throwing = "e")
  98. public void afterThrowing(JoinPoint joinPoint, Exception e) {
  99. Signature signature = joinPoint.getSignature();
  100. String name = signature.getName();
  101. System.out.println(name + "方法返回" + e);
  102. }
  103. /**
  104. * 环绕通知
  105. * <p>
  106. * 环绕通知是集大成这,可以用环绕通知实现上面的四个通知,这个方法的核心有点类似于在这里通过反射这些方法
  107. *
  108. * @param pjp 包含了目标方法的关键信息
  109. * @Around 注解标识这是一个环绕通知
  110. */
  111. @Around(value = "@annotation(LogAction)")
  112. public Object Around(ProceedingJoinPoint pjp) {
  113. Object proceed = null;
  114. try {
  115. System.out.println("环绕通知里面的【前置通知】。。。");
  116. //这个相当于method.invoke方法,我们可以在这个方法的前后分别添加日志,就相当于前置通知和后置通知
  117. proceed = pjp.proceed(); //这里相当于执行目标方法 如果不写目标方法就不会执行
  118. // proceed是目标方法的返回值
  119. System.out.println("环绕通知里面的【后置通知】...");
  120. } catch (Throwable throwable) {
  121. System.out.println("这里是执行环绕通知里面的【异常通知】。。。");
  122. throwable.printStackTrace();
  123. }
  124. finally {
  125. System.out.println("Logger类中的aroundPringLog方法开始记录日志了。。。最终");
  126. }
  127. return proceed;
  128. }
  129. }

2.通过配置实现更加灵活

  1. 1.概述
  2. 问题:
  3. 当我们配置了环绕通知之后,切入点方法没有执行,而通知方法执行了
  4. 分析:
  5. 通过对比动态代理的环绕通知代码,发现动态代理的环绕通知有明确的切入点方法调用.而我们的没有
  6. 解决:
  7. Spring框架为我们提供了一个接口,proceedingJoinPoint.该接口有一个方法proceed()此方法就相当于明确调用切入点方法.
  8. 该接口可以作为环绕通知的方法参数,在程序执行时,Spring框架会为我们提供该接口的实现类供我们使用
  9. Spring中的环绕通知:
  10. 它是spring框架为我们提供的一种可以在代码中手动控制增强方法何时执行的方式
  11. 2. 使用
  12. 2.1 基于注解:
  13. @Aspect
  14. public class AnnotationAudienceAround{
  15. //使用@Pointcut注解声明切入点表达式
  16. @Pointcut("execution(* com.qin.util.*.*(..))")
  17. public void pt1(){}
  18. @Around("pt1()")
  19. public Object aroundPringLog(ProceedingJoinPoint pjp){
  20. Object rtValue = null;
  21. try{
  22. Object[] args = pjp.getArgs();//得到方法执行所需的参数
  23. System.out.println("Logger类中的aroundPringLog方法开始记录日志了。。。前置");
  24. rtValue = pjp.proceed(args);//明确调用业务层方法(切入点方法)
  25. System.out.println("Logger类中的aroundPringLog方法开始记录日志了。。。后置");
  26. return rtValue;
  27. }catch (Throwable t){
  28. System.out.println("Logger类中的aroundPringLog方法开始记录日志了。。。异常");
  29. throw new RuntimeException(t);
  30. }finally {
  31. System.out.println("Logger类中的aroundPringLog方法开始记录日志了。。。最终");
  32. }
  33. }
  34. }
  35. 2.2 基于XML配置文件:
  36. 1. 通知类.
  37. //去掉了所有的注解
  38. public class XmlAudienceAround{
  39. public Object aroundPringLog(ProceedingJoinPoint pjp){
  40. Object rtValue = null;
  41. try{
  42. Object[] args = pjp.getArgs();//得到方法执行所需的参数
  43. System.out.println("Logger类中的aroundPringLog方法开始记录日志了。。。前置");
  44. rtValue = pjp.proceed(args);//明确调用业务层方法(切入点方法)
  45. System.out.println("Logger类中的aroundPringLog方法开始记录日志了。。。后置");
  46. return rtValue;
  47. }catch (Throwable t){
  48. System.out.println("Logger类中的aroundPringLog方法开始记录日志了。。。异常");
  49. throw new RuntimeException(t);
  50. }finally {
  51. System.out.println("Logger类中的aroundPringLog方法开始记录日志了。。。最终");
  52. }
  53. }
  54. }
  55. 2. xml文件中配置
  56. <!--声明bean-->
  57. <bean name="xmlAudienceAround" class="com.qin.util.XmlAudienceAround"/>
  58. <!--配置切面及通知-->
  59. <aop:config>
  60. <aop:aspect ref="xmlAudienceAround">
  61. <aop:pointcut id="pt1" expression="execution(* com.qin.util.*.*(..))"/>
  62. <aop:around method="aroundPringLog" pointcut-ref="pt1"/> 对于切面调用的什么方法
  63. </aop:aspect>
  64. </aop:config>
  65. 3.main方法中加载配置文件
  66. ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("配置文件地址");
  67. 下面还有一句,不太清楚

3.在接口方法上使用