完整剖析SpringAOP的自调用
摘要
spring全家桶帮助java web开发者节省了很多开发量,提升了效率。但是因为屏蔽了很多细节,导致很多开发者只知其然,不知其所以然,本文就是分析下使用spring的一些注解,不能够自调用的问题。因为本身这类文章很多,所以有些地方不会详述,直接引用其他文章。
问题
- 使用了Spring中哪些注解不能进行自调用
- 为什么代理了就不能自调用
- Spring常用的
@Cache,@Async,@Transaction这三种原理上有什么区别吗 - 如何解自调用的问题
- 使用不同的解法各自有什么坑
AOP的概述
首先需要澄清几个需要区分的名词 AOP Spring AOP AspectJ
AOP
Aspect-oriented programming,面向切面编程,一种解决问题的思想,将一些重复性的编码问题通过切面来实现。
很多人了解切面是通过Spring来了解的,所以会有种误解将SpringAOP和AOP划等号,其实不然。
Spring AOP
Spring AOP 算是一种简单的AOP的落地实现方式,它主要提供在Spring容器内的一种AOP实现方式,脱离了Spring就不work了。Spring AOP并不是一套完整的AOP解决方案。
Spring的的众多组件都是这样,Spring-Session,Spring-jdbc,Spring-Cache等等,都能解决一部分通用的需求,但是会有很多限制,
想用深了,更灵活的实现功能,还是要使用其他的专业组件/框架。
SpringAOP默认使用代理模式实现的,也就是JDK Proxy/CGLib。关于代理以及JDK Proxy和CGLib不在赘述了。
AspectJ
Spring AOP并不是一套完整的AOP解决方案,AspectJ是的。AspectJ在编译器织入切面到目标类
解法
上面介绍了SpringAop的实现,下面着重介绍解法。
方法1 - 注入代理bean到自己
这个原理没啥好解析的
@Autowired
@Lazy
private AsyncMethod asyncMethod;
public void testAsync() {
System.out.println(Thread.currentThread().getName());
// 调用注入的bean
asyncMethod.testAsnc3();
}
@Async
public void testAsnc3() {
System.out.println(Thread.currentThread().getName());
System.out.println("async3");
}
Note
会有循环依赖的问题,使用@Lazy解决
方法2 - AopContext.currentProxy() 获取当前代理对象
使用
首先需要配置@EnableAspectJAutoProxy(exposeProxy = true),允许代码中获取proxy类
public void testAsync() {
System.out.println(Thread.currentThread().getName());
System.out.println("async1");
((AsyncMethod)AopContext.currentProxy()).testAsnc2();
}
@Async
public void testAsnc2() {
System.out.println(Thread.currentThread().getName());
System.out.println("async2");
}
原理解析
这个实现可以看下AopContext类,
// 通过ThreadLocal来实现的
private static final ThreadLocal<Object> currentProxy = new NamedThreadLocal<Object>("Current AOP proxy");
然后就是Spring Aop自动设置代理,设置exposeProxy属性的问题了。
有人写过了,就不写了
Note
- 因为使用了SpringAOP,所以会有代理模式的限制
- AopContext.currentProxy()使用的是ThreadLocal的,所以不能跨线程了
- bean设置的限制,比如@Async代理创建方式不同其他|方式
方法3 - 直接使用AspectJ
既然自调用的问题是由于SpringAOP由代理模式实现引起的,那就不使用代理模式不就解决了吗
使用
- 切换为代理模式
@EnableAsync(mode = AdviceMode.ASPECTJ)
- 添加aspectj织入包依赖
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.8</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-instrument -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-instrument</artifactId>
<version>4.2.5.RELEASE</version>
</dependency>
- 使用
public void testAsync() {
System.out.println(Thread.currentThread().getName());
System.out.println("async1");
testAsnc2();
}
/**
* 测试ASPECTJ调用
*/
@Async
private void testAsnc2() {
System.out.println(Thread.currentThread().getName());
System.out.println("async2");
}
- 启动方式
AspectJ是编译器将切面织入到目标class的,启动的使用需要加上java agent的参数
-Dserver.port=1000 -javaagent:${classpath}\spring-instrument-4.2.5.RELEASE.jar
-javaagent:${classpath}\aspectjweaver-1.8.8.jar
总结
| 方法 | 限制 |
|---|---|
| 自调用 | 代理模式的限制,比如只能作用于public ,非static的方法 |
| AopContext.currentProxy() | 1. 代理模式的限制 2.ThreadLocal的限制,不能跨线程了 3.bean设置的限制,比如@Async代理创建方式不同其他 |
| AspectJ | 无限制,使用起来麻烦一点 |
关注公众号【方丈的寺院】,第一时间收到文章的更新,与方丈一起开始技术修行之路
参考
http://blog.kezhuw.name/2017/08/31/spring-aspectj-load-time-weaving/
https://cloud.tencent.com/developer/article/1497700
https://frightanic.com/software-development/spring-proxy-self-invocation/
https://www.baeldung.com/spring-aop-vs-aspectj
完整剖析SpringAOP的自调用的更多相关文章
- Vue项目搭建完整剖析全过程
Vue项目搭建完整剖析全过程 项目源码地址:https://github.com/ballyalex 有帮助的话就加个星星呗~! 项目技术栈:vue+webpack+bower+sass+axios ...
- WCF技术剖析之十:调用WCF服务的客户端应该如何进行异常处理
原文:WCF技术剖析之十:调用WCF服务的客户端应该如何进行异常处理 在前面一片文章(服务代理不能得到及时关闭会有什么后果?)中,我们谈到及时关闭服务代理(Service Proxy)在一个高并发环境 ...
- 利用JavaScriptSOAPClient直接调用webService --完整的前后台配置与调用示例
JavaScriptSoapClient下载地址:https://archive.codeplex.com/?p=javascriptsoapclient JavaScriptSoapClient的D ...
- 用java实现新浪爬虫,代码完整剖析(仅针对当前SinaSignOn有效)
先来看我们的web.xml文件,如下 <!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application ...
- PHPCMS中GET标签概述、 get 标签语法、get 标签创建工具、get 调用本系统演示样例、get 调用其它系统演示样例
一.get 标签概述 通俗来讲,get 标签是Phpcms定义的能直接调用数据库里面内容的简单化.友好化代码,她可调用本系统和外部数据,仅仅有你对SQL有一定的了解,她就是你的绝世好剑!也就是适合熟悉 ...
- 服务调用方案(Spring Http Invoker) - 我们到底能走多远系列(40)
我们到底能走多远系列(40) 扯淡: 判断是否加可以效力于这家公司,一个很好的判断是,接触下这公司工作几年的员工,了解下生活工作状态,这就是你几年后的状态,如果满意就可以考虑加入了. 主题: 场景: ...
- WCF技术剖析之十五:数据契约代理(DataContractSurrogate)在序列化中的作用
原文:WCF技术剖析之十五:数据契约代理(DataContractSurrogate)在序列化中的作用 [爱心链接:拯救一个25岁身患急性白血病的女孩[内有苏州电视台经济频道<天天山海经> ...
- WCF技术剖析之十四:泛型数据契约和集合数据契约(下篇)
原文:WCF技术剖析之十四:泛型数据契约和集合数据契约(下篇) [爱心链接:拯救一个25岁身患急性白血病的女孩[内有苏州电视台经济频道<天天山海经>为此录制的节目视频(苏州话)]]在.NE ...
- WCF技术剖析之八:ClientBase<T>中对ChannelFactory<T>的缓存机制
原文:WCF技术剖析之八:ClientBase<T>中对ChannelFactory<T>的缓存机制 和传统的分布式远程调用一样,WCF的服务调用借助于服务代理(Service ...
随机推荐
- Linux软件的安装
yum -y groups install "GNOME Desktop" 安装桌面系统startx 安装完成后输入指令进入到桌面化指令 安装tomcat sudo yum i ...
- Bootstrap笔记--快速入门
首先是Bootstrap的简介: 业余了解:下面这个网址可以查询IP地址的地理位置 下面学习:(具体可以参考Bootstrap中文网) 栅格系统 Bootstrap 提供了一套响应式.移动设备优先的流 ...
- Codeforces 436D Pudding Monsters
题意简述 开始有无限长的一段格子,有n个格子种有布丁怪兽,一开始连续的布丁怪兽算一个布丁怪兽. 每回合你可以将一个布丁怪兽向左或右移动,他会在碰到第一个布丁怪兽时停下,并与其合并. 有m个特殊格子,询 ...
- .NET中的缓存
构建高性能的应用程序的非常重要一项就是使用缓存.使用缓存可以避免重新从读取服务器端读取数据,节省数据从客户端到服务器间往返的时间,同时也减轻了服务器数据存取的压力.如果客户端非常频繁地读取服务器上的数 ...
- 快速了解Python并发编程的工程实现(下)
关于我 一个有思想的程序猿,终身学习实践者,目前在一个创业团队任team lead,技术栈涉及Android.Python.Java和Go,这个也是我们团队的主要技术栈. Github:https:/ ...
- maven学习(3)pom.xml文件说明以及常用指令
pom.xml文件的结构: <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http:/ ...
- springboot的整合springMvc中的postman的post中的form-data和raw区别
package com.example.demomap.Controller; import com.example.demomap.pojo.ParaEntity; import org.sprin ...
- java学习之- 线程运行状态
标签(空格分隔): 线程运行状态 线程的运行状态: 如下是是我编写的一个图,大家可以作为参考: 1.new一个thread子类也是创建了一个线程: 2.创建完毕之后start()-----运行, 3. ...
- 谈谈我对SOFA模块化的理解
今天我们谈谈SOFA模块化,首先看一段SOFA的介绍: SOFABoot是蚂蚁金服开源的基于Spring Boot的研发框架,它在Spring Boot的基础上,提供了诸如 Readiness Che ...
- Jedis操作Redis--Hash类型
/** * Hash(哈希表) * HDEL,HEXISTS,HGET,HGETALL,HINCRBY,HINCRBYFLOAT,HKEYS,HLEN,HMGET,HMSET, HSET,HSETNX ...