这六个Spring高级开发技巧掌握了吗?

开发 前端
如果在SpringWeb项目中,类型转换功能通常是由框架内部自动处理的,尤其是在Spring MVC的Controller层,当请求参数需要绑定到方法的参数时。

环境:Spring5.3.33

1. Lifecycle接口

Lifecycle接口是一个定义启动/停止生命周期控制方法的通用接口。它允许Bean对象和容器(通常是Spring的ApplicationContext本身)实现启动和停止操作,接口定义:

public interface Lifecycle {
  // Spring容器启动之前执行
  void start();
  // Spring容器在要关闭时执行
  void stop();
  // 判断是否正在运行
  boolean isRunning();
}

注意,常规的org.springframework.context.Lifecycle接口是显式启动和停止通知的简单约定,并不意味着在上下文刷新时自动启动。为了细粒度地控制自动启动和特定bean的优雅停止(包括启动和停止阶段),你应该实现org.springframework.context.SmartLifecycle接口。

如下示例:

public class PackLifecycle implements SmartLifecycle {
  private volatile boolean running ;
  @Override
  public void start() {
    this.running = true;
    System.out.println("lifecycle start ... ") ;
  }
  @Override
  public void stop() {
    this.running = false ;
    System.out.println("lifecycle stop ... ") ;
  }
  @Override
  public boolean isRunning() {
    return running ;
  }
}

start/stop执行时机

start方法执行

public abstract class AbstractApplicationContext {
  public void refresh() {
    // ...
    // 实例化单例bean
    finishBeanFactoryInitialization(beanFactory);
    // 完成上下文刷新操作最后一步执行
    finishRefresh();
  }
  protected void finishRefresh() {
    // 通过LifecycleProcessor#onRefresh方法执行Lifecycle#start方法
    getLifecycleProcessor().onRefresh();
  }
}

stop方法执行

public abstract class AbstractApplicationContext {
  // 当容器关闭时执行
  public void close() {
    doClose();
  }
  protected void doClose() {
    // 通过LifecycleProcessor#onClose方法执行Lifecycle#stop方法
    this.lifecycleProcessor.onClose();
  }
}

你可以通过自定义Lifecycle,在容器启动完成时和容器关闭时做你需要的人和事。

2. FactoryBean接口

如果你想自定义完全控制bean的实例化,那么你可以通过实现FactoryBean接口。

FactoryBean<T>接口提供了三种方法:

  • T getObject():  返回此工厂创建的对象的实例。实例可能是共享的,这取决于此工厂返回的是单件还是原型。
  • boolean isSingleton():  如果此FactoryBean返回singletons,则返回true;否则返回false。此方法的默认实现返回true。
  • Class<?> getObjectType(): 返回getObject()方法返回的对象类型,如果类型事先未知,则返回null。

如下示例:

public class User {}
@Component("user")
public class UserFactoryBean implements FactoryBean<User> {
  @Override
  public User getObject() throws Exception {
    // 自定义对象实例化
    User user = new User() ;
    return user ;
  }
  @Override
  public Class<?> getObjectType() {
    return User.class;
  }
}

虽然我们定义的是FactoryBean实例,但是我们使用的时候还是可以按照User类型注入使用即可,如下示例:

@Resource
private User user;

那如何获取UserFactoryBean这个对象呢?我们可以通过如下方式:

try (GenericApplicationContext context = new GenericApplicationContext()) {
  // ...
  System.out.println(context.getBean("&user")) ;
}

在beanName之前添加'&'符合即可获取真实的UserFactoryBean对象。

3. 非web环境优雅关闭容器

如果在非web应用程序环境中(例如,在富客户端桌面环境中)使用Spring的IoC容器,请向JVM注册关闭挂钩。这样做可以确保正常关闭,并在单例bean上调用相关的destroy方法,从而释放所有资源。通过容器对象ConfigurableApplicationContext#registerShutdownHook()方法注册关闭钩子。

public class User implements DisposableBean {
  @Override
  public void destroy() throws Exception {
    System.out.println("User Object destroy...") ;
  }
}


public static void main(String[] args) {
  GenericApplicationContext context = new GenericApplicationContext() ;
  context.registerBean(User.class) ;
  // 该方法会启动一个线程,该线程会关闭onClose方法;这样bean相关的生命周期方法都能被调用
  context.registerShutdownHook() ;
  context.refresh() ; 
}
// 控制台输出;如果没有调用registerShutdownHook则不会有任何输出
User Object destroy...

注意:在SpringBoot环境下,上面的registerShutdownHook是自动调用。

4. 资源注入

我们可以直接通过@Value注解注入资源,如下示例:

