1、异常处理

1.1 全局异常

  1. @Slf4j
  2. @ControllerAdvice
  3. public class GlobalExceptionHandler {
  4. @ExceptionHandler(value = Exception.class)
  5. @ResponseBody
  6. public String XXXExceptionHandler(Exception e) {
  7. log.info(e.toString());
  8. return "123";
  9. }
  10. }

1.2 自定义异常

  1. // 异常直接抛给 tomcat, 如果后续处理异常,则显示错误页面
  2. throw new CustomExceptionHandler("错误");
  3. @NoArgsConstructor
  4. @ResponseStatus(value = HttpStatus.FORBIDDEN,reason = "拒绝访问") // 403
  5. public class CustomExceptionHandler extends RuntimeException{
  6. public CustomExceptionHandler(String message){
  7. super(message);
  8. }
  9. }
  1. @Order(value = Ordered.HIGHEST_PRECEDENCE) // 最高级别,处理所有异常(其他任何异常配置失效)
  2. @Component
  3. public class CustomerHandlerException implements HandlerExceptionResolver {
  4. @Override
  5. public ModelAndView resolveException(HttpServletRequest httpServletRequest,
  6. HttpServletResponse httpServletResponse,
  7. Object handler, Exception e) {
  8. try {
  9. httpServletResponse.sendError(555,"wuwuwu异常"); // 抛给 tomcat
  10. } catch (IOException ioException) {
  11. ioException.printStackTrace();
  12. }
  13. return new ModelAndView();
  14. }
  15. }

2、 异步请求

启动类: @EnableAsync

  1. @GetMapping("/callable")
  2. private Callable<Object> getCallable(){
  3. log.info(Thread.currentThread().getName() + " 进入 getCallable 方法");
  4. Callable<Object> callable = new Callable<Object>() {
  5. @Override
  6. public Object call() throws Exception {
  7. log.info(Thread.currentThread().getName() + " 进入 call 方法");
  8. Object say = sayHello();
  9. log.info(Thread.currentThread().getName() + " 从 Service 方法返回");
  10. return say;
  11. }
  12. };
  13. log.info(Thread.currentThread().getName() + " 从 getCallable 方法返回");
  14. return callable;
  15. }
  16. @GetMapping("/webAsyncTask")
  17. private WebAsyncTask<Object> getWebAsyncTask(){
  18. log.info(Thread.currentThread().getName() + " 进入 getWebAsyncTask 方法");
  19. // 3s 没返回,则认为超时
  20. WebAsyncTask<Object> webAsyncTask = new WebAsyncTask<>(3000, new Callable<Object>() {
  21. @Override
  22. public Object call() throws Exception {
  23. log.info(Thread.currentThread().getName() + " 进入call方法");
  24. Object say = sayHello();
  25. log.info(Thread.currentThread().getName() + " 从 Service 方法返回");
  26. return say;
  27. }
  28. });
  29. log.info(Thread.currentThread().getName() + " 从 Service 方法返回");
  30. webAsyncTask.onCompletion(new Runnable() {
  31. @Override
  32. public void run() {
  33. log.info(Thread.currentThread().getName() + " 执行完毕");
  34. }
  35. });
  36. webAsyncTask.onTimeout(new Callable<Object>() {
  37. @Override
  38. public Object call() throws Exception {
  39. log.info(Thread.currentThread().getName() + " onTimeout");
  40. // 超时的时候,直接抛异常,让外层统一处理超时异常
  41. throw new TimeoutException("调用超时");
  42. }
  43. });
  44. return webAsyncTask;
  45. }
  46. @GetMapping("/deferredResult")
  47. private DeferredResult<Object> getDeferredResult(){
  48. log.info(Thread.currentThread().getName() + " 进入 getDeferredResult 方法");
  49. DeferredResult<Object> deferredResult = new DeferredResult<>(3000L);
  50. // 调用长时间执行任务
  51. execute(deferredResult);
  52. // 当长时间任务中使用deferred.setResult("world");这个方法时,会从长时间任务中返回,继续controller里面的流程
  53. log.info(Thread.currentThread().getName() + "从 execute 方法返回");
  54. // 超时的回调方法
  55. deferredResult.onTimeout(new Runnable(){
  56. @Override
  57. public void run() {
  58. log.info(Thread.currentThread().getName() + " onTimeout");
  59. // 返回超时信息
  60. deferredResult.setErrorResult("time out!");
  61. }
  62. });
  63. // 处理完成的回调方法,无论是超时还是处理成功,都会进入这个回调方法
  64. deferredResult.onCompletion(new Runnable(){
  65. @Override
  66. public void run() {
  67. log.info(Thread.currentThread().getName() + " onCompletion");
  68. }
  69. });
  70. return deferredResult;
  71. }
  72. @Async
  73. public void execute(DeferredResult<Object> deferredResult) {
  74. log.info(Thread.currentThread().getName() + "进入 execute 方法");
  75. try {
  76. // 模拟长时间任务调用,睡眠 2s
  77. TimeUnit.SECONDS.sleep(5);
  78. // 2s 后给 Deferred 发送成功消息,告诉 Deferred ,我这边已经处理完了,可以返回给客户端了
  79. deferredResult.setResult("world");
  80. } catch (InterruptedException e) {
  81. e.printStackTrace();
  82. }
  83. }
  84. private Object sayHello() {
  85. try {
  86. TimeUnit.SECONDS.sleep(4);
  87. } catch (InterruptedException e) {
  88. e.printStackTrace();
  89. }
  90. Map<String,Object> map = new HashMap<>();
  91. map.put("a",1);
  92. map.put("b",2);
  93. return map;
  94. }

