around-spring目标方法aop跳过代码执行

hvvq6cgz  于 2021-07-13  发布在  Java
关注(0)|答案(1)|浏览(410)

据我所知, @Around springaop上的注解可以处理方法上的任何返回类型;与 void 类型返回null。
以下是记录方法持续时间的简单建议:

  1. @Target(ElementType.METHOD)
  2. @Retention(RetentionPolicy.RUNTIME)
  3. public @interface Profiling { }
  1. @Aspect
  2. @Component
  3. public class ProfilingAspect {
  4. // ...
  5. @Around("@annotation(profilingAnnotation)")
  6. public Object logDuration(ProceedingJoinPoint joinPoint, Profiling profilingAnnotation) throws Throwable {
  7. long startTime = Instant.now().toEpochMilli();
  8. Object result = null;
  9. try {
  10. result = joinPoint.proceed(); // on void methods, this supposed to return null
  11. } catch (Throwable e) {
  12. logger.error(e.getMessage(), e);
  13. throw e;
  14. } finally {
  15. long endTime = Instant.now().toEpochMilli(); // Below is not ran all together
  16. long duration = endTime - startTime;
  17. logger.info(joinPoint.getSignature().toShortString()+": "+duration+"ms");
  18. }
  19. //return the result to the caller
  20. return result;
  21. }
  22. }

但是,当在这个方法上调用时,它不会返回任何东西,而是跳过之后的所有代码 proceed() 总而言之。甚至 finally 阻止。
这是有问题的代码:

  1. @GetMapping("/members/exportpdf")
  2. @Profiling
  3. public void exportToPDF(@RequestParam(required = false)String role, HttpServletResponse response) throws DocumentException, IOException, ExecutionException, InterruptedException {
  4. CompletableFuture<List<GuildMember>> guildMembers;
  5. if (role==null) {
  6. guildMembers = guildService.findAll(); // Async Method
  7. } else {
  8. guildMembers = guildService.findByType(role); // Async Method
  9. }
  10. response.setContentType("application/pdf");
  11. DateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd_HH:mm:ss");
  12. String currentDateTime = dateFormatter.format(new Date());
  13. String headerKey = "Content-Disposition";
  14. String headerValue = "inline; filename=guildmembers_" + currentDateTime + ".pdf";
  15. response.setHeader(headerKey, headerValue);
  16. PDFExporter exporter = new PDFExporter(guildMembers);
  17. exporter.export(response).get(); // exporter.export(..) is an Async method returning CompletableFuture<Void>
  18. }

这怎么可能?我在配置上遗漏了什么吗?还是Spring的虫子?
注意。我使用的是springboot2.4.4和starter依赖项
编辑。 PDFExporter.export() 使用 OutputStreamHttpServletResponse 打印 application/pdf 返回给用户 CompletableFuture<Void> . 至于为什么,该方法与上面的异步函数通信,因此我想保证操作以某种方式完成。

ztyzrc3y

ztyzrc3y1#

