二、spring源码初体验(XmlBeanFactory)

2022-03-28 18:53:59 浏览数 (3)

核心类

1.DefaultListableBeanFactory

代码语言:javascript复制
AliasRegistry:定义对alias的简单增删改等操作
SimpleAliasRegistry:主要使用map作为alias的缓存,并对接口AliasRegistry进行实现
SingletonBeanRegistry:定义对单例的注册及获取
BeanFactory:定义获取bean及bean的各种属性
DefaultSingletonBeanRegistry:对接口SingletonBeanRegistry各函数的实现
HierarchicalBeanFactory:继承BeanFactory,也就是在BeanFactory定义的功能的基础上增加了对parentFactory的支持
BeanDefinitionRegistry:定义对BeanDefinition的各种增删改操作
FactoryBeanRegistrySupport:在DefaultSingletonBeanRegistry基础上增加了对FactoryBean的特殊处理功能
ConfigurableBeanFactory:提供配置Factory的各种方法
ListableBeanFactory:根据各种条件获取bean的配置清单
AbstractBeanFactory:综合FactoryBeanRegistrySupport和ConfigurationBeanFactory的功能
AutowireCapableBeanFactory:提供创建bean、自动注入、初始化以及应用bean的后处理器
AbstractAutowireCapableBeanFactory:综合AbstractBeanFactory并对接口AutowireCapableBeanFactory进行实现
ConfigurableListableBeanFactory:BeanFactory配置清单,指定忽略类型及接口等
DefaultListableBeanFactory:综合上面所有功能,主要是对Bean注册后的处理
XmlBeanFactory对DefaultListableBeanFactory进行了扩展,主要用于从xml文档中读取BeanDefinition。对于注册及获取Bean都是使用从父类DefaultListableBeanFactory继承的方法去实现,而维度与父类不同的个性化实现就是增加了XmlBeanDefinitionReader类型的reader属性。在XmlBeanFactory中主要使用reader属性对资源文件进行读取和注册。

2.XmlBeanDefinitionReader

代码语言:javascript复制
BeanDefinitionReader:主要定义资源文件读取并转化为beanDefiniton
EnvironmentCapable:定义获取Environment方法
DocumentLoader:定义从资源文件加载到转化为Document的功能。AbstractBeanDefinitionReader:对EnvironmntCapale、BeanDefinitionReader类定义的功能进行实现。BeanDefinitionDocumentReader:定义读取Document并注册Beandefinition功能
BeanDefinitionParserDelegate:定义解析Element的各种方法。

1.继承自AbstractBeanDefinitionReader中的方法,来使用ResourceLoaner将资源文件转换成对应的Resource文件。

2.通过DocumentLoader对Resource文件进行转换,将Resource文件转换成Document文件。

3.通过实现接口BeanDefinitionDocumentReader的DefaultBeanDefinitionDocumentReader类对Document进行解析,并使用BeanDefinitionParserDelegate对Element进行解析。

代码语言:javascript复制
BeanFactory bf = new XmlBeanFactory(new ClassPathResource("beanFactoryTest.xml"));

以上代码的时序图(只截取与spring相关的关键部分)

尝试脚撕调用顺序

先是构建ClassPathResource

代码语言:javascript复制
// 先调用这个方法然后调用下面的重载方法
public ClassPathResource(String path) {
   this(path, (ClassLoader) null);
}

// 上面的方法调用的这个
public ClassPathResource(String path, @Nullable ClassLoader classLoader) {
   Assert.notNull(path, "Path must not be null");
   String pathToUse = StringUtils.cleanPath(path); // 将字符串中的"\"转换成"/"
   if (pathToUse.startsWith("/")) { // 兼容逻辑地址
      pathToUse = pathToUse.substring(1);
   }
   this.path = pathToUse;
   this.classLoader = (classLoader != null ? classLoader : ClassUtils.getDefaultClassLoader());
}

点击进入ClassUtils.getDefaultClassLoader()