3、发送邮件

Pom

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

yaml

  1. spring:
  2. # 126邮箱SMTP服务器地址:smtp.126.com,端口号:465或者994
  3. # 2163邮箱SMTP服务器地址:smtp.163.com,端口号:465或者994
  4. # yeah邮箱SMTP服务器地址:smtp.yeah.net,端口号:465或者994
  5. # qq邮箱SMTP服务器地址:smtp.qq.com,端口号465或587*
  6. mail:
  7. default-encoding: UTF-8
  8. host: smtp.qq.com
  9. username: 953598751@qq.com
  10. password: xxx
  11. port: 587
  12. properties:
  13. mail:
  14. stmp:
  15. socketFactoryClass: javax.net.ssl.SSLSocketFactory
  16. #表示开启 DEBUG 模式,这样,邮件发送过程的日志会在控制台打印出来,方便排查错误
  17. debug: true

Test

  1. @Autowired
  2. private JavaMailSender javaMailSender;
  3. private final ClassPathResource classPathResource = new ClassPathResource("static/test.jpg");
  4. @Test
  5. public void sendString(){
  6. // 构建一个邮件对象
  7. SimpleMailMessage message = new SimpleMailMessage();
  8. // 设置邮件主题
  9. message.setSubject("这是一封测试邮件");
  10. // 设置邮件发送者,这个跟 application.yml中设置的要一致
  11. message.setFrom("953598751@qq.com");
  12. // 设置邮件接收者,可以有多个接收者,中间用逗号隔开,以下类似
  13. message.setTo("1349587649@qq.com");
  14. // 设置邮件抄送人,可以有多个抄送人。
  15. // 将邮件同时送给收信人以外的人,用户所写的邮件抄送一份给别人,对方可以看见该用户的E-mail发送给了谁。
  16. // message.setCc("idu8972@dingtalk.com");
  17. // 设置隐秘抄送人,可以有多个。
  18. // 将邮件同时送给收信人以外的人,用户所写的邮件抄送一份给别人,但是对方不能查看到这封邮件同时还发送给了哪些人。
  19. // message.setBcc("idu8972@dingtalk.com");
  20. // 设置邮件发送日期
  21. message.setSentDate(new Date());
  22. // 设置邮件的正文
  23. message.setText("这是测试邮件的正文");
  24. // 发送邮件
  25. javaMailSender.send(message);
  26. }
  27. /**
  28. * 发送带附件的邮件
  29. */
  30. @Test
  31. public void sendAttachFileMail() throws MessagingException, IOException {
  32. MimeMessage mimeMessage = javaMailSender.createMimeMessage();
  33. // true表示构建一个可以带附件的邮件对象
  34. MimeMessageHelper helper = new MimeMessageHelper(mimeMessage,true);
  35. helper.setSubject("这是一封测试邮件");
  36. helper.setFrom("953598751@qq.com");
  37. helper.setTo("1349587649@qq.com");
  38. helper.setSentDate(new Date());
  39. helper.setText("这是测试邮件的正文");
  40. // 第一个参数是自定义的名称,后缀需要加上,第二个参数是文件的位置
  41. helper.addAttachment("报表.pdf",classPathResource.getFile());
  42. javaMailSender.send(mimeMessage);
  43. }
  44. /**
  45. * 正文中带图片的邮件
  46. */
  47. @Test
  48. public void sendImgResMail() throws MessagingException, IOException {
  49. MimeMessage mimeMessage = javaMailSender.createMimeMessage();
  50. MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true);
  51. helper.setSubject("这是一封测试邮件");
  52. helper.setFrom("953598751@qq.com");
  53. helper.setTo("1349587649@qq.com");
  54. helper.setSentDate(new Date());
  55. // src='cid:p01' 占位符写法 ,第二个参数true表示这是一个html文本
  56. helper.setText("<H1>hello 大家好,这是一封测试邮件,这封邮件包含两种图片,分别如下</H1>" +
  57. "<H3>第一张图片:</H3><img src='cid:img01'/>",true);
  58. // 第一个参数指的是 html 中占位符的名字,第二个参数就是文件的位置
  59. helper.addInline("img01",new FileSystemResource(classPathResource.getFile()));
  60. helper.addAttachment("test.jpg",classPathResource.getFile());
  61. javaMailSender.send(mimeMessage);
  62. }

