框架或工具:Lombok

项目地址:https://github.com/h837272998/next-springboot

一、Maven依赖

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  3. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  4. <modelVersion>4.0.0</modelVersion>
  5. <parent>
  6. <groupId>org.springframework.boot</groupId>
  7. <artifactId>spring-boot-starter-parent</artifactId>
  8. <version>2.1.7.RELEASE</version>
  9. <relativePath/> <!-- lookup parent from repository -->
  10. </parent>
  11. <groupId>com.hjh.myspringboot</groupId>
  12. <artifactId>myspringboot</artifactId>
  13. <version>0.0.1-SNAPSHOT</version>
  14. <name>myspringboot</name>
  15. <description>自学习springboot</description>
  16. <properties>
  17. <java.version>1.8</java.version>
  18. </properties>
  19. <dependencies>
  20. <dependency>
  21. <groupId>org.projectlombok</groupId>
  22. <artifactId>lombok</artifactId>
  23. <optional>true</optional>
  24. </dependency>
  25. <dependency>
  26. <groupId>org.springframework.boot</groupId>
  27. <artifactId>spring-boot-starter-test</artifactId>
  28. <scope>test</scope>
  29. </dependency>
  30. </dependencies>
  31. <build>
  32. <plugins>
  33. <plugin>
  34. <groupId>org.springframework.boot</groupId>
  35. <artifactId>spring-boot-maven-plugin</artifactId>
  36. </plugin>
  37. </plugins>
  38. </build>
  39. </project>

二、配置文件

springboot 提供了两种配置文件格式。分别是properties和yml。选择一种使用,结果都一样。这里选择properties。(为了看懂其他项目两者最好都掌握,喜欢用哪个看个人习惯)

server.port t端口

server.servlet.context-path 上下文路径

spring.datasource.* 数据库连接配置

  1. server.port=8080
  2. server.servlet.context-path=
  3. spring.datasource.driver-class-name=com.mysql.jdbc.Driver
  4. spring.datasource.url=jdbc:mysql://localhost:3306/spring?serverTimezone=GMT&useSSL=false
  5. spring.datasource.username=root
  6. spring.datasource.password=123456

三、RESTful API

常见的API

操作 url method
增加 /user/add?name=xx POST
删除 /user/delete?id=x GET
修改 /user/update?id=x&name=xxx POST
获取 /user/get?id=x GET
查询 /user/list?name=xx GET

采用RESTful API

操作 url method
增加 /user POST
删除 /user/x DELETE
修改 /user/x PUT
获取 /user/x GET
查询 /user?name=xx GET

RESTful架构:

遵循统一接口原则,统一接口包含了一组受限的预定义操作,不论什么资源,都是通过使用相同的接口进行资源访问

RESTful 是一种风格,不是强制标准。

四、编写RESTful和测试用例。

实现对用户表的增删改查

User.java

  1. @Data
  2. public class User {
  3. @JsonView(View.Summary.class)
  4. private long id;
  5. @JsonView(View.Summary.class)
  6. private String username;
  7. @JsonView(View.SummaryWithDetail.class)
  8. private String password;
  9. @JsonView(View.SummaryWithDetail.class)
  10. private Date createDate;
  11. }

View.java

  1. public class View {
  2. public interface Summary{}
  3. public interface SummaryWithDetail extends Summary{}
  4. }

