SpringBoot ApplicationContext接口深入分析

目录
  • ApplicationContext简述
  • Spring IOC容器实现方式
  • ApplicationContext接口梳理
  • AnnotationConfigApplicationContext
  • MessageSource
  • 修改Springboot application.yml配置
  • 创建国际化资源文件
  • ApplicationEventPublisher
  • ResourcePatternResolver
  • EnvironmentCapable
  • Lifecycle
  • Closeable
  • BeanFactory
  • 总结
  • ApplicaionContext创建
  • AbstractApplicationContext
  • GenericApplicationContext
  • AnnotationConfigApplicationContext

ApplicationContext简述

ApplicationContext代表IOC容器,在SpringIOC容器中读取Bean配置创建Bean实例之前,必须对它进行实例化,只有在容器实例化后才可以从IOC容器里获取Bean实例并使用。

Spring IOC容器实现方式

Spring 提供了两种类型的IOC容器实现:

  • BeanFactory:IOC容器的基本实现。
  • ApplicationContext:提供了更多的高级特性,是BeanFactory的子接口。

两种方式比较:

  • BeanFactory:BeanFactory是Spring框架的基础设施,面向Spring本身
  • ApplicationContext : 面向使用Spring框架的开发者,**几乎所有的应用场合都直接使用ApplicationContext而非底层的BeanFactory。**无论使用何种方式,配置文件是相同的。

ApplicationContext接口梳理

protected ConfigurableApplicationContext createApplicationContext() {
		Class<?> contextClass = this.applicationContextClass;
		if (contextClass == null) {
			try {
				switch (this.webApplicationType) {
				case SERVLET:
					contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS);
					break;
				case REACTIVE:
					contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
					break;
				default:
					contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
				}
			}
			catch (ClassNotFoundException ex) {
				throw new IllegalStateException(
						"Unable create a default ApplicationContext, please specify an ApplicationContextClass", ex);
			}
		}
		return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
	}

根据不同的环境创建不同的ApplicationContext实现

这里看一个基础的实现

AnnotationConfigApplicationContext

类图分析

根据类图看,ApplicationContext也是一个bean工厂的实现,继承自ResourceLoader也说明其具备加载文件,扫描bean的能力。ApplicationEventPublisher事件发布的能力

  • BeanFactory:Spring 管理 Bean 的顶层接口,ApplicationContext 继承了 BeanFactory 的两个子类:HierarchicalBeanFactory 和 ListableBeanFactory。HierarchicalBeanFactory 是一个具有层级关系的 BeanFactory,拥有属性 parentBeanFactory。 ListableBeanFactory 实现了枚举方法可以列举出当前 BeanFactory 中所有的 bean 对象而不必根据 name 一个一个的获取。
  • ApplicationEventPublisher:用于封装事件发布功能的接口,向事件监听器(Listener)发送事件消息。
  • ResourceLoader:Spring 加载资源的顶层接口,用于加载资源文件。ApplicationContext 继承 ResourceLoader 的子类 ResourcePatternResolver,该接口是将 location 解析为 Resource 对象的策略接口。
  • MessageSource :解析 message 的策略接口,用于支撑国际化等功能。
  • EnvironmentCapable :用于获取 Environment 的接口。

下面就这些接口来一一分析

MessageSource

MessageSource 定义了获取 message 的策略方法 getMessage(),在 ApplicationContext 体系中,该方法 AbstractApplicationContext 实现,在 AbstractApplicationContext 中,它持有一个 MessageSource 实例,将 getMessage() 的实现给该实例来实现,如下:

private MessageSource messageSource;
public String getMessage(MessageSourceResolvable resolvable, Locale locale) throws NoSuchMessageException {
    return this.getMessageSource().getMessage(resolvable, locale);
}
private MessageSource getMessageSource() throws IllegalStateException {
    if (this.messageSource == null) {
        throw new IllegalStateException("MessageSource not initialized - call 'refresh' before accessing messages via the context: " + this);
    } else {
        return this.messageSource;
    }
}

此外还有一个 initMessageSource()方法,在 refresh()中被调用,用来初始化一些国际化的属性。

