转载

Spring核心系列之ApplicationContext

Hello,大家好,今天开始,小弟准备推出Spring系列的博客,希望大家喜欢。关于Spring其实我就不用再多介绍了,做过Web开发的,基本都使用Spring,包括现在比较时尚的Spring cloud微服务架构,其实也是基于Spring boot ,Spring boot 说到底其实还是传统的Spring三件套,只是避免了用户自己配置Bean,而是采用了自动配置,Spring boot后期有时间也专门给大家出一系列博客。这一期作为Spring的第一篇,先来点基础的,OK,来套路,文章结构:

  1. Spring中的Resource接口.
  2. BeanFactory和ApplicationContext
  3. WebApplicationContext

1. Spring中的Resource接口.

其实对于很多业务开发程序员来讲,这个接口是比较陌生的,大家知道的是在初始化容器的时候直接指定文件路径,然后容器就初始化好了,这个Resource接口是Spring提供的,它为应用提供了更强的底层资源访问能力。比JDK自带的File和URL类不知道强了多少。列下类结构:

Spring核心系列之ApplicationContext

说几个重点的: ClassPathResource: 以类路径为相对路劲下的资源 。 FileSystemResource : 以文件系统路径查找的资源。 ServletContextResource : 以相对于Web应用跟目录的方式访问。 ByteArrayResource: 二进制数组表示的资源。 比如现在Web的路径下又一个Spring的配置文件,我们可以这样加载:

Resource res =new ServletContectResource(/WEB-INF/classes/spring/application.xml);

看到代码,相信比较敏感的小伙伴应该感觉到了,这种加载方式显然是不合适的,因为在使用不同的资源类型时,必须使用相应的Resource资源类,这是比较麻烦的,是否可以在不现实使用Resource实现类的情况下,仅通过资源地址的特殊标识就可以访问相应的资源呢?Spring这么伟大的框架,当然是可以的。 Spring不仅能够通过"classpath:","file"等资源地址前缀识别不同资源类型,还支持Ant风格带通配符的资源地址。Spring内部会通过资源加载器来根据不同的资源路径选择相应的Resource实现类。避免的使用者自己选择实现类。

1.1 资源地址表达式

先看下Spring内置的一些资源前缀表达的含义:

Spring核心系列之ApplicationContext

然后Ant风格:

  • ?:匹配文件名中的一个字符.
  • *:匹配文件命中的任意字符.
  • ** :匹配多层路径.

具体就不举例子了,Ant风格的通配符太常用了。

1.2 资源加载器

Spring核心系列之ApplicationContext

这些接口我就不多讲了,没什么实际意义,只说一个,PathMatchingResourcePatternResolver,这个是Spring提供的标准资源加载器,能够支持Ant风格的资源路径,并根据不同前缀类型返回相应的Resource.举个栗子:

public class GaoshiApplication {

	public static void main(String[] args) throws IOException {

		PathMatchingResourcePatternResolver resolver=new PathMatchingResourcePatternResolver();
		Resource[] res =resolver.getResources("file:/Users/zdy/Desktop/sql.txt");
		for (Resource  resource : res){
			System.out.println(resource.getDescription()+"----"+resource.getFilename());
		}
		SpringApplication.run(GaoshiApplication.class, args);
	}
}
输出结果:
URL [file:/Users/zdy/Desktop/sql.txt]----sql.txt

好了,其实这一小节为什么要给大家讲Resource和PathMatchingResourcePatternResolver资源加载器呢,很多人可能感觉没什么用,反正Spring内部自己使用就完了,跟我没关系。其实不是的,老铁们,Spring内部自己使用不假,首先,知道点底层原理不好吗?其次, 大家如果自己有配置文件在classpath下,然后想通过代码去加载(虽然这种需求非常少),那么大家就可以自己使用Spring提供的资源加载器了。这个加载器还是很方便的,直接根据前缀加载资源,而且还支持Ant通配符。你说厉害不厉害。

2. BeanFactory和ApplicationContext

2.1 BeanFactory

BeanFactory,其实很多人听过,只是没有用过,大家启动容器的时候可能都是用的ApplicationContext的某个实现类,所以这个BeanFactory慢慢的都淡了,更别说它的实现类了。说下特性:

  1. BeanFactory是延迟加载,如果Bean的某一个属性没有注入,BeanFacotry加载后,直至第一次使用调用getBean方法才会抛出异常;而ApplicationContext则在初始化自身是检验,这样有利于检查所依赖属性是否注入;所以通常情况下我们选择使用ApplicationContext。
  2. ApplicationContext继承自BeanFactory,提供的功能更加强大,一般情况下都是使用ApplicationContext

然后举个初始化BeanFactory的例子吧:

//调用资源加载器加载Spring配置文件
		PathMatchingResourcePatternResolver resolver=new PathMatchingResourcePatternResolver();
		Resource res =resolver.getResource("classpath:/application.xml");
		
		//创建默认Spring提供的BeanFactory实现类
		DefaultListableBeanFactory bf =new DefaultListableBeanFactory();
		
		//BeanDefinition 读取器,专门读取资源到容器
		XmlBeanDefinitionReader reader =new XmlBeanDefinitionReader(bf);
		
		//读取资源进到容器 
		reader.loadBeanDefinitions(res);
		
		//此时容器初始化完毕后可以取里面的bean了.
		bf.getBean("...");