4、跨域

  1. @Configuration
  2. @EnableWebMvc // 使用 swagger 不能有该注解
  3. public class WebConfig extends WebMvcConfigurerAdapter {
  4. @Override
  5. public void addCorsMappings(CorsRegistry registry) {
  6. registry.addMapping("/**")
  7. .allowedOrigins("*")
  8. .allowedMethods("GET","POST")
  9. .allowCredentials(false).maxAge(3600);
  10. }
  11. }
  12. @Configuration
  13. class CorsConfig implements WebMvcConfigurer {
  14. @Override
  15. public void addCorsMappings(CorsRegistry registry) {
  16. //添加映射路径
  17. registry.addMapping("/**")
  18. //是否发送Cookie
  19. .allowCredentials(true)
  20. //设置放行哪些原始域 SpringBoot2.4.4下低版本使用.allowedOrigins("*")
  21. .allowedOriginPatterns("*")
  22. //放行哪些请求方式
  23. .allowedMethods("GET", "POST")
  24. //.allowedMethods("*") //或者放行全部
  25. //放行哪些原始请求头部信息
  26. .allowedHeaders("*")
  27. //暴露哪些原始请求头部信息
  28. .exposedHeaders("*");
  29. }
  30. /** swagger 访问 **/
  31. @Override
  32. public void addResourceHandlers(ResourceHandlerRegistry registry) {
  33. registry.addResourceHandler("/static/**").addResourceLocations("classpath:/static/");
  34. registry.addResourceHandler("/swagger-ui.html").addResourceLocations("classpath:/META-INF/resources/");
  35. registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
  36. WebMvcConfigurer.super.addResourceHandlers(registry);
  37. }
  38. }
  39. public class MyCorsFilter extends CorsFilter {
  40. public MyCorsFilter() {
  41. super(configurationSource());
  42. }
  43. private static UrlBasedCorsConfigurationSource configurationSource() {
  44. CorsConfiguration config = new CorsConfiguration();
  45. config.setAllowCredentials(true);
  46. config.addAllowedOrigin("http://domain1.com");
  47. config.addAllowedHeader("*");
  48. config.addAllowedMethod("GET","POST");
  49. UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
  50. source.registerCorsConfiguration("/**", config);
  51. return source;
  52. }
  53. }
  54. // 注解形式
  55. @CrossOrigin(origins = "*",maxAge = 3600)

