applicationcontextaware没有加载_Spring容器和Bean加载

Spring容器与Bean加载详解:从ApplicationContextAware到Bean生命周期
本文详细介绍了Spring容器的IOC和DI概念,以及Bean的加载过程。从XML配置到BeanDefinition解析,再到IOC容器初始化的四个步骤。Bean生命周期包括实例化、属性赋值、初始化和销毁等阶段,文中还提到了ApplicationContextAware、BeanFactoryAware等Aware接口的作用,以及BeanPostProcessor等扩展接口的功能。

Spring容器的IOC和DI概念

IOC(控制反转):对于组件的控制权进行了转移,传统的程序设计是由客户端new出对象,是程序主动创建所依赖的对象。而IOC是专门将对象的创建交给容器处理,组件的控制器交由到了IOC容器中。

32e8391e5f1b58398fa83f86c5498a2e.png

DI(依赖注入):组件之间的依赖关系由容器在运行期决定,容器动态将某个依赖关系注入到组件中,主要为了提升组件的复用。

IOC侧重于实现了容器对于组件的管理,DI侧重于对于组件的依赖关系。

Bean加载过程

早期的Spring都是通过配置XML进行Bean加载的,过程大致如下:

40def523278fade0938c663a9e9e1b95.png

  • XML是配置文件,定义了Spring标签定义;

  • Resource是Spring对于资源的抽象;

  • Document是从Resource中读取的XML定义解析为Document;

  • BeanDefinition是XML文件定义Java对象的一个过程;

  • Bean最后被实例化成Object对象;

Resource持有URL Resource,主要是为了读取XML的Resource。

读取到XML的Resource之后主要进行XML标签解析,也就是Resource -> Document -> BeanDefinition过程。

BeanDefinition是XML文档的一种表现形式。

IOC容器初始化

IOC容器初始化的核心流程主要有四个步骤:

  1. Bean定义和定位,Bean可能放在XML中,或者一个注解,这些都可以被Resource定位,读取Resource获取BeanDefinition注册到Bean定义的注册表中。

  2. 第一次想容器getBean操作会触发Bean创建过程,实例化出一个Bean,根据BeanDefinition中类信息实例化Bean。

  3. 将实例化的Bean放到单例缓存中。

  4. 之后再次获取向容器getBean时从缓存取。

19e275a036a08632449537cb57689f43.png

Bean生命周期

Bean生命周期主要经历四个阶段:

  1. 实例化:createBeanInstance方法,主要创建BeanWrapper;

  2. 属性赋值:将BeanDefinition中属性赋值到BeanWrapper中;

  3. 初始化和setter注入:利用BeanWrapper初始化Bean实例;

  4. 销毁:注册响应销毁方法,在容器关闭时触发Bean实例销毁操作;

512e57e44cdf836d4b49863e092692c9.png

在Bean实例化过程中,Spring为我们预留了好多API,我们可以在Bean的实例化前后,初始化前后做一些自己的逻辑。

1299195848c844a1b046d08fd60b7116.png

1. 根据BeanDefinition信息,实例化对象,Constructor构造方法;  

2. 根据BeanDefinition信息,配置Bean的所有属性(将bean的引用注入到bean对应的属性,*可能存在循环依赖问题);  

3. 如果Bean实现了BeanNameAware接口,工厂调用Bean的setBeanName,参数为Bean的Id;  

4. 如果Bean实现BeanFactoryAware接口,Spring将调用setBeanFactory()方法,将BeanFactory容器实例传入;

5. 如果Bean实现ApplicationContextAware接口,Spring将调用setApplicationContext()方法,将bean所在的应用上下文的引用传入进来;

5. 如果存在类实现了BeanPostProcessor接口,执行这些实现类的postProcessBeforeInitialization方法,这相当于在Bean初始化之前插入逻辑  ;

6. 如果Bean实现InitializingBean接口, 执行afterPropertiesSet方法;

7. 如果Bean指定了init-method方法,就会调用该方法。例:\  ;

8. 如果存在类实现了BeanPostProcessor接口,执行这些实现类的postProcessAfterInitialization方法,这相当于在Bean初始化之后插入逻辑  ;

9. 这个阶段Bean已经可以使用了,scope为singleton的Bean会被缓存在IOC容器中

10. 如果Bean实现了DisposableBean接口, 在容器销毁的时候执行destroy方法。