protected void initMessageSource() {
    ConfigurableListableBeanFactory beanFactory = this.getBeanFactory();
    if (beanFactory.containsLocalBean("messageSource")) {
        this.messageSource = (MessageSource)beanFactory.getBean("messageSource", MessageSource.class);
        if (this.parent != null && this.messageSource instanceof HierarchicalMessageSource) {
            HierarchicalMessageSource hms = (HierarchicalMessageSource)this.messageSource;
            if (hms.getParentMessageSource() == null) {
                hms.setParentMessageSource(this.getInternalParentMessageSource());
            }
        }
        if (this.logger.isTraceEnabled()) {
            this.logger.trace("Using MessageSource [" + this.messageSource + "]");
        }
    } else {
        DelegatingMessageSource dms = new DelegatingMessageSource();
        dms.setParentMessageSource(this.getInternalParentMessageSource());
        this.messageSource = dms;
        beanFactory.registerSingleton("messageSource", this.messageSource);
        if (this.logger.isTraceEnabled()) {
            this.logger.trace("No 'messageSource' bean, using [" + this.messageSource + "]");
        }
    }
}

什么是国际化?

当我们web项目涉及到国外部署或者国外用户使用时,需要展示不同语言信息,所以就需要国际化支持,下面将讲解Springboot国际化支持操作

例如抛异常,我门希望在不同的环境能够显示不同的报错

修改Springboot application.yml配置

spring:
  messages:
    basename: i18n/messages #配置国际化资源文件路径

创建国际化资源文件

messages.properties不带后缀为默认语言资源

简体中文 messages_zh_CN.properties

unknown.exception=未知异常,请联系管理员!
user.login.notExists={0} 用户不存在!

英文 messages_en_US.properties

unknown.exception=Unknown error,Please contact the administrator!
user.login.notExists={0} user not exists!

messages.properties文件内容就和简体中文文件一致,如果未设置Locale参数,默认就为该文件内容,此文件也可不用

unknown.exception=未知异常,请联系管理员!
user.login.notExists={0} 用户不存在!

获取错误信息通过messageSource获取

public class MessageUtils {
	/**
	 * 根据消息键和参数 获取消息 委托给spring messageSource
	 * @param code 消息键
	 * @param args 参数
	 * @return 获取国际化翻译值
	 */
	public static String message(String code, Object... args) {
		MessageSource messageSource = SpringUtils.getBean(MessageSource.class);
		return messageSource.getMessage(code, args, LocaleContextHolder.getLocale());
	}
}

ApplicationEventPublisher

用于封装事件发布功能的接口,向事件监听器(Listener)发送事件消息。

该接口提供了一个 publishEvent() 用于通知在此应用程序中注册的所有的监听器。该方法在 AbstractApplicationContext 中实现。

public void publishEvent(ApplicationEvent event) {
    this.publishEvent(event, (ResolvableType)null);
}
public void publishEvent(Object event) {
    this.publishEvent(event, (ResolvableType)null);
}
protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
    Assert.notNull(event, "Event must not be null");
    Object applicationEvent;
    if (event instanceof ApplicationEvent) {
        applicationEvent = (ApplicationEvent)event;
    } else {
        applicationEvent = new PayloadApplicationEvent(this, event);
        if (eventType == null) {
            eventType = ((PayloadApplicationEvent)applicationEvent).getResolvableType();
        }
    }
    if (this.earlyApplicationEvents != null) {
        this.earlyApplicationEvents.add(applicationEvent);
    } else {
        this.getApplicationEventMulticaster().multicastEvent((ApplicationEvent)applicationEvent, eventType);
    }
    if (this.parent != null) {
        if (this.parent instanceof AbstractApplicationContext) {
            ((AbstractApplicationContext)this.parent).publishEvent(event, eventType);
        } else {
            this.parent.publishEvent(event);
        }
    }
}

根据不同的事件类型触发对应的监听器,并且会触发父容器的发布发布事件

ResourcePatternResolver

ResourcePatternResolver 接口继承 ResourceLoader 接口,为将 location 解析为 Resource 对象的策略接口。它提供的 getResources() 在 AbstractApplicationContext 中实现,在 AbstractApplicationContext 中它持有一个 ResourcePatternResolver 的实例对象。 其定义如下:

public Resource[] getResources(String locationPattern) throws IOException {
    return this.resourcePatternResolver.getResources(locationPattern);
}

该方法的具体实现在 PathMatchingResourcePatternResolve

