你必须知道的SpringAsync的一些坑
前言
大家都知道 用@Trancational,@Async,@Retryable 等注解时,需要用代理调用才生效,用this调用是不生效的。
大多数情况,是由另外的类发起调用,这是没有问题的。
少数情况下,存在本类调用本类方法,这种情况下要做到注解生效,该怎么办呢?
常见的解决方法有以下几种: 从applicationContext种获取bean 自己注入自己 AopContext.currentProxy() 需要使用 @EnableAspectJAutoProxy(exposeProxy = true)@Async注解的原理
@EnableAsync注解上标注了@Import(AsyncConfigurationSelector.class),AsyncConfigurationSelector在默认情况下,会选择出来ProxyAsyncConfiguration类进行导入,即把ProxyAsyncConfiguration类作为@Configuration类配置到ApplicationContext @Configuration @Role(BeanDefinition.ROLE_INFRASTRUCTURE) public class ProxyAsyncConfiguration extends AbstractAsyncConfiguration { @Bean(name = TaskManagementConfigUtils.ASYNC_ANNOTATION_PROCESSOR_BEAN_NAME) @Role(BeanDefinition.ROLE_INFRASTRUCTURE) public AsyncAnnotationBeanPostProcessor asyncAdvisor() { Assert.notNull(this.enableAsync, "@EnableAsync annotation metadata was not injected"); AsyncAnnotationBeanPostProcessor bpp = new AsyncAnnotationBeanPostProcessor(); Class<? extends Annotation> customAsyncAnnotation = this.enableAsync.getClass("annotation"); if (customAsyncAnnotation != AnnotationUtils.getDefaultValue(EnableAsync.class, "annotation")) { bpp.setAsyncAnnotationType(customAsyncAnnotation); } if (this.executor != null) { bpp.setExecutor(this.executor); } if (this.exceptionHandler != null) { bpp.setExceptionHandler(this.exceptionHandler); } bpp.setProxyTargetClass(this.enableAsync.getBoolean("proxyTargetClass")); bpp.setOrder(this.enableAsync.getNumber("order")); return bpp; } }
这段代码的作用是把AsyncAnnotationBeanPostProcessor作为Bean注册到Context中。
我们看下这个Async相关的BPP做了什么操作: // 潜质处理不做任何动作,可保证在调用bean的init之前,bean本身没有任何变化。 @Override public Object postProcessBeforeInitialization(Object bean, String beanName) { return bean; } @Override public Object postProcessAfterInitialization(Object bean, String beanName) { // 如果是AOP相关的基础组件bean,如ProxyProcessorSupport类及其子类,则直接返回。 if (bean instanceof AopInfrastructureBean) { // Ignore AOP infrastructure such as scoped proxies. return bean; } if (bean instanceof Advised) { // 如果已经是Advised的,即已经是被动态代理的实例,则直接添加advisor。 Advised advised = (Advised) bean; if (!advised.isFrozen() && isEligible(AopUtils.getTargetClass(bean))) { // 如果没有被frozen(即冷冻,不再做改动的动态代理实例)且是Eligbile(合适的),则把其添加到advisor中。根据配置决定插入位置。 // Add our local Advisor to the existing proxy"s Advisor chain... if (this.beforeExistingAdvisors) { advised.addAdvisor(0, this.advisor); } else { advised.addAdvisor(this.advisor); } return bean; } } if (isEligible(bean, beanName)) { // 如果是Eligible合适的,且还不是被代理的类,则创建一个代理类的实例并返回。 ProxyFactory proxyFactory = prepareProxyFactory(bean, beanName); if (!proxyFactory.isProxyTargetClass()) { evaluateProxyInterfaces(bean.getClass(), proxyFactory); } proxyFactory.addAdvisor(this.advisor); customizeProxyFactory(proxyFactory); return proxyFactory.getProxy(getProxyClassLoader()); } // No async proxy needed. return bean; } // 准备ProxyFactory对象 protected ProxyFactory prepareProxyFactory(Object bean, String beanName) { ProxyFactory proxyFactory = new ProxyFactory(); proxyFactory.copyFrom(this); // 设置被代理的bean为target,这个bean是真实的bean。 proxyFactory.setTarget(bean); return proxyFactory; }
Spring在对一个类进行AOP代理后,会为此类加上Advised接口,返回的动态代理对象都会带上Advised接口修饰,那么第一段逻辑判断bean instanceof Advised的目的就是判断是否已经是被动态代理的类,如果是,则为其添加一个Advisor增强器。
如果不是动态代理的对象,因为@Async要为方法增加代理,并转换为异步执行,故需要把原始bean转换为被AOP动态代理的bean。 快问快答
问题1:@Async注解在使用第三种方法AopContext.currentProxy() 调用本类方法能生效吗?
答案:不能生效
原因:EnableAspectJAutoProxy(exposeProxy=true)用的是 AnnotationAwareAspectJAutoProxyCreator 这个bbp 所以EnableAspectJAutoProxy(exposeProxy=true) 这个注解的 exposeProxy=true 只针对由这个bbp创建的代理类生效,而Async注解的代理是由自己创建的
问题2:@Transactional注解在使用第三种方法AopContext.currentProxy() 调用本类方法能生效吗?
答案:可以生效
原因:Transactional注解种的bbp是 InfrastructureAdvisorAutoProxyCreator,而 EnableAspectJAutoProxy 会把 InfrastructureAdvisorAutoProxyCreator 替换成 AnnotationAwareAspectJAutoProxyCreator。代码在 AopConfigUtils 种的 registerOrEscalateApcAsRequired 方法 /** * Stores the auto proxy creator classes in escalation order. */ private static final List> APC_PRIORITY_LIST = new ArrayList>(); /** * Setup the escalation list. */ static { APC_PRIORITY_LIST.add(InfrastructureAdvisorAutoProxyCreator.class); APC_PRIORITY_LIST.add(AspectJAwareAdvisorAutoProxyCreator.class); APC_PRIORITY_LIST.add(AnnotationAwareAspectJAutoProxyCreator.class); } private static BeanDefinition registerOrEscalateApcAsRequired(Class<?> cls, BeanDefinitionRegistry registry, Object source) { Assert.notNull(registry, "BeanDefinitionRegistry must not be null"); if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) { BeanDefinition apcDefinition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME); if (!cls.getName().equals(apcDefinition.getBeanClassName())) { int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName()); int requiredPriority = findPriorityForClass(cls); if (currentPriority < requiredPriority) { apcDefinition.setBeanClassName(cls.getName()); } } return null; } RootBeanDefinition beanDefinition = new RootBeanDefinition(cls); beanDefinition.setSource(source); beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE); beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition); return beanDefinition; }
所以 Transactional注解是能享受到 EnableAspectJAutoProxy 注解的福利的
问题3:@Transactional @Async 一起用的话 调用本类方法能生效吗?
答案:可以生效
原因:AsyncAnnotationBeanPostProcessor 优先级是最低 而AnnotationAwareAspectJAutoProxyCreator 优先级是最高,所以Async注解可以不用自己创建代理,而复用AnnotationAwareAspectJAutoProxy创建的代理。
引申:为什么引入spring cloud starter sleuth包后 @Async注解就能用AopContext.currentProxy
原因:sleuth包中定义了 @Async的切面
买书的六大网站有哪些?买书有六大网站可以选择。买新书京东,当当,淘宝,天猫。买旧书孔夫子旧书网,多抓鱼,闲鱼转转。首先声明一下我说的这些不是广告,只是陈述一下我买书的经历经验。首先说一下京东。京东自营的
iOS15。0Beta5发布,新增22项改动前言距离上次更新刚好两周。苹果准时地推送了iOS15Beta5。一晃已经到第五版了。作为对比,iOS14一共有8个Beta版本。看样子怎么也算是正式进入后半段了。而回到固件上来。这
雷军向小米用户发放1999红包,涉及18万用户一个品牌回馈用户的方式有很多种,米面粮油小礼盒比比皆是,能够直接发钱的例子可是少之又少。近日小米集团CEO雷军因回馈用户的方式过于实在再上热搜大手一挥,每人送1999红包。(图源网
新购电脑,选AMD板U套装就对了上周隶属于锐龙5000G系列的锐龙75700G和锐龙55600G正式上市,标志着消费市场上的锐龙APU在时隔3年之后终于迎来了换代。对于饱受缺卡困扰的玩家来说,锐龙75700G和锐
物联网卡到底能不能用在手机上?物联网卡能不能在手机上使用,有很多人在问这个问题,有人说不能在手机上使用,有人说可以在手机上使用了,物联网卡到底能不能在手机上使用呢?,咱们今天就聊聊这个话题。那什么是物联网卡呢?
正点原子I。MX6U嵌入式Qt开发指南第十一章网络编程3今日头条西瓜视频抖音短视频同名正点原子原子哥今日头条西瓜视频抖音短视频账号正点原子原子哥感谢各位的关注和支持,你们的关注和支持是正点原子无限前进的动力。第十一章网络编程由于本章内容
监控系统选型必知必会!ZabbixPrometheus等(内含自学教程)没有衡量,就没有管理。彼得德鲁克Linux系统在企业中的应用程度已经非常广泛,人们听到过太多关于Docker和Kubernetes的消息,以至于忘记了监控和日志记录也是同样重要的任
开学了,要换个本儿吗?笔记本电脑超强科普购买攻略疫情以来全民进入了zoomuni时代这时候一台能带的动的电脑是多么重要!你要知道不同专业对电脑可都有不一样的需求苹果虽然好但却不支持部分专业软件轻薄本虽然便捷,但做剪辑却不是最优选
老牌商务机迈进亿时代摩托罗拉edgespro全面评测掐指算来,年龄最大的90后已过而立之年,绝大多数90后也已成长为职场上的中坚力量。作为经常从事商业活动的新时代年轻商务人士,一部匹配自身气质的高素质智能手机不可或缺。不过放眼国内智
麒麟芯回来了?华为新机发布,搭载中芯国际14nm工艺芯片大家都知道,本来华为手机使用自己研制的麒麟芯片,前景一片大好。但是,美国修改了规则,使得华为不得不出售了荣耀手机业务,随着形势越来越严峻,华为的Mate40和P50系列都不得不采用
有效提问200以内的耳机都是垃圾近日,微博上出现超话怎么有效提问得到自己想要的安利推荐呢?网友有妙招一时之间,这种按头安利提问屡试不爽,被称之为有效提问。这些高智商操作令人拍案叫绝,哈哈哈提到耳机,他的演变路程也