实战:SpringBoot 15个功能强大Bean

83 篇文章 0 订阅
31 篇文章 0 订阅

下面这15+个bean,可以很方便的帮我们获取当前环境信息,运行信息,参数信息等等

1. 应用程序参数Environment和ApplicationArguments

SpringBoot程序在启动时,可以通过如下方式设置启动参数:

java -jar app.jar --pack.title=xxx --pack.version=1.0.0

一般访问上面的参数可以通过如下方式:

@Resourceprivate Environment env ;public void getArgs() {  String title = env.getProperty("pack.title") ;  String version = env.getProperty("pack.version") ;}

其实SpringBoot为我们注册了一个Bean对象ApplicationArguments,在代码中可以直接注入该对象

@Resourceprivate ApplicationArguments applicationArguments ;public void getArgs() {  List<String> titles = applicationArguments.getOptionValues("pack.title") ;  List<String> version = applicationArguments.getOptionValues("pack.version") ;}

我们还可以拿到原始配置的参数信息

String[] args = applicationArguments.getSourceArgs() ;

输出结果

[--pack.title=xxx, --pack.version=1.0.0]

可以根据自己的需要进行解析处理。

2. Banner

如果你想在项目中获取Banner信息,那么你可以直接在代码中注入Banner对象。

@Resourceprivate Banner banner ;@Resourceprivate Environment env ;public void printBanner() {  banner.printBanner(env, PackApplication.class, System.out) ;}

注意:确保你没有关闭Banner,也就是说你没有进行如下的配置。

spring:  main:    banner-mode: off #关闭Banner

编程方式

SpringApplication app = new SpringApplication(PackApplication.class);app.setBannerMode(Mode.OFF) ;

如果你关闭了Banner,那么上面的注入将会报错。

3. 类型转换器ConversionService

ConversionService是个非常重要及强大的类,该类在SpringBoot启动过程中将配置文件中的配置数据转换为对应的数据类型。Controller请求参数数据类型进行转换。在代码中我们可以直接注入该类,如下示例:

@Resourceprivate ConversionService conversionService ;Integer ret = conversionService.convert("6666",   TypeDescriptor.valueOf(String.class),   TypeDescriptor.valueOf(Integer.class)) ;

你也可以注册自定义的类型转换

@Configurationpublic class WebConfig implements WebMvcConfigurer {  @Override  public void addFormatters(FormatterRegistry registry) {    registry.addConverterFactory(new ConverterFactory<String, User>() {      @Override      public <T extends User> Converter<String, T> getConverter(Class<T> targetType) {        return new Converter<String, T>() {          @Override          public T convert(String source) {            String[] s = source.split(",") ;            return (T) new User(Integer.valueOf(s[0]), s[1]) ;          }        } ;      }    }) ;  }}

添加自定义类型转换后,我们可以在代码中自己通过ConversionService进行类型转换。

4. Servlet相关对象:ServletRequest、ServletResponse、HttpSession、WebRequest

SpringBoot启动过程中会注册以下4个与Web相关的Bean对象。

  • ServletRequest

  • ServletResponse

  • HttpSession

  • WebRequest

这4个对象,我们可以在代码中任意的注入,而不必担心线程安全问题。因为这些对象实际对应的是ObjectFactory。而对应的内部实现是从ThreadLocal中获取。如下示例:

@Resourceprivate HttpServletRequest request ;@Resourceprivate HttpServletResponse response ;
public void getParam() {  String value = request.getParameter("name") ;}

每个请求到来时都会将当前对应的Request存入到ThreadLocal中。

5. 国际化MessageSource

我们可以在代码中直接注入MessageSource对象,进行国际化资源的访问,如下示例:

@Resourceprivate MessageSource messageSource ;public void getMsg() {  String message = this.messageSource.getMessage("pack.info.message",     new Object[] {"张三"}, "我是默认消息", Locale.CHINA) ;}

上面代码如果你没有设置默认消息,那么会报错,在默认情况下Spring实例化的MessageSource对象是DelegatingMessageSource这是一个空的实现。要能使的我们的国际化资源生效,你还需要在配置文件中进行如下配置:

spring:  messages:    basename: i18n/message #这里根据自己实际情况自定义basename

这样配置后,SpringBoot会自动注册ResourceBundleMessageSource对象。

其实我们也可以不使用MessageSource,而直接注入ApplicationContext对象,如下示例:

@Resourceprivate ConfigurableApplicationContext context ;
String message = context.getMessage("pack.info.message",   new Object[] {"张三"}, "我是默认消息", Locale.CHINA) ;

通过ApplicationContext对象获取资源消息,起内部还是使用的上面的MessageSource。

6. 事件发布对象ApplicationEventMulticaster

在项目中你要发布事件你可以通过ApplicationContext对象来发布,如下示例:

@Resourceprivate ConfigurableApplicationContext context ;// 发布事件context.publishEvent(new TxApplicationEvent(context)) ;

一般在项目中通过上面的方式发布一个事件。而实际Spring为我们还注册了一个ApplicationEventMulticaster对象,该对象专门用来广播事件。

