您的位置:首页 > 娱乐 > 八卦 > Spring开发实践(一)

Spring开发实践(一)

2024/12/23 1:40:03 来源:https://blog.csdn.net/weixin_43349479/article/details/140344122  浏览:    关键词:Spring开发实践(一)

目录

    • `@ExceptionHandler`注解的作用和用法示例
      • 作用
      • 用法示例
        • 1. 在控制器类中使用 `@ExceptionHandler`
        • 2. 使用 `@ControllerAdvice` 进行全局异常处理
      • 总结
    • `RequestBodyAdvice`和`Spring MVC`的拦截器有什么区别?
      • 1. 作用范围
      • 2. 方法和生命周期
      • 3. 使用示例
        • RequestBodyAdvice 示例
        • HandlerInterceptor 示例
      • 4. 配置方式
      • 总结
    • `serialVersionUID`
    • 在`Spring`加载过程中执行某些方法
      • 1. 使用 `CommandLineRunner` 或 `ApplicationRunner`
      • 2. 使用 `@EventListener` 注解监听 `ApplicationReadyEvent`
      • 3. 在 `main` 方法中执行代码
      • 4. 使用 `ApplicationContext` 的 `refresh` 方法
      • `ApplicationRunner` 和 `CommandLineRunner` 的执行时机
      • 示例代码
        • 使用 `ApplicationRunner`
        • 使用 `CommandLineRunner`
      • 总结
      • `@PostConstruct` 的执行时机
      • 影响Spring加载时间的因素
      • 示例代码
      • 优化建议
      • 总结

@ExceptionHandler注解的作用和用法示例

@ExceptionHandler 注解是 Spring 框架中用于处理异常的注解。它可以用来定义一个方法,当控制器中的某个方法抛出指定类型的异常时,这个方法会被调用,从而实现对异常的集中处理。

作用

  • 集中处理异常:通过在控制器中定义异常处理方法,可以将异常处理逻辑集中在一个地方,避免在每个控制器方法中重复编写异常处理代码。
  • 提高代码可读性:将异常处理逻辑从业务逻辑中分离出来,使代码更加清晰和易于维护。
  • 灵活性:可以根据不同的异常类型定义不同的处理方法,从而实现灵活的异常处理策略。

用法示例

1. 在控制器类中使用 @ExceptionHandler
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.RestController;@RestController
public class MyController {@GetMapping("/example")public String example() {if (true) { // 模拟一个异常throw new RuntimeException("An error occurred");}return "Hello, World!";}// 定义一个异常处理方法@ExceptionHandler(RuntimeException.class)@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)public String handleRuntimeException(RuntimeException ex) {return "An error occurred: " + ex.getMessage();}
}

在这个示例中,当 example 方法抛出 RuntimeException 时,handleRuntimeException 方法会被调用,并返回一个错误信息。

2. 使用 @ControllerAdvice 进行全局异常处理

@ControllerAdvice 是一个更高级的注解,它可以用来定义全局的异常处理逻辑,适用于所有控制器。

import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.http.HttpStatus;@ControllerAdvice
public class GlobalExceptionHandler {@ExceptionHandler(RuntimeException.class)@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)public String handleRuntimeException(RuntimeException ex) {return "Global error handler: " + ex.getMessage();}@ExceptionHandler(NullPointerException.class)@ResponseStatus(HttpStatus.BAD_REQUEST)public String handleNullPointerException(NullPointerException ex) {return "Null pointer exception: " + ex.getMessage();}
}

在这个示例中,GlobalExceptionHandler 类中的方法会处理所有控制器中抛出的 RuntimeExceptionNullPointerException

总结

  • @ExceptionHandler 注解用于定义异常处理方法,可以在控制器类中使用,也可以结合 @ControllerAdvice 注解进行全局异常处理。
  • 通过使用 @ExceptionHandler,可以将异常处理逻辑集中在一个地方,提高代码的可读性和可维护性。

RequestBodyAdviceSpring MVC的拦截器有什么区别?

RequestBodyAdvice 和 Spring MVC 的拦截器(HandlerInterceptor)都是用于在请求处理过程中进行自定义操作的机制,但它们的作用范围和使用场景有所不同。以下是它们之间的主要区别:

1. 作用范围

  • RequestBodyAdvice

    • 主要用于处理带有 @RequestBody 注解的方法参数。
    • 作用范围仅限于请求体的读取和转换过程。
    • 适用于在请求体反序列化之前和之后进行自定义处理。
  • HandlerInterceptor

    • 作用范围更广,可以在请求处理的多个阶段进行拦截。
    • 适用于整个请求处理流程,包括请求到达控制器之前、控制器方法执行之后、视图渲染之前等多个阶段。
    • 可以用于各种场景,如认证、授权、日志记录、性能监控等。

2. 方法和生命周期

  • RequestBodyAdvice

