当前位置: 首页 > article >正文

ApplicationContextAware 类

优质博文:IT-BLOG-CN

需求: 使用@autowired注入一些对象,但发现不可以直接使用@Autowired,因为方法是static的,要使用该方法当前对象也必须是static,正常情况下@Autowired无法注入静态的bean,于是发现项目中用到了springContextHolder,通过使用

private T t= SpringContextHolder.getBean(T.class);

或者我们在刚开始学习的时候,会使用如下这种方式来获取Bean。但是这样就会存在一个问题,因为它需要重新装载spring-core.xml文件,并实例化上下文bean,如果有些线程配置类也是在这个配置文件中,那么会造成做相同工作的的线程会被启两次。一次是 web容器初始化时启动,另一次是上述代码显示的实例化了一次。这样就产生了冗余。下面就来说说解决方案。

ApplicationContext appContext = new ClassPathXmlApplicationContext("spring-core.xml");  
T t = (T)appContext.getBean("t");  

一、SpringContextHolder 工具类

自定义SpringContextHolder工具类,全网统一模板。需要将该类注入到Spring IOC中。因此路径很重要。

package com.zzx.utils;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

/**
 * @author
 * @date
 */
@Component
@Slf4j
public class SpringContextHolder implements ApplicationContextAware, DisposableBean {

    private static ApplicationContext applicationContext = null;

    /**
     * 取得存储在静态变量中的ApplicationContext.
     */
    public static ApplicationContext getApplicationContext() {
        assertContextInjected();
        return applicationContext;
    }

    /**
     * 从静态变量applicationContext中取得Bean, 自动转型为所赋值对象的类型.
     */
    public static <T> T getBean(String name) {
        assertContextInjected();
        return (T) applicationContext.getBean(name);
    }

    /**
     * 从静态变量applicationContext中取得Bean, 自动转型为所赋值对象的类型.
     */
    public static <T> T getBean(Class<T> requiredType) {
        assertContextInjected();
        return applicationContext.getBean(requiredType);
    }

    /**
     * 检查ApplicationContext不为空.
     */
    private static void assertContextInjected() {
        if (applicationContext == null) {
            throw new IllegalStateException("applicaitonContext属性未注入, 请在applicationContext" +
                    ".xml中定义SpringContextHolder或在SpringBoot启动类中注册SpringContextHolder.");
        }
    }

    /**
     * 清除SpringContextHolder中的ApplicationContext为Null.
     */
    public static void clearHolder() {
        log.debug("清除SpringContextHolder中的ApplicationContext:"
                + applicationContext);
        applicationContext = null;
    }

    @Override
    public void destroy() throws Exception {
        SpringContextHolder.clearHolder();
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        if (SpringContextHolder.applicationContext != null) {
            log.warn("SpringContextHolder中的ApplicationContext被覆盖, 原有ApplicationContext为:" + SpringContextHolder.applicationContext);
        }
        SpringContextHolder.applicationContext = applicationContext;
    }
}

二、ApplicationContextAware接口

Spring容器初始化的时候会检测容器中的所有Bean,如果发现某个Bean实现了ApplicationContextAware接口,Spring容器会在创建该Bean之后,自动调用该BeansetApplicationContextAware()方法,调用该方法时,会将容器本身作为参数传给该方法,该方法将Spring传入的参数(容器本身)赋给该类对象的applicationContext实例变量,因此接下来可以通过该applicationContext实例变量来访问容器本身。

三、BeanFactory

BeanFactorySpring的“心脏”。它就是Spring IoC容器的真面目。Spring使用BeanFactory来实例化、配置和管理BeanBeanFactoryIOC容器的核心接口,它定义了IOC的基本功能,我们看到它主要定义了getBean方法。getBean方法是IOC容器获取bean对象和引发依赖注入的起点。方法的功能是返回特定的名称的BeanBeanFactory只能管理单例Singleton Bean的生命周期。它不能管理原型prototype非单例Bean的生命周期。这是因为原型Bean实例被创建之后便被传给了客户端,容器失去了对它们的引用。

BeanFactory的源码:

public interface BeanFactory {
    // 用来引用一个实例,或把它和工厂产生的Bean区分开,就是说,如果一个FactoryBean的名字为a,那么,&a会得到那个Factory  四个不同形式的getBean方法,获取实例
    Object getBean(String var1) throws BeansException;

    <T> T getBean(String var1, Class<T> var2) throws BeansException;

    // 是否存在
    Object getBean(String var1, Object... var2) throws BeansException;

    <T> T getBean(Class<T> var1) throws BeansException;

    <T> T getBean(Class<T> var1, Object... var2) throws BeansException;

    <T> ObjectProvider<T> getBeanProvider(Class<T> var1);

    <T> ObjectProvider<T> getBeanProvider(ResolvableType var1);

    boolean containsBean(String var1);

    // 是否为单实例
    boolean isSingleton(String var1) throws NoSuchBeanDefinitionException;
    // 是否为原型(多实例)
    boolean isPrototype(String var1) throws NoSuchBeanDefinitionException;
    // 名称、类型是否匹配
    boolean isTypeMatch(String var1, ResolvableType var2) throws NoSuchBeanDefinitionException;

    boolean isTypeMatch(String var1, Class<?> var2) throws NoSuchBeanDefinitionException;
    // 获取类型
    @Nullable
    Class<?> getType(String var1) throws NoSuchBeanDefinitionException;

    @Nullable
    Class<?> getType(String var1, boolean var2) throws NoSuchBeanDefinitionException;
    // 根据实例的名字获取实例的别名
    String[] getAliases(String var1);
}

四、XmlBeanFactory

BeanFactory这是一个典型的工厂模式的工厂接口。BeanFactory最常见的实现类为XmlBeanFactory,可以从classpath或文件系统等获取资源。