UserController.java

  1. /**
  2. * @Description:
  3. * @Author: HJH
  4. * @Date: 2019-08-16 21:32
  5. */
  6. @Slf4j
  7. @RestController
  8. @RequestMapping("/user")
  9. public class UserController {
  10. @PostMapping
  11. public User add(@RequestBody User user){
  12. log.info("增加用户;"+user.toString());
  13. user.setId(1);
  14. return user;
  15. }
  16. //正则匹配 id只能为数字
  17. @DeleteMapping("/{id:\\d+}")
  18. public void delete(@PathVariable long id){
  19. log.info("删除用户id:"+id);
  20. }
  21. @PutMapping("/{id:\\d+}")
  22. public User update(@RequestBody User user){
  23. log.info("修改用户:"+user);
  24. return user;
  25. }
  26. @GetMapping("/{id:\\d+}")
  27. public User get(@PathVariable long id){
  28. User user = new User();
  29. user.setId(1);
  30. user.setUsername("hjh");
  31. user.setPassword("123");
  32. return user;
  33. }
  34. @GetMapping
  35. @JsonView(View.Summary.class)
  36. public List<User> list(User user){
  37. log.info("查询用户名:" + user);
  38. ArrayList<User> users = new ArrayList<>();
  39. users.add(new User());
  40. users.add(new User());
  41. users.add(new User());
  42. return users;
  43. }
  44. }

测试用例

UserControllerTest.java

  1. // 引入静态对象。简化
  2. import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
  3. import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
  4. /**
  5. * @Description:
  6. * @Author: HJH
  7. * @Date: 2019-08-16 21:38
  8. */
  9. @Slf4j
  10. @RunWith(SpringRunner.class)
  11. @SpringBootTest
  12. public class UserControllerTest {
  13. @Autowired
  14. private WebApplicationContext webApplicationContext;
  15. private MockMvc mockMvc;
  16. @Before
  17. public void before(){
  18. mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
  19. }
  20. @Test
  21. public void whenAddSuccess() throws Exception {
  22. String content = "{\"username\":\"hjh\",\"password\":null,\"createDate\":"+new Date().getTime()+"}";
  23. String result = mockMvc.perform(post("/user")
  24. .contentType(MediaType.APPLICATION_JSON_UTF8)
  25. .content(content)) //请求
  26. .andExpect(status().isOk()) //断言响应结果
  27. .andExpect(jsonPath("$.id").value(1))
  28. .andReturn().getResponse().getContentAsString();
  29. log.info("增加结果;"+result);
  30. }
  31. @Test
  32. public void whenUpdateSuccess() throws Exception {
  33. String content = "{\"username\":\"hjh\",\"password\":null,\"createDate\":"+new Date().getTime()+"}";
  34. String result = mockMvc.perform(put("/user/1")
  35. .contentType(MediaType.APPLICATION_JSON_UTF8)
  36. .content(content)) //请求
  37. .andExpect(status().isOk()) //断言响应结果
  38. .andReturn().getResponse().getContentAsString();
  39. log.info("修改结果;"+result);
  40. }
  41. @Test
  42. public void whenDeleteSuccess() throws Exception {
  43. String result = mockMvc.perform(delete("/user/1")
  44. .contentType(MediaType.APPLICATION_JSON_UTF8))
  45. .andExpect(status().isOk()) //断言响应结果
  46. .andReturn().getResponse().getContentAsString();
  47. }
  48. @Test
  49. public void whenGetSuccess() throws Exception {
  50. String result = mockMvc.perform(get("/user/1")
  51. .contentType(MediaType.APPLICATION_JSON_UTF8))
  52. .andExpect(status().isOk()) //响应结果
  53. .andReturn().getResponse().getContentAsString();
  54. log.info("获得结果;"+result);
  55. }
  56. @Test
  57. public void whenListSuccess() throws Exception {
  58. String result = mockMvc.perform(get("/user").param("username","hjh")
  59. .contentType(MediaType.APPLICATION_JSON_UTF8))
  60. .andExpect(status().isOk()) //断言响应结果
  61. .andReturn().getResponse().getContentAsString();
  62. log.info("查询结果;"+result);
  63. }
  64. }

Jackson @JsonVIew

@JsonView可以过滤序列化对象的字段属性,使有选择的序列化对象

可以将View类理解为一组标识,Summary只是其中一种标识,其中DetailSummary继承了Summary

当使用@JsonView序列化User对象的时候,就只会序列化选择的属性,可以隐藏一些不想序列化的字段属性。

