[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"article-spring-boot-async-zhujieshixiaoyuanyinfenxi":3},{"id":4,"title":5,"slug":6,"summary":7,"content":8,"contentHtml":9,"wordCount":10,"readingTime":11,"categoryId":12,"tags":13,"coverImage":13,"thumbnail":13,"status":14,"isTop":15,"isRecommended":15,"allowComments":15,"password":13,"viewCount":11,"likeCount":15,"commentCount":15,"seoTitle":5,"seoKeywords":16,"seoDescription":17,"source":13,"sourceUrl":13,"publishTime":18},"2004932213135699969","Spring Boot @Async 注解失效原因分析","spring-boot-async-zhujieshixiaoyuanyinfenxi","@Async 是 Spring 提供的一个核心注解，用于将方法调用变为异步执行。它依赖 Spring 的 AOP（面向切面编程）机制来实现。当你在一个方法上加上 @Async 时，Spring 会在运行时为该方法所在的 Bean 创建一个代理对象。所有对该方法的外部调用，实际上都是调用这个代理对象，由代理对象负责将方法的执行提交给一个线程池，从而实现异步效果。 基于这个原理，我们可以总结出 @Async 失效的几种典型场景。 --- 1. 调用者与被调用者在同一个类中（同类方法自调用） 这是 最常见 的失效场景。 原因： Spring 的 AOP 是基于代理实现的。只有通过 Spring 容器获取到的 Bean（即代理对象）去调用 @Async 方法时，AOP 切面才能生效。如果在同一个类的内部，一个方法直接调用另一个被 @Async 标注的方法，这个调用是对象内部的直接方法调用，没有经过...","@Async 是 Spring 提供的一个核心注解，用于将方法调用变为异步执行。它依赖 Spring 的 AOP（面向切面编程）机制来实现。当你在一个方法上加上 @Async 时，Spring 会在运行时为该方法所在的 Bean 创建一个代理对象。所有对该方法的外部调用，实际上都是调用这个代理对象，由代理对象负责将方法的执行提交给一个线程池，从而实现异步效果。\n\n基于这个原理，我们可以总结出 @Async 失效的几种典型场景。\n\n\n---\n1. 调用者与被调用者在同一个类中（同类方法自调用）\n\n这是 最常见 的失效场景。\n\n原因：\n\nSpring 的 AOP 是基于代理实现的。只有通过 Spring 容器获取到的 Bean（即代理对象）去调用 @Async 方法时，AOP 切面才能生效。如果在同一个类的内部，一个方法直接调用另一个被 @Async 标注的方法，这个调用是对象内部的直接方法调用，没有经过 Spring 生成的代理对象，因此异步注解会失效，方法仍然会同步执行。\n\n实例：\n\n@Service\npublic class MyService {\n\n    public void doSomething() {\n        \u002F\u002F 内部调用，不会异步执行\n        this.asyncMethod(); \n    }\n\n    @Async\n    public void asyncMethod() {\n        System.out.println(\"执行异步任务，线程：\" + Thread.currentThread().getName());\n    }\n}\n\n解决方案：\n\n- 方案一（推荐）：将异步方法移到另一个 Bean 中。\n\n确保调用是通过 Spring 容器的 Bean 进行的。\n\n@Service\npublic class MyService {\n    @Autowired\n    private AsyncTaskService asyncTaskService;\n\n    public void doSomething() {\n        \u002F\u002F 调用另一个 Bean 的方法，异步生效\n        asyncTaskService.asyncMethod();\n    }\n}\n\n@Service\npublic class AsyncTaskService {\n    @Async\n    public void asyncMethod() {\n        System.out.println(\"执行异步任务，线程：\" + Thread.currentThread().getName());\n    }\n}\n\n- 方案二（不推荐，有侵入性）：通过 ApplicationContext 自调用。\n\n在方法内部从 Spring 容器中获取当前 Bean 的代理对象，再用这个代理对象去调用方法。\n\n@Service\npublic class MyService implements ApplicationContextAware {\n\n    private ApplicationContext applicationContext;\n\n    public void doSomething() {\n        \u002F\u002F 获取代理对象\n        MyService proxyService = applicationContext.getBean(MyService.class);\n        proxyService.asyncMethod(); \u002F\u002F 异步生效\n    }\n\n    @Async\n    public void asyncMethod() {\n        System.out.println(\"执行异步任务，线程：\" + Thread.currentThread().getName());\n    }\n\n    @Override\n    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {\n        this.applicationContext = applicationContext;\n    }\n}\n\n\n---\n2. 方法不是 public\n\n原因：\n\nSpring 的 AOP 代理（无论是 JDK 动态代理还是 CGLIB 代理）都要求目标方法必须是 public。这是 Java 语言的访问限制和代理机制所决定的。如果方法是 private、protected 或 default 访问级别，代理无法对其进行拦截，@Async 自然失效。\n\n实例：\n\n@Service\npublic class MyService {\n\n    \u002F\u002F 方法是 private，@Async 失效\n    @Async\n    private void asyncMethod() {\n        System.out.println(\"执行异步任务\");\n    }\n}\n\n解决方案：\n\n- 将方法的访问修饰符改为 public。\n\n\n---\n3. 类没有被 Spring 管理\n\n原因：\n\n@Async 的异步功能是由 Spring 的容器在初始化 Bean 时，通过 AOP 动态织入的。如果这个类没有被声明为 Spring 的 Bean（例如，没有使用 @Component, @Service, @Controller 等注解，或者没有在 XML 中配置），那么 Spring 就不会为其创建代理，@Async 也就无从谈起。\n\n实例：\n\n\u002F\u002F 这个类不是 Spring Bean\npublic class MyService {\n\n    @Async\n    public void asyncMethod() {\n        System.out.println(\"执行异步任务\");\n    }\n}\n\n\u002F\u002F 在另一个类中使用 new 关键字创建对象\n@Service\npublic class AnotherService {\n    public void test() {\n        MyService service = new MyService(); \u002F\u002F 非Spring管理的对象\n        service.asyncMethod(); \u002F\u002F @Async 失效\n    }\n}\n\n解决方案：\n\n- 确保包含 @Async 方法的类是一个 Spring Bean。例如，在类上添加 @Service 注解。\n\n\n---\n4. 没有启用异步功能\n\n原因：\n\nSpring Boot 默认是不开启 @Async 注解的处理的。你必须在配置类或者启动类上显式地添加 @EnableAsync 注解来启用它。否则，Spring 会忽略所有的 @Async 注解，方法会以同步方式执行。\n\n实例：\n\n\u002F\u002F 没有添加 @EnableAsync\n@SpringBootApplication\npublic class MyApplication {\n    public static void main(String[] args) {\n        SpringApplication.run(MyApplication.class, args);\n    }\n}\n\n解决方案：\n\n- 在你的主配置类或启动类上添加 @EnableAsync 注解。\n\n@SpringBootApplication\n@EnableAsync \u002F\u002F 启用异步功能\npublic class MyApplication {\n    public static void main(String[] args) {\n        SpringApplication.run(MyApplication.class, args);\n    }\n}\n\n\n---\n5. 方法被 static 或 final 修饰\n\n原因：\n\n- static 方法： 静态方法属于类本身，不属于某个对象。Spring 的 AOP 是基于对象的代理，因此无法对静态方法进行拦截和增强。\n\n- final 方法： 如果目标对象是通过 CGLIB 动态生成子类来实现代理的，那么代理类需要覆盖目标方法。final 方法不能被子类覆盖，所以 CGLIB 无法代理它。如果是 JDK 动态代理，虽然代理的是接口，但被代理的实现类中的 final 方法同样无法被代理逻辑替换。\n\n实例：\n\n@Service\npublic class MyService {\n\n    \u002F\u002F static 方法，@Async 失效\n    @Async\n    public static void staticAsyncMethod() {\n        System.out.println(\"静态方法执行异步任务\");\n    }\n\n    \u002F\u002F final 方法，@Async 失效\n    @Async\n    public final void finalAsyncMethod() {\n        System.out.println(\"Final 方法执行异步任务\");\n    }\n}\n\n解决方案：\n\n- 移除 static 或 final 修饰符。\n\n\n---\n6. 方法返回值为 void 且抛出了未捕获的异常\n\n原因：\n\n当 @Async 方法返回值为 void 时，如果方法内部抛出了异常，这个异常无法被调用者捕获，因为调用者在方法“提交”到线程池后就已经返回了。异常会被 Spring 的 AsyncUncaughtExceptionHandler 捕获并打印到日志中（默认行为）。从调用者的角度看，方法似乎“悄无声息”地失败了，这可能被误认为是“失效”。\n\n实例：\n\n@Service\npublic class MyService {\n\n    @Async\n    public void asyncMethodWithException() {\n        System.out.println(\"执行一个会抛出异常的异步任务\");\n        throw new RuntimeException(\"异步任务内部发生错误\");\n    }\n}\n\n@Service\npublic class AnotherService {\n    @Autowired\n    private MyService myService;\n\n    public void test() {\n        try {\n            myService.asyncMethodWithException();\n            System.out.println(\"调用者：我以为异步任务成功了！\");\n        } catch (Exception e) {\n            \u002F\u002F 这里捕获不到异常\n            e.printStackTrace();\n        }\n    }\n}\n\n解决方案：\n\n- 返回 Future 或 CompletableFuture： 这是推荐的做法。通过返回的 Future 对象，调用者可以在需要时获取结果或捕获异常。\n\n@Service\npublic class MyService {\n\n    @Async\n    public CompletableFuture\u003CString> asyncMethodWithResult() {\n        System.out.println(\"执行异步任务\");\n        if (true) {\n            throw new RuntimeException(\"异步任务出错\");\n        }\n        return CompletableFuture.completedFuture(\"任务成功\");\n    }\n}\n\n@Service\npublic class AnotherService {\n    @Autowired\n    private MyService myService;\n\n    public void test() {\n        CompletableFuture\u003CString> future = myService.asyncMethodWithResult();\n        try {\n            String result = future.get(); \u002F\u002F 这里会抛出 ExecutionException\n            System.out.println(\"异步任务结果: \" + result);\n        } catch (Exception e) {\n            System.err.println(\"调用者捕获到异常: \" + e.getMessage());\n        }\n    }\n}\n\n- 自定义 AsyncUncaughtExceptionHandler： 如果必须返回 void，可以实现一个全局的异常处理器来统一处理这些未捕获的异常。\n\n\n---\n总结\n\n@Async 失效的根本原因是 Spring 的 AOP 代理没有生效。请记住以下几点，以确保 @Async 正常工作：\n\n1. 必须是 public 方法。\n\n2. 必须通过 Spring 容器管理的 Bean 调用。\n\n3. 不能在同类中直接调用（自调用）。\n\n4. 必须在配置类上添加 @EnableAsync。\n\n5. 方法不能是 static 或 final。\n\n6. 为了能捕获异常，推荐返回 Future 或 CompletableFuture。","\u003Cp data-line=\"0\">@Async 是 Spring 提供的一个核心注解，用于将方法调用变为异步执行。它依赖 Spring 的 AOP（面向切面编程）机制来实现。当你在一个方法上加上 @Async 时，Spring 会在运行时为该方法所在的 Bean 创建一个代理对象。所有对该方法的外部调用，实际上都是调用这个代理对象，由代理对象负责将方法的执行提交给一个线程池，从而实现异步效果。\u003C\u002Fp>\n\u003Cp data-line=\"2\">基于这个原理，我们可以总结出 @Async 失效的几种典型场景。\u003C\u002Fp>\n\u003Chr data-line=\"5\">\n\u003Col data-line=\"6\">\n\u003Cli data-line=\"6\">调用者与被调用者在同一个类中（同类方法自调用）\u003C\u002Fli>\n\u003C\u002Fol>\n\u003Cp data-line=\"8\">这是 最常见 的失效场景。\u003C\u002Fp>\n\u003Cp data-line=\"10\">原因：\u003C\u002Fp>\n\u003Cp data-line=\"12\">Spring 的 AOP 是基于代理实现的。只有通过 Spring 容器获取到的 Bean（即代理对象）去调用 @Async 方法时，AOP 切面才能生效。如果在同一个类的内部，一个方法直接调用另一个被 @Async 标注的方法，这个调用是对象内部的直接方法调用，没有经过 Spring 生成的代理对象，因此异步注解会失效，方法仍然会同步执行。\u003C\u002Fp>\n\u003Cp data-line=\"14\">实例：\u003C\u002Fp>\n\u003Cp data-line=\"16\">@Service\u003Cbr>\npublic class MyService {\u003C\u002Fp>\n\n        \u003Cdetails  data-line=\"19\" class=\"md-editor-code\" open=\"\">\n          \u003Csummary class=\"md-editor-code-head\">\n            \u003Cdiv class=\"md-editor-code-flag\">\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003C\u002Fdiv>\n            \u003Cdiv class=\"md-editor-code-action\">\n              \u003Cspan class=\"md-editor-code-lang\">\u003C\u002Fspan>\n              \u003Cspan class=\"md-editor-copy-button\" data-tips=\"复制代码\">复制代码\u003C\u002Fspan>\n              \n              \u003Cspan class=\"md-editor-collapse-tips\">\u003Csvg xmlns=\"http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" class=\"lucide lucide-circle-chevron-left md-editor-icon\">\u003Ccircle cx=\"12\" cy=\"12\" r=\"10\"\u002F>\u003Cpath d=\"m14 16-4-4 4-4\"\u002F>\u003C\u002Fsvg>\u003C\u002Fspan>\n            \u003C\u002Fdiv>\n          \u003C\u002Fsummary>\n          \u003Cpre>\u003Ccode class=\"language-\" language=>\u003Cspan class=\"md-editor-code-block\">\u003Cspan class=\"hljs-function\">\u003Cspan class=\"hljs-keyword\">public\u003C\u002Fspan> \u003Cspan class=\"hljs-keyword\">void\u003C\u002Fspan> \u003Cspan class=\"hljs-title\">doSomething\u003C\u002Fspan>()\u003C\u002Fspan> {\n    \u003Cspan class=\"hljs-comment\">\u002F\u002F 内部调用，不会异步执行\u003C\u002Fspan>\n    \u003Cspan class=\"hljs-keyword\">this\u003C\u002Fspan>.asyncMethod(); \n}\n\n@Async\n\u003Cspan class=\"hljs-function\">\u003Cspan class=\"hljs-keyword\">public\u003C\u002Fspan> \u003Cspan class=\"hljs-keyword\">void\u003C\u002Fspan> \u003Cspan class=\"hljs-title\">asyncMethod\u003C\u002Fspan>()\u003C\u002Fspan> {\n    System.\u003Cspan class=\"hljs-keyword\">out\u003C\u002Fspan>.println(\u003Cspan class=\"hljs-string\">&quot;执行异步任务，线程：&quot;\u003C\u002Fspan> + Thread.currentThread().getName());\n}\u003C\u002Fspan>\u003Cspan rn-wrapper aria-hidden=\"true\">\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\n        \u003C\u002Fdetails>\n      \u003Cp data-line=\"28\">}\u003C\u002Fp>\n\u003Cp data-line=\"30\">解决方案：\u003C\u002Fp>\n\u003Cul data-line=\"32\">\n\u003Cli data-line=\"32\">方案一（推荐）：将异步方法移到另一个 Bean 中。\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Cp data-line=\"34\">确保调用是通过 Spring 容器的 Bean 进行的。\u003C\u002Fp>\n\u003Cp data-line=\"36\">@Service\u003Cbr>\npublic class MyService {\u003Cbr>\n@Autowired\u003Cbr>\nprivate AsyncTaskService asyncTaskService;\u003C\u002Fp>\n\n        \u003Cdetails  data-line=\"41\" class=\"md-editor-code\" open=\"\">\n          \u003Csummary class=\"md-editor-code-head\">\n            \u003Cdiv class=\"md-editor-code-flag\">\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003C\u002Fdiv>\n            \u003Cdiv class=\"md-editor-code-action\">\n              \u003Cspan class=\"md-editor-code-lang\">\u003C\u002Fspan>\n              \u003Cspan class=\"md-editor-copy-button\" data-tips=\"复制代码\">复制代码\u003C\u002Fspan>\n              \n              \u003Cspan class=\"md-editor-collapse-tips\">\u003Csvg xmlns=\"http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" class=\"lucide lucide-circle-chevron-left md-editor-icon\">\u003Ccircle cx=\"12\" cy=\"12\" r=\"10\"\u002F>\u003Cpath d=\"m14 16-4-4 4-4\"\u002F>\u003C\u002Fsvg>\u003C\u002Fspan>\n            \u003C\u002Fdiv>\n          \u003C\u002Fsummary>\n          \u003Cpre>\u003Ccode class=\"language-\" language=>\u003Cspan class=\"md-editor-code-block\">\u003Cspan class=\"hljs-function\">\u003Cspan class=\"hljs-keyword\">public\u003C\u002Fspan> \u003Cspan class=\"hljs-keyword\">void\u003C\u002Fspan> \u003Cspan class=\"hljs-title\">doSomething\u003C\u002Fspan>()\u003C\u002Fspan> {\n    \u003Cspan class=\"hljs-comment\">\u002F\u002F 调用另一个 Bean 的方法，异步生效\u003C\u002Fspan>\n    asyncTaskService.asyncMethod();\n}\u003C\u002Fspan>\u003Cspan rn-wrapper aria-hidden=\"true\">\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\n        \u003C\u002Fdetails>\n      \u003Cp data-line=\"45\">}\u003C\u002Fp>\n\u003Cp data-line=\"47\">@Service\u003Cbr>\npublic class AsyncTaskService {\u003Cbr>\n@Async\u003Cbr>\npublic void asyncMethod() {\u003Cbr>\nSystem.out.println(&quot;执行异步任务，线程：&quot; + Thread.currentThread().getName());\u003Cbr>\n}\u003Cbr>\n}\u003C\u002Fp>\n\u003Cul data-line=\"55\">\n\u003Cli data-line=\"55\">方案二（不推荐，有侵入性）：通过 ApplicationContext 自调用。\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Cp data-line=\"57\">在方法内部从 Spring 容器中获取当前 Bean 的代理对象，再用这个代理对象去调用方法。\u003C\u002Fp>\n\u003Cp data-line=\"59\">@Service\u003Cbr>\npublic class MyService implements ApplicationContextAware {\u003C\u002Fp>\n\n        \u003Cdetails  data-line=\"62\" class=\"md-editor-code\" open=\"\">\n          \u003Csummary class=\"md-editor-code-head\">\n            \u003Cdiv class=\"md-editor-code-flag\">\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003C\u002Fdiv>\n            \u003Cdiv class=\"md-editor-code-action\">\n              \u003Cspan class=\"md-editor-code-lang\">\u003C\u002Fspan>\n              \u003Cspan class=\"md-editor-copy-button\" data-tips=\"复制代码\">复制代码\u003C\u002Fspan>\n              \n              \u003Cspan class=\"md-editor-collapse-tips\">\u003Csvg xmlns=\"http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" class=\"lucide lucide-circle-chevron-left md-editor-icon\">\u003Ccircle cx=\"12\" cy=\"12\" r=\"10\"\u002F>\u003Cpath d=\"m14 16-4-4 4-4\"\u002F>\u003C\u002Fsvg>\u003C\u002Fspan>\n            \u003C\u002Fdiv>\n          \u003C\u002Fsummary>\n          \u003Cpre>\u003Ccode class=\"language-\" language=>\u003Cspan class=\"md-editor-code-block\">\u003Cspan class=\"hljs-keyword\">private\u003C\u002Fspan> ApplicationContext applicationContext;\n\n\u003Cspan class=\"hljs-function\">\u003Cspan class=\"hljs-keyword\">public\u003C\u002Fspan> \u003Cspan class=\"hljs-keyword\">void\u003C\u002Fspan> \u003Cspan class=\"hljs-title\">doSomething\u003C\u002Fspan>()\u003C\u002Fspan> {\n    \u003Cspan class=\"hljs-comment\">\u002F\u002F 获取代理对象\u003C\u002Fspan>\n    MyService proxyService = applicationContext.getBean(MyService.\u003Cspan class=\"hljs-keyword\">class\u003C\u002Fspan>);\n    proxyService.asyncMethod(); \u003Cspan class=\"hljs-comment\">\u002F\u002F 异步生效\u003C\u002Fspan>\n}\n\n@Async\n\u003Cspan class=\"hljs-function\">\u003Cspan class=\"hljs-keyword\">public\u003C\u002Fspan> \u003Cspan class=\"hljs-keyword\">void\u003C\u002Fspan> \u003Cspan class=\"hljs-title\">asyncMethod\u003C\u002Fspan>()\u003C\u002Fspan> {\n    System.\u003Cspan class=\"hljs-keyword\">out\u003C\u002Fspan>.println(\u003Cspan class=\"hljs-string\">&quot;执行异步任务，线程：&quot;\u003C\u002Fspan> + Thread.currentThread().getName());\n}\n\n@Override\n\u003Cspan class=\"hljs-function\">\u003Cspan class=\"hljs-keyword\">public\u003C\u002Fspan> \u003Cspan class=\"hljs-keyword\">void\u003C\u002Fspan> \u003Cspan class=\"hljs-title\">setApplicationContext\u003C\u002Fspan>(\u003Cspan class=\"hljs-params\">ApplicationContext applicationContext\u003C\u002Fspan>) throws BeansException\u003C\u002Fspan> {\n    \u003Cspan class=\"hljs-keyword\">this\u003C\u002Fspan>.applicationContext = applicationContext;\n}\u003C\u002Fspan>\u003Cspan rn-wrapper aria-hidden=\"true\">\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\n        \u003C\u002Fdetails>\n      \u003Cp data-line=\"79\">}\u003C\u002Fp>\n\u003Chr data-line=\"82\">\n\u003Col start=\"2\" data-line=\"83\">\n\u003Cli data-line=\"83\">方法不是 public\u003C\u002Fli>\n\u003C\u002Fol>\n\u003Cp data-line=\"85\">原因：\u003C\u002Fp>\n\u003Cp data-line=\"87\">Spring 的 AOP 代理（无论是 JDK 动态代理还是 CGLIB 代理）都要求目标方法必须是 public。这是 Java 语言的访问限制和代理机制所决定的。如果方法是 private、protected 或 default 访问级别，代理无法对其进行拦截，@Async 自然失效。\u003C\u002Fp>\n\u003Cp data-line=\"89\">实例：\u003C\u002Fp>\n\u003Cp data-line=\"91\">@Service\u003Cbr>\npublic class MyService {\u003C\u002Fp>\n\n        \u003Cdetails  data-line=\"94\" class=\"md-editor-code\" open=\"\">\n          \u003Csummary class=\"md-editor-code-head\">\n            \u003Cdiv class=\"md-editor-code-flag\">\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003C\u002Fdiv>\n            \u003Cdiv class=\"md-editor-code-action\">\n              \u003Cspan class=\"md-editor-code-lang\">\u003C\u002Fspan>\n              \u003Cspan class=\"md-editor-copy-button\" data-tips=\"复制代码\">复制代码\u003C\u002Fspan>\n              \n              \u003Cspan class=\"md-editor-collapse-tips\">\u003Csvg xmlns=\"http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" class=\"lucide lucide-circle-chevron-left md-editor-icon\">\u003Ccircle cx=\"12\" cy=\"12\" r=\"10\"\u002F>\u003Cpath d=\"m14 16-4-4 4-4\"\u002F>\u003C\u002Fsvg>\u003C\u002Fspan>\n            \u003C\u002Fdiv>\n          \u003C\u002Fsummary>\n          \u003Cpre>\u003Ccode class=\"language-\" language=>\u003Cspan class=\"md-editor-code-block\">\u003Cspan class=\"hljs-comment\">\u002F\u002F 方法是 private，@Async 失效\u003C\u002Fspan>\n@Async\n\u003Cspan class=\"hljs-function\">\u003Cspan class=\"hljs-keyword\">private\u003C\u002Fspan> \u003Cspan class=\"hljs-keyword\">void\u003C\u002Fspan> \u003Cspan class=\"hljs-title\">asyncMethod\u003C\u002Fspan>()\u003C\u002Fspan> {\n    System.\u003Cspan class=\"hljs-keyword\">out\u003C\u002Fspan>.println(\u003Cspan class=\"hljs-string\">&quot;执行异步任务&quot;\u003C\u002Fspan>);\n}\u003C\u002Fspan>\u003Cspan rn-wrapper aria-hidden=\"true\">\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\n        \u003C\u002Fdetails>\n      \u003Cp data-line=\"99\">}\u003C\u002Fp>\n\u003Cp data-line=\"101\">解决方案：\u003C\u002Fp>\n\u003Cul data-line=\"103\">\n\u003Cli data-line=\"103\">将方法的访问修饰符改为 public。\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Chr data-line=\"106\">\n\u003Col start=\"3\" data-line=\"107\">\n\u003Cli data-line=\"107\">类没有被 Spring 管理\u003C\u002Fli>\n\u003C\u002Fol>\n\u003Cp data-line=\"109\">原因：\u003C\u002Fp>\n\u003Cp data-line=\"111\">@Async 的异步功能是由 Spring 的容器在初始化 Bean 时，通过 AOP 动态织入的。如果这个类没有被声明为 Spring 的 Bean（例如，没有使用 @Component, @Service, @Controller 等注解，或者没有在 XML 中配置），那么 Spring 就不会为其创建代理，@Async 也就无从谈起。\u003C\u002Fp>\n\u003Cp data-line=\"113\">实例：\u003C\u002Fp>\n\u003Cp data-line=\"115\">\u002F\u002F 这个类不是 Spring Bean\u003Cbr>\npublic class MyService {\u003C\u002Fp>\n\n        \u003Cdetails  data-line=\"118\" class=\"md-editor-code\" open=\"\">\n          \u003Csummary class=\"md-editor-code-head\">\n            \u003Cdiv class=\"md-editor-code-flag\">\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003C\u002Fdiv>\n            \u003Cdiv class=\"md-editor-code-action\">\n              \u003Cspan class=\"md-editor-code-lang\">\u003C\u002Fspan>\n              \u003Cspan class=\"md-editor-copy-button\" data-tips=\"复制代码\">复制代码\u003C\u002Fspan>\n              \n              \u003Cspan class=\"md-editor-collapse-tips\">\u003Csvg xmlns=\"http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" class=\"lucide lucide-circle-chevron-left md-editor-icon\">\u003Ccircle cx=\"12\" cy=\"12\" r=\"10\"\u002F>\u003Cpath d=\"m14 16-4-4 4-4\"\u002F>\u003C\u002Fsvg>\u003C\u002Fspan>\n            \u003C\u002Fdiv>\n          \u003C\u002Fsummary>\n          \u003Cpre>\u003Ccode class=\"language-\" language=>\u003Cspan class=\"md-editor-code-block\">@Async\n\u003Cspan class=\"hljs-function\">\u003Cspan class=\"hljs-keyword\">public\u003C\u002Fspan> \u003Cspan class=\"hljs-keyword\">void\u003C\u002Fspan> \u003Cspan class=\"hljs-title\">asyncMethod\u003C\u002Fspan>()\u003C\u002Fspan> {\n    System.\u003Cspan class=\"hljs-keyword\">out\u003C\u002Fspan>.println(\u003Cspan class=\"hljs-string\">&quot;执行异步任务&quot;\u003C\u002Fspan>);\n}\u003C\u002Fspan>\u003Cspan rn-wrapper aria-hidden=\"true\">\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\n        \u003C\u002Fdetails>\n      \u003Cp data-line=\"122\">}\u003C\u002Fp>\n\u003Cp data-line=\"124\">\u002F\u002F 在另一个类中使用 new 关键字创建对象\u003Cbr>\n@Service\u003Cbr>\npublic class AnotherService {\u003Cbr>\npublic void test() {\u003Cbr>\nMyService service = new MyService(); \u002F\u002F 非Spring管理的对象\u003Cbr>\nservice.asyncMethod(); \u002F\u002F @Async 失效\u003Cbr>\n}\u003Cbr>\n}\u003C\u002Fp>\n\u003Cp data-line=\"133\">解决方案：\u003C\u002Fp>\n\u003Cul data-line=\"135\">\n\u003Cli data-line=\"135\">确保包含 @Async 方法的类是一个 Spring Bean。例如，在类上添加 @Service 注解。\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Chr data-line=\"138\">\n\u003Col start=\"4\" data-line=\"139\">\n\u003Cli data-line=\"139\">没有启用异步功能\u003C\u002Fli>\n\u003C\u002Fol>\n\u003Cp data-line=\"141\">原因：\u003C\u002Fp>\n\u003Cp data-line=\"143\">Spring Boot 默认是不开启 @Async 注解的处理的。你必须在配置类或者启动类上显式地添加 @EnableAsync 注解来启用它。否则，Spring 会忽略所有的 @Async 注解，方法会以同步方式执行。\u003C\u002Fp>\n\u003Cp data-line=\"145\">实例：\u003C\u002Fp>\n\u003Cp data-line=\"147\">\u002F\u002F 没有添加 @EnableAsync\u003Cbr>\n@SpringBootApplication\u003Cbr>\npublic class MyApplication {\u003Cbr>\npublic static void main(String[] args) {\u003Cbr>\nSpringApplication.run(MyApplication.class, args);\u003Cbr>\n}\u003Cbr>\n}\u003C\u002Fp>\n\u003Cp data-line=\"155\">解决方案：\u003C\u002Fp>\n\u003Cul data-line=\"157\">\n\u003Cli data-line=\"157\">在你的主配置类或启动类上添加 @EnableAsync 注解。\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Cp data-line=\"159\">@SpringBootApplication\u003Cbr>\n@EnableAsync \u002F\u002F 启用异步功能\u003Cbr>\npublic class MyApplication {\u003Cbr>\npublic static void main(String[] args) {\u003Cbr>\nSpringApplication.run(MyApplication.class, args);\u003Cbr>\n}\u003Cbr>\n}\u003C\u002Fp>\n\u003Chr data-line=\"168\">\n\u003Col start=\"5\" data-line=\"169\">\n\u003Cli data-line=\"169\">方法被 static 或 final 修饰\u003C\u002Fli>\n\u003C\u002Fol>\n\u003Cp data-line=\"171\">原因：\u003C\u002Fp>\n\u003Cul data-line=\"173\">\n\u003Cli data-line=\"173\">\n\u003Cp data-line=\"173\">static 方法： 静态方法属于类本身，不属于某个对象。Spring 的 AOP 是基于对象的代理，因此无法对静态方法进行拦截和增强。\u003C\u002Fp>\n\u003C\u002Fli>\n\u003Cli data-line=\"175\">\n\u003Cp data-line=\"175\">final 方法： 如果目标对象是通过 CGLIB 动态生成子类来实现代理的，那么代理类需要覆盖目标方法。final 方法不能被子类覆盖，所以 CGLIB 无法代理它。如果是 JDK 动态代理，虽然代理的是接口，但被代理的实现类中的 final 方法同样无法被代理逻辑替换。\u003C\u002Fp>\n\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Cp data-line=\"177\">实例：\u003C\u002Fp>\n\u003Cp data-line=\"179\">@Service\u003Cbr>\npublic class MyService {\u003C\u002Fp>\n\n        \u003Cdetails  data-line=\"182\" class=\"md-editor-code\" open=\"\">\n          \u003Csummary class=\"md-editor-code-head\">\n            \u003Cdiv class=\"md-editor-code-flag\">\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003C\u002Fdiv>\n            \u003Cdiv class=\"md-editor-code-action\">\n              \u003Cspan class=\"md-editor-code-lang\">\u003C\u002Fspan>\n              \u003Cspan class=\"md-editor-copy-button\" data-tips=\"复制代码\">复制代码\u003C\u002Fspan>\n              \n              \u003Cspan class=\"md-editor-collapse-tips\">\u003Csvg xmlns=\"http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" class=\"lucide lucide-circle-chevron-left md-editor-icon\">\u003Ccircle cx=\"12\" cy=\"12\" r=\"10\"\u002F>\u003Cpath d=\"m14 16-4-4 4-4\"\u002F>\u003C\u002Fsvg>\u003C\u002Fspan>\n            \u003C\u002Fdiv>\n          \u003C\u002Fsummary>\n          \u003Cpre>\u003Ccode class=\"language-\" language=>\u003Cspan class=\"md-editor-code-block\">\u003Cspan class=\"hljs-comment\">\u002F\u002F static 方法，@Async 失效\u003C\u002Fspan>\n@Async\n\u003Cspan class=\"hljs-function\">\u003Cspan class=\"hljs-keyword\">public\u003C\u002Fspan> \u003Cspan class=\"hljs-keyword\">static\u003C\u002Fspan> \u003Cspan class=\"hljs-keyword\">void\u003C\u002Fspan> \u003Cspan class=\"hljs-title\">staticAsyncMethod\u003C\u002Fspan>()\u003C\u002Fspan> {\n    System.\u003Cspan class=\"hljs-keyword\">out\u003C\u002Fspan>.println(\u003Cspan class=\"hljs-string\">&quot;静态方法执行异步任务&quot;\u003C\u002Fspan>);\n}\n\n\u003Cspan class=\"hljs-comment\">\u002F\u002F final 方法，@Async 失效\u003C\u002Fspan>\n@Async\n\u003Cspan class=\"hljs-function\">\u003Cspan class=\"hljs-keyword\">public\u003C\u002Fspan> final \u003Cspan class=\"hljs-keyword\">void\u003C\u002Fspan> \u003Cspan class=\"hljs-title\">finalAsyncMethod\u003C\u002Fspan>()\u003C\u002Fspan> {\n    System.\u003Cspan class=\"hljs-keyword\">out\u003C\u002Fspan>.println(\u003Cspan class=\"hljs-string\">&quot;Final 方法执行异步任务&quot;\u003C\u002Fspan>);\n}\u003C\u002Fspan>\u003Cspan rn-wrapper aria-hidden=\"true\">\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\n        \u003C\u002Fdetails>\n      \u003Cp data-line=\"193\">}\u003C\u002Fp>\n\u003Cp data-line=\"195\">解决方案：\u003C\u002Fp>\n\u003Cul data-line=\"197\">\n\u003Cli data-line=\"197\">移除 static 或 final 修饰符。\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Chr data-line=\"200\">\n\u003Col start=\"6\" data-line=\"201\">\n\u003Cli data-line=\"201\">方法返回值为 void 且抛出了未捕获的异常\u003C\u002Fli>\n\u003C\u002Fol>\n\u003Cp data-line=\"203\">原因：\u003C\u002Fp>\n\u003Cp data-line=\"205\">当 @Async 方法返回值为 void 时，如果方法内部抛出了异常，这个异常无法被调用者捕获，因为调用者在方法“提交”到线程池后就已经返回了。异常会被 Spring 的 AsyncUncaughtExceptionHandler 捕获并打印到日志中（默认行为）。从调用者的角度看，方法似乎“悄无声息”地失败了，这可能被误认为是“失效”。\u003C\u002Fp>\n\u003Cp data-line=\"207\">实例：\u003C\u002Fp>\n\u003Cp data-line=\"209\">@Service\u003Cbr>\npublic class MyService {\u003C\u002Fp>\n\n        \u003Cdetails  data-line=\"212\" class=\"md-editor-code\" open=\"\">\n          \u003Csummary class=\"md-editor-code-head\">\n            \u003Cdiv class=\"md-editor-code-flag\">\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003C\u002Fdiv>\n            \u003Cdiv class=\"md-editor-code-action\">\n              \u003Cspan class=\"md-editor-code-lang\">\u003C\u002Fspan>\n              \u003Cspan class=\"md-editor-copy-button\" data-tips=\"复制代码\">复制代码\u003C\u002Fspan>\n              \n              \u003Cspan class=\"md-editor-collapse-tips\">\u003Csvg xmlns=\"http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" class=\"lucide lucide-circle-chevron-left md-editor-icon\">\u003Ccircle cx=\"12\" cy=\"12\" r=\"10\"\u002F>\u003Cpath d=\"m14 16-4-4 4-4\"\u002F>\u003C\u002Fsvg>\u003C\u002Fspan>\n            \u003C\u002Fdiv>\n          \u003C\u002Fsummary>\n          \u003Cpre>\u003Ccode class=\"language-\" language=>\u003Cspan class=\"md-editor-code-block\">@Async\n\u003Cspan class=\"hljs-function\">\u003Cspan class=\"hljs-keyword\">public\u003C\u002Fspan> \u003Cspan class=\"hljs-keyword\">void\u003C\u002Fspan> \u003Cspan class=\"hljs-title\">asyncMethodWithException\u003C\u002Fspan>()\u003C\u002Fspan> {\n    System.\u003Cspan class=\"hljs-keyword\">out\u003C\u002Fspan>.println(\u003Cspan class=\"hljs-string\">&quot;执行一个会抛出异常的异步任务&quot;\u003C\u002Fspan>);\n    \u003Cspan class=\"hljs-keyword\">throw\u003C\u002Fspan> \u003Cspan class=\"hljs-keyword\">new\u003C\u002Fspan> RuntimeException(\u003Cspan class=\"hljs-string\">&quot;异步任务内部发生错误&quot;\u003C\u002Fspan>);\n}\u003C\u002Fspan>\u003Cspan rn-wrapper aria-hidden=\"true\">\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\n        \u003C\u002Fdetails>\n      \u003Cp data-line=\"217\">}\u003C\u002Fp>\n\u003Cp data-line=\"219\">@Service\u003Cbr>\npublic class AnotherService {\u003Cbr>\n@Autowired\u003Cbr>\nprivate MyService myService;\u003C\u002Fp>\n\n        \u003Cdetails  data-line=\"224\" class=\"md-editor-code\" open=\"\">\n          \u003Csummary class=\"md-editor-code-head\">\n            \u003Cdiv class=\"md-editor-code-flag\">\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003C\u002Fdiv>\n            \u003Cdiv class=\"md-editor-code-action\">\n              \u003Cspan class=\"md-editor-code-lang\">\u003C\u002Fspan>\n              \u003Cspan class=\"md-editor-copy-button\" data-tips=\"复制代码\">复制代码\u003C\u002Fspan>\n              \n              \u003Cspan class=\"md-editor-collapse-tips\">\u003Csvg xmlns=\"http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" class=\"lucide lucide-circle-chevron-left md-editor-icon\">\u003Ccircle cx=\"12\" cy=\"12\" r=\"10\"\u002F>\u003Cpath d=\"m14 16-4-4 4-4\"\u002F>\u003C\u002Fsvg>\u003C\u002Fspan>\n            \u003C\u002Fdiv>\n          \u003C\u002Fsummary>\n          \u003Cpre>\u003Ccode class=\"language-\" language=>\u003Cspan class=\"md-editor-code-block\">\u003Cspan class=\"hljs-function\">\u003Cspan class=\"hljs-keyword\">public\u003C\u002Fspan> \u003Cspan class=\"hljs-keyword\">void\u003C\u002Fspan> \u003Cspan class=\"hljs-title\">test\u003C\u002Fspan>()\u003C\u002Fspan> {\n    \u003Cspan class=\"hljs-keyword\">try\u003C\u002Fspan> {\n        myService.asyncMethodWithException();\n        System.\u003Cspan class=\"hljs-keyword\">out\u003C\u002Fspan>.println(\u003Cspan class=\"hljs-string\">&quot;调用者：我以为异步任务成功了！&quot;\u003C\u002Fspan>);\n    } \u003Cspan class=\"hljs-keyword\">catch\u003C\u002Fspan> (Exception e) {\n        \u003Cspan class=\"hljs-comment\">\u002F\u002F 这里捕获不到异常\u003C\u002Fspan>\n        e.printStackTrace();\n    }\n}\u003C\u002Fspan>\u003Cspan rn-wrapper aria-hidden=\"true\">\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\n        \u003C\u002Fdetails>\n      \u003Cp data-line=\"233\">}\u003C\u002Fp>\n\u003Cp data-line=\"235\">解决方案：\u003C\u002Fp>\n\u003Cul data-line=\"237\">\n\u003Cli data-line=\"237\">返回 Future 或 CompletableFuture： 这是推荐的做法。通过返回的 Future 对象，调用者可以在需要时获取结果或捕获异常。\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Cp data-line=\"239\">@Service\u003Cbr>\npublic class MyService {\u003C\u002Fp>\n\n        \u003Cdetails  data-line=\"242\" class=\"md-editor-code\" open=\"\">\n          \u003Csummary class=\"md-editor-code-head\">\n            \u003Cdiv class=\"md-editor-code-flag\">\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003C\u002Fdiv>\n            \u003Cdiv class=\"md-editor-code-action\">\n              \u003Cspan class=\"md-editor-code-lang\">\u003C\u002Fspan>\n              \u003Cspan class=\"md-editor-copy-button\" data-tips=\"复制代码\">复制代码\u003C\u002Fspan>\n              \n              \u003Cspan class=\"md-editor-collapse-tips\">\u003Csvg xmlns=\"http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" class=\"lucide lucide-circle-chevron-left md-editor-icon\">\u003Ccircle cx=\"12\" cy=\"12\" r=\"10\"\u002F>\u003Cpath d=\"m14 16-4-4 4-4\"\u002F>\u003C\u002Fsvg>\u003C\u002Fspan>\n            \u003C\u002Fdiv>\n          \u003C\u002Fsummary>\n          \u003Cpre>\u003Ccode class=\"language-\" language=>\u003Cspan class=\"md-editor-code-block\">\u003Cspan class=\"hljs-meta\">@Async\u003C\u002Fspan>\n\u003Cspan class=\"hljs-keyword\">public\u003C\u002Fspan> \u003Cspan class=\"hljs-title class_\">CompletableFuture\u003C\u002Fspan>&lt;\u003Cspan class=\"hljs-title class_\">String\u003C\u002Fspan>&gt; \u003Cspan class=\"hljs-title function_\">asyncMethodWithResult\u003C\u002Fspan>(\u003Cspan class=\"hljs-params\">\u003C\u002Fspan>) {\n    \u003Cspan class=\"hljs-title class_\">System\u003C\u002Fspan>.\u003Cspan class=\"hljs-property\">out\u003C\u002Fspan>.\u003Cspan class=\"hljs-title function_\">println\u003C\u002Fspan>(\u003Cspan class=\"hljs-string\">&quot;执行异步任务&quot;\u003C\u002Fspan>);\n    \u003Cspan class=\"hljs-keyword\">if\u003C\u002Fspan> (\u003Cspan class=\"hljs-literal\">true\u003C\u002Fspan>) {\n        \u003Cspan class=\"hljs-keyword\">throw\u003C\u002Fspan> \u003Cspan class=\"hljs-keyword\">new\u003C\u002Fspan> \u003Cspan class=\"hljs-title class_\">RuntimeException\u003C\u002Fspan>(\u003Cspan class=\"hljs-string\">&quot;异步任务出错&quot;\u003C\u002Fspan>);\n    }\n    \u003Cspan class=\"hljs-keyword\">return\u003C\u002Fspan> \u003Cspan class=\"hljs-title class_\">CompletableFuture\u003C\u002Fspan>.\u003Cspan class=\"hljs-title function_\">completedFuture\u003C\u002Fspan>(\u003Cspan class=\"hljs-string\">&quot;任务成功&quot;\u003C\u002Fspan>);\n}\u003C\u002Fspan>\u003Cspan rn-wrapper aria-hidden=\"true\">\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\n        \u003C\u002Fdetails>\n      \u003Cp data-line=\"250\">}\u003C\u002Fp>\n\u003Cp data-line=\"252\">@Service\u003Cbr>\npublic class AnotherService {\u003Cbr>\n@Autowired\u003Cbr>\nprivate MyService myService;\u003C\u002Fp>\n\n        \u003Cdetails  data-line=\"257\" class=\"md-editor-code\" open=\"\">\n          \u003Csummary class=\"md-editor-code-head\">\n            \u003Cdiv class=\"md-editor-code-flag\">\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003C\u002Fdiv>\n            \u003Cdiv class=\"md-editor-code-action\">\n              \u003Cspan class=\"md-editor-code-lang\">\u003C\u002Fspan>\n              \u003Cspan class=\"md-editor-copy-button\" data-tips=\"复制代码\">复制代码\u003C\u002Fspan>\n              \n              \u003Cspan class=\"md-editor-collapse-tips\">\u003Csvg xmlns=\"http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" class=\"lucide lucide-circle-chevron-left md-editor-icon\">\u003Ccircle cx=\"12\" cy=\"12\" r=\"10\"\u002F>\u003Cpath d=\"m14 16-4-4 4-4\"\u002F>\u003C\u002Fsvg>\u003C\u002Fspan>\n            \u003C\u002Fdiv>\n          \u003C\u002Fsummary>\n          \u003Cpre>\u003Ccode class=\"language-\" language=>\u003Cspan class=\"md-editor-code-block\">\u003Cspan class=\"hljs-function\">\u003Cspan class=\"hljs-keyword\">public\u003C\u002Fspan> \u003Cspan class=\"hljs-keyword\">void\u003C\u002Fspan> \u003Cspan class=\"hljs-title\">test\u003C\u002Fspan>()\u003C\u002Fspan> {\n    CompletableFuture&lt;String&gt; future = myService.asyncMethodWithResult();\n    \u003Cspan class=\"hljs-keyword\">try\u003C\u002Fspan> {\n        String result = future.\u003Cspan class=\"hljs-keyword\">get\u003C\u002Fspan>(); \u003Cspan class=\"hljs-comment\">\u002F\u002F 这里会抛出 ExecutionException\u003C\u002Fspan>\n        System.\u003Cspan class=\"hljs-keyword\">out\u003C\u002Fspan>.println(\u003Cspan class=\"hljs-string\">&quot;异步任务结果: &quot;\u003C\u002Fspan> + result);\n    } \u003Cspan class=\"hljs-keyword\">catch\u003C\u002Fspan> (Exception e) {\n        System.err.println(\u003Cspan class=\"hljs-string\">&quot;调用者捕获到异常: &quot;\u003C\u002Fspan> + e.getMessage());\n    }\n}\u003C\u002Fspan>\u003Cspan rn-wrapper aria-hidden=\"true\">\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003Cspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\n        \u003C\u002Fdetails>\n      \u003Cp data-line=\"266\">}\u003C\u002Fp>\n\u003Cul data-line=\"268\">\n\u003Cli data-line=\"268\">自定义 AsyncUncaughtExceptionHandler： 如果必须返回 void，可以实现一个全局的异常处理器来统一处理这些未捕获的异常。\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Chr data-line=\"271\">\n\u003Cp data-line=\"272\">总结\u003C\u002Fp>\n\u003Cp data-line=\"274\">@Async 失效的根本原因是 Spring 的 AOP 代理没有生效。请记住以下几点，以确保 @Async 正常工作：\u003C\u002Fp>\n\u003Col data-line=\"276\">\n\u003Cli data-line=\"276\">\n\u003Cp data-line=\"276\">必须是 public 方法。\u003C\u002Fp>\n\u003C\u002Fli>\n\u003Cli data-line=\"278\">\n\u003Cp data-line=\"278\">必须通过 Spring 容器管理的 Bean 调用。\u003C\u002Fp>\n\u003C\u002Fli>\n\u003Cli data-line=\"280\">\n\u003Cp data-line=\"280\">不能在同类中直接调用（自调用）。\u003C\u002Fp>\n\u003C\u002Fli>\n\u003Cli data-line=\"282\">\n\u003Cp data-line=\"282\">必须在配置类上添加 @EnableAsync。\u003C\u002Fp>\n\u003C\u002Fli>\n\u003Cli data-line=\"284\">\n\u003Cp data-line=\"284\">方法不能是 static 或 final。\u003C\u002Fp>\n\u003C\u002Fli>\n\u003Cli data-line=\"286\">\n\u003Cp data-line=\"286\">为了能捕获异常，推荐返回 Future 或 CompletableFuture。\u003C\u002Fp>\n\u003C\u002Fli>\n\u003C\u002Fol>\n",5833,19,"0","",2,0,"方法,public,@Async,Spring,调用","@Async 是 Spring 提供的一个核心注解，用于将方法调用变为异步执行。它依赖 Spring 的 AOP（面向切面编程）机制来实现。当你在一个方法上加上 @Async 时，Spring 会在运行时为该方法所在的 Bean 创建一个代理对象。所有对该方法的外部调用，实际上都是调用这个代理对象，由代理对象负责将方法的执行提交给一个线程池，从而实现异步效果。","2025-12-27 23:07:45"]