File file = new File("fileSystemConfig.xml");
Resource resource = new FileSystemResource(file);
BeanFactory beanFactory = new XmlBeanFactory(resource);

Resource resource = new ClassPathResource("classpath.xml"); 
BeanFactory beanFactory = new XmlBeanFactory(resource);

我们通过在applicationContext.xml中配置:

<bean id="car" class="spring.ioc.demo.Car" p:brand="xiaopeng" p:color="red" p:maxSpeed="520" />

通过XmlBeanFactory实现启动Spring IoC容器:

public static void main(String[] args) {
   // ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
    // Resource res = resolver.getResource("classpath:applicationContext.xml");
    // BeanFactory factory = new XmlBeanFactory(res);  
       
  ApplicationContext factory=new ClassPathXmlApplicationContext("applicationContext.xml"); 
    Car car = factory.getBean("car", Car.class);
}

【1】XmlBeanFactory通过Resource装载Spring配置信息冰启动IoC容器,然后就可以通过factory.getBeanIoC容器中获取Bean了。
【2】通过BeanFactory启动IoC容器时,并不会初始化配置文件中定义的Bean,初始化动作发生在第一个调用时。
【3】对于单实例singletonBean来说,BeanFactory会缓存Bean实例,所以第二次使用getBean时直接从IoC容器缓存中获取Bean

五、ApplicationContext

如果说BeanFactorySpring的心脏,那么ApplicationContext就是完整的躯体了,ApplicationContextBeanFactory派生而来,提供了更多面向实际应用的功能。在BeanFactory中,很多功能需要以编程的方式实现,而在ApplicationContext中则可以通过配置实现。

BeanFactorty接口提供了配置框架及基本功能,但是无法支持springaop功能和web应用。而ApplicationContext接口作为BeanFactory的派生,因而提供BeanFactory所有的功能。而且ApplicationContext还在功能上做了扩展,相较于BeanFactortyApplicationContext还提供了以下的功能:
【1】MessageSource提供国际化的消息访问;
【2】资源访问,如URL和文件;
【3】事件传播特性,即支持aop特性;
【4】载入多个(有继承关系)上下文 ,使得每一个上下文都专注于一个特定的层次,比如应用的web层;

ApplicationContext:是IOC容器另一个重要接口, 它继承了BeanFactory的基本功能, 同时也继承了容器的高级功能,如:MessageSource(国际化资源接口)、ResourceLoader(资源加载接口)、ApplicationEventPublisher(应用事件发布接口)等。

六、总结

【1】BeanFactroy采用的是延迟加载形式来注入Bean的,即只有在使用到某个Bean时(调用getBean()),才对该Bean进行加载实例化,这样,我们就不能发现一些存在的Spring的配置问题。而ApplicationContext则相反,它是在容器启动时,一次性创建了所有的Bean。这样,在容器启动时,我们就可以发现Spring中存在的配置错误。 相对于基本的BeanFactoryApplicationContext唯一的不足是占用内存空间。当应用程序配置Bean较多时,程序启动较慢。
【2】BeanFacotry延迟加载,如果Bean的某一个属性没有注入,BeanFacotry加载后,直至第一次使用调用getBean方法才会抛出异常;而ApplicationContext则在初始化自身是检验,这样有利于检查所依赖属性是否注入;所以通常情况下我们选择使用ApplicationContext
【3】应用上下文则会在上下文启动后预载入所有的单实例Bean。通过预载入单实例bean,确保当你需要的时候,你就不用等待,因为它们已经创建好了。
【4】BeanFactoryApplicationContext都支持eanPostProcessorBeanFactoryPostProcessor的使用,但两者之间的区别是:BeanFactory需要手动注册,而ApplicationContext则是自动注册。ApplicationcontextbeanFactory加入了一些更好使用的功能。而且 beanFactory的许多功能需要通过编程实现而Applicationcontext可以通过配置实现。比如后处理beanApplicationcontext直接配置在配置文件即可而beanFactory这要在代码中显示的写出来才可以被容器识别。
【5】beanFactory主要是面对与spring框架的基础设施,面对spring自己。而Applicationcontex主要面对与spring使用的开发者。基本都会使用Applicationcontex并非beanFactory


http://www.kler.cn/news/160170.html

相关文章:

  • ELK 日志解决方案
  • AI网关究竟是什么,怎么样才算是AI算力的网关
  • 跟着GPT学习shell脚本,理论与实践相结合的学习计划。(一)
  • 团队git操作流程
  • 单片机开发常用的软件构架
  • html5各行各业官网模板源码下载(1)
  • 19、pytest通过mark标记测试函数
  • 每天一点python——day85
  • 记录一次vscode markdown的图片路径相关插件学习配置过程
  • 【微服务】分布式限流如何实现
  • Android10 Dialog bug
  • 【技术干货】宇视IPC音频问题解决步骤
  • 编程常见的问题
  • Java动态代理实现与原理详细分析
  • linux环境下编译安装OpenCV For Java(CentOS 7)
  • 判断完数(写出部分函数)
  • Uncle Maker: (Time)Stamping Out The Competition in Ethereum
  • Ribbon 饥饿加载
  • Fabric:创建应用通道
  • 图的建立与实现(使用邻接矩阵)(附赠Kruskal算法)
  • 『亚马逊云科技产品测评』活动征文| 基于etcd实现服务发现
  • Hello World!
  • TCP连接为什么是三次握手,而不是两次和四次
  • rabbitmq技术
  • 浅析计算机网络安全的的防范与措施
  • java开发中Dao层和Mapper层的关系
  • 微信玩具小程序商城开发技巧
  • 网络安全威胁——跨站脚本攻击
  • 【tower-boot 系列】redis集成
  • docker安装及配置mysql