- 使用 Spring Initializr 创建 Spring Boot 应用程序
- 在Spring Boot中配置Cassandra
- 在 Spring Boot 上配置 Tomcat 连接池
- 将Camel消息路由到嵌入WildFly的Artemis上
上一篇解析了 DispatcherServlet
与 ContextLoaderListener
这两个类,解析了应用初始化与请求处理的流程,但还有一些组件需要解析:
ConfigurableWebApplicationContext.refresh
刷新上下文ApplicationContext.getBean
从上下文中获取 beanDispatcherServlet.properties
文件中定义的策略处理ContextLoader.properties
文件中定义的策略处理View.render
视图渲染这一章来看看 ContextLoader.properties
文件中定义的策略处理
ContextLoader.properties
文件中只定义了一个策略
org.springframework.web.context.WebApplicationContext=org.springframework.web.context.support.XmlWebApplicationContext
默认使用 XmlWebApplicationContext
(基于 XML 加载)作为应用上下文
spring-web
内部定义 5 个应用上下文类:
WebApplicationContext
的基础实现,但不能通过配置文件和注解加载应用配置与 bean,一般用于扩展实现(如 SpringBoot),很少直接使用WebApplicationContext
的基础实现,但不支持 i18n,主要用于测试,不用产品环境WebApplicationContext
实现,是 SpringMVC 的默认 Context@Configuration, @bean
等加载应用配置与 bean 的WebApplicationContext
实现XmlWebApplicationContext
的实现差不多,但可以用 Groovy 代替 xml 做配置文件,目前用得不多先来看看这 5 个应用上下文类各自的继承关系
- DefaultResourceLoader
- AbstractApplicationContext
- GenericApplicationContext
- GenericWebApplicationContext
- DefaultResourceLoader
- AbstractApplicationContext
- GenericApplicationContext
- StaticApplicationContext
- StaticWebApplicationContext
- DefaultResourceLoader
- AbstractApplicationContext
- AbstractRefreshableApplicationContext
- AbstractRefreshableConfigApplicationContext
- AbstractRefreshableWebApplicationContext
- XmlWebApplicationContext
- DefaultResourceLoader
- AbstractApplicationContext
- AbstractRefreshableApplicationContext
- AbstractRefreshableConfigApplicationContext
- AbstractRefreshableWebApplicationContext
- AnnotationConfigWebApplicationContext
- DefaultResourceLoader
- AbstractApplicationContext
- AbstractRefreshableApplicationContext
- AbstractRefreshableConfigApplicationContext
- AbstractRefreshableWebApplicationContext
- GroovyWebApplicationContext
我们可以发现每个类都继承 AbstractApplicationContext
,而 XmlWebApplicationContext
, AnnotationConfigWebApplicationContext
,GroovyWebApplicationContext
都继承 AbstractRefreshableWebApplicationContext
DefaultResourceLoader
的主要功能是实现资源加载
public class DefaultResourceLoader implements ResourceLoader {}
先来看看接口ResourceLoader
public interface ResourceLoader {
// 根据一个字符位置信息获取资源
Resource getResource(String location);
// 获取资源加载器
ClassLoader getClassLoader();
}
DefaultResourceLoader
是 ResourceLoader
的默认实现
public class DefaultResourceLoader implements ResourceLoader {
@Override
public ClassLoader getClassLoader() {
// 如果有指定的classLoader,则返回指定的,没有则返回默认的
return (this.classLoader != null ? this.classLoader : ClassUtils.getDefaultClassLoader());
}
@Override
public Resource getResource(String location) {
// 自定义协议解析
for (ProtocolResolver protocolResolver : getProtocolResolvers()) {
Resource resource = protocolResolver.resolve(location, this);
if (resource != null) {
return resource;
}
}
// 如果以/开头,则认为是classpath资源
if (location.startsWith("/")) {
return getResourceByPath(location);
}
// 如果以classpath:开头的classpath资源
else if (location.startsWith(CLASSPATH_URL_PREFIX)) {
return new ClassPathResource(location.substring(CLASSPATH_URL_PREFIX.length()), getClassLoader());
}
else {
try {
// 尝试以文件或url对待
URL url = new URL(location);
return (ResourceUtils.isFileURL(url) ? new FileUrlResource(url) : new UrlResource(url));
}
catch (MalformedURLException ex) {
// 失败则默认是classpath资源
return getResourceByPath(location);
}
}
}
}
AbstractApplicationContext
的主要功能是通过名字、类型或注解获取 bean 实例,获取上下文的环境对象与资源、刷新上下文数据
public abstract class AbstractApplicationContext extends DefaultResourceLoader
implements ConfigurableApplicationContext {}
接口 ConfigurableApplicationContext
及其继承的接口主要定义以下的方法
public interface ConfigurableApplicationContext {
// 获取bean
Object getBean(String name) throws BeansException;
<T> T getBean(String name, Class<T> requiredType) throws BeansException;
Object getBean(String name, Object... args) throws BeansException;
<T> T getBean(Class<T> requiredType) throws BeansException;
<T> T getBean(Class<T> requiredType, Object... args) throws BeansException;
// 通过类型或注解获取bean
<T> Map<String, T> getBeansOfType(@Nullable Class<T> type) throws BeansException;
<T> Map<String, T> getBeansOfType(@Nullable Class<T> type, boolean includeNonSingletons, boolean allowEagerInit)
throws BeansException;
Map<String, Object> getBeansWithAnnotation(Class<? extends Annotation> annotationType) throws BeansException;
// 获取环境
ConfigurableEnvironment getEnvironment();
// 刷新上下文数据
void refresh() throws BeansException, IllegalStateException;
// 根据locationPattern获取多个资源,如通配符*
Resource[] getResources(String locationPattern) throws IOException;
}
AbstractApplicationContext.getEnvironment
获取环境
public abstract class AbstractApplicationContext extends DefaultResourceLoader
implements ConfigurableApplicationContext {
@Override
public ConfigurableEnvironment getEnvironment() {
if (this.environment == null) {
this.environment = createEnvironment();
}
return this.environment;
}
protected ConfigurableEnvironment createEnvironment() {
// 内置的标准环境(也可以通过setEnvironment方法自定义环境处理机制)
// 这是可以使用 `application-dev.yml, application-test.yml, application-prod.yml, ...` 来根据环境加载不同的配置的底层实现
// 是spring-boot的基本功能
return new StandardEnvironment();
}
}
AbstractApplicationContext.getBean
获取 bean
public abstract class AbstractApplicationContext extends DefaultResourceLoader
implements ConfigurableApplicationContext {
@Override
public Object getBean(String name) throws BeansException {
return getBeanFactory().getBean(name);
}
@Override
public <T> T getBean(String name, Class<T> requiredType) throws BeansException {
return getBeanFactory().getBean(name, requiredType);
}
@Override
public Object getBean(String name, Object... args) throws BeansException {
return getBeanFactory().getBean(name, args);
}
@Override
public <T> T getBean(Class<T> requiredType) throws BeansException {
return getBeanFactory().getBean(requiredType);
}
@Override
public <T> T getBean(Class<T> requiredType, Object... args) throws BeansException {
return getBeanFactory().getBean(requiredType, args);
}
// 留给子类实现
public abstract ConfigurableListableBeanFactory getBeanFactory() throws IllegalStateException;
}
因为不同的Context
注册 bean 的方式不一样,所以getBeanFactory
留给子类来实现
AbstractApplicationContext.getBeansOfType
通过类型或注解获取 bean
public abstract class AbstractApplicationContext extends DefaultResourceLoader
implements ConfigurableApplicationContext {
@Override
public <T> Map<String, T> getBeansOfType(@Nullable Class<T> type) throws BeansException {
return getBeanFactory().getBeansOfType(type);
}
@Override
public <T> Map<String, T> getBeansOfType(@Nullable Class<T> type, boolean includeNonSingletons, boolean allowEagerInit)
throws BeansException {
return getBeanFactory().getBeansOfType(type, includeNonSingletons, allowEagerInit);
}
@Override
public Map<String, Object> getBeansWithAnnotation(Class<? extends Annotation> annotationType)
throws BeansException {
return getBeanFactory().getBeansWithAnnotation(annotationType);
}
// 留给子类实现
public abstract ConfigurableListableBeanFactory getBeanFactory() throws IllegalStateException;
}
AbstractApplicationContext.refresh
刷新上下文数据
public abstract class AbstractApplicationContext extends DefaultResourceLoader
implements ConfigurableApplicationContext {
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// ... 代码省略
// 初始化事件容器与监听器,检查必须的属性配置,并载入必要的实例
prepareRefresh();
// 刷新上下文的bean,获取bean工厂
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// 预备bean工厂
prepareBeanFactory(beanFactory);
try {
// 后置处理bean工厂
postProcessBeanFactory(beanFactory);
// ... 代码省略
// 调用bean工厂的后置处理器,以使在所有bean实例化之前,可以自定义添加自己的BeanPostProcessor(bean实例化后置操作)
invokeBeanFactoryPostProcessors(beanFactory);
// 给bean工厂注册BeanPostProcessor(bean实例化后置操作)
registerBeanPostProcessors(beanFactory);
// ... 代码省略
// 实例化applicationEventMulticaster bean,作为应用事件广播器
initApplicationEventMulticaster();
// 扩展实现,留给开发者,默认不实现
onRefresh();
// 注册应用事件监听器
registerListeners();
// 初始化所有单例的bean
finishBeanFactoryInitialization(beanFactory);
// 刷新上下文数据完成,做一些后续处理
finishRefresh();
}
catch (BeansException ex) {
// ... 代码省略
}
finally {
// ... 代码省略
}
}
}
// 刷新上下文的bean,获取bean工厂
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
// 刷新上下文的bean
refreshBeanFactory();
// 获取bean工厂
return getBeanFactory();
}
// 刷新上下文的bean,由子类实现
protected abstract void refreshBeanFactory() throws BeansException, IllegalStateException;
// 获取bean工厂,由子类实现
public abstract ConfigurableListableBeanFactory getBeanFactory() throws IllegalStateException;
// 扩展实现,留给开发者,默认不实现
protected void onRefresh() throws BeansException {}
// 初始化所有单例的bean
protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
// ... 代码省略
// 固化所有bean的配置,后面不再更改
beanFactory.freezeConfiguration();
// 初始化所有单例的bean
beanFactory.preInstantiateSingletons();
}
// 刷新上下文数据完成,做一些后续处理
protected void finishRefresh() {
// 清除一些资源缓存
clearResourceCaches();
// 实例化lifecycleProcessor bean
initLifecycleProcessor();
// 实例化Lifecycle bean,并调用这些bean的start方法
getLifecycleProcessor().onRefresh();
// 派发事件
publishEvent(new ContextRefreshedEvent(this));
// ... 代码省略
}
}
Context
注册 bean 的方式不一样,所以refreshBeanFactory, postProcessBeanFactory
留给子类来实现ConfigurableListableBeanFactory
如何加载、实例化 bean,后面再解析AbstractApplicationContext.prepareBeanFactory
预备 bean 工厂
public abstract class AbstractApplicationContext extends DefaultResourceLoader
implements ConfigurableApplicationContext {
protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
// ... 代码省略
// 添加对 #{} SpEL Spring 表达式语言的支持
if (!shouldIgnoreSpel) {
beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));
}
// 添加属性编辑器,xml、yaml 中定义的值转换成对象就是依赖这里实现的
beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));
// 添加一个BeanPostProcessor,后置处理器
beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));
// ... 代码省略
// 注册几个可以autowirable自动载入的实例
beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
beanFactory.registerResolvableDependency(ResourceLoader.class, this);
beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
beanFactory.registerResolvableDependency(ApplicationContext.class, this);
// ... 代码省略
// 注册几个单例bean
if (!beanFactory.containsLocalBean(ENVIRONMENT_BEAN_NAME)) {
beanFactory.registerSingleton(ENVIRONMENT_BEAN_NAME, getEnvironment());
}
if (!beanFactory.containsLocalBean(SYSTEM_PROPERTIES_BEAN_NAME)) {
beanFactory.registerSingleton(SYSTEM_PROPERTIES_BEAN_NAME, getEnvironment().getSystemProperties());
}
if (!beanFactory.containsLocalBean(SYSTEM_ENVIRONMENT_BEAN_NAME)) {
beanFactory.registerSingleton(SYSTEM_ENVIRONMENT_BEAN_NAME, getEnvironment().getSystemEnvironment());
}
if (!beanFactory.containsLocalBean(APPLICATION_STARTUP_BEAN_NAME)) {
beanFactory.registerSingleton(APPLICATION_STARTUP_BEAN_NAME, getApplicationStartup());
}
}
}
ResourceEditorRegistrar
如何注册属性编辑器、属性编辑器如何解析为对象,后面再解析AbstractApplicationContext.getResources
根据 locationPattern 获取多个资源,如通配符*
public abstract class AbstractApplicationContext extends DefaultResourceLoader
implements ConfigurableApplicationContext {
private ResourcePatternResolver resourcePatternResolver;
public AbstractApplicationContext() {
this.resourcePatternResolver = getResourcePatternResolver();
}
protected ResourcePatternResolver getResourcePatternResolver() {
return new PathMatchingResourcePatternResolver(this);
}
@Override
public Resource[] getResources(String locationPattern) throws IOException {
return this.resourcePatternResolver.getResources(locationPattern);
}
}
PathMatchingResourcePatternResolver
如何解析、加载 locationPattern 指定的资源,后面再解析总的来说,AbstractApplicationContext
类完成上下文环境的大部分功能,包括环境加载、bean 的加载与前置后置处理、事件派发、完成一些初始化工作等,
但扩展了几个接口给子类实现,如如何加载、注册、实例化 bean 等
GenericApplicationContext
的主要功能是注册、管理 bean 的定义与别名
public class GenericApplicationContext extends AbstractApplicationContext implements BeanDefinitionRegistry {}
BeanDefinitionRegistry
这个接口主要定义了注册 bean 的定义及别名
public interface BeanDefinitionRegistry {
// 注册bean定义
void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
throws BeanDefinitionStoreException;
// 删除bean定义
void removeBeanDefinition(String beanName) throws NoSuchBeanDefinitionException;
// 获取bean定义
BeanDefinition getBeanDefinition(String beanName) throws NoSuchBeanDefinitionException;
// 检查bean定义
boolean containsBeanDefinition(String beanName);
// 注册bean别名
void registerAlias(String name, String alias);
// 删除bean别名
void removeAlias(String alias);
// 检查bean别名
boolean isAlias(String name);
// 获取bean别名
String[] getAliases(String name);
}
来看看 GenericApplicationContext
如何实现这些接口的
public class GenericApplicationContext extends AbstractApplicationContext implements BeanDefinitionRegistry {
@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);
}
@Override
public void registerAlias(String beanName, String alias) {
this.beanFactory.registerAlias(beanName, alias);
}
@Override
public void removeAlias(String alias) {
this.beanFactory.removeAlias(alias);
}
@Override
public boolean isAlias(String beanName) {
return this.beanFactory.isAlias(beanName);
}
}
最终还是落脚在 beanFactory
上
GenericWebApplicationContext
的主要功能是添加了设置 bean 配置文件来源,允许通过配置的方式实例化上下文环境
public class GenericWebApplicationContext extends GenericApplicationContext
implements ConfigurableWebApplicationContext, ThemeSource {}
ConfigurableWebApplicationContext
public interface ConfigurableWebApplicationContext {
// 设置bean配置文件来源
void setConfigLocation(String configLocation);
// 设置多个bean配置文件来源
void setConfigLocations(String... configLocations);
// 获取bean配置文件来源
String[] getConfigLocations();
}
ConfigurableWebApplicationContext
扩展了WebApplicationContext
,定义了允许通过配置的方式实例化上下文环境
public class GenericWebApplicationContext extends GenericApplicationContext
implements ConfigurableWebApplicationContext, ThemeSource {
@Override
protected ConfigurableEnvironment createEnvironment() {
// StandardServletEnvironment扩展了StandardEnvironment
// 增加了可以从Servlet context init parameters和Servlet config init parameters增加应用配置来源
return new StandardServletEnvironment();
}
// 不可设置bean配置文件来源
@Override
public void setConfigLocation(String configLocation) {
if (StringUtils.hasText(configLocation)) {
throw new UnsupportedOperationException(
"GenericWebApplicationContext does not support setConfigLocation(). " +
"Do you still have an 'contextConfigLocations' init-param set?");
}
}
@Override
public void setConfigLocations(String... configLocations) {
if (!ObjectUtils.isEmpty(configLocations)) {
throw new UnsupportedOperationException(
"GenericWebApplicationContext does not support setConfigLocations(). " +
"Do you still have an 'contextConfigLocations' init-param set?");
}
}
@Override
public String[] getConfigLocations() {
throw new UnsupportedOperationException(
"GenericWebApplicationContext does not support getConfigLocations()");
}
}
GenericWebApplicationContext
并未实现 ConfigurableWebApplicationContext
的核心方法,也就不能通过文件加载配置,
该类设计的目的不是在web.xml
中进行声明式的安装,而是编程式的安装,例如使用WebApplicationInitializers
来构建内嵌的上下文;一般很少用到
因为 StaticApplicationContext
实现功能比较少,放在这里一起解析
public class StaticApplicationContext extends GenericApplicationContext {
private final StaticMessageSource staticMessageSource;
public StaticApplicationContext(@Nullable ApplicationContext parent) throws BeansException {
super(parent);
// 上下文对象中有一个messageSource组件,实现了i18n功能
// 而StaticMessageSource实现的是由程序载入文本,而非文件,便是去掉了i18n功能
this.staticMessageSource = new StaticMessageSource();
getBeanFactory().registerSingleton(MESSAGE_SOURCE_BEAN_NAME, this.staticMessageSource);
}
}
StaticWebApplicationContext
实现功能也比较少
public class StaticWebApplicationContext extends StaticApplicationContext
implements ConfigurableWebApplicationContext, ThemeSource {
// 不可设置bean配置文件来源
@Override
public void setConfigLocation(String configLocation) {
throw new UnsupportedOperationException("StaticWebApplicationContext does not support config locations");
}
@Override
public void setConfigLocations(String... configLocations) {
throw new UnsupportedOperationException("StaticWebApplicationContext does not support config locations");
}
@Override
public String[] getConfigLocations() {
return null;
}
}
StaticWebApplicationContext
也并未实现 ConfigurableWebApplicationContext
的核心方法,也就不能通过文件加载配置,
该类设计的目的主要用于测试,不用于产品环境
AbstractRefreshableApplicationContext
的主要功能是创建 bean 工厂,刷新上下文数据
public abstract class AbstractRefreshableApplicationContext extends AbstractApplicationContext {
@Override
protected final void refreshBeanFactory() throws BeansException {
// ... 代码省略
try {
// 创建bean工厂
DefaultListableBeanFactory beanFactory = createBeanFactory();
// ... 代码省略
// 加载bean的定义
loadBeanDefinitions(beanFactory);
this.beanFactory = beanFactory;
}
catch (IOException ex) {
throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
}
}
// 创建bean工厂
protected DefaultListableBeanFactory createBeanFactory() {
// 默认使用DefaultListableBeanFactory创建bean工厂
return new DefaultListableBeanFactory(getInternalParentBeanFactory());
}
// 加载bean的定义,由子类实现
protected abstract void loadBeanDefinitions(DefaultListableBeanFactory beanFactory)
throws BeansException, IOException;
}
AbstractRefreshableConfigApplicationContext
的主要功能是可以通过文件加载配置
public abstract class AbstractRefreshableConfigApplicationContext extends AbstractRefreshableApplicationContext
implements BeanNameAware, InitializingBean {
// 设置配置文件来源,以",; \t\n"分隔多个
public void setConfigLocation(String location) {
setConfigLocations(StringUtils.tokenizeToStringArray(location, CONFIG_LOCATION_DELIMITERS));
}
public void setConfigLocations(@Nullable String... locations) {
if (locations != null) {
this.configLocations = new String[locations.length];
for (int i = 0; i < locations.length; i++) {
// 解析路径,替换${}占位符
this.configLocations[i] = resolvePath(locations[i]).trim();
}
}
else {
this.configLocations = null;
}
}
// 获取配置文件来源集,如果没有,则返回默认的
protected String[] getConfigLocations() {
return (this.configLocations != null ? this.configLocations : getDefaultConfigLocations());
}
// 默认的配置文件来源集由子类实现
protected String[] getDefaultConfigLocations() {
return null;
}
// 解析路径,替换${}占位符,有PropertySourcesPropertyResolver.resolveRequiredPlaceholders实现此功能
protected String resolvePath(String path) {
return getEnvironment().resolveRequiredPlaceholders(path);
}
}
AbstractRefreshableConfigApplicationContext
实现了 ConfigurableWebApplicationContext
的核心方法,也就是可以文件加载配置PropertySourcesPropertyResolver
如何是解析路径的,后面再解析因为 AbstractRefreshableWebApplicationContext
实现功能比较少,放在这里一起解析
public abstract class AbstractRefreshableWebApplicationContext extends AbstractRefreshableConfigApplicationContext
implements ConfigurableWebApplicationContext, ThemeSource {
@Override
protected ConfigurableEnvironment createEnvironment() {
// StandardServletEnvironment扩展了StandardEnvironment
// 增加了可以从Servlet context init parameters和Servlet config init parameters增加应用配置来源
return new StandardServletEnvironment();
}
}
XmlWebApplicationContext
的主要功能是定义了默认的配置文件,创建一个 bean 定义的 xml 解析器,并注册 bean 定义
public class XmlWebApplicationContext extends AbstractRefreshableWebApplicationContext {
// 默认配置文件
public static final String DEFAULT_CONFIG_LOCATION = "/WEB-INF/applicationContext.xml";
// 默认配置文件前缀
public static final String DEFAULT_CONFIG_LOCATION_PREFIX = "/WEB-INF/";
// 默认配置文件后缀
public static final String DEFAULT_CONFIG_LOCATION_SUFFIX = ".xml";
@Override
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
// 创建一个bean定义的xml解析器,用XmlBeanDefinitionReader实现
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
// ... 代码省略
// 载入bean定义
loadBeanDefinitions(beanDefinitionReader);
}
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws IOException {
String[] configLocations = getConfigLocations();
if (configLocations != null) {
for (String configLocation : configLocations) {
// 通过配置文件载入bean定义
reader.loadBeanDefinitions(configLocation);
}
}
}
@Override
protected String[] getDefaultConfigLocations() {
if (getNamespace() != null) {
// 如果有servlet-name(如testapp),用前缀后缀包裹为"/WEB-INF/testapp-servlet.xml"
return new String[] {DEFAULT_CONFIG_LOCATION_PREFIX + getNamespace() + DEFAULT_CONFIG_LOCATION_SUFFIX};
}
else {
// 如果没有,默认为"/WEB-INF/applicationContext.xml"文件
return new String[] {DEFAULT_CONFIG_LOCATION};
}
}
}
XmlWebApplicationContext
主要解决了 2 个问题:
testapp
),用前缀后缀包裹为/WEB-INF/testapp-servlet.xml
,如果没有 servlet-name,则为/WEB-INF/applicationContext.xml
SpringMVC 框架的默认加载机制便是使用XmlWebApplicationContext
作为上下文环境,从 xml
文件加载配置与 bean 定义
至于XmlBeanDefinitionReader
如何是解析 bean 定义的,后面再解析
AnnotationConfigWebApplicationContext
的主要功能是可以通过注解载入配置和 bean 定义
public class AnnotationConfigWebApplicationContext extends AbstractRefreshableWebApplicationContext
implements AnnotationConfigRegistry {}
public interface AnnotationConfigRegistry {
// 根据类名注册组件
void register(Class<?>... componentClasses);
// 根据包名扫描组件
void scan(String... basePackages);
}
这两个方法正好是通过注解如 @Configuration, @bean, @Component, @Controller, @Service
等注册 bean 的底层机制
来看看 AnnotationConfigWebApplicationContext
是如何实现的
public class AnnotationConfigWebApplicationContext extends AbstractRefreshableWebApplicationContext
implements AnnotationConfigRegistry {
// 组件类集合
private final Set<Class<?>> componentClasses = new LinkedHashSet<>();
// 扫描包名集合
private final Set<String> basePackages = new LinkedHashSet<>();
// 注册组件
@Override
public void register(Class<?>... componentClasses) {
Collections.addAll(this.componentClasses, componentClasses);
}
// 添加扫描包名
@Override
public void scan(String... basePackages) {
Collections.addAll(this.basePackages, basePackages);
}
// 加载bean定义
@Override
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) {
// 创建一个bean定义的注解解析器,用AnnotatedBeanDefinitionReader实现
AnnotatedBeanDefinitionReader reader = getAnnotatedBeanDefinitionReader(beanFactory);
// 创建一个基于包名的bean注解扫描器,用ClassPathBeanDefinitionScanner实现
ClassPathBeanDefinitionScanner scanner = getClassPathBeanDefinitionScanner(beanFactory);
// 创建一个bean命名生成器
BeanNameGenerator beanNameGenerator = getBeanNameGenerator();
if (beanNameGenerator != null) {
reader.setBeanNameGenerator(beanNameGenerator);
scanner.setBeanNameGenerator(beanNameGenerator);
beanFactory.registerSingleton(AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR, beanNameGenerator);
}
// 创建一个bean作用域元信息解析器,判断注册的bean是原生类型(prototype)还是单例类型(singleton)
ScopeMetadataResolver scopeMetadataResolver = getScopeMetadataResolver();
if (scopeMetadataResolver != null) {
reader.setScopeMetadataResolver(scopeMetadataResolver);
scanner.setScopeMetadataResolver(scopeMetadataResolver);
}
// 注册组件类
if (!this.componentClasses.isEmpty()) {
reader.register(ClassUtils.toClassArray(this.componentClasses));
}
// 扫描包
if (!this.basePackages.isEmpty()) {
scanner.scan(StringUtils.toStringArray(this.basePackages));
}
// 通过定义的配置来源注册组件类或扫描包名
String[] configLocations = getConfigLocations();
if (configLocations != null) {
for (String configLocation : configLocations) {
try {
Class<?> clazz = ClassUtils.forName(configLocation, getClassLoader());
reader.register(clazz);
}
catch (ClassNotFoundException ex) {
int count = scanner.scan(configLocation);
// ... 代码省略
}
}
}
}
// 创建一个bean定义的注解解析器,用AnnotatedBeanDefinitionReader实现
protected AnnotatedBeanDefinitionReader getAnnotatedBeanDefinitionReader(DefaultListableBeanFactory beanFactory) {
return new AnnotatedBeanDefinitionReader(beanFactory, getEnvironment());
}
// 创建一个基于包名的bean注解扫描器,用ClassPathBeanDefinitionScanner实现
protected ClassPathBeanDefinitionScanner getClassPathBeanDefinitionScanner(DefaultListableBeanFactory beanFactory) {
return new ClassPathBeanDefinitionScanner(beanFactory, true, getEnvironment());
}
}
实际上,注册 bean 是由AnnotatedBeanDefinitionReader
完成,扫描包是由ClassPathBeanDefinitionScanner
完成,这两个类后面再解析
GroovyWebApplicationContext
的运行机制与 XmlWebApplicationContext
差不多,从 groovy
文件加载配置与 bean 定义
public class GroovyWebApplicationContext extends AbstractRefreshableWebApplicationContext implements GroovyObject {
// 默认配置文件
public static final String DEFAULT_CONFIG_LOCATION = "/WEB-INF/applicationContext.groovy";
// 默认配置文件前缀
public static final String DEFAULT_CONFIG_LOCATION_PREFIX = "/WEB-INF/";
// 默认配置文件后缀
public static final String DEFAULT_CONFIG_LOCATION_SUFFIX = ".groovy";
@Override
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
// 创建一个bean定义的Groovy解析器,用GroovyBeanDefinitionReader实现
GroovyBeanDefinitionReader beanDefinitionReader = new GroovyBeanDefinitionReader(beanFactory);
// ... 代码省略
// 载入bean定义
loadBeanDefinitions(beanDefinitionReader);
}
protected void loadBeanDefinitions(GroovyBeanDefinitionReader reader) throws IOException {
String[] configLocations = getConfigLocations();
if (configLocations != null) {
for (String configLocation : configLocations) {
// 通过配置文件载入bean定义
reader.loadBeanDefinitions(configLocation);
}
}
}
@Override
protected String[] getDefaultConfigLocations() {
if (getNamespace() != null) {
// 如果有servlet-name(如testapp),用前缀后缀包裹为"/WEB-INF/testapp-servlet.groovy"
return new String[] {DEFAULT_CONFIG_LOCATION_PREFIX + getNamespace() + DEFAULT_CONFIG_LOCATION_SUFFIX};
}
else {
// 如果没有,默认为"/WEB-INF/applicationContext.groovy"文件
return new String[] {DEFAULT_CONFIG_LOCATION};
}
}
}
WebApplicationContext
定义了 Web 应用初始化的基本流程,主要有 5 个实现类,常用的是:基于 Xml 加载的XmlWebApplicationContext
与基于注解加载的AnnotationConfigWebApplicationContext
WebApplicationContext
的基础实现,都不能通过配置文件和注解加载应用配置与 bean,一般用于扩展实现(如 SpringBoot),很少直接使用@Configuration, @bean
等加载应用配置与 bean 的上下文环境XmlWebApplicationContext
的实现差不多,但可以用 Groovy 代替 xml 做配置文件,但目前 Groovy 远不及 Xml 普及,用的仍然不多这一节仍然有一些点留待下次解析:
ConfigurableListableBeanFactory
如何加载、实例化 beanResourceEditorRegistrar
如何注册属性编辑器、属性编辑器如何解析为对象PathMatchingResourcePatternResolver
如何解析、加载 locationPattern 指定的资源PropertySourcesPropertyResolver
如何是解析路径的XmlBeanDefinitionReader
如何是解析 bean 定义的AnnotatedBeanDefinitionReader
是如何注册 bean 的ClassPathBeanDefinitionScanner
是如何扫描包的更多博客,查看 https://github.com/senntyou/blogs
版权声明:自由转载-非商用-非衍生-保持署名(创意共享 3.0 许可证)
出现以下错误 Network access for Distributed Transaction Manager (MSDTC) has been disabled. Please enable D
在调试应用程序时出现以下错误。 The CLR has been unable to transition from COM context 0x3b2d70 to COM context 0x3b2
在 GAE Go 中,为了记录,我们需要使用 appengine.NewContext(r) 创建一个新的上下文,它返回 context.Context。 如何使用此上下文在请求范围内设置/获取变量?
我想使用 Puppeteer 从放置在页面上 iframe 内的选择器中获取数据,该页面在与其父框架域不同的域上运行。因此,我不是任何域的所有者 - 无法使用 frame.postMessage。 试
我正在尝试获取可用的应用程序上下文并想切换到 webview 上下文,但 appium 仅获取 Navive App。 应用程序还启用了 WebView。 Appium 版本:1.10.1 Chrom
这个问题在这里已经有了答案: How to fix this nullOk error when using the flutter_svg package? (7 个回答) 7 个月前关闭。 当我尝
我观看了关于 Core Data 的 2016 WWDC 视频并查看了各种教程。我见过使用 Core Data Framework 创建对象以持久保存到 managedObjectContext 中的
这是代码 obj = { a: 'some value'; m: function(){ alert(this.a); } } obj.m(); 结果是'som
我正在尝试做类似的事情 $(".className").click(function() { $(this).(".anotherClass").css("z-index","1");
var User = { Name: "Some Name", Age: 26, Show: function() { alert("Age= "+this.Age)}; }; fun
我目前正在使用我见过的常见 Context 模式,它允许子组件通过传递修饰函数来更新父组件的状态(即 Provider)通过共享的 Context。 我遇到的问题是,修改函数只引用原始状态,不引用最新
有没有办法让 React Context类型安全与流类型? 例如: Button.contextTypes = { color: React.PropTypes.string }; 最佳答案 不幸
我想知道是否有一种方法可以为不同的功能使用不同的上下文类。 我希望有一个功能使用 MinkExtensions 进行浏览器测试,另一个功能使用和 HTTP 客户端(如 Guzzle)进行 API 测试
我有这个配置文件 apiVersion: v1 clusters: - cluster: server: [REDACTED] // IP of my cluster name: stag
我在实现非抢先式调度时遇到了用于初始化TCB的代码。 typedef struct TCB_t { struct TCB_t *next; struct TCB_t
我想将一个函数设置为数组中每个元素的属性,但使用不同的参数调用它。我想我会使用匿名函数来解决它: for ( var i = 0; i < object_count; i++ ) { obje
这个问题已经有答案了: How to access the correct `this` inside a callback (15 个回答) 已关闭 7 年前。 我正在做一些练习,但我在管道方法中丢
我正在尝试通过 Java 和 Android Studio 学习和制作 Android 应用程序。我对Java的了解程度是两年前几个小时的youtube学习和大学基础类(class)。不过我确实知道如
我在(这个)上遇到了问题。错误ImageView无法应用。我在 fragment 类中执行此代码。 ViewFlipper v_flipper; @Nullable @Override public
我想使用 openGL 的某些功能,但与渲染视觉内容无关。有没有办法在没有任何依赖性的情况下创建它(不是对 Windows,也不是某些包[SDL,SFML,GLUT])?只允许使用没有外部库的库,就像
我是一名优秀的程序员,十分优秀!