先说结论:
@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代理