ResourcePatternResolver 在 ResourceLoader 的基础上增加了 getResources(String locationPattern),以支持根据路径匹配模式返回多个 Resource 实例,同时也新增了一种新的协议前缀 classpath*:,该协议前缀由其子类负责实现。

PathMatchingResourcePatternResolver 为 ResourcePatternResolver 最常用的子类,它除了支持 ResourceLoader 和 ResourcePatternResolver 新增的 classpath*: 前缀外,还支持 Ant 风格的路径匹配模式(类似于 **/*.xml)。

EnvironmentCapable

提供当前系统环境 Environment 组件。提供了一个 getEnvironment() 用于返回 Environment 实例对象,该方法在 AbstractApplicationContext 实现。

public ConfigurableEnvironment getEnvironment() {
    if (this.environment == null) {
        this.environment = this.createEnvironment();
    }
    return this.environment;
}

如果持有的 environment 实例对象为空,则调用 createEnvironment() 创建一个。

 protected ConfigurableEnvironment createEnvironment() {
  return new StandardEnvironment();
 }

StandardEnvironment 是一个适用于非 WEB 应用的 Environment。

Lifecycle

一个用于管理声明周期的接口。

在 AbstractApplicationContext 中存在一个 LifecycleProcessor 类型的实例对象 lifecycleProcessor,AbstractApplicationContext 中关于 Lifecycle 接口的实现都是委托给 lifecycleProcessor 实现的。如下:

public void start() {
    this.getLifecycleProcessor().start();
    this.publishEvent((ApplicationEvent)(new ContextStartedEvent(this)));
}
public void stop() {
    this.getLifecycleProcessor().stop();
    this.publishEvent((ApplicationEvent)(new ContextStoppedEvent(this)));
}
public boolean isRunning() {
    return this.lifecycleProcessor != null && this.lifecycleProcessor.isRunning();
}

在启动、停止的时候会分别发布 ContextStartedEvent 和 ContextStoppedEvent 事件。

Closeable

Closeable 接口用于关闭和释放资源,提供了 close() 以释放对象所持有的资源。在 ApplicationContext 体系中由AbstractApplicationContext 实现,用于关闭 ApplicationContext 销毁所有 bean ,此外如果注册有 JVM shutdown hook,同样要将其移除。如下:

public void close() {
    synchronized(this.startupShutdownMonitor) {
        this.doClose();
        if (this.shutdownHook != null) {
            try {
                Runtime.getRuntime().removeShutdownHook(this.shutdownHook);
            } catch (IllegalStateException var4) {
            }
        }
    }
}

调用 doClose() 发布 ContextClosedEvent 事件,销毁所有 bean(单例),关闭 BeanFactory 。如下:

protected void doClose() {
    if (this.active.get() && this.closed.compareAndSet(false, true)) {
        if (this.logger.isDebugEnabled()) {
            this.logger.debug("Closing " + this);
        }
        LiveBeansView.unregisterApplicationContext(this);
        try {
            this.publishEvent((ApplicationEvent)(new ContextClosedEvent(this)));
        } catch (Throwable var3) {
            this.logger.warn("Exception thrown from ApplicationListener handling ContextClosedEvent", var3);
        }
        if (this.lifecycleProcessor != null) {
            try {
                this.lifecycleProcessor.onClose();
            } catch (Throwable var2) {
                this.logger.warn("Exception thrown from LifecycleProcessor on context close", var2);
            }
        }
        this.destroyBeans();
        this.closeBeanFactory();
        this.onClose();
        if (this.earlyApplicationListeners != null) {
            this.applicationListeners.clear();
            this.applicationListeners.addAll(this.earlyApplicationListeners);
        }
        this.active.set(false);
    }
}

BeanFactory

applicationContext对beanFactory的实现实际上基本上都是通过成员变量来实现的DefaultListableBeanFactory

在applicationContext的实现GenericApplicationContext中

@Override
public void registerBeanDefinition(String beanName,BeanDefinition beanDefinition)
throws BeanDefinitionStoreException {
this.beanFactory.registerBeanDefinition(beanName,beanDefinition);
}
@Override
public void removeBeanDefinition(String beanName) throws NoSuchBeanDefinitionException{
this.beanFactory. removeBeanDefinition(beanName);
}
@Override
public BeanDefinition getBeanDefinition(String beanName) throws NoSuchBeanDefinitionException{ 
return this.beanFactory.getBeanDefinition(beanName);
}

总结

ApplicationContext集大成者

在实现对应接口功能时都是使用对应的实现类去做,而不是自己实现,这点在ApplicationContext上十分常见,继承自功能接口,这样很容易看出ApplicationContext具备的功能,但是自身并不实现,而调用对应的实现类。继承了很多的功能。

ApplicaionContext创建

protected ConfigurableApplicationContext createApplicationContext() {
		Class<?> contextClass = this.applicationContextClass;
		if (contextClass == null) {
			try {
				switch (this.webApplicationType) {
				case SERVLET:
					contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS);
					break;
				case REACTIVE:
					contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
					break;
				default:
					contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
				}
			}
			catch (ClassNotFoundException ex) {
				throw new IllegalStateException(
						"Unable create a default ApplicationContext, please specify an ApplicationContextClass", ex);
			}
		}
		return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
	}

springBoot会创建一个ConfigurableApplicationContext

通过webApplicationType判断创建什么类型的Context,如果是SERVLET那么实例化

AnnotationConfigServletWebServerApplicationContext,利用反射调用无参构造器进行实例化。

webApplicationType推断方法

static WebApplicationType deduceFromClasspath() {
		if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)
				&& !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
			return WebApplicationType.REACTIVE;
		}
		for (String className : SERVLET_INDICATOR_CLASSES) {
			if (!ClassUtils.isPresent(className, null)) {
				return WebApplicationType.NONE;
			}
		}
		return WebApplicationType.SERVLET;
	}

根据classPath下是对对应的类,来判断类型

根据webApplicationType加载对应的类

调用其构造函数进行初始化我这里看AnnotationConfigApplicationContext的构造过程

接下来通过构造方法看ApplicationContext的创建的过程

AbstractApplicationContext

public AbstractApplicationContext() {
		this.resourcePatternResolver = getResourcePatternResolver();
	}
	protected ResourcePatternResolver getResourcePatternResolver() {
		return new PathMatchingResourcePatternResolver(this);
	}

PathMatchingResourcePatternResolver 为 ResourcePatternResolver 最常用的子类,它除了支持 ResourceLoader 和 ResourcePatternResolver 新增的 classpath*: 前缀外,还支持 Ant 风格的路径匹配模式(类似于 **/*.xml)。用于扫描类路径下的类