可以看到,代码比较繁琐,而且很啰嗦。BeanFactory的功能还比较弱,所以大家就当了解吧。

2.2 ApplicationContext

这个ApplicationContext其实就是老铁们经常说的Spring容器了。因为相对要重要一些,所以列了大致的类继承图:

Spring核心系列之ApplicationContext

说下图中几个重要的类或接口:

  • ApplicationEventPublisher: 让容器拥有发布应用程序上下文事件的功能.
  • ResourcePatternResolver:实现了类似于资源加载器的功能,能够识别特定前缀加Ant风格的路径并加载到容器中.
  • LifeCycle:管理容器中bean的生命周期。
  • ConfigurableApplicationContext:主要新增了refresh()和close()方法,让ApplicationContext有用了启动,关系和刷新上下文的功能。
  • ClassPathXmlApplicationContext与FileSystemXmlApplicationContext :Spring提供的两个常用的ApplicationContext实现类,前者默认从类路径加载配置文件,后者从文件系统加载。 然后初始化一个ApplicationContext看看:
ApplicationContext ac =new ClassPathXmlApplicationContext("application.xml");
ApplicationContext ac =new FileSystemXmlApplicationContext(/User/Desktop/application.xml);

ClassPathXmlApplicationContext如果没有前缀默认就是classpath: FileSystemXmlApplicationContext如果没有前缀默认就是 file:

然后再说一个,Spring容器不仅可以通过xml文件来初始化,@Configuration注解大家应该知道,也是注册Bean用的,当然了,Spring也提供了相应的AnnotationConfigApplicationContext.

ApplicationContext context =new AnnotationConfigApplicationContext(Config.class);

Config类就是我们加了@Configuration的类了。

3. WebApplicationContext

其实WebApplicationContext也属于ApplicationContext,为什么单独拿出来讲呢,因为比较重要。WebApplicationContext是Spring专门为Web应用准备的。它允许从相对于Web根目录的路径中装载配置文件完成初始化工作。 WebApplicationContext与ServletContext可以相互获得 .在非Web应用的环境下,Bean只有singleton和prototype两种作用域,而WebApplicationContext为Bean添加了三个新的作用域:request,session,global session。来看下类继承图:

Spring核心系列之ApplicationContext
最最核心的XmlWebApplicationContext和AnnotationConfigWebApplicationContext,大家猜都应该猜到了,一个是XML配置的,一个是@Configuration配置的。

然后说下WebApplicationContext和ServletContext是如何相互获取的,其实很简单, 在WebApplicationContext里有ServletContext成员变量,直接get就完了。在ServletContext里有一个写死的attrbute,也是直接get..而且Spring提供了一个WebApplicationContextUtils来封装了这个写死的attribute.

其实就是直接调用了ServletContext.getAttribute(ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);
    public static WebApplicationContext getWebApplicationContext(ServletContext sc) {
        return getWebApplicationContext(sc, WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);
    }
Spring核心系列之ApplicationContext

然后说下WebApplication的初始化,做过Web的都知道,其实要么在Web.xml里面配置一个ContextLoaderListener,要么配置一个自启动的LoaderServlet.其实比较简单了,我展示下ContextLoaderListener.

<!-- 加载spring容器 -->
  <context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:spring/applicationContext-*.xml</param-value>
  </context-param>
  <listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  </listener>

如果不是根据xml文件启动Spring容器而是根据@Configuration类启动,其实也很简单.

<context-param>
    <param-name>contextClass</param-name>
    <param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext</param-value>
  </context-param>
  <!-- 加载spring容器 -->
  <context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>com.zdy.Configuration</param-value>
  </context-param>
  <listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  </listener>

配置一个contextClass参数ApplicationContext改为AnnotationConfigWebApplicationContext,然后contextConfigLocation不是指定配置文件xml位置了,改为指定Configuration类的位置。就OK了。

最后,需要强调的是, 由于Spring容器自带的Log4j功能,所以用户可以直接将Log4j的配置文件放置在类路径下,直接就生效了 ,但是,如果不在类路径下,需要在Web.xml中手动指定文件的位置和启动log4j的listener,这里我就不演示了,需要注意的是,这个log4j的listener或者servlet必须在spring容器的listenier或servlet前面。不要问为什么。就是这么规定的。不过这点大家不常用,所以我只是提一提。

结语

好了,其实大家也看出来了,讲Spring的时候我还是偏向于使用的,Spring的底层深层次的原理其实设计到的不多。Spring这个至尊框架,我觉得先要宏观使用上先整明白。后期有机会给大家多讲点底层实现。喜欢大家多关注接下来的Spring系列文章。 Over,Have a good day .

原文  https://juejin.im/post/5a4d92d8f265da4311209f40
正文到此结束
Loading...