简单的说就是可以控制控制器层某个方法输出对象的属性。例如在查看用户详细信息的时候可以看到用户的所有信息。当查询所有用户时就不显示密码。

五、数据验证

数据验证:对前台传输的数据进行验证。

  1. 在entity定义对应成员变量的验证

    @Past:过去的时间

    @NotBlank 不能为空

  1. 需要验证的方法中添加@Valid注解

    再通过BindingResult捕获错误

1. 常见的验证

2. 自定义消息

通过重写message

3. 自定义校检注解

定义注解MyConstraint

在注解里面的注解称为元注解,例如Target...

Target 作用目标:作用在METHOD(方法)和FIELD(字段)

Retention 保留位置:RetentionPolicy.RUNTIME : 注解保留在程序运行期间,此时可以通过反射获得定义在某个类上的所有注解。

MyConstraint.java

  1. @Target({ElementType.METHOD,ElementType.FIELD})
  2. @Retention(RetentionPolicy.RUNTIME)
  3. @Constraint(validatedBy = MyConstraintValidate.class)
  4. public @interface MyConstraint {
  5. String message() default "测试验证";
  6. Class<?>[] groups() default { };
  7. Class<? extends Payload>[] payload() default { };
  8. }

注解实现类

MyConstraintValidate.java

  1. @Slf4j
  2. public class MyConstraintValidate implements ConstraintValidator<MyConstraint, Object> {
  3. @Override
  4. public void initialize(MyConstraint constraintAnnotation) {
  5. //初始化
  6. }
  7. @Override
  8. public boolean isValid(Object value, ConstraintValidatorContext context) {
  9. log.info("MyConstraintValidate: ",value);
  10. return false;
  11. }
  12. }

通过isValid 判断值是否通过验证

六、异常处理

1. springboot原生异常

覆盖默认的处理方式

  1. 自定义一个bean,实现ErrorController接口,那么默认的错误处理机制将不再生效。
  2. 自定义一个bean,继承BasicErrorController类,使用一部分现成的功能,自己也可以添加新的public方法,使用@RequestMapping及其produces属性指定新的地址映射。
  3. 自定义一个ErrorAttribute类型的bean,那么还是默认的两种响应方式,只不过改变了内容项而已。
  4. 继承AbstractErrorController

BasicErrorController.class

SpringBoot在页面 发生异常的时候会自动把请求转到/error,SpringBoot内置了一个BasicErrorController对异常进行统一的处理.



浏览器404



应用程序404(postMan)



分析



修改浏览器响应的404

在templates添加error/404

404.html

  1. <!DOCTYPE html>
  2. <html xmlns:th="http://www.thymeleaf.org">
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>404</title>
  6. </head>
  7. <body>
  8. <h1>访问的页面不存在</h1>
  9. <p th:text="'状态码:'+${status}"></p>
  10. <p th:text="'错误:'+${error}"></p>
  11. <p th:text="'错误信息:'+${message}"></p>
  12. <p th:text="'路径:'+${path}"></p>
  13. <p th:text="'时间戳:'+${timestamp}"></p>
  14. </body>
  15. </html>

结果:

error/.. 如果需要模板渲染需要放在渲染路径。像thymeleaf,如果放在 classpath:static/errror/. 就不会被渲染,从而无法获得status等数据。

2. 自定义异常类和全局异常

  1. 自定义异常

    UserNotExistException.java
  1. public class UserNotExistException extends RuntimeException {
  2. private long id;
  3. public UserNotExistException(long id){
  4. super("the user is not exist...");
  5. this.id = id;
  6. }
  7. public long getId() {
  8. return id;
  9. }
  10. public void setId(long id) {
  11. this.id = id;
  12. }
  13. }

进行测试

  1. @GetMapping("/exceptionTest")
  2. public void test(){
  3. throw new UserNotExistException(1);
  4. }

结果测试:

添加Controller全局异常捕获

