深入理解Spring框架几个重要扩展接口

ops/2024/10/23 8:57:20/

本文介绍Spring框架的几个日常开发重要扩展接口,方便日常项目中按需扩展使用。

一、Processor 系列接口

用途: Processor 系列接口包括 BeanPostProcessor 和 BeanFactoryPostProcessor,它们的设计目的是在 Spring 容器启动过程中对 Bean 和 BeanFactory 进行自定义处理,实现一些额外的逻辑。加深理解SpringBean的生命周期理解,以及扩展更多自定义实现。

BeanPostProcessor该接口目前有两个方法:

  • postProcessBeforeInitialization 该在初始化方法之前调用。

  • postProcessAfterInitialization 该方法再初始化方法之后调用。

源码如下:

java">public interface BeanPostProcessor {/*** Apply this {@code BeanPostProcessor} to the given new bean instance <i>before</i> any bean* initialization callbacks (like InitializingBean's {@code afterPropertiesSet}* or a custom init-method). The bean will already be populated with property values.* The returned bean instance may be a wrapper around the original.* <p>The default implementation returns the given {@code bean} as-is.* @param bean the new bean instance* @param beanName the name of the bean* @return the bean instance to use, either the original or a wrapped one;* if {@code null}, no subsequent BeanPostProcessors will be invoked* @throws org.springframework.beans.BeansException in case of errors* @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet*/@Nullabledefault Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {return bean;}/*** Apply this {@code BeanPostProcessor} to the given new bean instance <i>after</i> any bean* initialization callbacks (like InitializingBean's {@code afterPropertiesSet}* or a custom init-method). The bean will already be populated with property values.* The returned bean instance may be a wrapper around the original.* <p>In case of a FactoryBean, this callback will be invoked for both the FactoryBean* instance and the objects created by the FactoryBean (as of Spring 2.0). The* post-processor can decide whether to apply to either the FactoryBean or created* objects or both through corresponding {@code bean instanceof FactoryBean} checks.* <p>This callback will also be invoked after a short-circuiting triggered by a* {@link InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation} method,* in contrast to all other {@code BeanPostProcessor} callbacks.* <p>The default implementation returns the given {@code bean} as-is.* @param bean the new bean instance* @param beanName the name of the bean* @return the bean instance to use, either the original or a wrapped one;* if {@code null}, no subsequent BeanPostProcessors will be invoked* @throws org.springframework.beans.BeansException in case of errors* @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet* @see org.springframework.beans.factory.FactoryBean*/@Nullabledefault Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {return bean;}}
java">public interface InstantiationAwareBeanPostProcessor extends BeanPostProcessor {/*** Apply this BeanPostProcessor <i>before the target bean gets instantiated</i>.* The returned bean object may be a proxy to use instead of the target bean,* effectively suppressing default instantiation of the target bean.* <p>If a non-null object is returned by this method, the bean creation process* will be short-circuited. The only further processing applied is the* {@link #postProcessAfterInitialization} callback from the configured* {@link BeanPostProcessor BeanPostProcessors}.* <p>This callback will be applied to bean definitions with their bean class,* as well as to factory-method definitions in which case the returned bean type* will be passed in here.* <p>Post-processors may implement the extended* {@link SmartInstantiationAwareBeanPostProcessor} interface in order* to predict the type of the bean object that they are going to return here.* <p>The default implementation returns {@code null}.* @param beanClass the class of the bean to be instantiated* @param beanName the name of the bean* @return the bean object to expose instead of a default instance of the target bean,* or {@code null} to proceed with default instantiation* @throws org.springframework.beans.BeansException in case of errors* @see #postProcessAfterInstantiation* @see org.springframework.beans.factory.support.AbstractBeanDefinition#getBeanClass()* @see org.springframework.beans.factory.support.AbstractBeanDefinition#getFactoryMethodName()*/@Nullabledefault Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {return null;}/*** Perform operations after the bean has been instantiated, via a constructor or factory method,* but before Spring property population (from explicit properties or autowiring) occurs.* <p>This is the ideal callback for performing custom field injection on the given bean* instance, right before Spring's autowiring kicks in.* <p>The default implementation returns {@code true}.* @param bean the bean instance created, with properties not having been set yet* @param beanName the name of the bean* @return {@code true} if properties should be set on the bean; {@code false}* if property population should be skipped. Normal implementations should return {@code true}.* Returning {@code false} will also prevent any subsequent InstantiationAwareBeanPostProcessor* instances being invoked on this bean instance.* @throws org.springframework.beans.BeansException in case of errors* @see #postProcessBeforeInstantiation*/default boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {return true;}/*** Post-process the given property values before the factory applies them* to the given bean, without any need for property descriptors.* <p>Implementations should return {@code null} (the default) if they provide a custom* {@link #postProcessPropertyValues} implementation, and {@code pvs} otherwise.* In a future version of this interface (with {@link #postProcessPropertyValues} removed),* the default implementation will return the given {@code pvs} as-is directly.* @param pvs the property values that the factory is about to apply (never {@code null})* @param bean the bean instance created, but whose properties have not yet been set* @param beanName the name of the bean* @return the actual property values to apply to the given bean (can be the passed-in* PropertyValues instance), or {@code null} which proceeds with the existing properties* but specifically continues with a call to {@link #postProcessPropertyValues}* (requiring initialized {@code PropertyDescriptor}s for the current bean class)* @throws org.springframework.beans.BeansException in case of errors* @since 5.1* @see #postProcessPropertyValues*/@Nullabledefault PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName)throws BeansException {return null;}/*** Post-process the given property values before the factory applies them* to the given bean. Allows for checking whether all dependencies have been* satisfied, for example based on a "Required" annotation on bean property setters.* <p>Also allows for replacing the property values to apply, typically through* creating a new MutablePropertyValues instance based on the original PropertyValues,* adding or removing specific values.* <p>The default implementation returns the given {@code pvs} as-is.* @param pvs the property values that the factory is about to apply (never {@code null})* @param pds the relevant property descriptors for the target bean (with ignored* dependency types - which the factory handles specifically - already filtered out)* @param bean the bean instance created, but whose properties have not yet been set* @param beanName the name of the bean* @return the actual property values to apply to the given bean (can be the passed-in* PropertyValues instance), or {@code null} to skip property population* @throws org.springframework.beans.BeansException in case of errors* @see #postProcessProperties* @see org.springframework.beans.MutablePropertyValues* @deprecated as of 5.1, in favor of {@link #postProcessProperties(PropertyValues, Object, String)}*/@Deprecated@Nullabledefault PropertyValues postProcessPropertyValues(PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeansException {return pvs;}}

 