    • 提供的方法包括 supportsbeforeBodyReadafterBodyReadhandleEmptyBody
    • 这些方法在请求体读取和转换的特定阶段被调用。
  • HandlerInterceptor

    • 提供的方法包括 preHandlepostHandleafterCompletion
    • preHandle:在控制器方法执行之前调用,可以用于请求预处理。
    • postHandle:在控制器方法执行之后、视图渲染之前调用,可以用于修改模型数据或视图。
    • afterCompletion:在请求处理完成之后调用,可以用于资源清理或记录日志。

3. 使用示例

RequestBodyAdvice 示例
import org.springframework.http.HttpInputMessage;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.RequestBodyAdvice;import java.io.IOException;
import java.lang.reflect.Type;@ControllerAdvice
public class CustomRequestBodyAdvice implements RequestBodyAdvice {@Overridepublic boolean supports(Class<? extends HttpMessageConverter<?>> converterType, Type targetType, Class<? extends Annotation> contextClass) {return true;}@Overridepublic HttpInputMessage beforeBodyRead(HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) throws IOException {System.out.println("Before Body Read");return inputMessage;}@Overridepublic Object afterBodyRead(Object body, HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {System.out.println("After Body Read: " + body);return body;}@Overridepublic Object handleEmptyBody(Object body, HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {System.out.println("Handle Empty Body");return body;}
}
HandlerInterceptor 示例
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;@Component
public class CustomInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {System.out.println("Pre Handle");return true; // 返回 true 继续处理请求,返回 false 中断请求}@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {System.out.println("Post Handle");}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {System.out.println("After Completion");}
}

4. 配置方式

  • RequestBodyAdvice

    • 通过实现 RequestBodyAdvice 接口,并使用 @ControllerAdvice 注解进行全局配置。
  • HandlerInterceptor

    • 通过实现 HandlerInterceptor 接口,并在配置类中注册拦截器。
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;@Configuration
public class WebConfig implements WebMvcConfigurer {@Autowiredprivate CustomInterceptor customInterceptor;@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(customInterceptor);}
}

总结

  • RequestBodyAdvice 专注于处理 @RequestBody 注解的方法参数,适用于请求体的读取和转换过程。
  • HandlerInterceptor 作用范围更广,适用于整个请求处理流程,可以在多个阶段进行拦截和处理。

serialVersionUID

在Java中,serialVersionUID 是一个用于序列化和反序列化过程中的版本控制标识符。它的主要作用如下:

  1. 版本控制serialVersionUID 用于确保在反序列化时,加载的类与序列化时保存的类是兼容的。如果类的版本不一致,反序列化过程将会失败并抛出 InvalidClassException 异常。

  2. 显式声明:虽然Java可以自动生成 serialVersionUID,但显式声明它可以避免在类结构发生变化时(如添加或删除字段)导致的序列化不兼容问题。显式声明 serialVersionUID 可以确保类的不同版本之间的兼容性。

  3. 序列化一致性:当一个类实现 Serializable 接口时,建议显式声明 serialVersionUID,以确保在不同的Java编译器和运行环境中序列化的一致性。

示例代码

import java.io.Serializable;public class ExampleClass implements Serializable {private static final long serialVersionUID = 1L;private String name;private int age;// Constructors, getters, setters, etc.
}

在这个示例中,serialVersionUID 被显式声明为 1L。如果以后对 ExampleClass 进行修改(例如添加新的字段),可以通过更改 serialVersionUID 来控制版本兼容性。

Spring加载过程中执行某些方法

SpringApplication.run 方法本身是同步的,这意味着它会阻塞当前线程,直到Spring应用程序启动完成。因此,在 SpringApplication.run 方法之后的代码会在应用程序启动完成后执行。

如果你希望在Spring应用程序启动完成后执行一些代码,可以使用以下几种方法:

1. 使用 CommandLineRunnerApplicationRunner

你可以实现 CommandLineRunnerApplicationRunner 接口,并将其作为一个Spring Bean。Spring会在应用程序启动完成后自动调用这些接口的 run 方法。

import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;@Component
public class MyCommandLineRunner implements CommandLineRunner {@Overridepublic void run(String... args) throws Exception {// 在Spring应用程序启动完成后执行的代码System.out.println("Spring应用程序启动完成后执行的代码");}
}

2. 使用 @EventListener 注解监听 ApplicationReadyEvent

你可以使用 @EventListener 注解来监听 ApplicationReadyEvent 事件,该事件会在Spring应用程序启动完成后发布。

import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;@Component
public class MyApplicationListener {@EventListener(ApplicationReadyEvent.class)public void onApplicationReady() {// 在Spring应用程序启动完成后执行的代码System.out.println("Spring应用程序启动完成后执行的代码");}
}

3. 在 main 方法中执行代码

如果你只需要在 main 方法中执行一些代码,可以直接在 SpringApplication.run 方法之后编写代码。

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplication
public class MyApplication {public static void main(String[] args) {SpringApplication.run(MyApplication.class, args);// 在Spring应用程序启动完成后执行的代码System.out.println("Spring应用程序启动完成后执行的代码");}
}

4. 使用 ApplicationContextrefresh 方法

如果你需要在 SpringApplication.run 方法之后立即执行一些代码,可以通过获取 ApplicationContext 并调用其 refresh 方法。

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;@SpringBootApplication
public class MyApplication {public static void main(String[] args) {ConfigurableApplicationContext context = SpringApplication.run(MyApplication.class, args);// 在Spring应用程序启动完成后执行的代码System.out.println("Spring应用程序启动完成后执行的代码");}
}

ApplicationRunnerCommandLineRunner 是Spring Boot提供的两个接口,用于在Spring应用程序启动完成后执行一些特定的代码。

ApplicationRunnerCommandLineRunner 的执行时机