ControllrExceptionhandler.java

  1. /**
  2. * @Description:全局控制器异常处理器
  3. * @Author: HJH
  4. * @Date: 2019-08-17 15:43
  5. */
  6. @ControllerAdvice
  7. public class ControllerExceptionHandler {
  8. /**
  9. * @Description:处理UserNotExistException,返回的是json对象
  10. * @Author: HJH
  11. * @Date: 2019-08-17 16:46
  12. * @Param: [ex]
  13. * @Return: java.util.Map<java.lang.String,java.lang.Object>
  14. */
  15. @ExceptionHandler(UserNotExistException.class)
  16. @ResponseBody
  17. @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
  18. public Map<String,Object> handleUserNotExistException(UserNotExistException ex){
  19. Map<String,Object> result = new HashMap<>();
  20. result.put("id",ex.getId());
  21. result.put("message", ex.getMessage());
  22. return result;
  23. }
  24. /**
  25. * @Description:全局Exception处理。返回的是html页面
  26. * @Author: HJH
  27. * @Date: 2019-08-17 15:56
  28. * @Param: [req, e]
  29. * @Return: ModelAndView
  30. */
  31. @ExceptionHandler(value = Exception.class)
  32. @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
  33. public ModelAndView defaultErrorHandler(HttpServletRequest req, Exception ex) throws Exception {
  34. ModelAndView mav = new ModelAndView();
  35. mav.addObject("exception", ex);
  36. mav.addObject("url", req.getRequestURL());
  37. mav.setViewName("error/errorPage");
  38. return mav;
  39. }
  40. }

测试控制器

  1. @GetMapping("/exceptionTest")
  2. public void test(@RequestParam String ex){
  3. if("runtime".equals(ex)){
  4. throw new RuntimeException("this is a RuntimeException");
  5. }
  6. throw new UserNotExistException(1);
  7. }

测试结果

  1. /user/exceptionTest?ex=user

    使用浏览器和app(PostMan)



  2. /user/exceptionTest?ex=runtime

    使用浏览器和app(PostMan)

通过上面就可以对控制器层的异常进行捕获

弊端,只能返回页面或者json格式。在需要json格式异常时也只是返回页面

改进

  1. @ExceptionHandler(value = Exception.class)
  2. @ResponseBody
  3. @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
  4. public Object defaultErrorHandler(HttpServletRequest req, Exception ex) throws Exception {
  5. if ("application/json".equals(req.getHeader("Content-Type"))){
  6. Map<String, Object> result = new HashMap<>();
  7. result.put("message", ex.getMessage());
  8. return result;
  9. }else {
  10. ModelAndView mav = new ModelAndView();
  11. mav.addObject("exception", ex);
  12. mav.addObject("url", req.getRequestURL());
  13. mav.setViewName("error/errorPage");
  14. return mav;
  15. }
  16. }

测试结果:还是ok的。但没有真正的测试 ,当是ajax请求抛出错误是否能够判断。但思路应该是可行的

虽然加了@ResponseBody但是返回是ModelAndView时还是可以解析成页面

七、对API的拦截

1. 过滤器(Filter)

定义一个时间过滤器,实现对API访问进行计时

  1. @Slf4j
  2. @Component
  3. public class TimeFilter implements Filter {
  4. @Override
  5. public void init(FilterConfig filterConfig) throws ServletException {
  6. log.info("TimeFilter Init");
  7. }
  8. @Override
  9. public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
  10. log.info("time filter start");
  11. long begin = System.currentTimeMillis();
  12. filterChain.doFilter(servletRequest,servletResponse);
  13. log.info("the request spent :"+(System.currentTimeMillis()-begin));
  14. log.info("time filter finish");
  15. }
  16. @Override
  17. public void destroy() {
  18. log.info("TimeFilter Destroy");
  19. }
  20. }

使用@Component 就可以使过滤器生效

调用一个API,结果

自定义的拦截器可以利用注解实现注入使过滤器生效。但是其他第三方过滤器时该如何配置。