5、获取文件

  1. // 获取 classpath 路径下的文件
  2. ClassPathResource classPathResource = new ClassPathResource("static/index.html");
  3. File file = classPathResource.getFile();
  4. System.out.println(file.getName());
  5. // 获取项目路径下的文件
  6. FileSystemResource fileSystemResource = new FileSystemResource("pom.xml");
  7. File file1 = fileSystemResource.getFile();
  8. FileReader fileReader = new FileReader(file1);
  9. char[] chars = new char[100];
  10. if(fileReader.read(chars)!=-1){
  11. System.out.println(String.valueOf(chars));
  12. }

6、远程调试

  • 启动命令: java -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=8000 -jar web.jar

-agentlib:jdwp=transport=dt_socket,server=y,address=8000

以 Socket 方式监听 8000 端口,程序启动阻塞(suspend 的默认值为y)直到被连接。

-agentlib:jdwp=transport=dt_socket,server=y,address=localhost:8000,timeout=5000

以 Socket 方式监听 8000 端口,当程序启动后5秒无调试者连接的话终止,程序启动阻塞(suspend的默认值为y)直到被连接。

-agentlib:jdwp=transport=dt_shmem,server=y,suspend=n

选择可用的共享内存连接地址并使用 stdout 打印,程序启动不阻塞。

-agentlib:jdwp=transport=dt_socket,address=myhost:8000

以 socket 方式连接到 myhost:8000上的调试程序,在连接成功前启动阻塞。

-agentlib:jdwp=transport=dt_socket,server=y,address=8000,onthrow=java.io.IOException,launch=/usr/local/bin/debugstub

以 Socket 方式监听 8000 端口,程序启动阻塞(suspend的默认值为y)直到被连接。当抛出 IOException 时中断调试,转而执行 usr/local/bin/debugstub程序。

  • 参数说明 | transport | 指定运行的被调试应用和调试者之间的通信协议,它由几个可选值: | | —- | —- | | | dt_socket
    :主要的方式,采用 socket 方式连接 | | server | 当前应用作为调试服务端还是客户端,默认为 n

    如果你想将当前应用作为被调试应用,设置该值为 y
    ;如果你想将当前应用作为客户端,作为调试的发起者,设置该值为 n | | suspend | 当前应用启动后,是否阻塞应用直到被连接,默认值为 y

    在大部分的应用场景,这个值为 n
    ,即不需要应用阻塞等待连接。
    一个可能为 y
    的应用场景是,你的程序在启动时出现了一个故障,为了调试,必须等到调试方连接上来后程序再启动 | | address | 暴露的调试连接端口,默认值为 8000 | | onthrow | 当程序抛出设定异常时,中断调试 | | onuncaught | 当程序抛出未捕获异常时,是否中断调试,默认值为 n | | launch | 当调试中断时,执行的程序。 | | timeout | 该参数限定为 java -agentlib:jdwp=…
    可用,单位为毫秒ms。
    当 suspend = y 时,该值表示等待连接的超时;当 suspend = n 时,该值表示连接后的使用超时。 |
  • IDEA 调试

Edit Configurations > 添加一个 Remote JVM Debug > 配置 Host 和 Port > 启动该配置

