Spring 扩展点 BeanFactoryPostProcessor 使用技巧

2023-11-17 16:51:04 浏览数 (1)

1、Spring 扩展点 BeanFactoryPostProcessor 使用技巧

方法

代码语言:java复制
void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;

其中提供一个 ConfigurableListableBeanFactory 回调对象,里面提供了很多框架内部使用的方法,建议的话使用它的实现类去注册 Bean 的信息。

不建议使用里面的方法直接实例化对象,因为过早的创建对象会导致后续 Spring 一些对 Bean 修饰执行不到,生命周期不完整。

1.1、BeanDefinitionRegistryPostProcessor

BeanFactoryPostProcessor 的实现类。

调用位置

代码语言:text复制
org.springframework.context.support.PostProcessorRegistrationDelegate#invokeBeanFactoryPostProcessors(org.springframework.beans.factory.config.ConfigurableListableBeanFactory, java.util.List<org.springframework.beans.factory.config.BeanFactoryPostProcessor>)

方法

代码语言:java复制
void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException;

一般来使用当前方法来注册 BeanDefinition。

代码语言:text复制
void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)            throws BeanDefinitionStoreException;

第一个参数填的是 Bean 的名称,第二个就是 Bean 的信息。一般可以使用以下方式去获取 BeanDefinition。

1.1.1、ScannedGenericBeanDefinition
代码语言:java复制
public class Test {     private static final PathMatchingResourcePatternResolver PATTERN_RESOLVER = new PathMatchingResourcePatternResolver();     private static final CachingMetadataReaderFactory READER_FACTORY = new CachingMetadataReaderFactory();     static final String DEFAULT_RESOURCE_PATTERN = "**/*.class";     public static void main(String[] args) throws IOException {        System.out.println(doScan("com.rookie.mybatis.study"));    }     private static List<ScannedGenericBeanDefinition> doScan(String basePackage) throws IOException {        basePackage = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX                  resolveBasePackage(basePackage)   '/'   DEFAULT_RESOURCE_PATTERN;        List<ScannedGenericBeanDefinition> result = new ArrayList<>();//        当前会扫描该类下所有的类文件        Resource[] resources = PATTERN_RESOLVER.getResources(basePackage);        for (Resource resource : resources) {            MetadataReader metadataReader = READER_FACTORY.getMetadataReader(resource);            ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);            sbd.setSource(resource);            result.add(sbd);        }        return result;    }     private static String resolveBasePackage(String basePackage) {        return ClassUtils.convertClassNameToResourcePath(new StandardEnvironment().resolveRequiredPlaceholders(basePackage));    }}

以上方式会将包下的所有类都扫描到然后添加到 Spring 容器当中,以上代码参考于 Mybatis 注册 Bean 的方式。

1.1.2、AnnotatedGenericBeanDefinition

这种方式就很直接了。直接 new 一个对象将需要的 Bean 的 class 放进去。

代码语言:text复制
AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(Test.class);
1.1.3、RootBeanDefinition

这个实现类和上面很类似,只不过多了几个构造方法:

代码语言:text复制
RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(Test.class);
代码语言:text复制
public RootBeanDefinition(String beanClassName);

最主要的还是以下的构造器:

代码语言:less复制
public RootBeanDefinition(@Nullable Class<?> beanClass, @Nullable ConstructorArgumentValues cargs,@Nullable MutablePropertyValues pvs);

这个的话多了几个参数,ConstructorArgumentValues,看名字就大概可以猜的出是传构造参数的。当我们需要创建的 Bean 是有参构造器时,我们就可以使用当前方法将构造参数传递进去。

代码语言:text复制
        ConstructorArgumentValues constructorArgumentValues = new ConstructorArgumentValues();//        添加构造参数        constructorArgumentValues.addGenericArgumentValue(111);

这个添加的顺序就代表了构造器参数的顺序。

第二个对象我们可以通过它去修改类属性的值。

代码语言:text复制
// 修改类属性的值MutablePropertyValues mutablePropertyValues = new MutablePropertyValues();mutablePropertyValues.addPropertyValue("age",555);

当然我们也可以通过其他工具类去创建:

代码语言:text复制
BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.rootBeanDefinition(TestBean.class);beanDefinitionBuilder.addConstructorArgValue(1);registry.registerBeanDefinition("testBean",beanDefinitionBuilder.getBeanDefinition());
1.2、环境变量 Environment

一般我们创建 Bean 的时候都喜欢去读 yml 的配置文件,但是在当前对象中,直接使用注入的方式是获取不到配置文件的:

代码语言:text复制
@Value("${test.case}")private String name; @Autowiredprivate Environment environment;

这时候我们可以配合另外一个扩展点去获取环境变量。因为在当前扩展点执行的之后,Environment 是已经创建了放置进了 IOC 容器当中,我们需要通过下面的方式去获取:

代码语言:less复制
@Componentpublic class Test implements BeanDefinitionRegistryPostProcessor, ApplicationContextAware {     @Override    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {     }     @Override    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {     }     @Override    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {        Environment environment = applicationContext.getEnvironment();        String xx = environment.getProperty("xx");    }}

我正在参与2023腾讯技术创作特训营第三期有奖征文,组队打卡瓜分大奖!

0 人点赞