添加配置文件。将timeFilter的@Component去掉使用下面也可以实现添加过滤器。

WebConfig.java

  1. @Configuration
  2. public class WebConfig {
  3. @Bean
  4. public FilterRegistrationBean timeFilter(){
  5. FilterRegistrationBean registrationBean = new FilterRegistrationBean();
  6. TimeFilter timeFilter = new TimeFilter();
  7. registrationBean.setFilter(timeFilter);
  8. List<String> urls = new ArrayList<>();
  9. urls.add("/*"); //拦截url
  10. registrationBean.setUrlPatterns(urls);
  11. return registrationBean;
  12. }
  13. }

2. 拦截器(Interceptor)

在过滤器中。使用的Filter



是在包 javax.servlet中定义的。并不知道spring的那一套

使用拦截器实现运行时间。但可以获取执行函数

TimeInterceptor.java

  1. /**
  2. * @Description:
  3. * @Author: HJH
  4. * @Date: 2019-08-17 19:31
  5. */
  6. @Slf4j
  7. @Component
  8. public class TimeInterceptor implements HandlerInterceptor {
  9. /**
  10. * @Description:控制器方法调用之前
  11. * @Author: HJH
  12. * @Date: 2019-08-17 19:35
  13. * @Param: [request, response, handler]
  14. * @Return: boolean
  15. */
  16. @Override
  17. public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
  18. throws Exception {
  19. log.info("Interceptor perHandle");
  20. HandlerMethod handlerMethod = (HandlerMethod) handler;
  21. log.info("Interceptor Class Name: "+handlerMethod.getBean().getClass().getName());
  22. log.info("Interceptor Method Name: "+handlerMethod.getMethod().getName());
  23. request.setAttribute("startTime",System.currentTimeMillis());
  24. return true;
  25. }
  26. /**
  27. * @Description:控制器完成之后。但当控制器抛出错误时就不会进入该函数
  28. * @Author: HJH
  29. * @Date: 2019-08-17 19:36
  30. * @Param: [request, response, handler, modelAndView]
  31. * @Return: void
  32. */
  33. @Override
  34. public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
  35. @Nullable ModelAndView modelAndView) throws Exception {
  36. log.info("Interceptor postHandle");
  37. long start = (long) request.getAttribute("startTime");
  38. log.info("Time Interceptor Spent:"+(System.currentTimeMillis()-start));
  39. }
  40. /**
  41. * @Description:类似try-catch 的finally
  42. * @Author: HJH
  43. * @Date: 2019-08-17 19:37
  44. * @Param: [request, response, handler, ex]
  45. * @Return: void
  46. */
  47. @Override
  48. public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,
  49. @Nullable Exception ex) throws Exception {
  50. log.info("Interceptor afterCompletion");
  51. long start = (long) request.getAttribute("startTime");
  52. log.info("Time Interceptor Spent:"+(System.currentTimeMillis()-start));
  53. log.info("Exception is "+ex);
  54. }
  55. }

WebInterceptorConfig.java

  1. @Configuration
  2. public class WebInterceptorConfig implements WebMvcConfigurer {
  3. @Autowired
  4. private TimeInterceptor timeInterceptor;
  5. @Override
  6. public void addInterceptors(InterceptorRegistry registry) {
  7. registry.addInterceptor(timeInterceptor);
  8. }
  9. }

运行结果

可以看到 afterCompletion一定会执行的

3. 切片(Aspect)

spring aop

切片实现

要点:切入点 (通过注解)1. 在什么方法上起作用。2. 在什么时候起作用。

要点:增强(通过方法):起作用时执行的业务逻辑

