Spring 是一款目前主流的 Java EE 轻量级开源框架,可以说的上是JAVA领域最流行,也是开发者们必须要掌握的框架
IOC AOP是Spring核心的两个概念,当然IOC是基础,就是由Spring来负责控制对象的生命周期和对象间的关系,不用通过New方式来创建对象,减少代码之间的耦合
public class ApplicationTest {
public static void main(String[] args) {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
System.err.println( applicationContext.getBean(Demo.class));
}
}
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:batch="http://www.springframework.org/schema/batch"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/batch
http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<context:component-scan base-package="com.dlh.spring.context" />
beans>
这样我们就创建了一个Spring容器,可以看到Demo这个类被Spring管理了,这里的ClassPathXmlApplicationContext 就是Spring容器的实现,接下来我们来了解下这个类
提供了一些基础的方法,获取bean 、判断容器中是否包含bean、判断bean的Scope模式(单例或者原型)、是否匹配某个类或泛型
这些接口由子类实现(AbstractApplicationContext 就实现了getBean方法 ,当然具体的实现还是交给了DefaultListableBeanFactory这个才是最终负责Bean的定义实例创建)
HierarchicalBeanFactory
public interface HierarchicalBeanFactory extends BeanFactory {
BeanFactory getParentBeanFactory();
boolean containsLocalBean(String name);
}
Hierarchical 层次,这里多提供了两个接口,获取父容器,判断当前容器是否包含bean
3. ListableBeanFactory
正如类名中的List,提供了有关批量获取bean的方法,AbstractApplicationContext也提供了实现
public interface ResourceLoader {
String CLASSPATH_URL_PREFIX = ResourceUtils.CLASSPATH_URL_PREFIX;
Resource getResource(String location);
ClassLoader getClassLoader();
}
提供了获取Resource的接口,demo里面的spring.xml就是资源,Spring通过解析它创建容器注册Bean定义,前提是我们要找到它
public interface ResourcePatternResolver extends ResourceLoader {
String CLASSPATH_ALL_URL_PREFIX = "classpath*:";
Resource[] getResources(String locationPattern) throws IOException
}
批量获取接口
DefaultResourceLoader
默认资源加载实现类
MessageSource
国际化信息接口
EnvironmentCapable
public interface EnvironmentCapable {
Environment getEnvironment();
}
提供一个方法获取 Environment ,Environment 在Spring中也是个比较重要的概念,存储了一些配置信息
可以看到Environment实现了PropertyResolver接口,就是解析Property文件来获取配置,AbstractEnvironment有个MutablePropertySources字段来存储当前环境的配置
public Object getBean(String name) throws BeansException {
assertBeanFactoryActive();
return getBeanFactory().getBean(name);
}
public abstract ConfigurableListableBeanFactory getBeanFactory() throws IllegalStateException;
// 子类AbstractRefreshableConfigApplicationContext
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;
}
}
可以看到getBean具体的逻辑 交给了ConfigurableListableBeanFactory ( 具体是DefaultListableBeanFactory 这个类)
`前面在介绍ClassPathXmlApplicationContext时候,了解到在实际getBean的时候是委托给DefaultListableBeanFactory来实现的,
ClassPathXmlApplicationContext是应用程序上下文实现类,实现下加载资源,国际化,监听器,后处理器管理,解析注册bean定义等流程上的功能
DefaultListableBeanFactory更加专业 负责bean的注册,实例创建
这是DefaultListableBeanFactory的继承体系,有部分接口在前面有提到过了,我们来看下继承体系中的接口和类的作用
public interface AliasRegistry {
void registerAlias(String name, String alias);
void removeAlias(String alias);
boolean isAlias(String name);
String[] getAliases(String name);
}
别名注册
public interface BeanDefinitionRegistry extends AliasRegistry {
void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
throws BeanDefinitionStoreException;
void removeBeanDefinition(String beanName) throws NoSuchBeanDefinitionException;
BeanDefinition getBeanDefinition(String beanName) throws NoSuchBeanDefinitionException;
boolean containsBeanDefinition(String beanName);
String[] getBeanDefinitionNames();
int getBeanDefinitionCount();
boolean isBeanNameInUse(String beanName);
}
接口 定义了BeanDefinition相关的接口
内部属性 | 描述 |
---|---|
Map |
根据BeanDefinition创建好的实例 |
Map |
创建早期bean的工厂 |
Map |
提前暴露的早期Bean 未完全初始化完成 属性待填充 |
Set registeredSingletons | 缓存已经实例化的bean的名称 |
Set singletonsCurrentlyInCreation | 正在创建的bean |
Map |
Key值代表的Bean被其他多少bean依赖 |
Map |
Key值代表的Bean依赖其它多少Bean |
方法 | 描述 |
---|---|
setBeanExpressionResolver | 设置SpringEL表达式解析器 容器refresh过程中设置 StandardBeanExpressionResolver |
setConversionService | 设置一个ConversionService属性转换器,容器refresh过程中设置默认是FormattingConversionService |
addPropertyEditorRegistrar | 添加一个PropertyEditorRegistrar (向容器中注册PropertyEditorRegistry),PropertyEditorRegistry可向容器中添加属性编辑器PropertyEditor 容器refresh过程中向Spring添加了一个ResourceEditorRegistrar |
registerCustomEditor | 注册一个属性编辑器实现类 |
setTypeConverter | 添加一个类型转换接口 |
addBeanPostProcessor | Spring容器中注册的BeanDefinition 对应Class如果实现了BeanPostProcessor将其添加到Spring容器中 |
addEmbeddedValueResolver | 添加一个StringValueResolver接口,StringValueResolver 实现类用了解析Spring配置文件中的占位符比如${name} |
方法 | 描述 |
---|---|
ignoreDependencyType | 忽略自动装配的类 |
ignoreDependencyInterface | 忽略自动装配的接口 |
isAutowireCandidate | 判断指定的Bean是否有资格作为自动装配的目标对象 |
preInstantiateSingletons | 实例化所有非延迟加载单例类 |
关于IOC容器个人理解有两个容器,一个是应用程序上下文实现类(外层容器设置 内层容器注册BeanDefinition获取bean实例所需的资源),内层容器DefaultListableBeanFactory 只负责bean注册 实例创建相关的逻辑
接下来我们看看Spring容器的refresh流程 ,这个方法是在AbstractApplicationContext 里面
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// 子类实现初始化环境上下文信息Environment
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
// 创建容器并向容器中注册BeanDefinition
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
prepareBeanFactory(beanFactory);
try {
/**
* 设置EL表达式解析器
* 添加处理Aware回调接口的Bean处理器
* 忽略给定接口的字段装配功能
* BeanFactory ResourceLoader ApplicationEventPublisher ApplicationContext注册ResolvableDependency(就是指定该类型接口,如果外部要注入该类型接口的对象,则会注入我们指定的对象,而不会去管其他接口实现类)
* 注册ApplicationListenerDetector 实现了Bean后处理器接口,如果对应bean是ApplicationListener实例,销毁bean时移除监听器
*/
postProcessBeanFactory(beanFactory);
// Invoke factory processors registered as beans in the context.
/**
* 初始化容器后处理器
* 先调用实现BeanDefinitionRegistryPostProcessor接口容器后处理器类的postProcessBeanDefinitionRegistry方法
* 再调用实现BeanFactoryPostProcessor接口容器后处理器类的postProcessBeanFactory方法
*/
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
/**
* 容器中注册的Bean对应的Class如果实现了BeanPostProcessor,对应Class添加到Spring容器
*/
registerBeanPostProcessors(beanFactory);
// Initialize message source for this context.
initMessageSource();
// Initialize event multicaster for this context.
// 设置容器中的事件广播类 ,没有定义相关Bean则默认值是SimpleApplicationEventMulticaster
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.
// 子类扩展 SpringBoot 应用程序上下文ServletWebServerApplicationContext实现onRefresh方法创建了一个WebServer来监听端口
onRefresh();
// Check for listener beans and register them.
// 监听器添加到ApplicationEventMulticaster
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons.
// 容器初始化完成初始化容器中的所有单例bean,如果bean继承了SmartInitializingSingleton接口同时调用afterSingletonsInstantiated方法
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.
// 设置容器中的LifecycleProcessor onRefresh
// 发布ContextRefreshedEvent实际 DispatcherServlet父类FrameworkServlet监听到这个事件初始化MVC相关组件
finishRefresh();
}
catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
// Destroy already created singletons to avoid dangling resources.
destroyBeans();
// Reset 'active' flag.
cancelRefresh(ex);
// Propagate exception to caller.
throw ex;
}
finally {
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...
resetCommonCaches();
}
}
}
registerBeanPostProcessors方法会在Spring容器中查找BeanDefinition 对应的class是实现了BeanPostProcessor接口的bean,也就是在这之前已经将BeanDefinition 注册到容器当中了,具体是在obtainFreshBeanFactory创建容器后加载资源文件来解析bean的,我们来看下具体流程(以ClassPathXmlApplicationContext 创建为例子)
protected final void refreshBeanFactory() throws BeansException {
if (hasBeanFactory()) {
destroyBeans();
closeBeanFactory();
}
try {
DefaultListableBeanFactory beanFactory = createBeanFactory();
beanFactory.setSerializationId(getId());
customizeBeanFactory(beanFactory);
loadBeanDefinitions(beanFactory);
synchronized (this.beanFactoryMonitor) {
this.beanFactory = beanFactory;
}
}
catch (IOException ex) {
throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
}
}
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
beanDefinitionReader.setEnvironment(this.getEnvironment());
beanDefinitionReader.setResourceLoader(this);
// ResourceEntityResolver 有一个ResourceLoader定义了加载资源的方法,AbstractXmlApplicationContext就继承了这个接口
beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
initBeanDefinitionReader(beanDefinitionReader);
loadBeanDefinitions(beanDefinitionReader);
}
从两个路径下获取资源再调用XmlBeanDefinitionReader loadBeanDefinitions方法
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
Resource[] configResources = getConfigResources();
if (configResources != null) {
reader.loadBeanDefinitions(configResources);
}
// new ClassPathXmlApplicationContext("spring.xml") 这种构造方式configLocations 有值
String[] configLocations = getConfigLocations();
if (configLocations != null) {
reader.loadBeanDefinitions(configLocations);
}
}
拿到资源文件路径开开始解析,父类AbstractBeanDefinitionReader循环读取资源路径下的配置文件来解析BeanDefinition
public int loadBeanDefinitions(String... locations) throws BeanDefinitionStoreException {
Assert.notNull(locations, "Location array must not be null");
int count = 0;
for (String location : locations) {
count += loadBeanDefinitions(location);
}
return count;
}
将资源文件路径转化为Resource ResourcePatternResolver匹配到多个Resource ,获取资源后调用到BeanDefinitionReader loadBeanDefinitions (这里类里面有几个这样的加载bean定义的方法 入参不同,这里传的参数是Resource )
public int loadBeanDefinitions(String location, @Nullable Set<Resource> actualResources) throws BeanDefinitionStoreException {
ResourceLoader resourceLoader = getResourceLoader();
if (resourceLoader == null) {
throw new BeanDefinitionStoreException(
"Cannot load bean definitions from location [" + location + "]: no ResourceLoader available");
}
if (resourceLoader instanceof ResourcePatternResolver) {
// Resource pattern matching available.
try {
Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
int count = loadBeanDefinitions(resources);
if (actualResources != null) {
Collections.addAll(actualResources, resources);
}
if (logger.isTraceEnabled()) {
logger.trace("Loaded " + count + " bean definitions from location pattern [" + location + "]");
}
return count;
}
catch (IOException ex) {
throw new BeanDefinitionStoreException(
"Could not resolve bean definition resource pattern [" + location + "]", ex);
}
}
else {
// Can only load single resources by absolute URL.
Resource resource = resourceLoader.getResource(location);
int count = loadBeanDefinitions(resource);
if (actualResources != null) {
actualResources.add(resource);
}
if (logger.isTraceEnabled()) {
logger.trace("Loaded " + count + " bean definitions from location [" + location + "]");
}
return count;
}
}
调用到XmlBeanDefinitionReader doLoadBeanDefinitions
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
Assert.notNull(encodedResource, "EncodedResource must not be null");
if (logger.isTraceEnabled()) {
logger.trace("Loading XML bean definitions from " + encodedResource);
}
Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
if (!currentResources.add(encodedResource)) {
throw new BeanDefinitionStoreException(
"Detected cyclic loading of " + encodedResource + " - check your import definitions!");
}
try (InputStream inputStream = encodedResource.getResource().getInputStream()) {
InputSource inputSource = new InputSource(inputStream);
if (encodedResource.getEncoding() != null) {
inputSource.setEncoding(encodedResource.getEncoding());
}
return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
}
catch (IOException ex) {
throw new BeanDefinitionStoreException(
"IOException parsing XML document from " + encodedResource.getResource(), ex);
}
finally {
currentResources.remove(encodedResource);
if (currentResources.isEmpty()) {
this.resourcesCurrentlyBeingLoaded.remove();
}
}
}
解析资源文件成Document调用registerBeanDefinitions 注册bean定义
//省略try catch
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
throws BeanDefinitionStoreException {
Document doc = doLoadDocument(inputSource, resource);
int count = registerBeanDefinitions(doc, resource);
if (logger.isDebugEnabled()) {
logger.debug("Loaded " + count + " bean definitions from " + resource);
}
return count;
}
BeanDefinitionDocumentReader 有一个XmlReaderContext变量 ,XmlReaderContext 内部又有个NamespaceHandlerResolver
定义了不同命名空间的bean解析器
createReaderContext 创建了XmlReaderContext 对象跟踪代码到最后发现是读取了META-INF/spring.handlers配置
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
//默认是DefaultBeanDefinitionDocumentReader
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
int countBefore = getRegistry().getBeanDefinitionCount();
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
return getRegistry().getBeanDefinitionCount() - countBefore;
}
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
if (delegate.isDefaultNamespace(root)) {
NodeList nl = root.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
if (node instanceof Element) {
Element ele = (Element) node;
if (delegate.isDefaultNamespace(ele)) {
parseDefaultElement(ele, delegate);
}
else {
delegate.parseCustomElement(ele);
}
}
}
}
else {
delegate.parseCustomElement(root);
}
}
上面解析的代码,有几个条件分支,实际只有两种情况,调用parseDefaultElement 或者
BeanDefinitionParserDelegate的parseCustomElement方法
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:batch="http://www.springframework.org/schema/batch"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/batch
http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<bean id="interFace" class="com.dlh.spring.context.InterFaceImpl"/>
<context:component-scan base-package="com.dlh.spring.context" />
<bean id="beanNameAutoProxy" class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
<property name="beanNames" value="interFace" />
<property name="interceptorNames">
<list>
<value>myMethodInterceptorvalue>
list>
property>
bean>
<bean id="myMethodInterceptor" class="com.dlh.spring.context.MyMethodInterceptor"/>
beans>
我们解析的spring配置文件长这样,解析成DOM元素
满足parseBeanDefinitions方法的外层if条件,里面的if节点 是循环解析解析整个DOM的子节点
<bean id="interFace" class="com.dlh.spring.context.InterFaceImpl"/>```
<context:component-scan base-package="com.dlh.spring.context" />```
这里
所以当解析 component-scan 节点时调用的是BeanDefinitionParserDelegate的parseCustomElement方法
看这个类名当中的意思我们已经能猜到是将bean解析交给其它类来处理,具体来看下代码
public BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd) {
String namespaceUri = getNamespaceURI(ele);
if (namespaceUri == null) {
return null;
}
// 这里实现类是DefaultNamespaceHandlerResolver ,handlerMappings 保存了namespaceUri 跟 NamespaceHandler 的映射关系
NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
if (handler == null) {
error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);
return null;
}
// 调用NamespaceHandler 接口子类parse方法解析
return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
}
这个方法的逻辑就是根据节点的namespaceUri 找到对应的命名空间解析器,再调用具体解析器的parse方法(parse之前会先调用NamespaceHandler init方法)注册Bean到容器
也就是 namespaceURI 是http://www.springframework.org/schema/beans的节点 ,其它类型的节点都要有对应的命名空间处理器 NamespaceHandler
其它框架与Spring继承也会实现NamespaceHandler 接口来解析配置文件
我们再回头看看parseBeanDefinitions的另外一种情况,就是namespaceURI 是http://www.springframework.org/schema/beans的节点,会调用parseDefaultElement 方法
if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
importBeanDefinitionResource(ele);
}
else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
processAliasRegistration(ele);
}
else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
processBeanDefinition(ele, delegate);
}
else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
// recurse
doRegisterBeanDefinitions(ele);
}
这里有针对nodeName(也就是子节点bean context这样的节点标识)四种情况
nodeName | 处理逻辑 |
---|---|
import | 重新导入一个资源文件解析 这个节点有一个Resource属性代表资源文件路径 |
alias | 注册一些bean的别名 |
beans | 重新再走向doRegisterBeanDefinitions 流程 beans子节点才是真正要解析的数据 |
bean | 这个就代表Spring容器中的一个bean了具体的解析过程都在BeanDefinitionParserDelegate |