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种方法都可以解决这个问题。
spring中aop不生效的几种解决办法的更多相关文章
- 关于Spring中,定时任务执行两次的解决办法
原因:如果spring-quartz.xml文件,在Spring的配置文件spring-config.xml中被加载,那么定时任务会被Spring和SpringMVC扫描两次,所以会被执行两次. 解决 ...
- Oracle中除数为0的两种解决办法(decode与nullif)
Oracle中Decode函数,语句DECODE(tag,''ZCGS'',0,1)=decode(''@corp-No@'',''6010'',1,0) decode(字段或字段的运算,值1,值2, ...
- JAVA高级架构师基础功:Spring中AOP的两种代理方式:动态代理和CGLIB详解
在spring框架中使用了两种代理方式: 1.JDK自带的动态代理. 2.Spring框架自己提供的CGLIB的方式. 这两种也是Spring框架核心AOP的基础. 在详细讲解上述提到的动态代理和CG ...
- 浅析Spring中AOP的实现原理——动态代理
一.前言 最近在复习Spring的相关内容,刚刚大致研究了一下Spring中,AOP的实现原理.这篇博客就来简单地聊一聊Spring的AOP是如何实现的,并通过一个简单的测试用例来验证一下.废话不 ...
- Spring中AOP原理,源码学习笔记
一.AOP(面向切面编程):通过预编译和运行期动态代理的方式在不改变代码的情况下给程序动态的添加一些功能.利用AOP可以对应用程序的各个部分进行隔离,在Spring中AOP主要用来分离业务逻辑和系统级 ...
- Spring中AOP简介与切面编程的使用
Spring中AOP简介与使用 什么是AOP? Aspect Oriented Programming(AOP),多译作 "面向切面编程",也就是说,对一段程序,从侧面插入,进行操 ...
- 框架源码系列十:Spring AOP(AOP的核心概念回顾、Spring中AOP的用法、Spring AOP 源码学习)
一.AOP的核心概念回顾 https://docs.spring.io/spring/docs/5.1.3.RELEASE/spring-framework-reference/core.html#a ...
- Spring 中aop切面注解实现
spring中aop的注解实现方式简单实例 上篇中我们讲到spring的xml实现,这里我们讲讲使用注解如何实现aop呢.前面已经讲过aop的简单理解了,这里就不在赘述了. 注解方式实现aop我们 ...
- Spring中AOP相关源码解析
前言 在Spring中AOP是我们使用的非常频繁的一个特性.通过AOP我们可以补足一些面向对象编程中不足或难以实现的部分. AOP 前置理论 首先在学习源码之前我们需要了解关于AOP的相关概念如切点切 ...
- AOP 与 Spring中AOP使用(上)
AOP简介 在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程, 通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术. AOP是OOP的延续 ...
随机推荐
- springBoot简要复习总结
Spring Boot Spring Boot 的特点 1. 独立运行的 Spring 项目 Spring Boot 可以以 jar 包的形式独立运行,Spring Boot 项目只需通过命令&quo ...
- 第二章 Spring Boot 整合 Kafka消息队列 生产者
系列文章目录 第一章 Kafka 配置部署及SASL_PLAINTEXT安全认证 第二章 Spring Boot 整合 Kafka消息队列 生产者 第三章 Spring Boot 整合 Kaf ...
- 实现C++智能指针
在对象切片一文中,提到可使用充当智能指针的类shape_wrapper,可以简化资源的管理,从根本上消除资源(包括内存)泄漏的可能性,本节来看下如何将shape_wrapper改造成一个完整的智能指针 ...
- 无列名注入_2 [GYCTF2020]Ezsqli 1
无列名注入_2 在之前,我们使用了select `1` from (select 1,2 union select * from tableX)a这样一个形式来进行无列名注入,原理可以看我之前写的(抄 ...
- 深度系统deepin/uos动态壁纸
深度系统deepin下使用动态壁纸 演示视频: https://www.bilibili.com/video/BV1bB4y1c7Fq 最新版本(2022/2/9): uos版本 : https:// ...
- Ubuntu 通过 docker 运行 redis
1.首先拉取redis的镜像 docker pull redis 2.运行redis容器 docker run --name redis -p 6379:6379 -d redis --name re ...
- ssh免密登录设置 (普通用户和root用户)
普通用户如何设置免密码ssh到另一台服务器 一.场景描述: 我有A,B两台服务器,需要实现让A服务器的普通用户(ftpuser)不用输入密码就可以连接到B的(ftpuser)(此处A,B服务器的账户可 ...
- MybatisPlus查询一对多list结果collection实例
前言 查询用户信息,结果放到一个实体类里 这个实体类里有两个List<对象>字段,分别是这个用户的角色列表.岗位列表 以下仅供参考 实体类 package cn.daenx.myadmin ...
- java后端http大文件传输接口笔记
笔记 接口方 package com.chinaums.demo.example.controller; import org.springframework.web.bind.annotation. ...
- Kibana介绍及二进制安装
概述 官方文档:https://www.elastic.co/cn/kibana GitHub地址:https://github.com/elastic/kibana Kibana 是一款开源的数据可 ...