案例一获取当前项目启动过程中所有的实现扩展实现类。

java">ConfigurableApplicationContext context = SpringApplication.run(SkywalkApplication.class, args);
String[] beanNames = context.getBeanNamesForType(BeanPostProcessor.class, true, false);
for(String bean:beanNames){System.out.println("实现后置处理器:"+bean);
}

运行如下:

案例二:监控Bean的初始化信息例如最耗时的Bean。

java">package com.boot.skywalk.processor;import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.stereotype.Component;import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;/*** 通用的Bean的后置处理器统计最耗时的Bean*/
@Component
public class CommonBeanPostProcessor implements BeanPostProcessor {/*** 初始化前的Map,Bean->时间点*/private static final Map<String,Long> BEGIN_INIT_MAP=new ConcurrentHashMap<>();/*** 初始化后的Map,Bean->时间点*/private static final Map<String,Long> INIT_COST_MAP=new ConcurrentHashMap<>();/*** 最耗时的Bean*/private static final int TOP_COST_INIT_BEAN=3;/*** Bean初始化之前* @param bean* @param beanName* @return* @throws BeansException*/@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {BEGIN_INIT_MAP.putIfAbsent(beanName, System.currentTimeMillis());return bean;}@Overridepublic Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {Long initPoint = BEGIN_INIT_MAP.get(beanName);INIT_COST_MAP.put(beanName, System.currentTimeMillis()-initPoint);return bean;}/*** 流式获取3,List返回Map.collect(Collectors.toMap(p -> p.getKey(), p -> p.getValue())* @return*/public Map<String,Long> getTopCostInitBean(){Map<String, Long> result = INIT_COST_MAP.entrySet()// 降序排列.stream().sorted((a,b)-> (int) (b.getValue()-a.getValue()))// 转换Map或者是List<Object[]>.limit(TOP_COST_INIT_BEAN).collect(Collectors.toMap(p -> p.getKey(), p -> p.getValue()));return result;}
java"> CommonBeanPostProcessor beanPostProcessor= context.getBean(CommonBeanPostProcessor.class);Map<String, Long> topCostInitBean = beanPostProcessor.getTopCostInitBean();topCostInitBean.forEach((key,value)->{System.out.println(key+":"+value+"ms");});

运行截图,可以据此数据来分析项目中哪些Bean可以设置为延迟加载,提高项目启动速度。

BeanFactoryPostProcessor:

Spring 在容器启动时,会检测容器中是否存在实现了 BeanFactoryPostProcessor 接口的 Bean,并在 BeanFactory 实例化之后、Bean 实例化之前调用其相应的方法。通过实现 BeanFactoryPostProcessor 接口,我们可以在容器启动时对 BeanFactory 进行配置,如修改 Bean 的定义、添加 Bean 的属性值等

java">@FunctionalInterface
public interface BeanFactoryPostProcessor {/*** Modify the application context's internal bean factory after its standard* initialization. All bean definitions will have been loaded, but no beans* will have been instantiated yet. This allows for overriding or adding* properties even to eager-initializing beans.* @param beanFactory the bean factory used by the application context* @throws org.springframework.beans.BeansException in case of errors*/void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;}

二、Aware 接口

Aware 接口的设计目的是增强 Bean 对容器的感知能力,使 Bean 能够更方便地与容器进行交互,获取容器中的特定资源或实例。

Spring提供了大量以 Aware 命名的接口,如BeanNameAware、BeanFactoryAware、ApplicationContextAware等。

这些接口定义了回调方法,通过这些回调方法,Spring容器可以将容器中的一些资源、状态、环境信息注入到Bean中。

例如:ApplicationContextAware

org.springframework.context.support.ApplicationContextAwareProcessor#invokeAwareInterfaces

首先会判断对象是否属于 Aware接口类型,接着根据不同的Aware接口实现类,调用不同的实现类的逻辑。 

java">	private void invokeAwareInterfaces(Object bean) {if (bean instanceof EnvironmentAware) {((EnvironmentAware) bean).setEnvironment(this.applicationContext.getEnvironment());}if (bean instanceof EmbeddedValueResolverAware) {((EmbeddedValueResolverAware) bean).setEmbeddedValueResolver(this.embeddedValueResolver);}if (bean instanceof ResourceLoaderAware) {((ResourceLoaderAware) bean).setResourceLoader(this.applicationContext);}if (bean instanceof ApplicationEventPublisherAware) {((ApplicationEventPublisherAware) bean).setApplicationEventPublisher(this.applicationContext);}if (bean instanceof MessageSourceAware) {((MessageSourceAware) bean).setMessageSource(this.applicationContext);}if (bean instanceof ApplicationStartupAware) {((ApplicationStartupAware) bean).setApplicationStartup(this.applicationContext.getApplicationStartup());}if (bean instanceof ApplicationContextAware) {((ApplicationContextAware) bean).setApplicationContext(this.applicationContext);}}

案例一:通过Aware接口获取ApplicationContext和BeanFactory访问容器中其他Bean。

先看相关接口源码,比较简单。

java">public interface ApplicationContextAware extends Aware {/*** Set the ApplicationContext that this object runs in.* Normally this call will be used to initialize the object.* <p>Invoked after population of normal bean properties but before an init callback such* as {@link org.springframework.beans.factory.InitializingBean#afterPropertiesSet()}* or a custom init-method. Invoked after {@link ResourceLoaderAware#setResourceLoader},* {@link ApplicationEventPublisherAware#setApplicationEventPublisher} and* {@link MessageSourceAware}, if applicable.* @param applicationContext the ApplicationContext object to be used by this object* @throws ApplicationContextException in case of context initialization errors* @throws BeansException if thrown by application context methods* @see org.springframework.beans.factory.BeanInitializationException*/void setApplicationContext(ApplicationContext applicationContext) throws BeansException;}
java">public interface BeanFactoryAware extends Aware {/*** Callback that supplies the owning factory to a bean instance.* <p>Invoked after the population of normal bean properties* but before an initialization callback such as* {@link InitializingBean#afterPropertiesSet()} or a custom init-method.* @param beanFactory owning BeanFactory (never {@code null}).* The bean can immediately call methods on the factory.* @throws BeansException in case of initialization errors* @see BeanInitializationException*/void setBeanFactory(BeanFactory beanFactory) throws BeansException;}
java">@Service
public class HomeApplicationContextAwareService implements ApplicationContextAware {private ApplicationContext applicationContext;@Overridepublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException {this.applicationContext=applicationContext;}public void  getBean(){Home home = (Home)applicationContext.getBean("home");home.test();}
}
java">@Service
public class HomeBeanFactoryAwareService implements BeanFactoryAware {private BeanFactory beanFactory;@Overridepublic void setBeanFactory(BeanFactory beanFactory) throws BeansException {this.beanFactory=beanFactory;}public void getBean(){Home bean = (Home) beanFactory.getBean("home");bean.test();}
}

三、ImportSelector 接口

用途: ImportSelector 接口的设计目的是允许在配置类中根据条件动态选择需要导入的其他配置类,以实现模块化和条件化配置。

常见应用场景:

