Spring中@Async注解失效原因和原理分析(附代码)

先说结论:

@Async方法用在不可重写的方法(private、static、final)上时会失效,本类中调用@Async方法修饰的方法时也会失效

public、protected、default修饰的方法使用@Async注解正常生效

ps:@Transactional在以上几种场景也会失效,原因和底层原理也是一样

底层原理:

spring在扫描bean时,会对有@Async注解的bean做如下处理:
1.生成这个bean的代理类,类中的方法都通过代理类bean来调用
2.这个注解标记的方法(可重写)会进行特殊处理,变成异步执行,执行时用 被代理对象 调用该方法
3.调用未标记的方法时,则也是通过代理对象调用,但没有特殊处理,相当于 被代理对象 直接调用该方法

代码分析:

Controller类:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.lang.reflect.Method;
import java.util.Objects;

/**
 * 正常CglibService中的方法如果是异步执行,
 * 控制台会先打印execute,再打印finished,说明注解生效
 * 如果不是,
 * 则会先打印finished,再打印execute,说明注解失效
 */
@RestController
@RequestMapping("/cglibProxyTest")
public class CglibProxyTestController {

    @Autowired
    private CglibService cglibService;  //CglibService类有Async注解,会生成代理类和代理对象,这里实际是代理对象

    /**
     * public方法可以被代理,异步调用
     *
     * 执行结果:
     * m3 execute
     * m3 finished
     */
    @GetMapping("/m3")
    public void m3() {
        cglibService.m3();
        System.out.println("m3 execute");
    }

    /**
     * private方法无法被代理,不存在该方法
     *
     * 执行结果:
     * m1 execute
     */
    @GetMapping("/m1")
    public void m1() throws Exception {
        Class<? extends CglibService> clazz = cglibService.getClass();
        for (Method method : clazz.getDeclaredMethods()) {
            if(Objects.equals(method.getName(), "m1")) {
                method.setAccessible(true);
                method.invoke(cglibService);
            }
        }
        System.out.println("m1 execute");
    }

    /**
     * final方法无法被代理,不存在该方法
     *
     * 执行结果:
     * m6 execute
     */
    @GetMapping("/m6")
    public void m6() throws Exception {
        Class<? extends CglibService> clazz = cglibService.getClass();
        for (Method method : clazz.getDeclaredMethods()) {
            if(Objects.equals(method.getName(), "m6")) {
                method.setAccessible(true);
                method.invoke(cglibService);
            }
        }
        System.out.println("m6 execute");
    }


    /**
     * static方法无法被代理,不会异步执行
     *
     * 执行结果:
     * m5 finished
     * m5 execute
     */
    @GetMapping("/m5")
    public void m5() throws Exception {
        cglibService.m5();
        System.out.println("m5 execute");
    }

    /**
     * public方法可以被代理,无注解,不会异步执行
     * 相当于直接用被代理对象调用m7方法
     *
     * 执行结果:
     * m3 finished
     * m7 execute
     */
    @GetMapping("/m7")
    public void m7() throws Exception {
        cglibService.m7();
        System.out.println("m7 execute");
    }

    /**
     * protected方法可以被代理,异步调用
     * 在同一个包内,protected方法可以直接调用
     * 不在同一个包,可以通过反射调用
     *
     * 执行结果:
     * m2 execute
     * m2 finished
     * @throws Exception
     */
    @GetMapping("/m2")
    public void m2() throws Exception {
        cglibService.m2();
        System.out.println("m2 execute");
    }

    /**
     * default方法可以被代理,异步调用
     * 在同一个包内,default方法可以直接调用
     * 不在同一个包,可以通过反射调用
     *
     * 执行结果:
     * m4 execute
     * m4 finished
     * @throws Exception
     */
    @GetMapping("/m4")
    public void m4() throws Exception {
        cglibService.m4();
        System.out.println("m4 execute");
    }


}

Service类:

import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

@Service
public class CglibService {

    /**
     * final 方法不能被重写,也就无法被代理,注解失效
     * @throws Exception
     */
    @Async
    public final void m6() throws Exception {
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("m5 finished");
    }

    /**
     * static 方法不能被重写,也就无法被代理,注解失效
     * @throws Exception
     */
    @Async
    public static void m5() throws Exception {
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("m5 finished");
    }

    /**
     * private 方法不能被重写,也就无法被代理,注解失效
     * @throws Exception
     */
    @Async
    private void m1() throws Exception {
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("m1 finished");
    }

    /**
     * protected 方法可以被重写,可以代理,注解有效
     * @throws Exception
     */
    @Async
    protected void m2() throws Exception {
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("m2 finished");
    }

    /**
     * public 方法可以被重写,可以代理,注解有效
     * @throws Exception
     */
    @Async
    public void m3() {
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("m3 finished");
    }

    /**
     * default 方法可以被重写,可以代理,注解有效
     * @throws Exception
     */
    @Async
    void m4() {
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("m4 finished");
    }

    /**
     * 自调用,注解不生效
     * 生成代理类时因为无注解,所以m7不会有异步执行逻辑
     * 相当于直接用被代理对象调用m7方法,m7内部调用的是原始的m3方法,而不是被代理的m3方法
     */
    public void m7() {
        m3();
    }
}

debug可以发现,Controller中的Service对象实际是spring生成的代理对象,由于被代理的类不是接口,所以使用的是CGLIB代理

  • 14
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值