GenericApplicationContext

	public GenericApplicationContext() {
		this.beanFactory = new DefaultListableBeanFactory();
	}

创建bean工厂,初始化一个空的bean工厂,后续注册获取bean都依靠这个bean工厂进行实现

AnnotationConfigApplicationContext

public AnnotationConfigApplicationContext() {
		this.reader = new AnnotatedBeanDefinitionReader(this);
		this.scanner = new ClassPathBeanDefinitionScanner(this);
	}

构造函数中创建了AnnotatedBeanDefinitionReader和 ClassPathBeanDefinitionScanner

AnnotatedBeanDefinitionReader是一个根据指定类注册BeanDefinnation的功能,同时能根据Condition跳过未达到条件的Bean

ClassPathBeanDefinitionScannery

BeanDeifination扫描类

继承自RespourceAware和EnviromentCapable

设置environment

	private static Environment getOrCreateEnvironment(BeanDefinitionRegistry registry) {
		Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
		if (registry instanceof EnvironmentCapable) {
			return ((EnvironmentCapable) registry).getEnvironment();
		}
		return new StandardEnvironment();
	}

设置资源加载器resourceLoader通过资源加载,和环境完成bean的扫描

	@Override
	public void setResourceLoader(@Nullable ResourceLoader resourceLoader) {
		this.resourcePatternResolver = ResourcePatternUtils.getResourcePatternResolver(resourceLoader);
		this.metadataReaderFactory = new CachingMetadataReaderFactory(resourceLoader);
		this.componentsIndex = CandidateComponentsIndexLoader.loadIndex(this.resourcePatternResolver.getClassLoader());
	}

