05.Spring Framework 之扩展机制

1. BeanFactory 后置处理器

1.1 概述

BeanFactory 后置处理器有两种类型,分别是 BeanFactoryPostProcessor 和 BeanDefinitionRegistryPostProcessor

BeanFactoryPostProcessor 在 BeanFactory 标准初始化之后调用,用来定制和修改 BeanFactory 的内容,所有的 bean 定义已经保存加载到 beanFactory,但是 bean 的实例还未创建

BeanDefinitionRegistryPostProcessor 在所有 bean 定义信息将要被加载时触发,优先于 BeanFactoryPostProcessor 执行,利用 BeanDefinitionRegistryPostProcessor 可以给容器再额外添加一些组件

BeanDefinitionRegistry:Bean 定义信息的保存中心,以后 BeanFactory 就是按照 BeanDefinitionRegistry 里面保存的每一个 bean 定义信息创建 bean 实例

1.2 执行顺序

BeanFactory 后置处理器执行流程

1.3 环境搭建

代码已经上传至 https://github.com/masteryourself-tutorial/tutorial-spring ,详见 tutorial-spring-framework/tutorial-spring-framework-beanfactorypostprocessor 工程

1. MyBeanDefinitionRegistryPostProcessor
@Component
public class MyBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        System.out.println(String.format(">>>>>>>>>>>>>>>> {%s} %s", "MyBeanDefinitionRegistryPostProcessor", "postProcessBeanFactory 执行了"));
        System.out.println(String.format(">>>>>>>>>>>>>>>> {%s} bean 的数量 %d", "MyBeanDefinitionRegistryPostProcessor", beanFactory.getBeanDefinitionCount()));
    }

    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
        System.out.println(String.format(">>>>>>>>>>>>>>>> {%s} %s", "MyBeanDefinitionRegistryPostProcessor", "postProcessBeanDefinitionRegistry 执行了"));
        System.out.println(String.format(">>>>>>>>>>>>>>>> {%s} bean 的数量 %d", "MyBeanDefinitionRegistryPostProcessor", registry.getBeanDefinitionCount()));
        AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(Cat.class).getBeanDefinition();
        registry.registerBeanDefinition("hello", beanDefinition);
    }

}
2. MyBeanFactoryPostProcessor
@Component
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        System.out.println(String.format("================ {%s} %s", "MyBeanFactoryPostProcessor", "postProcessBeanFactory 执行了"));
        System.out.println(String.format("================ {%s} bean 的数量是 %d", "MyBeanFactoryPostProcessor", beanFactory.getBeanDefinitionCount()));
    }

}
3. ExtensionApplication
public class ExtensionApplication {

    public static void main(String[] args) {
        // >>>>>>>>>>>>>>>> {MyBeanDefinitionRegistryPostProcessor} postProcessBeanDefinitionRegistry 执行了
        // >>>>>>>>>>>>>>>> {MyBeanDefinitionRegistryPostProcessor} bean 的数量 9
        // >>>>>>>>>>>>>>>> {MyBeanDefinitionRegistryPostProcessor} postProcessBeanFactory 执行了
        // >>>>>>>>>>>>>>>> {MyBeanDefinitionRegistryPostProcessor} bean 的数量 10
        // ================ {MyBeanFactoryPostProcessor} postProcessBeanFactory 执行了
        // ================ {MyBeanFactoryPostProcessor} bean 的数量是 10
        // person 构造函数执行了
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
        context.close();
    }

}

2. EventListener 事件监听机制

2.1 使用方式
2.1.1 ApplicationListener

监听容器中发布的事件,事件驱动模型开发

监听 ApplicationEvent 及其子类的事件

2.1.2 @EventListener

可以使用注解简化开发,原理是通过 EventListenerMethodProcessor 处理器解析方法上的 @EventListener 注解

2.2 环境搭建

代码已经上传至 https://github.com/masteryourself-tutorial/tutorial-spring ,详见 tutorial-spring-framework/tutorial-spring-framework-listener 工程

1. MyApplicationListener
@Component
public class MyApplicationListener implements ApplicationListener<ApplicationEvent> {

    @Override
    public void onApplicationEvent(ApplicationEvent event) {
        System.out.println("=============== MyApplicationListener 接收到事件:" + event.getClass());
    }

}
2. MyAnnotationListener
@Component
public class MyAnnotationListener {

