Spring aop失效的几种解决方案

先看下这个问题的背景:假设有一个spring应用,开发人员希望自定义一个注解@Log,可以加到指定的方法上,实现自动记录日志(入参、出参、响应耗时这些)

package com.cnblogs.yjmyzz.springbootdemo.aspect;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Log {

}

然后再写一个Aspect来解析这个注解,对打了Log注解的方法进行增强处理 

package com.cnblogs.yjmyzz.springbootdemo.aspect;

import org.aspectj.lang.JoinPoint;
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.springframework.stereotype.Component;

import java.lang.reflect.Method;

@Component
@Aspect
public class LogAspect {

  @Pointcut("execution (* com.cnblogs.yjmyzz.springbootdemo.service..*.*(..))")
  public void logPointcut() {

  }

  @Around("logPointcut()")
  public void around(JoinPoint point) {
    String methodName = point.getSignature().getName();
    Object[] args = point.getArgs();
    Class<?>[] argTypes = new Class[point.getArgs().length];
    for (int i = 0; i < args.length; i++) {
      argTypes[i] = args[i].getClass();
    }
    Method method = null;
    try {
      method = point.getTarget().getClass().getMethod(methodName, argTypes);
    } catch (Exception e) {
      e.printStackTrace();
    }
    //获取方法上的注解
    Log log = method.getAnnotation(Log.class);
    if (log != null) {
      //演示方法执行前,记录一行日志
      System.out.println("before:" + methodName);
    }
    try {
      //执行方法
      ((ProceedingJoinPoint) point).proceed();
    } catch (Throwable throwable) {
      throwable.printStackTrace();
    } finally {
      if (log != null) {
        //演示方法执行后,记录一行日志
        System.out.println("after:" + methodName);
      }
    }
  }
}

写一个测试Service类:

package com.cnblogs.yjmyzz.springbootdemo.service;

import com.cnblogs.yjmyzz.springbootdemo.aspect.Log;
import org.springframework.stereotype.Component;

@Component
public class HelloService {

  @Log
  public void sayHi(String msg) {
    System.out.println("\tsayHi:" + msg);
  }

  public void anotherSayHi(String msg) {
    this.sayHi(msg);
  }

}

最后来跑一把:

package com.cnblogs.yjmyzz.springbootdemo;

import com.cnblogs.yjmyzz.springbootdemo.service.HelloService;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

/**
 * @author 菩提树下的杨过
 */
@ComponentScan("com.cnblogs.yjmyzz")
@Configuration
@EnableAspectJAutoProxy
public class SampleApplication {

  public static void main(String[] args) {
    AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SampleApplication.class);
    HelloService helloService = context.getBean(HelloService.class);
    helloService.sayHi("hi-1");
    System.out.println("\n");
    helloService.anotherSayHi("hi-2");
  }
}

输出如下:

显然HelloService中的anotherSayHi方法,并未被aop增强。 原因其实很简单,了解AOP原理的同学想必都知道,AOP的实现有二类,如果是基于接口的,会采用动态代理,生成一个代理类,如果是基于类的,会采用CGLib生成子类,然后在子类中扩展父类中的方法。

本文中HelloService并不是一个接口,所以从上图的断点中可以看出,当Spring运行时,HelloService被增加为...EnhancerBySpringCGLib...。但是当调用到anotherSayHi时

方法的调用方,其实是原始的HelloSerfvice实例,即:是未经过Spring AOP增强的对象实例。所以解决问题的思路就有了,想办法用增强后的HelloService实例来调用!

方法一:用Autowired 注入自身的实例

这个方法,第一眼看上去感觉有些怪,自己注入自己,感觉有点象递归/死循环的搞法,但确实可以work,Spring在解决循环依赖上有自己的处理方式,避免了死循环。

方法二:从Spring上下文获取增强后的实例引用

原理与方法一其实类似,不多解释。

方法三: 利用AopContext

不过这个方法要注意的是,主类入口上,必须加上exporseProxy=true,参考下图:

最后来验证下这3种方法是否生效:

