摘要

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. node获取本机动态IP,并对应修改相关JavaScript文件的IP地址

    目录 由于本机是自动获取分配的动态IP,所以每次重启后需要重新更改与IP相关文件 参考 时间:2018-08-02,更新时间:2018-11-06 注意:在win10环境运行无问题 由于本机是自动获取 ...

  2. centos部署oracle rac单实例11.2.0.3数据库(使用asm磁盘)

    部署oracle rac单实例数据库,需要安装grid和datavase两部分,所以首先创建两个用户oracle和grid,因为不能使用root用户进行安装,在安装之前首先需要修改一些系统参数和安装一 ...

  3. xlistview错误

    apply plugin: 'com.android.library' android { compileSdkVersion buildToolsVersion '26.0.1' defaultCo ...

  4. 创建docker容器遇到的错误

    1.问题截图 2.问题描述 出现该问题就是docker版本和系统版本不兼容导致的. 现在的系统版本和docker的版本如下: 3.问题解决 安装低版本的docker或者高版本的系统(Centos7.4 ...

  5. 颜色下拉菜单(combox)

    using System; using System.Drawing; using System.Collections; using System.ComponentModel; using Sys ...

  6. 从0到1发布一个npm包

    从0到1发布一个npm包 author: @TiffanysBear 最近在项目业务中有遇到一些问题,一些通用的方法或者封装的模块在PC.WAP甚至是APP中都需要使用,但是对于业务的PC.WAP.A ...

  7. Oracle中的通用函数

    1.nvl(列,默认值)函数处理null select nvl(null,3),nvl(4,3) from dual    结果显示为3,4.因为nvl中的第一个为null时,返回结果为第二个值,第一 ...

  8. net start mysql

    net start mysql 解决的方法: 如何以管理员身份打开黑窗口 左下角开始菜单,找到小娜,cmd 回车, 命令提示符右击,以管理员身份打开 依次输入下面两行代码 mysqld -instal ...

  9. shiro登录名的获取

    登录名的获取:通过的SecurityUtils的shiro import org.apache.shiro.SecurityUtils; //登录用户名 String loginAccount = S ...

  10. Git安装与使用(windows环境)(一)----Git安装、生成公钥和私钥、添加SSH

    安装 1.从官网下载git:http://git-scm.com/downloads 2.安装git,选择git组件安装,如下图 3.一直next,直到出现下面的窗口.这里是选择命令行形式.(可以理解 ...