摘要

spring全家桶帮助java web开发者节省了很多开发量,提升了效率。但是因为屏蔽了很多细节,导致很多开发者只知其然,不知其所以然,本文就是分析下使用spring的一些注解,不能够自调用的问题。因为本身这类文章很多,所以有些地方不会详述,直接引用其他文章。

问题

  1. 使用了Spring中哪些注解不能进行自调用
  2. 为什么代理了就不能自调用
  3. Spring常用的 @Cache, @Async@Transaction 这三种原理上有什么区别吗
  4. 如何解自调用的问题
  5. 使用不同的解法各自有什么坑

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属性的问题了。

有人写过了,就不写了

https://cloud.tencent.com/developer/article/1497700

Note

  1. 因为使用了SpringAOP,所以会有代理模式的限制
  2. AopContext.currentProxy()使用的是ThreadLocal的,所以不能跨线程了
  3. bean设置的限制,比如@Async代理创建方式不同其他|方式

方法3 - 直接使用AspectJ

既然自调用的问题是由于SpringAOP由代理模式实现引起的,那就不使用代理模式不就解决了吗

使用

  1. 切换为代理模式
@EnableAsync(mode = AdviceMode.ASPECTJ)
  1. 添加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>
  1. 使用
 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");
}
  1. 启动方式

    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://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/scheduling/annotation/ProxyAsyncConfiguration.html#asyncAdvisor--

https://www.baeldung.com/spring-aop-vs-aspectj

完整剖析SpringAOP的自调用的更多相关文章

  1. Vue项目搭建完整剖析全过程

    Vue项目搭建完整剖析全过程 项目源码地址:https://github.com/ballyalex 有帮助的话就加个星星呗~!  项目技术栈:vue+webpack+bower+sass+axios ...

  2. WCF技术剖析之十:调用WCF服务的客户端应该如何进行异常处理

    原文:WCF技术剖析之十:调用WCF服务的客户端应该如何进行异常处理 在前面一片文章(服务代理不能得到及时关闭会有什么后果?)中,我们谈到及时关闭服务代理(Service Proxy)在一个高并发环境 ...

  3. 利用JavaScriptSOAPClient直接调用webService --完整的前后台配置与调用示例

    JavaScriptSoapClient下载地址:https://archive.codeplex.com/?p=javascriptsoapclient JavaScriptSoapClient的D ...

  4. 用java实现新浪爬虫,代码完整剖析(仅针对当前SinaSignOn有效)

    先来看我们的web.xml文件,如下 <!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application ...

  5. PHPCMS中GET标签概述、 get 标签语法、get 标签创建工具、get 调用本系统演示样例、get 调用其它系统演示样例

    一.get 标签概述 通俗来讲,get 标签是Phpcms定义的能直接调用数据库里面内容的简单化.友好化代码,她可调用本系统和外部数据,仅仅有你对SQL有一定的了解,她就是你的绝世好剑!也就是适合熟悉 ...

  6. 服务调用方案(Spring Http Invoker) - 我们到底能走多远系列(40)

    我们到底能走多远系列(40) 扯淡:  判断是否加可以效力于这家公司,一个很好的判断是,接触下这公司工作几年的员工,了解下生活工作状态,这就是你几年后的状态,如果满意就可以考虑加入了. 主题: 场景: ...

  7. WCF技术剖析之十五:数据契约代理(DataContractSurrogate)在序列化中的作用

    原文:WCF技术剖析之十五:数据契约代理(DataContractSurrogate)在序列化中的作用 [爱心链接:拯救一个25岁身患急性白血病的女孩[内有苏州电视台经济频道<天天山海经> ...

  8. WCF技术剖析之十四:泛型数据契约和集合数据契约(下篇)

    原文:WCF技术剖析之十四:泛型数据契约和集合数据契约(下篇) [爱心链接:拯救一个25岁身患急性白血病的女孩[内有苏州电视台经济频道<天天山海经>为此录制的节目视频(苏州话)]]在.NE ...

  9. WCF技术剖析之八:ClientBase<T>中对ChannelFactory<T>的缓存机制

    原文:WCF技术剖析之八:ClientBase<T>中对ChannelFactory<T>的缓存机制 和传统的分布式远程调用一样,WCF的服务调用借助于服务代理(Service ...

随机推荐

  1. java线程池,阿里为什么不允许使用Executors?

    带着问题 阿里Java代码规范为什么不允许使用Executors快速创建线程池? 下面的代码输出是什么? ThreadPoolExecutor executor = new ThreadPoolExe ...

  2. springboot整合websocket高级版

    目录 sockjs介绍 产生的原因 环境搭建 springboot整合sockjs 使用场景 聊天室开发 点对点通信 群聊 效果 总结 加入战队 微信公众号 上一章节我们说了websocket的优缺点 ...

  3. 观书有感(摘自12期CSDN)

    CSDN要闻 Visual Studio 将登陆Mac平台 在11月的Connect()上,微软正式发布了Visual Studio For Max预览版,这是微软这一编程工具首次进入苹果平台.Vis ...

  4. Excel批量导入(导出同理)

    在做JavaWeb中添加数据使我们在所难免会遇到的,在大数据的环境下批量添加和批量删除是必须的,而批量删除只需要获取到我们需要删除的ID就可以了,在批量添加中我们就会遇到问题,问题是得到批量的数据,这 ...

  5. [android视频教程] 传智播客android开发视频教程

    本套视频共有67集,是传智播客3G-Android就业班前8天的的课程量.本套视频教程是黎活明老师在2011年底对传智播客原来的Android核心基础课程精心重新录制的,比早期的Android课程内容 ...

  6. Flink 源码解析 —— Flink JobManager 有什么作用?

    JobManager 的作用 https://t.zsxq.com/2VRrbuf 博客 1.Flink 从0到1学习 -- Apache Flink 介绍 2.Flink 从0到1学习 -- Mac ...

  7. python练习题-1

    1.输出正方形 x=input("请输入:") x=int(x) for i in range(0,x): if (i==0) or (i==x-1): print("* ...

  8. 帝国CMS(EmpireCMS) v7.5后台任意代码执行

    帝国CMS(EmpireCMS) v7.5后台任意代码执行 一.漏洞描述 EmpireCMS 7.5版本及之前版本在后台备份数据库时,未对数据库表名做验证,通过修改数据库表名可以实现任意代码执行. 二 ...

  9. 数据库炸了——是谁动了我的wait_timeout

    1.起因 隐约听到坐在我对面的测试说测试环境的接口有问题 他们一番商讨后,朝我这边反馈说,现在测试环境的接口报504 我条件反射的回了句那是接口超时,再多试几次(测试环境的性能比较差,尤其是数据库,经 ...

  10. MQTT的学习之Mosquitto安装和使用

    Mosquitto是一个实现了MQTT3.1协议的代理服务器,由MQTT协议创始人之一的Andy Stanford-Clark开发,它为我们提供了非常棒的轻量级数据交换的解决方案.本文的主旨在于记录M ...