代码语言:javascript复制
public static ClassLoader getDefaultClassLoader() {
   ClassLoader cl = null;
   try {
       // 获取当前的线程类的类加载器,这是目前最安全的办法,防止部署到tomcat里时候其他资源是用tomcat内部加载,而这里用的是jdk的类加载器)
      cl = Thread.currentThread().getContextClassLoader(); 
   }
   catch (Throwable ex) {
      // Cannot access thread context ClassLoader - falling back... // 只是不让异常抛出,不做处理
   }
   if (cl == null) { // 如果无法获取到
      // No thread context class loader -> use class loader of this class.
      // 注释写的很清楚,如果拿不到线程上下文的类加载器,就用当前类(spring的classUtils)的类加载器
      cl = ClassUtils.class.getClassLoader();
      if (cl == null) {
         // getClassLoader() returning null indicates the bootstrap ClassLoader
         try {
            cl = ClassLoader.getSystemClassLoader(); // 会初始化系统类加载器,并且类加载器是classloader的类加载器,则返回这个加载器
         }
         catch (Throwable ex) {
            // Cannot access system ClassLoader - oh well, maybe the caller can live with null...
            // 大佬还是很幽默的,如果连系统类的类加载器都没有。那么,可能这些东西都能跑在null上?}
      }
   }
   return cl;
}

于是乎ClassPathResource就有了两个必须属性,classPath和classLoader

然后回到构建XmlBeanFactory

代码语言:javascript复制
// 调用下面的重载方法
public XmlBeanFactory(Resource resource) throws BeansException {
   this(resource, null);
}
public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
   super(parentBeanFactory);
   this.reader.loadBeanDefinitions(resource);
}
// 先解读第一句代码super,来到XmlBeanFactory的父类DefaultListableBeanFactory
public DefaultListableBeanFactory(@Nullable BeanFactory parentBeanFactory) {
   super(parentBeanFactory);
}
// 来到父类AbstractAutowireCapableBeanFactory
public AbstractAutowireCapableBeanFactory(@Nullable BeanFactory parentBeanFactory) {
   this();
   setParentBeanFactory(parentBeanFactory); // 普通的set,后面再解释用来干嘛的这个factory
}
// this()就是调用空构造方法
public AbstractAutowireCapableBeanFactory() {
   super(); // 调用AbstractBeanFactory的空构造方法(里面什么都没有,就不跟进了)
   // 将这个类放进 private final Set<Class<?>> ignoredDependencyInterfaces = new HashSet<>();
   // 意思是在自动装配时忽略指定接口的实现类中字段依赖的注入
   // 这样的做法使得BeanFactoryAware的实现类中的BeanFactory依赖在自动装配时被忽略,而统一由框架设置依赖
   // 如ApplicationContextAware接口的设置会在ApplicationContextAwareProcessor类中完成ApplicationContext的注入
   // 保证了ApplicationContextAware和BeanFactoryAware中的容器保证是生成该bean的容器
   ignoreDependencyInterface(BeanNameAware.class); 
   ignoreDependencyInterface(BeanFactoryAware.class); 
   ignoreDependencyInterface(BeanClassLoaderAware.class); // 还有一个类似的ignoreDependencyType 是注入时忽略这个类型的参数注入
}

然后我们回到XmlBeanFactory的构造方法中的this.reader.loadBeanDefinitions方法

代码语言:javascript复制
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
   return loadBeanDefinitions(new EncodedResource(resource));
}
// 先进入EncodedResource的构造方法
public EncodedResource(Resource resource) {
   this(resource, null, null);
}
// 调用重载的方法,其实就是设置值,然后返回一个EncodedResource对象
private EncodedResource(Resource resource, @Nullable String encoding, @Nullable Charset charset) {
   super(); // object类的构造函数,之前在Object的源码分析的时候看过了
   Assert.notNull(resource, "Resource must not be null");
   this.resource = resource;
   this.encoding = encoding;
   this.charset = charset;
}
// 返回XmlBeanDefinitionReader类的loadBeanDefinitions方法
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
   Assert.notNull(encodedResource, "EncodedResource must not be null");
   if (logger.isInfoEnabled()) {
      logger.info("Loading XML bean definitions from "   encodedResource);
   }

    // 获取ThreadLocal中的数据Set,ThreadLocal的get方法如果没有数据就返回null
   Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
   // 如果ThreadLocal中没有数据,则new一个HashSet,并且放到这个对象的属性中
   if (currentResources == null) {
      currentResources = new HashSet<>(4);
      this.resourcesCurrentlyBeingLoaded.set(currentResources);
   }
   // 如果放入set失败,则报错(后面加载完要删除的)
   if (!currentResources.add(encodedResource)) {
      throw new BeanDefinitionStoreException(
            "Detected cyclic loading of "   encodedResource   " - check your import definitions!");
   }
   try {
       // 配置文件的inputStream
      InputStream inputStream = encodedResource.getResource().getInputStream();
      try {
         InputSource inputSource = new InputSource(inputStream);
         if (encodedResource.getEncoding() != null) {
            inputSource.setEncoding(encodedResource.getEncoding());
         }
         return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
      }
      finally {
         inputStream.close();
      }
   }
   catch (IOException ex) {
       // 资源加载失败
      throw new BeanDefinitionStoreException(
            "IOException parsing XML document from "   encodedResource.getResource(), ex);
   }
   finally {
       // 删除刚刚加载的资源
      currentResources.remove(encodedResource);
      // 如果ThreadLocalMap已经是空的,就从ThreadLocal中删除
      if (currentResources.isEmpty()) {
         this.resourcesCurrentlyBeingLoaded.remove();
      }
   }
}

然后进入43行的doLoadBeanDefinitions方法

代码语言:javascript复制
// 返回加载的bean数量
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
      throws BeanDefinitionStoreException {
   try {
       // 通过inputSource 转换成Document对象(中间有用过factory模式和Builder模式)
      Document doc = doLoadDocument(inputSource, resource);
      return registerBeanDefinitions(doc, resource);
   }
   catch (BeanDefinitionStoreException ex) {
      throw ex;
   }
   catch (SAXParseException ex) {
      throw new XmlBeanDefinitionStoreException(resource.getDescription(),
            "Line "   ex.getLineNumber()   " in XML document from "   resource   " is invalid", ex);
   }
   catch (SAXException ex) {
      throw new XmlBeanDefinitionStoreException(resource.getDescription(),
            "XML document from "   resource   " is invalid", ex);
   }
   catch (ParserConfigurationException ex) {
      throw new BeanDefinitionStoreException(resource.getDescription(),
            "Parser configuration exception parsing XML from "   resource, ex);
   }
   catch (IOException ex) {
      throw new BeanDefinitionStoreException(resource.getDescription(),
            "IOException parsing XML document from "   resource, ex);
   }
   catch (Throwable ex) {
      throw new BeanDefinitionStoreException(resource.getDescription(),
            "Unexpected exception parsing XML document from "   resource, ex);
   }
}

0 人点赞