@Resourceprivate ApplicationEventMulticaster eventMulticast ;eventMulticast.multicastEvent(new TxApplicationEvent(context)) ;

我们也可以自定义beanName为applicationEventMulticaster的Bean对象实现自定义。

7. 优雅关闭服务WebServerGracefulShutdownLifecycle

如果你是内嵌Tomcat启动(以Jar包形式)SpringBoot项目,那么会向容器中注册一个WebServerGracefulShutdownLifecycle对象,通过该对象你可以优雅的关闭服务。

备注:当然,你也可以通过actuator来进行优雅的关闭webserver。

@Resourceprivate WebServerGracefulShutdownLifecycle webServerGracefullShutdown ;public void shutdownWebServer() {  webServerGracefullShutdown.stop(() -> {    System.out.println("优雅关闭Web Server");    context.close() ;  }) ;}

注意:你还需要开启如下配置

server:  shutdown: graceful

8. ApplicationPid获取进程ID

如果你想在程序中获取当前SpringBoot运行的进程号,那么你可以使用ApplicationPid,该类非常方便的获取当前进程ID。

ApplicationPid pid = new ApplicationPid() ;System.out.printf("进程ID: %s%n", pid.toString()) ;

输出结果

进程ID: 24416

当然你还可以通过如下方式,获取当前的进程号

#在META-INF/spring.factories中注册监听器org.springframework.context.ApplicationListener=\org.springframework.boot.context.ApplicationPidFileWriter

该监听器会将当前的进程ID写入文件中,通过如下配置文件路径

spring:  pid:    file: d:/app.pid

文件内容:当前进程id

如果你觉得无聊,那么你还可以通过如下方式

String pid = ManagementFactory.getRuntimeMXBean().getName().split("@")[0]

这样也可以获取进程ID。

9. 应用运行主目录ApplicationHome

ApplicationHome提供访问应用程序主目录的途径。尝试为Jar文件、解压缩文件和直接运行的应用程序选择一个合理的主目录。

ApplicationHome home = new ApplicationHome() ;System.out.printf("dir: %s, source: %s%n", home.getDir(), home.getSource()) ;

在IDE中运行输出结果

dir: D:\java\workspace\test-app, source: null

打成Jar后运行输出结果

dir: D:\java\workspace\test-app\target,   source: D:\java\workspace\test-app\target\test-app-1.0.0.jar

通过jar运行后,source输出的是当前运行的jar包路径。

10. 获取Java版本JavaVersion

要想知道当前SpringBoot运行时的java版本可以通过JavaVersion获取

System.out.printf("Java Version: %s%n", JavaVersion.getJavaVersion()) ;

输出结果

Java Version: 17

JavaVersion是个枚举类,定义了17~22枚举值,你还可以调用isEqualOrNewerThanisOlderThan进行java版本的比较。

11. 应用临时目录ApplicationTemp

ApplicationTemp类提供了访问应用程序特定的临时目录的功能。一般来说,不同的Spring Boot应用程序将得到不同的位置,但是,只需重新启动应用程序即可获得相同的位置。

ApplicationTemp temp = new ApplicationTemp() ;System.out.printf("临时目录: %s%n", temp.getDir()) ;

输出结果

临时目录: C:\Users\MSI-NB\AppData\Local\Temp\561929B2C764E67BCDA2DF9DAE26EF121F7E5365

不论你在IDE下还是Jar方式运行,windows平台下临时目录都在这里的Temp下

12. SystemProperties系统属性/环境变量访问