    @EventListener(classes = {ApplicationEvent.class})
    public void applicationEventListen(ApplicationEvent event) {
        System.out.println("*************** MyAnnotationListener 接收到事件:" + event.getClass());
    }

}
3. ListenerApplication
public class ListenerApplication {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
        context.publishEvent(new ApplicationEvent("hello spring annotation") {
        });
        context.close();
        // *************** MyAnnotationListener 接收到事件:class org.springframework.context.event.ContextRefreshedEvent
        // =============== MyApplicationListener 接收到事件:class org.springframework.context.event.ContextRefreshedEvent
        // *************** MyAnnotationListener 接收到事件:class pers.masteryourself.tutorial.spring.framework.listener.ListenerApplication$1
        // =============== MyApplicationListener 接收到事件:class pers.masteryourself.tutorial.spring.framework.listener.ListenerApplication$1
        // *************** MyAnnotationListener 接收到事件:class org.springframework.context.event.ContextClosedEvent
        // =============== MyApplicationListener 接收到事件:class org.springframework.context.event.ContextClosedEvent
    }

}
Exception in thread "main" org.springframework.beans.factory.BeanDefinitionStoreException: IOException parsing XML document from class path resource [printerAnnotation.xml]; nested exception is java.io.FileNotFoundException: class path resource [printerAnnotation.xml] cannot be opened because it does not exist at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions(XmlBeanDefinitionReader.java:342) at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions(XmlBeanDefinitionReader.java:310) at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:196) at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:232) at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:203) at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:265) at org.springframework.context.support.AbstractXmlApplicationContext.loadBeanDefinitions(AbstractXmlApplicationContext.java:128) at org.springframework.context.support.AbstractXmlApplicationContext.loadBeanDefinitions(AbstractXmlApplicationContext.java:94) at org.springframework.context.support.AbstractRefreshableApplicationContext.refreshBeanFactory(AbstractRefreshableApplicationContext.java:130) at org.springframework.context.support.AbstractApplicationContext.obtainFreshBeanFactory(AbstractApplicationContext.java:671) at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:553) at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:144) at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:85) at com.spring.bean.PrinterAPP.main(PrinterAPP.java:9) Caused by: java.io.FileNotFoundException: class path resource [printerAnnotation.xml] cannot be opened because it does not exist at org.springframework.core.io.ClassPathResource.getInputStream(ClassPathResource.java:199) at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions(XmlBeanDefinitionReader.java:333) ... 13 more这是什么错误
最新发布
11-21
# 题目重述 你遇到的错误信息如下: ``` Exception in thread "main" org.springframework.beans.factory.BeanDefinitionStoreException: IOException parsing XML document from class path resource [printerAnnotation.xml]; nested exception is java.io.FileNotFoundException: class path resource [printerAnnotation.xml] cannot be opened because it does not exist ``` 该问题是程序在运行时无法找到 `printerAnnotation.xml` 配置文件,导致 Spring 容器初始化失败。 --- # 详解 这个错误的根本原因是:**Spring 试图从类路径(classpath)中加载名为 `printerAnnotation.xml` 的配置文件,但该文件不存在或未被正确放置。** 具体分析如下: - 程序使用了: ```java new ClassPathXmlApplicationContext("printerAnnotation.xml"); ``` 这表示 Spring 会去 **类路径的根目录下** 查找名为 `printerAnnotation.xml` 的文件。 - 常见问题包括: 1. 文件名拼写错误(如大小写不匹配、多空格、扩展名 `.xml` 缺失)。 2. 文件没有放在正确的目录下(应位于 `src/main/resources` 目录中)。 3. 构建工具(Maven/Gradle)未将资源目录正确编译进 `classes` 文件夹。 4. IDE 缓存未更新,导致实际运行时找不到文件。 --- ### ✅ 解决方案 #### ✅ 步骤 1:确认文件存在且命名正确 确保项目中有以下文件: ``` src/main/resources/printerAnnotation.xml ``` > 注意:必须是 `resources` 目录,不是 `src` 根目录或其他位置。 #### ✅ 步骤 2:检查文件内容是否完整 确保 `printerAnnotation.xml` 内容如下(示例): ```xml <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation=" http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd"> <!-- 扫描指定包下的@Component等注解 --> <context:component-scan base-package="com.spring.bean" /> <!-- 启用AspectJ自动代理 --> <aop:aspectj-autoproxy /> </beans> ``` #### ✅ 步骤 3:构建并确认输出目录包含该文件 编译后,应在 `target/classes/`(Maven)或 `build/resources/main/`(Gradle)中看到 `printerAnnotation.xml`。 如果你使用的是 IntelliJ IDEA 或 Eclipse,请执行: - Maven 项目:右键项目 → `Maven` → `Reload Project` 或运行 `mvn compile` - 刷新项目:按 `F5` 或右键项目 → Refresh #### ✅ 步骤 4:可选——使用相对路径测试定位问题 为了验证文件是否存在,可以添加一段代码查看资源是否能加载: ```java URL resource = PrinterAPP.class.getClassLoader().getResource("printerAnnotation.xml"); System.out.println("配置文件路径:" + resource); if (resource == null) { System.err.println("❌ 文件未找到,请检查是否在 resources 目录下!"); } ``` --- # 知识点(列出该代码中遇到的知识点) 1. **Spring 配置文件加载机制** `ClassPathXmlApplicationContext` 从类路径根目录加载 XML 文件,文件必须位于 `resources` 下才能被打包进 classpath。 2. **Maven 项目资源目录结构** `src/main/resources` 是标准资源目录,所有配置文件应放在此处,编译后自动复制到输出目录。 3. **FileNotFoundException 与类路径查找** 当资源路径错误或文件缺失时,JVM 抛出 `FileNotFoundException`,需检查文件位置和名称拼写。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值