栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

10.spring @async失效之谜

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

10.spring @async失效之谜

课程标题《站在动态代理模式角度分析@async注解失效之谜》
课程内容:
1.Jdk动态代理纯手写@async实现异步操作
2.基于SpringAop手写@async实现异步操作
3@async异步注解失效之谜效果演示
4.源码角度分析为什么加上@async注解会404
5.@async注解失效之谜源码分析

@async aop技术 底层基于代理模式封装

spring框架底层使用jdk动态代理呢?还是cglib代理呢?

被代理类 实现接口的情况下 jdk动态代理呢

被代理类没有实现接口的情况下cglib代理

@async注解失效之谜 基于JDK动态代理简单手写异步注解
package com.mayikt.proxy04;

import com.mayikt.proxy04.ext.MayiktAsync;


public interface MayiktService {
    @MayiktAsync
    void addMayikt();
}
package com.mayikt.proxy04.impl;

import com.mayikt.proxy04.MayiktService;
import com.mayikt.proxy04.ext.MayiktAsync;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;


@Slf4j
public class MayiktServiceImpl implements MayiktService {
    @Override
    @MayiktAsync
    public void addMayikt() {
        log.info("");
    }
}

package com.mayikt.proxy04;

import com.mayikt.proxy04.ext.MayiktAsync;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;


@Slf4j
public class ThreadInvocationHandler implements InvocationHandler {
    
    private Object target;

    public ThreadInvocationHandler(Object target) {
        this.target = target;
    }

    private ExecutorService executorService = Executors.newCachedThreadPool();

    
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //参数 那个对象中 该方法 该方法的时候传递参数值
        log.info("<目标方法之前执行....args:{}>", args);
        MayiktAsync mayiktAsync = method.getDeclaredAnnotation(MayiktAsync.class);
        if (mayiktAsync == null) {
            return method.invoke(target, args);
        }
        executorService.execute(() -> {
            try {
                method.invoke(target, args);// 底层基于java反射机制调用 该对象中 该方法
            } catch (Exception e) {
                log.error("e:{}", e);
            }
        });
//        Object result = method.invoke(target, args);// 底层基于java反射机制调用 该对象中 该方法
        log.info("<目标方法之后执行....args:{}>", args);
        return null;
    }
    // 底层 帮助自动生成代理类

    
    public  T getProxy() {
        return (T) Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(),
                this);
    }
}
基于aop的方式 封装异步注解

maven依赖

       
        
            org.springframework.boot
            spring-boot-starter-aop
        
package com.mayikt.ext;

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

@Component
@Aspect
@Slf4j
public class ExtAsyncAop {

    private ExecutorService executorService;

    public ExtAsyncAop() {
        executorService = Executors.newFixedThreadPool(10);
    }

    // 只要方法上用注解,可以直接拿到方法,不需要再判断,减少代码冗余
    // 获取到方法上有加上ExtAsync直接进入环绕通知
    @Around(value = "@annotation(com.mayikt.ext.MayiktAsync)")
    public void doBefore(ProceedingJoinPoint joinPoint) throws Exception {
        log.info(">>>拦截到方法上有加上异步注解ExtAsync");
        executorService.execute(() -> {
            //执行目标方法
            try {
                joinPoint.proceed();
            } catch (Throwable throwable) {
                log.error("e:{}", throwable);
            }
        });
    }
}
为什么控制类实现了接口无法注册到SpringMVC容器中?

async注解失效之谜 接口无法注册到springmvc种 最后变成接口404呢

** $Proxy1 implements MayiktService
*** $Proxy1 注册到springmvc容器中?*

1.springmvc会从ioc容器中查找到所有的bean对象

2.需要判断每个类上 是否有加上@Controller

3.如果我们的类上有加上@Controller 则将bean对象 注册到 springmvc容器。

springmvc容器 分类

*$Proxy1 implements MayiktService{ —注册到 springmvc容器 不会的

}

判断代理类上是否有加上@Controller 没有的 则不会注册到 springmvc容器中

$Proxy1.class

1 Jdk动态代理,基于接口实现;

2 Cglib动态代理,采用继承目标对象实现;

3.如果控制类中有加上异步注解,并且有实现接口的情况下,则采用Jdk动态代理,生成的动态代理 控制类没有注册到SpirngMVC容器中,报404错误;

4.如果控制类中有加上异步注解,但是没有实现接口的情况下,则采用Cglib动态代理,控制类可以注册到SpringMVC容器中,但是异步注解会失效,原因是没有经过代理。

springboot启动时加上该参数:-Dsun.misc.ProxyGenerator.saveGeneratedFiles=true
站在SpringMVC源码角度分析,源码入口AbstractHandlerMethodMapping(判断哪些对象为控制类并且将对象注册到SpringMVC容器中)

[

](https://blog.csdn.net/u012425860/article/details/120538924)

原理:采用Jdk动态代理技术,代理基于接口实现,而接口上没有加上@RestController注解,所以代理对象无法注册到SpringMVC容器中;而采用Cglib生成的代理对象继承了目标对象@RestController注解,这时候Cglib生成的代理对象是可以注入到SpringMVC容器中。

cglib 生成 代理类 继承 目标类MayiktServiceImpl 而

我们目标类上是加上@RestController

async注解失效情况

1.注解@Async的方法不是public方法

2.注解@Async的返回值只能为void或Future

3.注解@Async方法使用static修饰也会失效

4.spring无法扫描到异步类,没加注解@Async或@EnableAsync注解

5.调用方与被调用方不能在同一个类

6.类中需要使用@Autowired或@Resource等注解自动注入,不能自己手动new对象

7.在Async方法上标注@Transactional是没用的.但在Async方法调用的方法上标注@Transcational是有效的

1.在当前类直接使用@Async失效是因为没有经过代理类,没有走拦截。如果要生效,要把生成好的代理类对象传给目标对象,再通过目标对象.addOrderLog()方法,这时候才会经过代理对象走invoke方法做拦截。

2.官方建议最好单独新建一类,专门处理异步操作

this.addAsyncUser();----- 没有经过异步注解回调

走cglib回调 异步 开启线程处理

暑期编程PK赛 得CSDN机械键盘等精美礼品!
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/1017003.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号