11. 如果配置了destory-method方法,就调用该方法。例:\

其他扩展接口

1.InitializingBean接口

InitializingBean接口中只有一个afterPropertiesSet方法,从方法的名称上很容易理解,这个方法是在Bean的属性都设置值后被调用,用于完成一些初始化工作。当然,在Spring的配置文件中init-method的配置也是在Bean的属性都设置值后被调用,用于完成一些初始化工作,不过在执行顺序上,接口的方法先于配置。值得注意的是,这两种方式都是用于完成一些初始化工作,所以相应的方法中不要编写一些复杂且执行时间很长的逻辑。

2.DisposableBean接口

DisposableBean接口中只有一个destroy方法,该方法会在Bean被销毁、生命周期结束之前被调用,用于做一些销毁的收尾工作。同样,在Spring的配置文件中destroy-method配置也完成同样的工作,不过在执行顺序上,接口的方法先于配置。

3.ApplicationContextAware接口

ApplicationContextAware中只有一个setApplicationContext方法。实现了ApplicationContextAware接口的类,可以在该Bean被加载的过程中获取Spring的应用上下文ApplicationContext,通过ApplicationContext可以获取Spring容器内的很多信息。

4.BeanFactoryAware接口

BeanFactoryAware接口中只有一个setBeanFactory方法。实现了BeanFactoryAware接口的类,可以在该Bean被加载的过程中获取加载该Bean的BeanFactory,同时也可以获取这个BeanFactory中加载的其它Bean。

5.FactoryBean接口

FactoryBean接口可以实现Bean实例化的个性定制,让Spring容器加载我们想要的Bean。实现了FactoryBean接口的类,可以通过实现getObject方法,实现加载我们想要的Bean。

6.BeanPostProcessor接口

BeanPostProcessor接口中有两个方法,分别为postProcessBeforeInitialization和postProcessAfterInitialization。实现了BeanPostProcessor接口的类,会在每个Bean初始化(即调用setter)之前和之后,分别调用这个类中的postProcessBeforeInitialization方法和postProcessAfterInitialization方法,实现初始化的逻辑控制。

7.InstantiationAwareBeanPostProcessor接口

InstantiationAwareBeanPostProcessor接口中,常用的方法是postProcessBeforeInstantiation和postProcessAfterInstantiation。每个Bean的实例化(即调用构造函数)之前和之后,会分别调用实现了该接口的类中的postProcessBeforeInstantiation和postProcessAfterInstantiation方法。

8.BeanFactoryPostProcessor接口

BeanFactoryPostProcessor接口中只有postProcessBeanFactory方法。实现了该接口的类,可以在Bean被创建之前,获取容器中Bean的定义信息,并且可以进行修改。实现类中的postProcessBeanFactory方法只会被执行一次,且先于BeanPostProcessor接口的方法。

Aware接口

  • LoadTimeWeaverAware:加载Spring Bean时织入第三方模块,如AspectJ

  • BootstrapContextAware:资源适配器BootstrapContext,如JCA,CCI

  • ResourceLoaderAware:底层访问资源的加载器

  • PortletConfigAware:PortletConfig

  • PortletContextAware:PortletContext

  • ServletConfigAware:ServletConfig

  • ServletContextAware:ServletContext

  • MessageSourceAware:国际化

  • ApplicationEventPublisherAware:应用事件

  • NotificationPublisherAware:JMX通知

Aware接口特点

  • 都是Aware接口的子接口,即都继承了Aware接口 

  • 父接口Aware中没有定义任何方法

  • 接口内均定义了一个set方法,set参数就是我们需要获取的对象

扩展:

https://www.jianshu.com/p/f78b4b4435c2

https://www.jianshu.com/p/b3e97123fcaf

https://www.jianshu.com/p/a184b5a89850