TimeAspect.java

  1. @Slf4j
  2. @Aspect
  3. @Component
  4. public class TimeAspect {
  5. @Around("execution(* com.hjh.myspringboot.myspringboot.web.controller.UserController.*(..))")
  6. public Object handleControllerMethod(ProceedingJoinPoint pjp) throws Throwable {
  7. log.info("Time Aspect Start");
  8. //获取传输args
  9. Object[] args = pjp.getArgs();
  10. for (Object arg:args){
  11. log.info("arg is:" +arg);
  12. }
  13. long start = System.currentTimeMillis();
  14. Object o = pjp.proceed();
  15. log.info("Time Aspect spent:"+(new Date().getTime()-start));
  16. log.info("Time Aspect end");
  17. return o;
  18. }
  19. }

结果

总结

拦截顺序



七、文件上传下载

比较简单的实现和单元测试

增加单元测试

  1. @Test
  2. public void whenUploadSuccess() throws Exception {
  3. String result = mockMvc.perform(multipart("/file")
  4. .file(new MockMultipartFile("file","test.txt","multipart/form-data","hello".getBytes("utf-8"))))
  5. .andExpect(status().isOk())
  6. .andReturn().getResponse().getContentAsString();
  7. log.info("上传结果"+result);
  8. }

fileupload在springboot2.0x过时。

FileController

  1. @Slf4j
  2. @RestController
  3. @RequestMapping("/file")
  4. public class FileController {
  5. public static final String FOLDER = "D:/U";
  6. @PostMapping
  7. public Map upload(MultipartFile file) throws IOException {
  8. log.info("上传文件名"+file.getName());
  9. log.info("上传文件原始名"+file.getOriginalFilename());
  10. log.info("上传文件大小:"+file.getSize());
  11. File file1 = new File(FOLDER,System.currentTimeMillis()+".txt");
  12. file.transferTo(file1);
  13. Map<String, String> map = new HashMap<>();
  14. map.put("path",file1.getAbsolutePath());
  15. return map;
  16. }
  17. @GetMapping("/{id}")
  18. public void download(@PathVariable String id, HttpServletResponse response, HttpServletRequest request) throws IOException {
  19. try(FileInputStream inputStream = new FileInputStream(new File(FOLDER, id + ".txt"));
  20. OutputStream outputStream = response.getOutputStream()) {
  21. response.setContentType("application/x-download");
  22. response.addHeader("Content-Disposition","attachment;filename=test.txt");
  23. IOUtils.copy(inputStream,outputStream);
  24. outputStream.flush();
  25. }
  26. }
  27. }

八、异步处理

当同时访问一个需要处理10s的方法时

结果

通过输出:

第一个访问过来10s后第二个访问才被处理。也就是说在当前方法中。同一时间只能处理一个请求。只有当前请求处理完后才能处理下一个。

1、使用Runable异步

  1. @GetMapping("async2")
  2. public Callable<String> async2(){
  3. log.info("主线程开始");
  4. Callable<String> result = new Callable<String>() {
  5. @Override
  6. public String call() throws Exception {
  7. log.info("副线程开始");
  8. for (int i=0;i<10;i++){
  9. Thread.sleep(1000);
  10. }
  11. log.info("副线程返回");
  12. return "success";
  13. }
  14. };
  15. log.info("主线程结束");
  16. return result;
  17. }

在同一时间可以处理同一类请求,提高了服务器的吞吐量

场景比较单一,添加一个副线程。

2. 使用DeferredResult异步处理请求

使用消息队列解决请求丢失等问题,可以用于订单处理等,重要的,高可用,高并发的请求。

下面使用异步模拟处理订单

使用RibbitMq作为消息队列

添加maven依赖

  1. <dependency>
  2. <groupId>org.springframework.boot</groupId>
  3. <artifactId>spring-boot-starter-amqp</artifactId>
  4. </dependency>

添加mq配置

  1. spring.application.name=spring-boot
  2. spring.rabbitmq.host=127.0.0.1
  3. spring.rabbitmq.port=5672
  4. spring.rabbitmq.username=admin
  5. spring.rabbitmq.password=123456

添加springboot配置

使用Exchange类型的Direct。添加两条队列。一个用于接收下单,一个接收订单处理结果。

