Day 5 Spring的后处理器

文章介绍了Spring框架中的后处理器机制,包括BeanFactoryPostProcessor和BeanPostProcessor。BeanFactoryPostProcessor在BeanDefinitionMap填充完毕后、Bean实例化前执行,用于动态注册和修改BeanDefinition。BeanPostProcessor则在Bean实例化后、放入单例池前执行,常用于属性填充和初始化方法调用。文章还展示了如何实现这两个接口并进行动态注册Bean以及处理Bean初始化的示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1 Spring后处理器

Spring的后处理器是Spring对外开发的重要扩展点,允许我们介入到整个Bean实例化流程中来,以达到动态注册BeanDefinition动态修改BeanDefinition,以及动态修改Bean的作用。

  • BeanFactoryPostProcessor: Bean工厂后处理器,在BeanDefinitionMap填充完毕,Bean实例化之前执行。只会执行一次。

  • BeanPostProcessor: Bean后处理器,一般在Bean实例化之后,填充到单例池singletonObjects之前执行。会执行多次,每个bean创建完成后,放入singletonObjects之前。

解析:

动态注册BeanDefinition:在创建完BeanDefinition后,加入到BeanDefinitionMap的过程。

1.1 BeanFactoryPostProcessor

BeanFactoryPostProcessor是一个接口规范,实现了该接口的类只要交由Spring容器管理的话,那么Spring就会调该接口的方法,用于对BeanDefinition注册和修改的功能。

  • 实现该接口的类

  • 交给Spring管理

  • Spring自动调用

public class MyBeanFactoryPostPorcess implements BeanFactoryPostProcessor {
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        // 调用工厂后处理器
        BeanDefinition beanDefinition = beanFactory.getBeanDefinition("dataSource");
        System.out.println("beanDefinition.getBeanClassName() = " + beanDefinition.getBeanClassName());
    }
}

 <!--注入工厂后处理器-->
   <bean id="factoryProfessor" class="cn.msf.processor.MyBeanFactoryPostPorcess"></bean>

结果

测试动态生成bean,并不需要利用bean标签

public class MyBeanFactoryPostPorcess implements BeanFactoryPostProcessor {
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        // 动态注册
        BeanDefinition definition = new RootBeanDefinition();
        definition.setBeanClassName("cn.msf.dao.impl.PersonDaoImpl");
        DefaultListableBeanFactory defaultListableBeanFactory = (DefaultListableBeanFactory) beanFactory;
        defaultListableBeanFactory.registerBeanDefinition("personDao",definition);

        // 调用工厂后处理器
        BeanDefinition beanDefinition = beanFactory.getBeanDefinition("dataSource");
        System.out.println("beanDefinition.getBeanClassName() = " + beanDefinition.getBeanClassName());
    }
}

结果

一般可以使用BeanFactoryPostProcessor的子接口BeanDefinitionRegistryPostProcessor可以避免强转。

在Day4下中最后的图进行扩展。Day4 下 Spring 的get方法和实例化流程

1.2 实现简单的MyComponent注解

首先定义注解

@Target(ElementType.TYPE) // 注解的位置在类上
@Retention(RetentionPolicy.RUNTIME)
public @interface MyComponent {
    String value();
}

配置后工厂处理器

public class MyBeanFactoryPostProcessor2 implements BeanDefinitionRegistryPostProcessor {
    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
        Map<String, Class> map = BaseClassScanUtils.scanMyComponentAnnotation("cn.msf");
        map.forEach((name,clazz)->{
            String beanClassName = clazz.getName();
            RootBeanDefinition beanDefinition = new RootBeanDefinition();
            beanDefinition.setBeanClassName(beanClassName);
            registry.registerBeanDefinition(name,beanDefinition);
        });
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {

    }
}

// 包扫描代码
package cn.msf.utils;
import cn.msf.anno.MyComponent;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.util.ClassUtils;

import java.lang.annotation.Annotation;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class BaseClassScanUtils {

    //设置资源规则
    private static final String RESOURCE_PATTERN = "/**/*.class";

    public static Map<String, Class> scanMyComponentAnnotation(String basePackage) {

        //创建容器存储使用了指定注解的Bean字节码对象
        Map<String, Class> annotationClassMap = new HashMap<String, Class>();

        //spring工具类,可以获取指定路径下的全部类
        ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
        try {
            String pattern = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
                    ClassUtils.convertClassNameToResourcePath(basePackage) + RESOURCE_PATTERN;
            Resource[] resources = resourcePatternResolver.getResources(pattern);
            //MetadataReader 的工厂类
            MetadataReaderFactory refractory = new CachingMetadataReaderFactory(resourcePatternResolver);
            for (Resource resource : resources) {
                //用于读取类信息
                MetadataReader reader = refractory.getMetadataReader(resource);
                //扫描到的class
                String classname = reader.getClassMetadata().getClassName();
                Class<?> clazz = Class.forName(classname);
                //判断是否属于指定的注解类型
                if(clazz.isAnnotationPresent(MyComponent.class)){
                    //获得注解对象
                    MyComponent annotation = clazz.getAnnotation(MyComponent.class);
                    //获得属value属性值
                    String beanName = annotation.value();
                    //判断是否为""
                    if(beanName!=null&&!beanName.equals("")){
                        //存储到Map中去
                        annotationClassMap.put(beanName,clazz);
                        continue;
                    }

                    //如果没有为"",那就把当前类的类名作为beanName
                    annotationClassMap.put(clazz.getSimpleName(),clazz);

                }
            }
        } catch (Exception exception) {
        }

        return annotationClassMap;
    }

    public static void main(String[] args) {
        Map<String, Class> stringClassMap = scanMyComponentAnnotation("cn.msf");
        System.out.println(stringClassMap);
    }
}
<bean id="factoryProfessor" class="cn.msf.processor.MyBeanFactoryPostProcessor2"></bean>