从运行结果上看,3种方法都可以解决这个问题。 

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • Spring IOC和aop的原理及实例详解

    这篇文章主要介绍了Spring IOC和aop的原理及实例详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 Spring是一个轻量级的控制反转(IoC)和面向切面(AOP)的容器框架.特点是面向接口编程,松耦合. 1:IOC(控制反转) 别名(DI:依赖注入) 首先来一段ioc的实现原来代码: public class ClassPathXmlApplicationContext implements BeanFactory { privat

  • Spring如何基于aop实现事务控制

    spring的事务控制本质上是通过aop实现的. 在springboot中使用时,可以通过注解@Transactional进行类或者方法级别的事务控制,也可以自己通过spring提供的事务管理器手动控制事务 一. @Transactional注解进行进行类或者方法级别的事务控制 不需要进行特别的设置,按照正常的配置整合spring和mybatis后,在需要进行事务控制的类上或者方法上加上 @Transactional注解,即可对其进行事务控制. 二.手动控制事务 当需要在一个方法的内部进行事务控

  • Spring AOP执行先后顺序实例详解

    这篇文章主要介绍了Spring AOP执行先后顺序实例详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 众所周知,spring声明式事务是基于AOP实现的,那么,如果我们在同一个方法自定义多个AOP,我们如何指定他们的执行顺序呢? 网上很多答案都是指定order,order越小越是最先执行,这种也不能算是错,但有些片面. 配置AOP执行顺序的三种方式: 通过实现org.springframework.core.Ordered接口 @Compo

  • Spring AOP中定义切点的实现方法示例

    本文实例讲述了Spring AOP中定义切点的实现方法.分享给大家供大家参考,具体如下: 一 配置 <?xml version="1.0" encoding="GBK"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:

  • 详解基于SpringBoot使用AOP技术实现操作日志管理

    操作日志对于程序员或管理员而言,可以快速定位到系统中相关的操作,而对于操作日志的管理的实现不能对正常业务实现进行影响,否则即不满足单一原则,也会导致后续代码维护困难,因此我们考虑使用AOP切面技术来实现对日志管理的实现. 文章大致内容: 1.基本概念 2.基本应用 3.日志管理实战 对这几部分理解了,会对AOP的应用应该很轻松. 一.基本概念 项目 描述 Aspect(切面) 跨越多个类的关注点的模块化,切面是通知和切点的结合.通知和切点共同定义了切面的全部内容--它是什么,在何时和何处完成其功

  • 解决springboot的aop切面不起作用问题(失效的排查)

    检查下springboot的启动类是否开启扫描 @SpringBootApplication @ComponentScan(basePackages = {"com.zhangpu.springboot"}) 另外springboot默认开启的EnableAspectJAutoProxy为true 如果不放心可以增加: @EnableAspectJAutoProxy(proxyTargetClass=true) 第二种可能: 没有导入 相关的jar <dependency>

  • springboot配置aop切面日志打印过程解析

    这篇文章主要介绍了springboot配置aop切面日志打印过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 一.SpringBoot Aop说明 1. Aop AOP(Aspect-Oriented Programming,面向切面编程),它利用一种"横切"的技术,将那些多个类的共同行为封装到一个可重用的模块.便于减少系统的重复代码,降低模块之间的耦合度,并有利于未来的可操作性和可维护性. 2. AOP相关概念: Aspect

  • spring AOP定义AfterThrowing增加处理实例分析

    本文实例讲述了spring AOP定义AfterThrowing增加处理.分享给大家供大家参考,具体如下: 一 配置 <?xml version="1.0" encoding="GBK"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance&quo

  • Spring aop失效的几种解决方案

    先看下这个问题的背景:假设有一个spring应用,开发人员希望自定义一个注解@Log,可以加到指定的方法上,实现自动记录日志(入参.出参.响应耗时这些) package com.cnblogs.yjmyzz.springbootdemo.aspect; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy

  • Spring事务失效问题分析及解决方案

    这篇文章主要介绍了Spring事务失效问题分析及解决方案,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 隔离级别 在 TransactionDefinition.java 接口中,定义了"四种"的隔离级别枚举: /** * [Spring 独有]使用后端数据库默认的隔离级别 * * MySQL 默认采用的 REPEATABLE_READ隔离级别 * Oracle 默认采用的 READ_COMMITTED隔离级别 */ int ISOL

  • Spring事务失效的几种原因

    数据库引擎不支持事务 在MySQL数据库中有几种引擎(InnoDB,MyISAM,Memory等等),仅仅InnoDB支持事务,如果数据库底层都不支持事务的话,那么再怎么折腾都是白搭. @transactional加在private方法上 @Transactional只能加在public方法上,如果需要在private方法中加入事务,可以使用Aspect配transactionManager使用. 本类方法调本类另一个方法 例如: @Service public class UserServic

  • Spring事务失效场景原理及解决方案

    1.事务失效-自身调用(通过REQUIRES.REQUIRES_NEW传播属性):自身调用即调该类自己的方法. 同类OrderServiceImpl 中 doSomeThing()方法 不存在事务,该方法去调用本类中的存在事务注解的 insertAndUpdateOrderInfo() 方法.但是insertAndUpdateOrderInfo() 其实是无法保证预想的事务性. 示列验证: OrderServiceImpl.insertAndUpdateOrderInfo方法中upateData

  • Spring事务失效的一种原因关于this调用的问题

    PROPAGATION_REQUIRED: 如果存在一个事务,则支持当前事务.如果没有事务则开启事务: PROPAGATION_REQUIRES_NEW:总是开启一个新的事务.如果一个事务已经存在,则将这个存在的事务挂起: 问题: Spring中一个没有事务的方法A调用一个默认事务(PROPAGATION_REQUIRED)的方法B时,如果使用this调用方法B,方法B抛出RuntimeException,此时方法B事务未生效,不会回滚. @Service public class Employ

  • 利用Spring AOP记录方法的执行时间

    一.前言 对于spring aop这个我就不多介绍了,网上一搜一大把,使用过spring的人都知道spring的ioc和aop.ioc我们常用,但在我们自己的系统中,aop的使用几乎为零,除了这个监控的小功能应用到了,其他的基本上没有使用到.下面小编就给大家整理下利用Spring AOP记录方法执行时间的解决方案,有需要的一起看看吧. 二.解决方案 1.传统方法 最简单.粗暴的方法是给各个需要统计的方法开始和结尾处加的时间戳,然后差值计算结果即可,代码如下: long startTime = S

  • Spring AOP源码深入分析

    目录 1. 前言 2. 术语 3. 示例 4. @EnableAspectJAutoProxy 5. AbstractAutoProxyCreator 6. 构建Advisor 7. 创建代理对象 8. DynamicAdvisedInterceptor 9. CglibMethodInvocation 10. Advice子类 1. 前言 Spring除了IOC和DI,还有另一个杀手锏功能——Spring AOP.AOP是一种面向切面的编程思想,它的关注点是横向的,不同于OOP的纵向.面向对象

  • 浅谈spring aop的五种通知类型

    spring aop通知(advice)分成五类:  前置通知[Before advice]:在连接点前面执行,前置通知不会影响连接点的执行,除非此处抛出异常. 正常返回通知[After returning advice]:在连接点正常执行完成后执行,如果连接点抛出异常,则不会执行. 异常返回通知[After throwing advice]:在连接点抛出异常后执行. 返回通知[After (finally) advice]:在连接点执行完成后执行,不管是正常执行完成,还是抛出异常,都会执行返回

  • Spring AOP注解失效的坑及JDK动态代理

    @Transactional @Async等注解不起作用 之前很多人在使用Spring中的@Transactional, @Async等注解时,都多少碰到过注解不起作用的情况. 为什么会出现这些情况呢?因为这些注解的功能实际上都是Spring AOP实现的,而其实现原理是通过代理实现的. JDK动态代理 以一个简单的例子理解一下JDK动态代理的基本原理: //目标类接口 public interface JDKProxyTestService { void run(); } //目标类 publ

  • 解决Spring AOP拦截抽象类(父类)中方法失效问题

    目录 背景 原因分析 解决方案 后记 背景 最近工作中需要对组内各个系统依赖的第三方接口进行监控报警,对于下游出现问题的接口能够及时感知.首先我们写了一个Spring AOP注解,用于收集调用第三方时返回的信息.而我们调用第三方的类抽象出一个父类.并在父类的方法中加入我们的自定义注解用于监控日志并打印日志. 很多子类继承了这个父类并使用父类中的方法.如: 当调用子类的doSomething方法时问题出现了,发现Spring AOP没有拦截doPost()方法.而将注解加在子类方法上时,Sprin

随机推荐