RabbitMQConfig.java

  1. Configuration
  2. public class RabbitMQConfig {
  3. @Bean
  4. public Queue queue1(){
  5. return new Queue("order");
  6. }
  7. @Bean
  8. public Queue queue2(){
  9. return new Queue("finish");
  10. }
  11. }

DeferredResultHolder.java

  1. @Component
  2. public class DeferredResultHolder {
  3. private Map<String , DeferredResult<String>> map = new HashMap<>();
  4. public Map<String, DeferredResult<String>> getMap() {
  5. return map;
  6. }
  7. public void setMap(Map<String, DeferredResult<String>> map) {
  8. this.map = map;
  9. }
  10. }
  1. 线程1:
  1. @Autowired
  2. private AmqpTemplate rabbitTemplate;
  3. @GetMapping("async4")
  4. public DeferredResult<String> async4() throws InterruptedException {
  5. log.info("主线程开始");
  6. String orderId = RandomStringUtils.randomNumeric(8);
  7. log.info("2.发送下单请求:"+orderId);
  8. rabbitTemplate.convertAndSend("order",orderId);
  9. DeferredResult<String> result = new DeferredResult();
  10. deferredResultHolder.getMap().put(orderId,result);
  11. return result;
  12. }
  1. 应用2:监听请求并处理
  1. @Slf4j
  2. @Component
  3. @RabbitListener(queues = "order")
  4. public class OrderReceiver {
  5. @Autowired
  6. private RabbitTemplate rabbitTemplate;
  7. @RabbitHandler
  8. public void process(String re) {
  9. new Thread(()->{
  10. log.info("3.监听到下单请求:"+re);
  11. //处理
  12. try {
  13. Thread.sleep(5000);
  14. } catch (InterruptedException e) {
  15. e.printStackTrace();
  16. }
  17. //处理完成
  18. log.info("4.订单处理完成:"+re);
  19. rabbitTemplate.convertAndSend("finish",re);
  20. }).start();
  21. }
  1. 线程2:接收完成订单,并响应请求。
  1. @Slf4j
  2. @Component
  3. @RabbitListener(queues = "finish")
  4. public class OrderFinishThread {
  5. @Autowired
  6. private DeferredResultHolder deferredResultHolder;
  7. @RabbitHandler
  8. public void process(String re) {
  9. new Thread(()->{
  10. log.info("5.监听到完成的订单2:"+re);
  11. deferredResultHolder.getMap().get(re).setResult("订单成功");
  12. }).start();
  13. }
  14. }

使用jmeter对Callable和DeferredResult异步进行简单的测试

并发100个请求

Callable

DeferredResult

-- 1 -- springboot的更多相关文章

  1. 解决 Springboot Unable to build Hibernate SessionFactory @Column命名不起作用

    问题: Springboot启动报错: Caused by: org.springframework.beans.factory.BeanCreationException: Error creati ...

  2. 【微框架】Maven +SpringBoot 集成 阿里大鱼 短信接口详解与Demo

    Maven+springboot+阿里大于短信验证服务 纠结点:Maven库没有sdk,需要解决 Maven打包找不到相关类,需要解决 ps:最近好久没有写点东西了,项目太紧,今天来一篇 一.本文简介 ...

  3. Springboot搭建web项目

    最近因为项目需要接触了springboot,然后被其快速零配置的特点惊呆了.关于springboot相关的介绍我就不赘述了,大家自行百度google. 一.pom配置 首先,建立一个maven项目,修 ...

  4. Java——搭建自己的RESTful API服务器(SpringBoot、Groovy)

    这又是一篇JavaWeb相关的博客,内容涉及: SpringBoot:微框架,提供快速构建服务的功能 SpringMVC:Struts的替代者 MyBatis:数据库操作库 Groovy:能与Java ...

  5. 解决 SpringBoot 没有主清单属性

    问题:SpringBoot打包成jar后运行提示没有主清单属性 解决:补全maven中的bulid信息 <plugin> <groupId>org.springframewor ...

  6. SpringBoot中yaml配置对象

    转载请在页首注明作者与出处 一:前言 YAML可以代替传统的xx.properties文件,但是它支持声明map,数组,list,字符串,boolean值,数值,NULL,日期,基本满足开发过程中的所 ...

  7. springboot 学习资源推荐

    springboot 是什么?对于构建生产就绪的Spring应用程序有一个看法. Spring Boot优先于配置的惯例,旨在让您尽快启动和运行.(这是springboot的官方介绍) 我们为什么要学 ...

  8. Springboot框架

    本片文章主要分享一下,Springboot框架为什么那么受欢迎以及如何搭建一个Springboot框架. 我们先了解一下Springboot是个什么东西,它是干什么用的.我是刚开始接触,查了很多资料, ...

  9. 如何在SpringBoot中使用JSP ?但强烈不推荐,果断改Themeleaf吧

    做WEB项目,一定都用过JSP这个大牌.Spring MVC里面也可以很方便的将JSP与一个View关联起来,使用还是非常方便的.当你从一个传统的Spring MVC项目转入一个Spring Boot ...

  10. 5分钟创建一个SpringBoot + Themeleaf的HelloWord应用

    第一步:用IDE创建一个普通maven工程,我用的eclipse. 第二步:修改pom.xml,加入支持SpringBoot和Themeleaf的依赖,文件内容如下: <?xml version ...

随机推荐

  1. c语言中sprintf()函数中的%使用

    #include <stdio.h> #include <string.h> int main() { ] = {}; ] = {}; ] = {}; /*打印2个%*/ st ...

  2. pause函数

    pause函数 调用该函数可以造成进程主动挂起,等待信号唤醒.调用该系统调用的进程将处于阻塞状态(主动放弃cpu) 直到有信号递达将其唤醒. int pause(void);     返回值:-1 并 ...

  3. Codeforces 1276D/1259G Tree Elimination (树形DP)

    题目链接 http://codeforces.com/contest/1276/problem/D 题解 我什么DP都不会做,吃枣药丸-- 设\(f_{u,j}\)表示\(u\)子树内,\(j=0\) ...

  4. CF1197B

    CF1197B 题意: 出n个柱子,每个柱子一个圆盘,其半径各不相同,每次只能将柱子上只有一个圆盘的移到相邻位置,问能否全部移到一个柱子上. 解法: 思路题. 如果所有盘子都能移动到同一个柱子上,那么 ...

  5. VSCode-VUE模板文件

    编辑自己的代码片段 ctrl+shift+p 输入snippet 选择 'Preferences: Configure User Snippets' 输入vue 选择vue.json,会打开vue.j ...

  6. 手游折扣app票选结果公布哪个好哪个靠谱一目了然

    2018年,是中国改革开放40年,也是中国互联网20年.“互联网推动了精神文明向更高水平的迈进,实现人的价值第一,创造美好生活,从生产高于生活.艺术高于成活,转向发现与实现生活本身美好,让想象成真.如 ...

  7. LC 898. Bitwise ORs of Subarrays

    We have an array A of non-negative integers. For every (contiguous) subarray B = [A[i], A[i+1], ..., ...

  8. LevelListDrawable

    用来管理一组Drawable的,我们可以为里面的drawable设置不同的level, 当他们绘制的时候,会根据level属性值获取对应的drawable绘制到画布上,根节点 为:<level- ...

  9. springboot和hadoop2.7.7集成开发

    1.本人在腾讯云安装hadoop2.7.7,详细安装请看以前的博客 2.pom.xml文件 <?xml version="1.0" encoding="UTF-8& ...

  10. dubbo 支持的9种协议

    转: dubbo 支持的9种协议 文章目录 一.9种协议        1.dubbo 协议 (默认)        2.rmi 协议        3.hessian 协议        4.htt ...