【问题标题】:How does ApplicationContextAware work in Spring?ApplicationContextAware 在 Spring 中是如何工作的?
【发布时间】:2014-02-28 11:02:00
【问题描述】:

在spring中,如果一个bean实现了ApplicationContextAware,那么它就可以访问applicationContext。因此,它能够获得其他豆类。 例如

public class SpringContextUtil implements ApplicationContextAware {
    private static ApplicationContext applicationContext;     

    public void setApplicationContext(ApplicationContext context) throws BeansException {
      applicationContext = context;
    }

    public static ApplicationContext getApplicationContext() {
      return applicationContext;
    }
}

那么SpringContextUtil.getApplicationContext.getBean("name")就可以得到bean的“名字”了。

为此,我们应该将SpringContextUtil 放在applications.xml 中,例如

<bean class="com.util.SpringContextUtil" />

这里的 bean SpringContextUtil 不包括属性 applicationContext。我猜当spring bean初始化时,这个属性被设置了。但是这是怎么做到的呢?方法setApplicationContext是怎么调用的?

【问题讨论】:

  • 春天是神奇的。拥抱魔法

标签: java spring


【解决方案1】:

当 spring 实例化 bean 时,它会寻找几个接口,例如 ApplicationContextAwareInitializingBean。如果找到它们,则调用方法。例如。 (非常简单)

Class<?> beanClass = beanDefinition.getClass();
Object bean = beanClass.newInstance();
if (bean instanceof ApplicationContextAware) {
    ((ApplicationContextAware) bean).setApplicationContext(ctx);
}

请注意,在较新的版本中,使用注释可能会更好,而不是实现特定于 spring 的接口。现在您可以简单地使用:

@Inject // or @Autowired
private ApplicationContext ctx;

【讨论】:

  • 非常感谢,这就是我想要的!也许我需要阅读一些 spring 代码来了解 spring 是如何工作的。
  • 在大多数情况下最好使用@Autowired,但在其他情况下可能不起作用,例如当你有一个“@Component”是一个单例但你需要注入一个具有会话范围的 bean。由于依赖项在应用程序上下文创建中自动装配,因此您不会真正注入会话 bean,通过对应用程序上下文的引用,您可以以编程方式检索会话 bean,这将正确返回会话 bean 实例。
  • 我希望 Spring 会注入一个动态生成的代理类 - 这样的类具有应用程序范围,但是在访问时,它会委托给会话范围实例,或者在没有绑定请求的情况下抛出异常当前运行的线程
  • @raspacorp 如果无法从注入的ApplicationContext 获取 sesson 范围 bean,则也无法从 ApplicationContextAware instance 获取。因为ApplicationContextAware instance 从与注入对象相同的applicationContext 对象中获取一个bean。
【解决方案2】:

Spring 源代码解释 ApplicationContextAware 的工作原理
当你使用ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
AbstractApplicationContext类中,refresh()方法的代码如下:

// Prepare the bean factory for use in this context.
prepareBeanFactory(beanFactory);

输入此方法,beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this)); 会将 ApplicationContextAwareProcessor 添加到 AbstractrBeanFactory 中。

protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
        // Tell the internal bean factory to use the context's class loader etc.
        beanFactory.setBeanClassLoader(getClassLoader());
        beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));
        beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));
        // Configure the bean factory with context callbacks.
        beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));
...........

当spring初始化AbstractAutowireCapableBeanFactory中的bean时, 在方法initializeBean中,调用applyBeanPostProcessorsBeforeInitialization实现bean后处理。该过程包括注入applicationContext。

@Override
    public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName)
            throws BeansException {
        Object result = existingBean;
        for (BeanPostProcessor beanProcessor : getBeanPostProcessors()) {
            result = beanProcessor.postProcessBeforeInitialization(result, beanName);
            if (result == null) {
                return result;
            }
        }
        return result;
    }

当BeanPostProcessor实现Object时执行postProcessBeforeInitialization方法,例如之前添加的ApplicationContextAwareProcessor

private void invokeAwareInterfaces(Object bean) {
        if (bean instanceof Aware) {
            if (bean instanceof EnvironmentAware) {
                ((EnvironmentAware) bean).setEnvironment(this.applicationContext.getEnvironment());
            }
            if (bean instanceof EmbeddedValueResolverAware) {
                ((EmbeddedValueResolverAware) bean).setEmbeddedValueResolver(
                        new EmbeddedValueResolver(this.applicationContext.getBeanFactory()));
            }
            if (bean instanceof ResourceLoaderAware) {
                ((ResourceLoaderAware) bean).setResourceLoader(this.applicationContext);
            }
            if (bean instanceof ApplicationEventPublisherAware) {
                ((ApplicationEventPublisherAware) bean).setApplicationEventPublisher(this.applicationContext);
            }
            if (bean instanceof MessageSourceAware) {
                ((MessageSourceAware) bean).setMessageSource(this.applicationContext);
            }
            if (bean instanceof ApplicationContextAware) {
                ((ApplicationContextAware) bean).setApplicationContext(this.applicationContext);
            }
        }
    }

【讨论】:

    【解决方案3】:

    由任何希望被通知其运行的 ApplicationContext 的对象实现的接口。

    以上内容摘自 Spring doc 网站https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/context/ApplicationContextAware.html

    所以,它似乎是在 Spring 容器启动时调用的,如果你想在那个时候做点什么。

    它只有一种设置上下文的方法,所以你现在可以获取上下文并在我认为的上下文中做某事。

    【讨论】:

      【解决方案4】:

      ApplicationContextAware 接口,当前应用上下文,通过它可以调用spring容器服务。我们可以通过类中的以下方法获取当前的applicationContext实例

      public void setApplicationContext(ApplicationContext context) throws BeansException.
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-10-31
        • 2017-06-27
        相关资源
        最近更新 更多