到此这篇关于SpringBoot ApplicationContext接口深入分析的文章就介绍到这了,更多相关SpringBoot ApplicationContext内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Android ApplicationContext接口深入分析

    目录 需求 实现方法 代码 调用 Application getApplicationContext() 参考 需求 Android(Kotlin)获取应用全局上下文 ApplicationContext. 希望可以在应用内任意位置获取上下文,而不是仅在 Activity 或 Service 里才能获取. ApplicationContext 是和应用全局相关的. 实现方法 自定义 MyApplication,保存自身的 Application 实例. MyApplication 配置到 And

  • SpringBoot ApplicationContext接口深入分析

    目录 ApplicationContext简述 Spring IOC容器实现方式 ApplicationContext接口梳理 AnnotationConfigApplicationContext MessageSource 修改Springboot application.yml配置 创建国际化资源文件 ApplicationEventPublisher ResourcePatternResolver EnvironmentCapable Lifecycle Closeable BeanFac

  • SpringBoot 钩子接口的实现代码

    目录 Aware 接口族 InitializingBean BeanPostProcessor BeanFactoryPostProcessor ImportSelector ImportBeanDefinitionRegistrar FactoryBean ApplicationListener ApplicationRunner Aware 接口族 Aware 意为感知,实现 Aware 接口并重写其方法,可以从上下文中获取当前的运行环境 常见的 aware 接口 BeanNameAware

  • Java SpringBoot 获取接口实现类汇总

    目录 前言 一.获取接口的所有实现类 1.枚举 2.业务接口 2.1 实现类 3.ApplicationContextAware接口实现类 4.获取到所有实现类使用 前言 有时候,根据业务逻辑的需求,需要获取到某个接口的所有实现类,然后根据业务类型来执行不同的实现类方法.有点类似策略模式. 如果没有用到 Spring的话,可以使用 ServiceLoaderl类JDK自带的一个类加载器(其他框架的SPI机制也是可以实现). ServiceLoader<MyInterface> loader =

  • 浅谈spring-boot 允许接口跨域并实现拦截(CORS)

    本文介绍了spring-boot 允许接口跨域并实现拦截(CORS),分享给大家,也给自己留个笔记 pom.xml(依赖的jar) // 在spring-boot-starter-web的启动器中,已经依赖好了 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependen

  • SpringBoot设置接口超时时间的方法

    SpringBoot设置接口访问超时时间有两种方式 一.在配置文件application.properties中加了spring.mvc.async.request-timeout=20000,意思是设置超时时间为20000ms即20s, 二.还有一种就是在config配置类中加入: public class WebMvcConfig extends WebMvcConfigurerAdapter { @Override public void configureAsyncSupport(fin

  • SpringBoot实现接口数据的加解密功能

    一.加密方案介绍 对接口的加密解密操作主要有下面两种方式: 自定义消息转换器 优势:仅需实现接口,配置简单. 劣势:仅能对同一类型的MediaType进行加解密操作,不灵活. 使用spring提供的接口RequestBodyAdvice和ResponseBodyAdvice 优势:可以按照请求的Referrer.Header或url进行判断,按照特定需要进行加密解密. 比如在一个项目升级的时候,新开发功能的接口需要加解密,老功能模块走之前的逻辑不加密,这时候就只能选择上面的第二种方式了,下面主要

  • springboot CommandLineRunner接口实现自动任务加载功能

    CommandLineRunner接口可以实现任务的自动加载,当项目启动完后,就会自动去执行CommandLineRunner接口里的run方法,你可以实现多个CommandLineRunner的实例,使用order来控制执行的顺序! /** * 项目启动后自动运行的代码CommandLineRunner */ @Component @Order(1) public class MyStartupRunner1 implements CommandLineRunner { private Log

  • springboot统一接口返回数据的实现

    一,没有异常的情况,正常返回数据 希望接口统一返回的数据格式如下: { "status": 0, "msg": "成功", "data": null } 和接口数据对应的bean /** * 统一返回结果的实体 * @param <T> */ public class Result<T> implements Serializable { private static final long serial

  • SpringBoot后端接口的实现(看这一篇就够了)

    摘要:本文演示如何构建起一个优秀的后端接口体系,体系构建好了自然就有了规范,同时再构建新的后端接口也会十分轻松. 一个后端接口大致分为四个部分组成:接口地址(url).接口请求方式(get.post等).请求数据(request).响应数据(response).如何构建这几个部分每个公司要求都不同,没有什么"一定是最好的"标准,但一个优秀的后端接口和一个糟糕的后端接口对比起来差异还是蛮大的,其中最重要的关键点就是看是否规范! 本文就一步一步演示如何构建起一个优秀的后端接口体系,体系构建

随机推荐