为了教你什么是mcve以及如何在这里更好地提问,我将向你展示我根据你的代码片段和描述创建的mcve:
我们需要依赖类来编译代码:

  1. package de.scrum_master.spring.q66958382;
  2. import java.lang.annotation.ElementType;
  3. import java.lang.annotation.Retention;
  4. import java.lang.annotation.RetentionPolicy;
  5. import java.lang.annotation.Target;
  6. @Target(ElementType.METHOD)
  7. @Retention(RetentionPolicy.RUNTIME)
  8. public @interface Profiling {}
  1. package de.scrum_master.spring.q66958382;
  2. public class GuildMember {
  3. private String name;
  4. public GuildMember(String name) {
  5. this.name = name;
  6. }
  7. @Override
  8. public String toString() {
  9. return "GuildMember{" +
  10. "name='" + name + '\'' +
  11. '}';
  12. }
  13. }
  14. ``` `PDFExporter` 实用程序:
  15. 也许你用的是 `PDFExporter` 但这只是猜测。在任何情况下,它看起来都不是spring组件,因为稍后您将调用构造函数,而不是从应用程序上下文获取bean示例。所以我在这里把它建模为一个简单的pojo。

package de.scrum_master.spring.q66958382;

import org.springframework.scheduling.annotation.Async;

import javax.servlet.http.HttpServletResponse;
import java.util.List;
import java.util.concurrent.CompletableFuture;

public class PDFExporter {
CompletableFuture<List> guildMembers;

public PDFExporter(CompletableFuture<List> guildMembers) {
this.guildMembers = guildMembers;
}

@Async
public CompletableFuture export(HttpServletResponse response) {
return CompletableFuture.supplyAsync(() -> {
try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); }
return null;
});
}
}

  1. 服务 `@Async` 方法:
  2. 接下来,我对你的公会服务做了一个有根据的猜测:

package de.scrum_master.spring.q66958382;

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

import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CompletableFuture;

@Service
public class GuildService {
@Async
public CompletableFuture<List> findAll() {
return CompletableFuture.supplyAsync(() -> {
try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); }
return Arrays.asList(new GuildMember("Jane"), new GuildMember("John"), new GuildMember("Eve"));
});
}

@Async
public CompletableFuture<List> findByType(String role) {
return CompletableFuture.supplyAsync(() -> {
try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); }
return Collections.singletonList(new GuildMember("Eve"));
});
}
}

  1. 分析方面要针对的组件:
  2. 这是您的示例组件,只是在 `guildMembers` 初始化。但它根本不会改变功能:

package de.scrum_master.spring.q66958382;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;

@Component
public class MyComponent {
@Autowired
private GuildService guildService;

@GetMapping("/members/exportpdf")
@Profiling
public void exportToPDF(@RequestParam(required = false) String role, HttpServletResponse response) throws IOException, ExecutionException, InterruptedException {
CompletableFuture<List> guildMembers = role == null ? guildService.findAll() : guildService.findByType(role);

  1. response.setContentType("application/pdf");
  2. DateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd_HH:mm:ss");
  3. String currentDateTime = dateFormatter.format(new Date());
  4. String headerKey = "Content-Disposition";
  5. String headerValue = "inline; filename=guildmembers_" + currentDateTime + ".pdf";
  6. response.setHeader(headerKey, headerValue);
  7. PDFExporter exporter = new PDFExporter(guildMembers);
  8. exporter.export(response).get();

}
}

  1. 驱动程序应用程序+ `@EnableAsync` 配置:

package de.scrum_master.spring.q66958382;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Configuration;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.scheduling.annotation.EnableAsync;

import java.io.IOException;
import java.util.concurrent.ExecutionException;

@SpringBootApplication
@Configuration
@EnableAsync
public class DemoApplication {
public static void main(String[] args) throws InterruptedException, IOException, ExecutionException {
try (ConfigurableApplicationContext appContext = SpringApplication.run(DemoApplication.class, args)) {
doStuff(appContext);
}
}

private static void doStuff(ConfigurableApplicationContext appContext) throws InterruptedException, IOException, ExecutionException {
MyComponent myComponent = appContext.getBean(MyComponent.class);
myComponent.exportToPDF("admin", new MockHttpServletResponse());
}
}

  1. 分析方面:
  2. 最后,但并非最不重要的是,这是一个方面。它也和您介绍的一样,只是关于如何返回结果的复杂度要低一点。但同样,这并没有改变这样一个事实,即方面按预期工作:

package de.scrum_master.spring.q66958382;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import java.time.Instant;

@Aspect
@Component
public class ProfilingAspect {
private static final Logger logger = LoggerFactory.getLogger(ProfilingAspect.class);

@Around("@annotation(profilingAnnotation)")
public Object logDuration(ProceedingJoinPoint joinPoint, Profiling profilingAnnotation) throws Throwable {
long startTime = Instant.now().toEpochMilli();
try {
return joinPoint.proceed();
}
catch (Throwable e) {
logger.error(e.getMessage(), e);
throw e;
}
finally {
long duration = Instant.now().toEpochMilli() - startTime;
logger.info(joinPoint.getSignature().toShortString() + ": " + duration + " ms");
}
}
}

  1. 控制台日志:
  2. 如果运行应用程序,控制台日志会显示:

. ____ _ __ _ _
/\ / ' __ _ () __ __ _ \ \ \
( ( )_
_ | '_ | '| | ' / ` | \ \ \
\/ __)| |)| | | | | || (
| | ) ) ) )
' || .__|| ||| |_, | / / / /
=========|
|==============|
/=////
:: Spring Boot :: (v2.1.8.RELEASE)

2021-04-06 09:18:10.793 INFO 13616 --- [ main] d.s.spring.q66958382.DemoApplication : Starting DemoApplication on Xander-Ultrabook with PID 13616 (C:\Users\alexa\Documents\java-src\spring-aop-playground\target\classes started by alexa in C:\Users\alexa\Documents\java-src\spring-aop-playground)
(...)
2021-04-06 09:18:14.809 INFO 13616 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path ''
2021-04-06 09:18:14.812 INFO 13616 --- [ main] d.s.spring.q66958382.DemoApplication : Started DemoApplication in 4.815 seconds (JVM running for 7.782)
(...)
2021-04-06 09:18:15.839 INFO 13616 --- [ main] d.s.spring.q66958382.ProfilingAspect : MyComponent.exportToPDF(..): 1014 ms
(...)

  1. 因此,如果它在您自己的应用程序中不起作用,则可能与您描述它的方式不同,或者您误解了日志。
展开查看全部

相关问题