<think>首先,用户的问题是关于Java动态加载类时如何访问Spring容器中的Bean。用户说:“我想在Java中动态加载类时如何使用当前Spring容器中的Bean 请问Java 动态加载类如何访问 Spring 容器中的 Bean”。 我需要参考系统级指令: - 所有行内数学表达式使用$...$格式,但这个问题是关于JavaSpring的,可能没有数学表达式,所以我可以忽略这个。 - 独立公式使用$$...$$,同样可能不适用。 - LaTeX语法正确:确保如果使用数学格式,它是正确的。 - 使用中文回答:整个响应必须用中文。 - 生成相关问题:在回答后,需要生成相关问题。 - 回答中引用的段落末尾添加引用标识:例如[^1]。参考提供的引用: - 引用[1]: 关于Spring容器加载BeanJVM加载类的关系。 - 引用[2]: 关于Bean规范,如getter/setter等。 - 引用[3]: 关于在Java类中获取Spring容器bean,但不能在初始化时通过特定方法获取。 用户的问题是关于动态加载类时访问Spring Bean。动态加载类可能涉及到使用ClassLoader加载类,然后实例化它,但在Spring上下文中,需要让这个新类能够访问Spring管理的Bean。 关键点: - 用户想动态加载一个类(可能是运行时加载新的类文件)。 - 然后在这个新加载的类中使用Spring容器中的Bean。 解决方案可能涉及: 1. 确保动态加载的类在Spring的上下文中,或者能够访问ApplicationContext2. 使用ApplicationContextAware接口或其他机制来访问Bean。 从引用[3]中,它提到在服务器启动时不能通过特定方法获取容器,但可能在其他时候可以。 常见方法是: - 实现ApplicationContextAware接口来获取ApplicationContext。 - 或者使用静态方式,如持有ApplicationContext的引用。 对于动态加载的类,问题是如何让这个类成为Spring Bean,或者如何注入依赖。 如果类是在运行时动态加载的,它可能不是由Spring管理的,所以我们需要手动注入Bean。 步骤: 1. 获取ApplicationContext实例。 2. 动态加载类(例如,使用Class.forName()或自定义ClassLoader)。 3. 实例化该类(可能是通过反射)。 4. 然后,如果需要访问Spring Bean,可以将ApplicationContext传递给这个实例,或者手动获取Bean并设置。 例如,可以定义一个接口或使用反射来设置依赖。 更好的方式:如果可能,让动态加载的类实现一个接口,然后从ApplicationContext获取Bean并注入。 或者,使用Spring的AutowireCapableBeanFactory来自动装配Bean。 在Spring中,AutowireCapableBeanFactory可以用于手动装配Bean,即使它们不是由Spring容器管理的。 所以,解决方案可能是: - 获取AutowireCapableBeanFactory或ApplicationContext。 - 使用它来装配动态创建的实例。 结构回答: 1. 解释问题:动态加载的类不是Spring Bean,所以不能直接注入。 2. 提供方法:使用ApplicationContextAware获取上下文,然后手动注入。 3. 给出代码示例。 4. 确保引用相关部分。 参考引用: - 引用[1]提到JVM加载类是延迟的,Spring加载Bean是启动时或按需。 - 引用[2]关于Bean规范。 - 引用[3]关于获取容器的方法。 在回答中,自然地添加引用标识。 回答结构清晰: - 引言:简要解释问题。 - 步骤:逐步说明如何实现。 - 代码示例:提供Java代码。 - 注意事项。 最后,生成相关问题。 用中文回答。 大纲: 1. **理解问题**:动态加载类时如何访问Spring Bean2. **解决方案**: - 方法1:实现ApplicationContextAware来存储ApplicationContext。 - 方法2:使用AutowireCapableBeanFactory来装配Bean。 - 步骤: a. 获取ApplicationContext(通过ApplicationContextAware)。 b. 动态加载类(使用Class.forName())。 c. 实例化类。 d. 使用beanFactory.autowireBean()来注入依赖。 3. **代码示例**:提供示例代码。 4. **注意事项**:如引用[3]提到的,不能在初始化时获取容器,但可以在运行时。 5. **引用**:在相关段落添加[^1], [^2], [^3]。 6. **相关问题**:生成2-3个相关问题。 相关问题基于主题,如动态加载Spring集成等。 例如: 1. 如何在Spring中动态注册Bean2. 动态加载类的安全性考虑? 3. Spring Bean的作用域如何影响动态加载? 确保回答真实可靠。</think>### Java动态加载类时访问Spring容器中的Bean 在Java开发中,动态加载类(例如通过`Class.forName()`或自定义ClassLoader在运行时加载类)时访问Spring容器中的Bean是一个常见需求。动态加载的类通常不是由Spring管理的Bean,因此无法直接使用依赖注入。Spring容器在启动时加载并管理Bean的生命周期,而JVM类加载是延迟的,只在需要时才加载类[^1]。两者虽有联系,但动态加载的类需要手动集成到Spring上下文中才能访问Bean。以下是清晰、逐步的解决方案,基于Spring框架的最佳实践。 #### 解决方案步骤 要实现动态加载类访问Spring Bean,核心思路是:**获取ApplicationContext,然后使用Spring的`AutowireCapableBeanFactory`手动装配依赖**。这样即使类不是Spring Bean,也能注入所需的Bean。以下是详细步骤: 1. **获取ApplicationContext实例**: - 在Spring应用中,可以通过实现`ApplicationContextAware`接口来获取ApplicationContextSpring会在Bean初始化时自动注入上下文。 - **注意**:引用[3]提到,在服务器启动Spring容器初始化时,不能直接通过`ContextLoader.getCurrentWebApplicationContext()`获取容器,因为这可能导致空指针异常[^3]。推荐在Bean中安全存储上下文。 2. **动态加载并实例化类**: - 使用Java反射(如`Class.forName()`)加载类,然后创建实例。 - 确保加载的类符合Bean规范:提供默认构造方法、getter/setter等[^2],以便Spring能正确注入属性。 3. **手动注入Spring Bean**: - 通过`AutowireCapableBeanFactory`(从ApplicationContext获取)调用`autowireBean()`方法,自动装配依赖。这会将Spring容器中的Bean注入到动态创建的实例中。 4. **使用注入后的实例**: - 装配完成后,实例可以直接访问Spring Bean,就像普通Spring Bean一样。 #### 代码示例 以下是一个完整的Java代码示例,演示如何在Spring Boot应用中实现这一过程。假设我们有一个Spring管理的`UserService` Bean,需要在动态加载的类`DynamicClass`中使用它。 ```java import org.springframework.beans.BeansException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.stereotype.Component; @Component public class DynamicClassLoader implements ApplicationContextAware { private ApplicationContext applicationContext; @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; // 存储ApplicationContext } // 动态加载类并注入Bean的方法 public Object loadAndInjectBean(String className) throws Exception { // 步骤1: 动态加载类 Class<?> clazz = Class.forName(className); Object instance = clazz.newInstance(); // 创建实例 // 步骤2: 获取AutowireCapableBeanFactory并装配依赖 AutowireCapableBeanFactory beanFactory = applicationContext.getAutowireCapableBeanFactory(); beanFactory.autowireBean(instance); // 注入Spring Bean到实例中 return instance; // 返回已注入Bean的实例 } } // 示例:动态加载的类(需符合Bean规范) public class DynamicClass { // 需要注入的Spring Bean private UserService userService; // 必须提供默认构造方法[^2] public DynamicClass() {} // Setter方法,用于Spring注入 public void setUserService(UserService userService) { this.userService = userService; } public void execute() { // 使用注入的Bean userService.doSomething(); } } // 示例:Spring Bean(如UserService) @Service public class UserService { public void doSomething() { System.out.println("UserService called."); } } ``` **代码说明**: - `DynamicClassLoader`实现了`ApplicationContextAware`,安全获取ApplicationContext[^3]。 - `loadAndInjectBean`方法动态加载类(如`DynamicClass`),并使用`autowireBean()`注入依赖。 - `DynamicClass`需要提供默认构造方法setter,以符合Bean规范[^2]。 - 使用时,在Spring Bean中调用`loadAndInjectBean("com.example.DynamicClass")`,即可获取已注入`UserService`的实例。 #### 注意事项 - **性能与安全**:动态加载类可能影响性能,因为每次加载都会触发JVM类加载机制[^1]。建议缓存实例或使用Spring的`BeanDefinition`动态注册Bean(更高级方案)。 - **作用域问题**:如果注入的Bean是原型作用域(prototype),每次注入都会创建新实例;单例作用域(singleton)则共享实例[^2]。 - **错误处理**:动态加载可能抛出`ClassNotFoundException`或`InstantiationException`,需添加异常处理。 - **引用限制**:引用[3]强调,避免在Spring容器初始化阶段获取上下文,否则可能导致未定义行为[^3]。本方法在运行时调用是安全的。 #### 相关问题 1. 如何在Spring中动态注册一个Bean容器中? 2. 动态加载类时,如何处理Bean的循环依赖问题? 3. Spring框架支持哪些类加载机制,与JVM类加载有何区别[^1]?
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值