测试

    • 3 BeanPostProcessor 后置处理器

作用在bean实例化之后,加载到SingletonObjects之前,每次产生一个bean都会触发。

例如: 属性的填充初始方init(bean属性的填充)的执行等,其中有一个对外进行扩展的点BeanPostProcessor,我们称为Bean后处理。跟上面的Bean工厂后处理器相似,它也是一个接口,实现了该接口并被容器管理的BeanPostProcessor,会在流程节点上被Spring自动调用。

  • 实现该接口的类

  • 交给Spring管理

  • Spring自动调用

入门测试

package cn.msf.processor;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;

/**
 * @author : msf
 * @date : 2023/1/10
 */
public class MyBeanPostProcessor implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println(beanName + ": postProcessBeforeInitialization");
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println(beanName + ": postProcessAfterInitialization");
        return bean;
    }
}


   <bean id="userDao" class="cn.msf.dao.impl.UserDaoImpl" init-method="init"></bean>
   <bean class="cn.msf.processor.MyBeanPostProcessor"></bean>


public class UserDaoImpl implements UserDao {

 public UserDaoImpl() {
        System.out.println("UserDao 实例化方法");
    }

    public void init() {
        System.out.println("UserDao 初始化方法");
    }
}

测试结果

### 苍穹外卖 Day1 不支持版本 5 的原因分析与解决方案 #### 问题背景 苍穹外卖系统在不同版本之间可能存在兼容性问题。根据提供的引用内容[^5],可以推测该系统可能在版本升级过程中遇到了一些设计或实现上的限制,导致某些功能无法正常运行。 #### 可能的原因 1. **依赖冲突**:如果苍穹外卖 Day1 使用了特定版本的库或框架(例如 Spring Boot 或 Redis),而版本 5 引入了不兼容的依赖项,则可能导致功能异常。 2. **API 变更**:版本 5 中的某些 API 可能发生了变化,导致 Day1 中的代码无法正确调用这些接口。 3. **事务管理问题**:根据引用[^2]提到的事务知识,如果版本 5 修改了事务的配置方式或行为,可能会导致原有逻辑失效。 4. **缓存机制冲突**:引用[^2]中提到了 Spring Cache 和 Redis 的集成。如果版本 5 对缓存机制进行了调整,可能导致缓存相关功能无法正常工作。 5. **全局异常处理的变化**:引用[^5]中描述了全局异常处理器的设计。如果版本 5 修改了异常处理的方式,可能会导致原有的异常捕获逻辑失效。 #### 解决方案 以下是针对上述可能原因提出的解决方案: 1. **检查依赖冲突**: - 使用工具(如 Maven Dependency Plugin)分析项目中的依赖关系,确保没有版本冲突。 - 如果发现冲突,尝试将相关依赖升级到与版本 5 兼容的版本。 2. **适配 API 变更**: - 查阅版本 5 的官方文档,了解其与旧版本之间的差异。 - 修改代码以适配新的 API 接口。 3. **调整事务管理**: - 检查版本 5 的事务配置是否发生变化。 - 根据需要更新事务注解(如 `@Transactional`)的配置。 4. **优化缓存机制**: - 确保缓存相关的注解(如 `@Cacheable` 和 `@CacheEvict`)与版本 5 的缓存策略一致。 - 如果版本 5 改变了缓存的默认行为,可以在配置文件中显式指定缓存策略。 5. **更新异常处理逻辑**: - 检查版本 5 的异常处理机制是否有变化。 - 如果有变化,修改全局异常处理器的代码以兼容新机制。 #### 示例代码 以下是一个示例代码片段,展示如何通过 Maven Dependency Plugin 检查依赖冲突: ```xml <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-dependency-plugin</artifactId> <version>3.2.0</version> <executions> <execution> <goals> <goal>tree</goal> </goals> </execution> </executions> </plugin> ``` ####
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

兜兜转转m

一毛钱助力博主实现愿望

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

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

打赏作者

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

抵扣说明:

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

余额充值