先看下这个问题的背景:假设有一个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种方法都可以解决这个问题。  

spring中aop不生效的几种解决办法的更多相关文章

  1. 关于Spring中,定时任务执行两次的解决办法

    原因:如果spring-quartz.xml文件,在Spring的配置文件spring-config.xml中被加载,那么定时任务会被Spring和SpringMVC扫描两次,所以会被执行两次. 解决 ...

  2. Oracle中除数为0的两种解决办法(decode与nullif)

    Oracle中Decode函数,语句DECODE(tag,''ZCGS'',0,1)=decode(''@corp-No@'',''6010'',1,0) decode(字段或字段的运算,值1,值2, ...

  3. JAVA高级架构师基础功:Spring中AOP的两种代理方式:动态代理和CGLIB详解

    在spring框架中使用了两种代理方式: 1.JDK自带的动态代理. 2.Spring框架自己提供的CGLIB的方式. 这两种也是Spring框架核心AOP的基础. 在详细讲解上述提到的动态代理和CG ...

  4. 浅析Spring中AOP的实现原理——动态代理

    一.前言   最近在复习Spring的相关内容,刚刚大致研究了一下Spring中,AOP的实现原理.这篇博客就来简单地聊一聊Spring的AOP是如何实现的,并通过一个简单的测试用例来验证一下.废话不 ...

  5. Spring中AOP原理,源码学习笔记

    一.AOP(面向切面编程):通过预编译和运行期动态代理的方式在不改变代码的情况下给程序动态的添加一些功能.利用AOP可以对应用程序的各个部分进行隔离,在Spring中AOP主要用来分离业务逻辑和系统级 ...

  6. Spring中AOP简介与切面编程的使用

    Spring中AOP简介与使用 什么是AOP? Aspect Oriented Programming(AOP),多译作 "面向切面编程",也就是说,对一段程序,从侧面插入,进行操 ...

  7. 框架源码系列十:Spring AOP(AOP的核心概念回顾、Spring中AOP的用法、Spring AOP 源码学习)

    一.AOP的核心概念回顾 https://docs.spring.io/spring/docs/5.1.3.RELEASE/spring-framework-reference/core.html#a ...

  8. Spring 中aop切面注解实现

    spring中aop的注解实现方式简单实例   上篇中我们讲到spring的xml实现,这里我们讲讲使用注解如何实现aop呢.前面已经讲过aop的简单理解了,这里就不在赘述了. 注解方式实现aop我们 ...

  9. Spring中AOP相关源码解析

    前言 在Spring中AOP是我们使用的非常频繁的一个特性.通过AOP我们可以补足一些面向对象编程中不足或难以实现的部分. AOP 前置理论 首先在学习源码之前我们需要了解关于AOP的相关概念如切点切 ...

  10. AOP 与 Spring中AOP使用(上)

    AOP简介 在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程, 通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术. AOP是OOP的延续 ...

随机推荐

  1. 使用win10 wsl子系统将 rust 程序静态编译为linux可执行文件

    chapter Ⅰ 事情起因 最近在学习rust, 想把一部分java服务迁移至rust编写,但由于公司服务器都是linux系统,所以在找windows下交叉编译为linux可执行文件的方法,把bin ...

  2. 运筹学之"名词解释"

    1.转移概率 转移概率是指某个销售者保持,获得或失去消费者的概率 2.阶石法中的改进指数 阶石法中的改进指数是指循着改进路线,当货物的运输量作为一个单位发生变化时,会引起总运输费用的改变 3.相关关系 ...

  3. Ubuntu 16.04 系统(解释器为 python3.12)在Pycharm虚拟环境中安装 pyspider 爬虫工具

    一:安装步骤步骤1. 系统Terminal命令行执行如下命令安装依赖的组件 PhantomJS $ wget https://bitbucket.org/ariya/phantomjs/downloa ...

  4. ES查询优化随记1: 多路向量查询 & KNN IO排查 & 高效Filter使用

    哈哈最近感觉自己不像算法倒像是DB,整天围着ES打转,今天查IO,明天查内存,一会优化查询,一会优化吞吐.毕竟RAG离不开知识库,我们的选型是ES,于是这一年都是和ES的各种纠葛.所以顺手把近期获得的 ...

  5. 2025 年实用、全面的 VS Code 插件推荐!

    前言 VS Code是一款由微软开源免费.轻量级.功能强大的源代码编辑器.其轻量级体现在基础安装简洁,仅含核心编辑功能.功能强大则源于它支持丰富的语言环境插件拓展,这种模块化设计让VS Code在源代 ...

  6. FFmpeg开发笔记(六十三)FFmpeg使用vvenc把视频转为H.266编码

    ​前面的两篇文章分别介绍了如何在Linux环境和Windows环境给FFmpeg集成H.266的编码器vvenc,接下来利用ffmpeg把视频文件转换为VVC格式,观察新生成的vvc视频能否正常播放. ...

  7. 雷池 7.x 主从节点分钟级自动同步 + 手动切换实战教程

    雷池7.x版本新增配置同步功能,可以设置主节点和从节点,可以自动每分钟将主节点的配置同步到从节点,在主节点异常情况下,使用者手动切换流量后,实现从节点马上承接业务流量. 准备环境 ● 检查主从节点机器 ...

  8. 【图像处理】使用matplotlib库显示灰度图像为自定义颜色(2)

    在下面的代码中,facies_img的值只有[0,1,2]表明图像是灰度图像.通过下面的代码可以让图像显示为彩色图像 import matplotlib.pyplot as plt import ma ...

  9. AI 运维诊断全攻略,深入浅出 OceanBase 系列直播重磅来袭!

    社区的 #数据库 与 #AI 爱好者们 当 SQL 遇见 AI 的系列直播即将开启! 今晚 19:30,聚焦#AI 时代的运维与诊断,带你共享 #大模型辅助数据库运维 的一线实战经验,探索 #Ocea ...

  10. 两步实现让antd与IDE和睦相处的处理案例

    导读: Web IDE的开发从来是整个大数据平台开发中非常繁复和笨重的一环,从零搭建一个 Web IDE 通常意味着大量的殚精竭虑和苦思冥想,时间成本更是不可计数.两个UI组件库一起用更是bug的代名 ...