7、静态资源服务

  1. @Configuration
  2. class CorsConfig implements WebMvcConfigurer {
  3. @Override
  4. public void addResourceHandlers(ResourceHandlerRegistry registry) {
  5. registry.addResourceHandler("/img/**").addResourceLocations("file:D:/app_3d/fpt/");
  6. registry.addResourceHandler("/static/**").addResourceLocations("classpath:/static/");
  7. registry.addResourceHandler("/swagger-ui.html").addResourceLocations("classpath:/META-INF/resources/");
  8. registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
  9. WebMvcConfigurer.super.addResourceHandlers(registry);
  10. }
  11. }

8、Mybatis plus LocalTime

  1. @JsonFormat(shape=JsonFormat.Shape.STRING, pattern="yyyy-MM-dd HH:mm:ss")
  2. @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
  3. private LocalDateTime updateTime;
  4. @Bean(name = "mapperObject")
  5. public ObjectMapper getObjectMapper() {
  6. JavaTimeModule javaTimeModule = new JavaTimeModule();
  7. javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
  8. javaTimeModule.addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
  9. javaTimeModule.addSerializer(LocalTime.class, new LocalTimeSerializer(DateTimeFormatter.ofPattern("HH:mm:ss")));
  10. return new ObjectMapper().registerModule(javaTimeModule);
  11. }

9、AOP

  1. <!-- <dependency>-->
  2. <!-- <groupId>org.aspectj</groupId>-->
  3. <!-- <artifactId>aspectjweaver</artifactId>-->
  4. <!-- <version>1.8.13</version>-->
  5. <!-- </dependency>-->
  6. <dependency>
  7. <groupId>org.springframework.boot</groupId>
  8. <artifactId>spring-boot-starter-aop</artifactId>
  9. </dependency>
  1. @Component
  2. public class User {
  3. public void add(String str){
  4. System.out.println("add..........." + str);
  5. }
  6. }
  7. @Component
  8. @Aspect
  9. @Order(1) // 设置增强顺序
  10. public class UserProxy1 {
  11. /**
  12. * 前置通知
  13. */
  14. @Before(value = "execution(* com.cug.aop.User.add(..))")
  15. public void before(){
  16. System.out.println("UserProxy1 before...........");
  17. }
  18. }
  19. /**
  20. * @Description 创建不同的方法,代表不同的通知类型
  21. */
  22. @Aspect
  23. @Component
  24. @Order(2) // 设置增强顺序
  25. public class UserProxy {
  26. /**
  27. * 相同切入点抽取
  28. */
  29. @Pointcut(value = "execution(* com.cug.aop.User.add(..))")
  30. public void pointDemo(){}
  31. /**
  32. * 前置通知
  33. */
  34. @Before(value = "pointDemo()")
  35. public void before(JoinPoint joinPoint){
  36. System.out.println(Arrays.toString(joinPoint.getArgs()));
  37. // 2
  38. System.out.println("before...........");
  39. }
  40. @After(value = "execution(* com.cug.aop.User.add(..))")
  41. public void after(){
  42. // 4
  43. System.out.println("after..........");
  44. }
  45. @AfterReturning(value = "execution(* com.cug.aop.User.add(..))")
  46. public void afterReturning(){
  47. // 3 增强的方法之后
  48. System.out.println("AfterReturning..........");
  49. }
  50. @AfterThrowing(value = "execution(* com.cug.aop.User.add(..))")
  51. public void afterThrowing(){
  52. System.out.println("AfterThrowing..........");
  53. }
  54. @Around(value = "execution(* com.cug.aop.User.add(..))")
  55. public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
  56. // 1
  57. System.out.println("Around before..........");
  58. // 被增强的方法执行
  59. proceedingJoinPoint.proceed();
  60. // 5
  61. System.out.println("Around after..........");
  62. return "around";
  63. }
  64. }
  65. @SpringBootTest
  66. class ApoApplicationTests {
  67. @Autowired
  68. private User user;
  69. @Test
  70. void contextLoads() {
  71. user.add("111");
  72. }
  73. }