当你需要访问系统属性时可以通过SystemProperties类非常方便的获取。如果你访问的属性不存在时(null),那么它会再从环境变量中获取(System#getenv)

System.out.printf("java.home=%s%n", SystemProperties.get("java.home")) ;

输出结果

java.home=D:\software\jre

注:这里的get方法参数是可变长参数,你可以传递多个key,获取时遍历遇到不为null的直接返回。

13. Instantiator实例化对象

Instantiator通过注入可用参数来实例化对象的简单工厂。

public interface DAO {}public class A implements DAO {}  public class B implements DAO {}

注备上面几个类,接下通过Instantiator一次性实例化多个对象。

Instantiator<DAO> instant = new Instantiator<>(DAO.class, p -> {}) ;List<DAO> ret = instant.instantiate(List.of("com.pack.A", "com.pack.B")) ;System.out.printf("%s%n", ret) ;

输出结果

[com.pack.A@3127cb44, com.pack.B@3234474]

非常方便的一次性帮助你实例化多个同类型的类

14. 资源加载PropertiesPropertySourceLoaderYamlPropertySourceLoader

如果你想将后缀为.properties,.xml,.yaml资源文件加载,那么你可以使用PropertiesPropertySourceLoaderYamlPropertySourceLoader

// 加载properties文件PropertiesPropertySourceLoader propertyLoader = new PropertiesPropertySourceLoader() ;List<PropertySource<?>> list = propertyLoader.load("pack", new ClassPathResource("pack.properties")) ;System.out.printf("pack.*: %s%n", list.get(0).getSource()) ;// 加载yaml文件YamlPropertySourceLoader yamlLoader = new YamlPropertySourceLoader() ;List<PropertySource<?>> yamls = yamlLoader.load("pack", new ClassPathResource("pack.yml")) ;System.out.printf("pack.*: %s%n", yamls.get(0).getSource()) ;

通过上面2个Loader非常方便的将资源文件加载,加载后的List<PropertySource>还可以注册到Environment中,在系统中直接访问。

在SpringBoot兴起之前,Spring项目通常需要手动配置PropertySourcesPlaceholderConfigurer来解析配置文件中的占位符,以及PropertyOverrideConfigurer来允许属性覆盖(该类可能用的也是表少的)。这2个类使用也相对比较简单,它能帮助我们加载配置文件及处理${xxx}占位符。然而,SpringBoot出现后,似乎在项目中基本不会去定义这2个类,SpringBoot已经帮我们自动的配置(PropertyOverrideConfigurer并没有需要我们自己配置)。接下来我们就再来介绍这2个类在SpringBoot中如何覆盖系统默认的配置及使用方式。

 实战案例

1 PropertySourcesPlaceholderConfigurer

属性不存在问题

在默认下,当使用${xxx}配置的属性环境中不存在时,将会报错。如下示例:

@Value("pack.title")private String title ;

当你的环境中没有配置pack.title属性时,容器启动将会报错。

图片

不过我们可以通过下面的方式来规避该错误

@Value("${pack.title:xx}")private String title ;

通过上面的方式设置默认值,这里你也直接使用冒号":"后面不设置值${pack.title:}。

通过自定义PropertySourcesPlaceholderConfigurer修改属性,使其支持不存在属性情况,如下:

@BeanPropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {  PropertySourcesPlaceholderConfigurer placeholderConfigurer = new PropertySourcesPlaceholderConfigurer() ;  // 设置为true,忽略不能解析的占位符  placeholderConfigurer.setIgnoreUnresolvablePlaceholders(true) ;  return placeholderConfigurer ;}

上面设置后服务能正确启动。

修改占位符

我们还可以修改占位符的定义方式(不推荐

@BeanPropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {  PropertySourcesPlaceholderConfigurer placeholderConfigurer = new PropertySourcesPlaceholderConfigurer() ;  // 设置占位符前缀&后缀,@[xxx]  placeholderConfigurer.setPlaceholderPrefix("@[") ;  placeholderConfigurer.setPlaceholderSuffix("]") ;  return placeholderConfigurer ;}

如上配置后,使用时就只能通过@[xxx]

自定义配置文件

在自定义时,你还可以指定自己的配置文件,初始化该bean时会加载对应的配置文件

@BeanPropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {  PropertySourcesPlaceholderConfigurer placeholderConfigurer = new PropertySourcesPlaceholderConfigurer() ;  // 指定配置文件  placeholderConfigurer.setLocations(new ClassPathResource("pack.properties")) ;  return placeholderConfigurer ;}

你也可以指定多个配置文件。

2 PropertyOverrideConfigurer

该配置文件相比较用的比较少了。该类同样是个BeanFactoryPostProcessor处理器。通过类名也能猜到是用来覆盖属性值的处理器类。直接上代码

@Componentpublic class PropertyOverrideBean {
  @Value("${pack.title}")  private String title ;  @Value("${pack.os}")  private String os ;  // getters, setters}
@Resourceprivate PropertyOverrideBean pob ;System.out.println(pob) ;

输出结果

PropertyOverrideBean [title=xxxooo, os=window]

接下来配置PropertyOverrideConfigurer

@BeanPropertyOverrideConfigurer propertyOverrideConfigurer() {  PropertyOverrideConfigurer propertyOverrideConfigurer = new PropertyOverrideConfigurer() ;  propertyOverrideConfigurer.setLocation(new ClassPathResource("pack.properties")) ;  return propertyOverrideConfigurer ;}

同样这里我们指定一个自定义配置文件;但是这里的pack.properties文件中的内容就要主要属性key的格式了,[beanName].[key]=xxxx这里的属性key的前缀必须是你要覆盖bean的名称。如下示例:

propertyOverrideBean.title=my titlepropertyOverrideBean.os=linux

上面的propertyOverrideBean就是PropertyOverrideBean的beanName。

输出结果:

PropertyOverrideBean [title=my title, os=linux]

覆盖了系统默认的属性值。

嵌套属性

@Componentpublic class PropertyOverrideBean {  // other property  private User user = new User() ;}public class User {  @Value("pack.age")  private Integer age ;  // getters, setters}

在配置文件中添加配置

propertyOverrideBean.user.age=88

输出结果

PropertyOverrideBean [title=my title, os=linux, user=User [age=88]]

注意:这里的嵌套属性User必须事先创建好否则将会抛出NPE异常。

15. 获取basePackages

如果你需要在代码中获取当前应用启动类所在的基包basePackages,那么你可以通过如下方式

private ConfigurableApplicationContext context ;System.out.printf("basepPckages: %s%n", AutoConfigurationPackages.get(context)) ;

输出结果

basepPckages: [com.pack]

内部注册的是一个BasePackages Bean,该类是静态私有的所以你没法直接访问,只能通过上面的方式。

  • 10
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

-无-为-

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值