@Value("${pack.images:file:///d:/images/1.png}")
private Resource res ;
// 将上面注入的资源,将图片直接输出到浏览器、
@GetMapping("/res0")
public void res0(HttpServletResponse response) throws Exception {
  response.setContentType("image/png") ;
  StreamUtils.copy(res.getInputStream(), response.getOutputStream()) ;
}

也可以注入资源数组;

@Component
public class PackResource {
  private final Resource[] templates ;
  public PackResource(@Value("${pack.templates.path}") Resource[] templates) {
    this.templates = templates;
  }
}

资源路径配置;

pack:
  templates:
    path: classpath*:com/pack/templates/*.ftl

ResourceLoaderAware接口

ResourceLoaderAware接口是一个特殊的回调接口,用于标识期望为其提供ResourceLoader引用的组件,如下示例:

@Component
public class PackResourceLoader implements ResourceLoaderAware {


  @Override
  public void setResourceLoader(ResourceLoader resourceLoader) {
    Resource resource = resourceLoader.getResource("classpath:com/pack/templates/1.txt") ;
    System.out.println(StreamUtils.copyToString(resource.getInputStream(), StandardCharsets.UTF_8)) ;
  }
}

注意:由于ApplicationContext是ResourceLoader,因此bean还可以实现ApplicationContextAware接口,并直接使用提供的应用程序上下文来加载资源。但是,一般来说,如果你只需要专用的ResourceLoader接口,那么最好使用该接口。代码将只耦合到资源加载接口(可以被视为实用程序接口),而不耦合到整个Spring ApplicationContext接口。

5. 参数验证

参数验证一般都只是用在Controller请求方法上,如下示例:

@PostMapping("")
public Object save(@Validated @RequestBody User user, BindingResult errors) {
  // TODO
}

在SpringBoot环境下(SpringBoot当你引入了validation模块后,会自动配置Validator),你可以在任意管理的Bean中使用参数验证功能,如下示例:

private final Validator validator ;
public UserService(Validator validator) {
  this.validator = validator ;
}
public void save(User user) {
  Errors errors = ...
  this.validator.validate(user, errors) ;
  if (errors.hasErrors()) {
    // TODO
  }
}

如果你不在SpringBoot环境下,那么你可以手动注册Validator

@Bean
public LocalValidatorFactoryBean validator() {
  return new LocalValidatorFactoryBean() ;
}

有关参数验证的其它更加全面的知识,请查看下面这篇文章:

必读!SpringBoot接口参数校验N种实用技巧大揭秘

SpringBoot参数验证@Validated和@Valid分清楚了吗?这些验证细节你知道吗?

6. 类型转换

如果在SpringWeb项目中,类型转换功能通常是由框架内部自动处理的,尤其是在Spring MVC的Controller层,当请求参数需要绑定到方法的参数时。然而,在应用程序的其他部分,比如Service层或其他组件中,有时我们确实需要手动执行类型转换。在这些情况下,我们可以利用Spring提供的ConversionService接口来完成数据类型之间的转换。

在Spring Boot环境下,系统自动为我们配置了ConversionService可以被注入到任何Bean对象中,以便我们在需要的时候使用它。如下示例:

private final ConversionService conversionService ;
public PackComponent(ConversionService conversionService) {
  this.conversionService = conversionService ;
}
public Object convert(Object source, Class<?> targetType) {  
  // 检查源对象和目标类型是否为null  
  if (source == null || targetType == null) {  
    throw new IllegalArgumentException("Source or target type cannot be null");  
  }  
  // 尝试进行类型转换  
  return conversionService.convert(source, targetType) ;  
}

非常方便的进行类型转换。

责任编辑:武晓燕 来源: Spring全家桶实战案例源码
相关推荐

2024-03-12 12:27:00

Vue 3前端开发

2023-09-24 13:55:42

Spring应用程序

2021-08-23 11:35:00

工具yyds开源

2018-05-20 16:17:50

物联网互联产品成熟度模型

2024-09-20 15:37:02

2021-09-03 09:57:13

开源技术 项目

2022-04-29 17:03:37

WordPress开发者网站安全

2016-12-15 09:53:07

自学编程技巧

2024-03-06 10:50:30

云计算云实例云提供商

2024-01-30 08:43:26

IF 语句JavaScripJS

2021-11-16 11:30:10

Linux命令运维

2016-01-04 15:20:46

2016趋势互联网

2021-10-09 10:00:52

远程招聘技巧招聘

2022-09-06 08:07:24

SQL语句查询

2023-02-08 17:00:07

IF 语句技巧代码

2015-07-30 14:43:04

导航栏iOS开发

2024-07-15 08:10:57

2023-10-10 18:24:46

PostgreSQL性能RDBMS

2011-08-29 10:03:38

开发团队

2020-04-20 10:10:20

IT领导者首席信息官CIO
点赞
收藏

51CTO技术栈公众号