  • Spring Boot启动过程

    1. Spring Boot应用程序启动。
    2. Spring容器初始化并加载所有的Bean。
    3. 调用所有实现了 CommandLineRunnerApplicationRunner 接口的Bean的 run 方法。
    4. Spring Boot应用程序完全启动。
  • 不会阻塞Spring加载过程
    ApplicationRunnerCommandLineRunnerrun 方法是在Spring容器初始化完成后调用的,因此它们不会阻塞Spring容器的加载过程。它们的执行是在Spring容器已经准备好之后进行的。

示例代码

使用 ApplicationRunner
import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.ApplicationArguments;
import org.springframework.stereotype.Component;@Component
public class MyApplicationRunner implements ApplicationRunner {@Overridepublic void run(ApplicationArguments args) throws Exception {// 在Spring应用程序启动完成后执行的代码System.out.println("Spring应用程序启动完成后执行的代码");}
}
使用 CommandLineRunner
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;@Component
public class MyCommandLineRunner implements CommandLineRunner {@Overridepublic void run(String... args) throws Exception {// 在Spring应用程序启动完成后执行的代码System.out.println("Spring应用程序启动完成后执行的代码");}
}

总结

  • 执行时机ApplicationRunnerCommandLineRunnerrun 方法是在Spring容器初始化完成后调用的。
  • 不会阻塞:它们的执行不会阻塞Spring容器的加载过程,但它们的代码会在Spring应用程序启动完成后执行。

@PostConstruct 注解用于在Spring Bean初始化完成后执行一些初始化逻辑。它会在Spring容器完全初始化该Bean之后调用。虽然 @PostConstruct 本身不会显著影响Spring的加载过程,但如果在 @PostConstruct 方法中执行了耗时操作,确实可能会导致Spring应用程序的启动时间变长。

@PostConstruct 的执行时机

  • Spring Bean生命周期
    1. Spring容器创建Bean实例。
    2. Spring容器注入依赖。
    3. 调用 @PostConstruct 注解的方法(如果有)。
    4. Bean初始化完成,准备就绪。

影响Spring加载时间的因素

  • 方法执行时间@PostConstruct 方法中的代码执行时间直接影响Spring应用程序的启动时间。如果方法中包含耗时操作(如长时间的计算、网络请求、数据库查询等),会导致Spring应用程序的启动时间变长。
  • 并发执行:Spring容器初始化Bean时是单线程执行的,因此 @PostConstruct 方法中的耗时操作会阻塞其他Bean的初始化。

示例代码

import javax.annotation.PostConstruct;
import org.springframework.stereotype.Component;@Component
public class MyBean {@PostConstructpublic void init() {// 在Bean初始化完成后执行的代码// 如果这里有耗时操作,会影响Spring应用程序的启动时间System.out.println("Bean初始化完成后执行的代码");}
}

优化建议

  1. 避免耗时操作:尽量避免在 @PostConstruct 方法中执行耗时操作。如果必须执行,可以考虑将这些操作移到其他地方,如 CommandLineRunnerApplicationRunner,或者使用异步方法执行。

  2. 异步执行:如果必须在 @PostConstruct 方法中执行耗时操作,可以使用异步方法执行,以避免阻塞Spring容器的初始化。

import javax.annotation.PostConstruct;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.stereotype.Component;@Component
@EnableAsync
public class MyBean {@PostConstructpublic void init() {// 调用异步方法performAsyncTask();}@Asyncpublic void performAsyncTask() {// 耗时操作System.out.println("执行耗时操作");}
}

总结

  • @PostConstruct 本身不会导致Spring加载过程变慢,但如果在 @PostConstruct 方法中执行了耗时操作,会影响Spring应用程序的启动时间。
  • 优化建议:避免在 @PostConstruct 方法中执行耗时操作,或者使用异步方法执行耗时任务。

通过合理设计和优化,可以确保Spring应用程序快速启动,同时完成必要的初始化任务。

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com