详解spring中aop不生效的几种解决办法
作者:菩提树下的杨过 发布时间:2022-07-24 18:11:40
先看下这个问题的背景:假设有一个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种方法都可以解决这个问题。
作者:菩提树下的杨过
出处:http://yjmyzz.cnblogs.com
来源:https://www.cnblogs.com/yjmyzz/p/why-spring-aop-does-not-work.html


猜你喜欢
- 在 javax.validation.constraints包中定义了非常多的校验注解,引入依赖:<dependency> &n
- 对象嵌套关联查询一对多List集合查询mybatis嵌套关联查询如下由于我的是一对集合查询,所以我有两个类。@Data@TableName(
- Java7中文件IO发生了很大的变化,专门引入了很多新的类:import java.nio.file.DirectoryStream;imp
- 下面一段代码给大家介绍了android 自定义顶部导航栏控件功能,具体代码如下所示:class HeaderBar @JvmOverload
- 本文实例讲述了Android开发实现浏览器全屏显示功能。分享给大家供大家参考,具体如下:业务需求:浏览器设置中支持全屏显示的功能。分析:只需
- 本文实例讲述了C#图像颜色聚类高效方法。分享给大家供大家参考。具体分析如下:图像颜色聚类的方法有很多,但是对于视频监控而言,现有方法很难满足
- 概述ZXing 是一个开源 Java 类库用于解析多种格式的 1D/2D 条形码。目标是能够对QR编码、Data Matrix、UPC的1D
- Android 关闭多个Activity的实现方法总的来说有四种方法:1、使用Application来进行关闭public class Ap
- Java 内存划分: 在Java内存分配中,java将内存分为:方法区,堆,虚拟机栈,本地方法栈,程序计
- Android—MaterialCardView的使用我们的征程是星辰大海,而非人间烟尘文章目录 Android---MaterialCar
- 本文实例为大家分享了Java实现分页功能的具体代码,供大家参考,具体内容如下不用根据改变SQL的形式去查询; 直接查询所有的数据,根据页码自
- 功能目标使用Treeview控件实现点左边的节点,在右边显示相关的页面知识点Treeview命名空间:System.Windows.Form
- 冒泡排序法:关键字较小的记录好比气泡逐趟上浮,关键字较大的记录好比石块下沉,每趟有一块最大的石块沉底。算法本质:(最大值是关键点,肯定放到最
- Spring Boot+Socket实现与html页面的长连接,客户端给服务器端发消息,服务器给客户端轮询发送消息,附案例源码功能介绍客户端
- FragmentManager 为了管理Activity中的fragments,需要使用FragmentManager. 为了得到它,需要调
- 在Android中,用string-array是一种简单的提取XML资源文件数据的方法。例子如下:把相应的数据放到values文件夹的arr
- 前言:项目中我们经常会遇到有时候需要等待其他线程完成任务后,主线程才能执行其他任务,那么我们将如何实现呢?Join 解决方案join 的工作
- 汉诺(Hanoi)塔问题:古代有一个梵塔,塔内有三个座A、B、C,A座上有n个盘子,盘子大小不等,大的在下,小的在上(如图)。有一个和尚想把
- Objects工具类jdk 1.7引进的工具类,都是静态调用的方法,jdk 1.8新增了部分方法重点方法equals用于字符串和包装对象的比
- using System.Collections.Generic;using System.Text;using System;namesp