  1. 根据不同的环境条件选择性地导入不同的配置类。
  2. 实现特定模块的自动配置功能,根据用户的配置情况动态加载相应的配置类。

源码如下

java">public interface ImportSelector {/*** Select and return the names of which class(es) should be imported based on* the {@link AnnotationMetadata} of the importing @{@link Configuration} class.* @return the class names, or an empty array if none*/String[] selectImports(AnnotationMetadata importingClassMetadata);/*** Return a predicate for excluding classes from the import candidates, to be* transitively applied to all classes found through this selector's imports.* <p>If this predicate returns {@code true} for a given fully-qualified* class name, said class will not be considered as an imported configuration* class, bypassing class file loading as well as metadata introspection.* @return the filter predicate for fully-qualified candidate class names* of transitively imported configuration classes, or {@code null} if none* @since 5.2.4*/@Nullabledefault Predicate<String> getExclusionFilter() {return null;}}

案例一:注入指定的Bean

java">public class ConfigurationImportSelector implements ImportSelector {@Overridepublic String[] selectImports(AnnotationMetadata importingClassMetadata) {return new String[]{EmailService.class.getName(),MessageService.class.getName(),PhoneService.class.getName()};}
}
java">@Import(ConfigurationImportSelector.class)
@Configuration
public class ServiceConfiguration {
}
java">public interface ConfigurationService {void doService();
}
java">public class EmailService implements ConfigurationService{@Overridepublic void doService() {System.out.println("Email Service");}
}
java">public class MessageService implements ConfigurationService{@Overridepublic void doService() {System.out.println("Message Service");}
}
java">public class EmailService implements ConfigurationService{@Overridepublic void doService() {System.out.println("Email Service");}
}

