Spring WebApplicationContext

50 篇文章 0 订阅
32 篇文章 0 订阅
public interface ApplicationContext extends EnvironmentCapable, ListableBeanFactory, HierarchicalBeanFactory,
        MessageSource, ApplicationEventPublisher, ResourcePatternResolver {
    String getId();
    String getApplicationName();
    String getDisplayName();
    long getStartupDate();
    ApplicationContext getParent();
    AutowireCapableBeanFactory getAutowireCapableBeanFactory() throws IllegalStateException;
}

ApplicationContext是spring的核心,Context我们通常解释为上下文环境,我想用“容器”来表述它更容易理解一些,ApplicationContext则是“应用的容器”了(IOC容器)。

容器又是个很抽象的概念,在 Java 中形如 ArrayList、HashMap 等都是容器。
那么 Spring 如何保存这么多 Bean 呢?

随便在一个 getBean 方法上下断点:

@Test                                                                                                      
public void test1() {                                                                                      
    ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");             
    UserMapper userMapper = context.getBean(UserMapper.class);//断了个点                                                                                                                          
}                                                                                                          

进入了AbstractApplicationContext#getBean(java.lang.Class<T>),AbstractApplicationContext 实现了 BeanFactory 接口并实现了其中的getBean方法。
这里写图片描述

getBeanFactory 是一个抽象方法。

@Override
public abstract ConfigurableListableBeanFactory getBeanFactory() throws IllegalStateException;

有如下两个实现:
这里写图片描述
Spring 使用的是第一个。

@Override                                                                                        
public final ConfigurableListableBeanFactory getBeanFactory() {                                  
    synchronized (this.beanFactoryMonitor) {                                                     
        if (this.beanFactory == null) {                                                          
            throw new IllegalStateException("BeanFactory not initialized or already closed - " + 
                    "call 'refresh' before accessing beans via the ApplicationContext");         
        }                                                                                        
        return this.beanFactory;                                                                 
    }                                                                                            
}                                                                                                                                                                                           

beanFactory 的定义:

private DefaultListableBeanFactory beanFactory; 

最终调用的是 DefaultListableBeanFactory 里的 getBean 方法。

@Override                                                                                          
public <T> T getBean(Class<T> requiredType) throws BeansException {                                
    return getBean(requiredType, (Object[]) null);                                                 
}                                                                                                  

@Override                                                                                          
public <T> T getBean(Class<T> requiredType, @Nullable Object... args) throws BeansException {      
    NamedBeanHolder<T> namedBean = resolveNamedBean(requiredType, args);                           
    if (namedBean != null) {                                                                       
        return namedBean.getBeanInstance();                                                        
    }                                                                                              
    BeanFactory parent = getParentBeanFactory();                                                   
    if (parent != null) {                                                                          
        return (args != null ? parent.getBean(requiredType, args) : parent.getBean(requiredType)); 
    }                                                                                              
    throw new NoSuchBeanDefinitionException(requiredType);                                         
}                                                                                                  
/** Map of bean definition objects, keyed by bean name */                                  
private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);

就是 Spring 中保存 Bean 的容器。


public interface WebApplicationContext extends ApplicationContext {
    String ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE = WebApplicationContext.class.getName() + ".ROOT";
    String SCOPE_REQUEST = "request";
    String SCOPE_SESSION = "session";
    String SCOPE_GLOBAL_SESSION = "globalSession";
    String SCOPE_APPLICATION = "application";
    String SERVLET_CONTEXT_BEAN_NAME = "servletContext";
    String CONTEXT_PARAMETERS_BEAN_NAME = "contextParameters";
    String CONTEXT_ATTRIBUTES_BEAN_NAME = "contextAttributes";
    ServletContext getServletContext();
}

在Web应用中,我们会用到WebApplicationContext,WebApplicationContext继承自ApplicationContext。
WebApplicationContext的初始化方式和BeanFactory、ApplicationContext有所区别,因为WebApplicationContext需要ServletContext实例,也就是说它必须拥有Web容器的前提下才能完成启动的工作。
有过Web开发经验的读者都知道可以在web.xml中配置Web容器监听器ServletContextListener,借助于该监听器我们就可以启动Spring Web应用上下文的工作。

spring为我们提供了用于启动WebApplicationContext的Web容器监听器:ContextLoaderListener,用来在web应用启动的时候来初始化WebApplicationContext

public class ContextLoaderListener extends ContextLoader implements ServletContextListener {
    public ContextLoaderListener() {
    }
    public ContextLoaderListener(WebApplicationContext context) {
        super(context);
    }
    @Override
    public void contextInitialized(ServletContextEvent event) {
        initWebApplicationContext(event.getServletContext());
    }
    @Override
    public void contextDestroyed(ServletContextEvent event) {
        closeWebApplicationContext(event.getServletContext());
        ContextCleanupListener.cleanupAttributes(event.getServletContext());
    }
}

当Servlet 容器启动或终止Web应用时,会触发ServletContextEvent事件,该事件由 ServletContextListener 来处理。
在 ServletContextListener 接口中定义了处理ServletContextEvent 事件的两个方法。
- contextInitialized(ServletContextEvent sce) :当Servlet 容器启动Web 应用时调用该方法。在调用完该方法之后,容器再对Filter 初始化,并且对那些在Web 应用启动时就需要被初始化的Servlet 进行初始化。
- contextDestroyed(ServletContextEvent sce) :当Servlet 容器终止Web 应用时调用该方法。在调用该方法之前,容器会先销毁所有的Servlet 和Filter 过滤器。

    public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
        ...
        this.context = createWebApplicationContext(servletContext);
        servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);
        ...
    }

我们发现,原来ContextLoader是把WebApplicationContext放在了ServletContext中,ServletContext也是一个“容器”,也是一个类似Map的结构,而WebApplicationContext在ServletContext中的KEY就是WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE,我们如果要使用WebApplicationContext则需要从ServletContext取出,Spring提供了一个WebApplicationContextUtils类,可以方便的取出WebApplicationContext,只要把ServletContext传入就可以了。
WebApplicationContextUtils是一个抽象类,其提供了一个很便利的方法来获取spring应用的上下文即WebApplicationContext。

其中的静态方法getWebApplicationContext(ServletContext sc),提供一个ServletContext 类型参数即可。
其原理十分简单,在spring容器初始化的方法ContextLoader.initWebApplicationContext(ServletContext)中通过servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);已经将WebApplicationContext的实例放入ServletContext 中了。
然后在工具类的org.springframework.web.context.support.WebApplicationContextUtils的
getWebApplicationContext(ServletContext)方法中就可以通过传入的ServletContext参数获取到WebApplicationContext实例了。

public static WebApplicationContext getWebApplicationContext(ServletContext sc) {
        return getWebApplicationContext(sc, WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);
    }
public static WebApplicationContext getWebApplicationContext(ServletContext sc, String attrName) {
        Assert.notNull(sc, "ServletContext must not be null");
        Object attr = sc.getAttribute(attrName);
        ...
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

N3verL4nd

你的鼓励将是我创作的最大动力

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

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

打赏作者

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

抵扣说明:

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

余额充值