案例二:SpringBoot的底层Import注解实现的自动配置扫描实现

跟进入。

点进去,这些自动配置的类都要注入到Spring容器中。 


http://www.ppmy.cn/ops/127785.html

相关文章

Linux基础命令(入门)

linux 用户 root 用户 一个特殊的管理帐户 也被称为超级用户 root已接近完整的系统控制 对系统损害几乎有无限的能力 除非必要,不要登录为 root 普通&#xff08; 非特权 &#xff09; 用户权限有限 造成损害的能力比较有限 linux的哲学思想&#xff08;优点&#xf…

你好啊!C++

写在前面 喜欢C的同学们有福啦&#xff0c;本期博主给大家推荐一本新鲜出炉的C图书&#xff0c;一起来看看吧&#xff01; 推荐图书 https://item.jd.com/14808522.html 特色亮点 1. 全面覆盖&#xff1a;基础语法、面向对象设计、STL与模板、多线程编程&#xff0c;应有尽…

【论文阅读】Bi-Mamba+: Bidirectional Mamba for Time Series Forecasting

文章目录 概要阅读背景知识引言创新之处 研究方法概述方法部分的核心模块多尺度打补丁&#xff08;Multi-Scale Patching&#xff09;Mamba&#xff1a;全局模式专家Local Window Transformer&#xff08;LWT&#xff09;&#xff1a;局部变化专家长短期路由器&#xff08;Long…

基于 Datawhale 开源的量化投资学习指南(6):量化选股策略

量化选股是一种利用数据和统计模型来选择具有潜在优异表现股票的方法。这种方法通过消除人为情绪干扰&#xff0c;使选股过程更加理性和客观&#xff0c;重点在于发现可能影响股票未来价格走势的系统性数据因素。本篇文章将探讨选股的基本原理&#xff0c;介绍单因子与多因子选…

记一个mysql的坑

数据库表user&#xff0c; 存在一个name字段&#xff0c;字段为varchar类型 现在user表有这么两条记录: idnameageclass1NULL18一班2lisi20二班 假如我根据下面这一条件去更新&#xff0c;更新成功数据行显示为0 update user set age 19 where age 18 and class “一班”…

15分钟学Go 第6天:变量与常量

第6天&#xff1a;变量与常量 在Go语言中&#xff0c;变量和常量是编程的基础概念。理解如何定义和使用它们不仅能帮助我们管理数据&#xff0c;还能增强代码的可读性和可维护性。在本章中&#xff0c;我们将详细探讨Go语言中的变量和常量&#xff0c;涵盖它们的定义、使用、作…

陪诊小程序之uniapp(从入门到精通)

1.uniapp如何使用vue3编写页面 <template><view class"content"><navbar name"navbar组件"></navbar><image class"logo" src"/static/logo.png"></image><view class"text-area"&…

微服务基础架构(图)

微服务基础架构是一种现代化的软件架构模式&#xff0c;旨在将大型复杂的应用程序拆分为多个小型、独立的服务。每个微服务专注于特定的业务功能&#xff0c;可独立开发、部署和扩展。 在微服务基础架构中&#xff0c;通常会使用轻量级的通信机制&#xff0c;如 RESTful API 或…