Spring之IOC

容器:存储单例Bean的实例

基础容器:BeanFactory

高级容器:ApplicationContext

基础容器与高级容器的区别:

基础容器产生Bean实例的时机是第一次被调用的时候。高级容器是服务启动的时候,就会创建所有单例的Bean实例。

 

BeanFactory和FactoryBean的区别:

BeanFactory是Spring的顶级接口,是Spring中容器的底层实现接口。BeanFactory 实现类管理者所有的单例bean实例。

FactoryBean是spring容器中管理的一个特殊的bean。因为该bean还能产生指定类型的bean实例。

BeanFactoryPostProcessor和BeanPostProcessor的区别:

BeanFactoryPostProcessor主要是在bean【实例创建】之前,beanDefiniton初始化前后去调用。

BeanPostProcessor主要是Bean【实例初始化】前后,对Bean实例进行一些后置操作。

 

(一)BeanFactory继承体系:

image.png

四级接口继承体系:

  1. BeanFactory 作为一个主接口不继承任何借口,暂且称之为一级接口。

  2. AutowireCapableBeanFactory、HirerarchicalBeanFactory、ListableBeanFactory3个子接口继承了BeanFactory,进行功能上的增强。这三个接口成为二级接口。

  3. ConfigurableBeanFactory可以被称为三级接口,对二级接口HirerarchicalBeanFactory进行了再次增强,此外他还继承了另一个外来的接口SingletonBeanRegistry

  4. ConfigurableListableBeanFactory是一个更强大得接口,继承了上述得所有接口,无所不包,成为四级接口。

 

继承关系中的两个抽象类和两个实现类:

  1. AbstractBeanFactory作为一个抽象类,实现了三级接口ConfigurableBeanFactory大部分功能。

  2. AbstractAutowireCapableBeanFactory同样也是抽象类,继承自AbstractBeanFactory,并额外实现了二级接口AutowireCapableBeanFactory。

  3. DefaultListableBeanFactory 继承自 AbstractAutowireCapableBeanFactory ,实现了最强大的四级接口 ConfigurableListableBeanFactory ,并实现了一个外来接口BeanDefinitionRegistry ,它并非抽象类。

  4. 最后是最强大的xmlBeanFactory,继承自DefaultListableBeanFactory,重写了一些功能,使自己更加强大。

那为何要定义这么多层次的接口呢?

查阅这些接口的源码和说明发现,每个接口都有他使用的场合,它主要是为了区分在Spring内部在操作过程中对象的传递和转化过程中,对对象的数据访问所做的限制。例如ListableBeanFactory接口表

示这些Bean是可列表的,而HierarchicalBeanFactory表示的是这些Bean是有继承关系的,也就是每个Bean有可能有父Bean。AutowireCapableBeanFactory接口定义Bean的自动装配规则。这四个接口

共同定义了Bean的集合、Bean之间的关系、以及Bean行为.

 

1.1 BeanFactory:

复制代码
public interface BeanFactory {
   //用来引用一个实例,或把它和工厂产生的Bean区分 就是说,如果一个FactoryBean的名字为a,那么,&a会得到那个Factory
   String FACTORY_BEAN_PREFIX = "&";
   /*
    * 四个不同形式的getBean方法,获取实例
    */
   Object getBean(String name) throws BeansException;
   <T> T getBean(String name, Class<T> requiredType) throws BeansException;
   <T> T getBean(Class<T> requiredType) throws BeansException;
   Object getBean(String name, Object... args) throws BeansException;
   // 是否存在
   boolean containsBean(String name);
   // 是否为单实例
   boolean isSingleton(String name) throws NoSuchBeanDefinitionException;
   // 是否为原型(多实例)
   boolean isPrototype(String name) throws NoSuchBeanDefinitionException;
   // 名称、类型是否匹配
   boolean isTypeMatch(String name, Class<?> targetType)
         throws NoSuchBeanDefinitionException;
   // 获取类型
   Class<?> getType(String name) throws NoSuchBeanDefinitionException;
   // 根据实例的名字获取实例的别名
   String[] getAliases(String name);
}
复制代码

在BeanFactory中只对IOC容器的基本行为做了定义,根本不关心bean是如何定义怎样加载的。

 

1.2.1 ListableBeanFactory

复制代码
public interface ListableBeanFactory extends BeanFactory {
    // 对于给定的名字是否含有
    boolean containsBeanDefinition(String beanName);
    // 返回工厂的BeanDefinition总数
    int getBeanDefinitionCount();
    // 返回工厂中所有Bean的名字
    String[] getBeanDefinitionNames();
    // 返回对于指定类型Bean(包括子类)的所有名字
    String[] getBeanNamesForType(Class<?> type);
    /*
     * 返回指定类型的名字
     * includeNonSingletons为false表示只取单例Bean,true则不是
     * allowEagerInit为true表示立刻加载,false表示延迟加载。
     * 注意:FactoryBeans都是立刻加载的。
     */
    String[] getBeanNamesForType(Class<?> type, boolean includeNonSingletons, boolean allowEagerInit);
    // 根据类型(包括子类)返回指定Bean名和Bean的Map
    <T> Map<String, T> getBeansOfType(Class<T> type) throws BeansException;
    <T> Map<String, T> getBeansOfType(Class<T> type, boolean includeNonSingletons, boolean allowEagerInit) throws BeansException;
    // 根据注解类型,查找所有有这个注解的Bean名和Bean的Map
    Map<String, Object> getBeansWithAnnotation(Class<? extends Annotation> annotationType) throws BeansException;
    // 根据指定Bean名和注解类型查找指定的Bean
    <A extends Annotation> A findAnnotationOnBean(String beanName, Class<A> annotationType);
}
复制代码

源码说明:

3 个跟BeanDefinition有关的总体操作:BeanDefinition的总数、名字的集合、指定类型的名字的集合。

2个getBeanNamesForType重载方法。根据指定类型(包括子类)获取其对应的所有bean名字。

2个getBeansOfType重载方法。根据类型(包括子类)返回指定Bean名和Bean的Map。

2个跟注解查找有关的方法。根据注解类型,查找Bean名和Bean的Map,以及根据指定Bean名和注解类型查找指定的Bean。

该工厂接口最大的特点是可以列出 工厂可以生产的所有实例。当然工厂并没有直接提供返回所有实例的方法,也没有这个必要。它可以返回指定类型的所有的实例。而且可以通过getBeanDefinitionNames得到工厂所有Bean的名字,然后根据这些名字得到所有的Bean. 这个工厂接口扩展类 BeanFactory的功能,作为上下文指出的二级接口,有9个独有的方法,扩展了跟BeanDefinition的功能,提供了BeanDefinition、BeanName、注解有关的各种操作。它可以根据条件返回Bean的信息集合

 

1.2.2 HierarchicalBeanFactory

该接口是一个二级接口们主要实现了工厂的分层。

public interface HierarchicalBeanFactory extends BeanFactory {
    // 返回本Bean工厂的父工厂
    BeanFactory getParentBeanFactory();
    // 本地工厂是否包含这个Bean
    boolean containsLocalBean(String name);
}

 

1.2.3 AutowireCapableBeanFactory

源码说明:
有5个静态不可变变量来指明装配策略。其中一个被spring3.0废弃,一个表示没有自动装配。其余三个分别表示根据名称、类型、构造方法进行装配。
8个跟自动装配有关的方法。2个执行BeanPostProcessors的方法。2个分解指定依赖的方法。
总结该工厂接口继承自BeanFactory,它扩展了自动装配的功能,根据类定义BeanDefinition装配Bean、执行前、后处理器。
复制代码
public interface AutowireCapableBeanFactory extends BeanFactory {
    // 这个常量表明工厂没有自动装配的Bean
    int AUTOWIRE_NO = 0;
    // 表明根据名称自动装配
    int AUTOWIRE_BY_NAME = 1;
    // 表明根据类型自动装配
    int AUTOWIRE_BY_TYPE = 2;
    // 表明根据构造方法快速装配
    int AUTOWIRE_CONSTRUCTOR = 3;
    //表明通过Bean的class的内部来自动装配(有没翻译错...)Spring3.0被弃用。
    @Deprecated
    int AUTOWIRE_AUTODETECT = 4;
    // 根据指定Class创建一个全新的Bean实例
    <T> T createBean(Class<T> beanClass) throws BeansException;
    // 给定对象,根据注释、后处理器等,进行自动装配
    void autowireBean(Object existingBean) throws BeansException;
    // 根据Bean名的BeanDefinition装配这个未加工的Object,执行回调和各种后处理器。
    Object configureBean(Object existingBean, String beanName) throws BeansException;
    // 分解Bean在工厂中定义的这个指定的依赖descriptor
    Object resolveDependency(DependencyDescriptor descriptor, String beanName) throws BeansException;
    // 根据给定的类型和指定的装配策略,创建一个新的Bean实例
    Object createBean(Class<?> beanClass, int autowireMode, boolean dependencyCheck) throws BeansException;
    // 与上面类似,不过稍有不同。
    Object autowire(Class<?> beanClass, int autowireMode, boolean dependencyCheck) throws BeansException;
    // 根据名称或类型自动装配
    void autowireBeanProperties(Object existingBean, int autowireMode, boolean dependencyCheck) throws BeansException;
    //也是自动装配
    void applyBeanPropertyValues(Object existingBean, String beanName) throws BeansException;
    //初始化一个Bean...
    Object initializeBean(Object existingBean, String beanName) throws BeansException;
    //初始化之前执行BeanPostProcessors
    Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName) throws BeansException;
    //初始化之后执行BeanPostProcessors
    Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName) throws BeansException;
    // 分解指定的依赖
    Object resolveDependency(DependencyDescriptor descriptor, String beanName, Set<String> autowiredBeanNames, TypeConverter typeConverter) throws BeansException;
}
复制代码

 

1.3 ConfigurableBeanFactory

复制代码
public interface ConfigurableBeanFactory extends HierarchicalBeanFactory,SingletonBeanRegistry {
    String SCOPE_SINGLETON = "singleton"; // 单例
    String SCOPE_PROTOTYPE = "prototype"; // 原型
    //搭配HierarchicalBeanFactory接口的getParentBeanFactory方法
    void setParentBeanFactory(BeanFactory parentBeanFactory) throws IllegalStateException;
    //设置、返回工厂的类加载器
    void setBeanClassLoader(ClassLoader beanClassLoader);
    ClassLoader getBeanClassLoader();
    //设置、返回一个临时的类加载器
    void setTempClassLoader(ClassLoader tempClassLoader);
    ClassLoader getTempClassLoader();
    //设置、是否缓存元数据,如果false,那么每次请求实例,都会从类加载器重新加载(热加载)
    void setCacheBeanMetadata(boolean cacheBeanMetadata);
    boolean isCacheBeanMetadata();//是否缓存元数据
    //Bean表达式分解器
    void setBeanExpressionResolver(BeanExpressionResolver resolver);
    BeanExpressionResolver getBeanExpressionResolver();
    //设置、返回一个转换服务
    void setConversionService(ConversionService conversionService);
    ConversionService getConversionService();
    //设置属性编辑登记员...
    void addPropertyEditorRegistrar(PropertyEditorRegistrar registrar);
    //注册常用属性编辑器
    void registerCustomEditor(Class<?> requiredType, Class<? extends
            PropertyEditor> propertyEditorClass);
    //用工厂中注册的通用的编辑器初始化指定的属性编辑注册器
    void copyRegisteredEditorsTo(PropertyEditorRegistry registry);
    //设置、得到一个类型转换器
    void setTypeConverter(TypeConverter typeConverter);
    TypeConverter getTypeConverter();
    //增加一个嵌入式的StringValueResolver
    void addEmbeddedValueResolver(StringValueResolver valueResolver);
    String resolveEmbeddedValue(String value);//分解指定的嵌入式的值
    void addBeanPostProcessor(BeanPostProcessor beanPostProcessor);//设置一个Bean后处理器
    int getBeanPostProcessorCount();//返回Bean后处理器的数量
    void registerScope(String scopeName, Scope scope);//注册范围
    String[] getRegisteredScopeNames();//返回注册的范围名
    Scope getRegisteredScope(String scopeName);//返回指定的范围
    AccessControlContext getAccessControlContext();//返回本工厂的一个安全访问上下文
    void copyConfigurationFrom(ConfigurableBeanFactory otherFactory);//从其他的工厂复制相关的所有配置
    oid registerAlias(String beanName, String alias) throws BeanDefinitionStoreException;
    void resolveAliases(StringValueResolver valueResolver);//根据指定的StringValueResolver移除所有的别名
    //返回指定Bean合并后的Bean定义
    BeanDefinition getMergedBeanDefinition(String beanName) throws NoSuchBeanDefinitionException;
    boolean isFactoryBean(String name) throws NoSuchBeanDefinitionException;//判断指定Bean是否为一个工厂Bean
    void setCurrentlyInCreation(String beanName, boolean inCreation);//设置一个Bean是否正在创建
    boolean isCurrentlyInCreation(String beanName);//返回指定Bean是否已经成功创建
    void registerDependentBean(String beanName, String dependentBeanName);//注册一个依赖于指定bean的Bean
    String[] getDependentBeans(String beanName);//返回依赖于指定Bean的所欲Bean名
    String[] getDependenciesForBean(String beanName);//返回指定Bean依赖的所有Bean名
    void destroyBean(String beanName, Object beanInstance);//销毁指定的Bean
    void destroyScopedBean(String beanName);//销毁指定的范围Bean
    void destroySingletons(); //销毁所有的单例类
}
复制代码

 

1.4 ConfigurableListableBeanFactory

1
BeanFactory的集大成者
复制代码
public interface ConfigurableListableBeanFactory extends ListableBeanFactory, AutowireCapableBeanFactory,
        ConfigurableBeanFactory {
    void ignoreDependencyType(Class<?> type);//忽略自动装配的依赖类型
    void ignoreDependencyInterface(Class<?> ifc);//忽略自动装配的接口
    //注册一个可分解的依赖
    void registerResolvableDependency(Class<?> dependencyType, Object autowiredValue);
    //判断指定的Bean是否有资格作为自动装配的候选者
    boolean isAutowireCandidate(String beanName, DependencyDescriptor descriptor) throws NoSuchBeanDefinitionException;
    // 返回注册的Bean定义
    BeanDefinition getBeanDefinition(String beanName) throws NoSuchBeanDefinitionException;
    // 暂时冻结所有的Bean配置
    void freezeConfiguration();
    // 判断本工厂配置是否被冻结
    boolean isConfigurationFrozen();
    // 使所有的非延迟加载的单例类都实例化。
    void preInstantiateSingletons() throws BeansException;
}
复制代码

总结:

BeanFactory 的定义由 AbstractBeanFactory 抽象类实现接口的 getBean 方法. 而 AbstractBeanFactory 又继承了实现了 SingletonBeanRegistry 的DefaultSingletonBeanRegistry 类。这样 AbstractBeanFactory 抽象类就具备了单例Bean的注册功能。
AbstractBeanFactory中又定义了两个抽象方法:
1 getBeanDefinition(String beanName)【因为每当需要bean定义元数据时,AbstractBeanFactory这个类都会调用这个方法,根据具体bean工厂实现的性质,此操作可能会很昂贵
(例如,由于外部注册表中的目录查找)。然而,对于可列表bean工厂,这通常只相当于本地哈希查找】、该抽象方法由DefaultListableBeanFactory实现。
2 createBean(String beanName, BeanDefinition beanDefinition) ,这个抽象方法由AbstractAutowireCapableBeanFactory 实现。
最终DefaultListableBeanFactory还会继承抽象类AbstractAutowireCapableBeanFactory 也就可以调用抽象类中的createBean 方法了。

 

2 BeanDefinitionRegistry

复制代码
public interface BeanDefinitionRegistry extends AliasRegistry {
    // 给定bean名称,注册一个新的bean定义
    void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeanDefinitionStoreException;
    //根据指定Bean名移除对应的Bean定义
    void removeBeanDefinition(String beanName) throws NoSuchBeanDefinitionException;
    //根据指定bean名得到对应的Bean定义
    BeanDefinition getBeanDefinition(String beanName) throws NoSuchBeanDefinitionException;
    //查找,指定的Bean名是否包含Bean定义
    boolean containsBeanDefinition(String beanName);
    String[] getBeanDefinitionNames();//返回本容器内所有注册的Bean定义名称
    int getBeanDefinitionCount();//返回本容器内注册的Bean定义数目
    boolean isBeanNameInUse(String beanName);//指定Bean名是否被注册过。
}
复制代码

BeanDefinition继承体系:

SpringIOC容器管理了我们定义的各种Bean对象及其相互的关系,Bean对象在Spring实现中是以BeanDefinition来描述的,其继承体系如下:

 

(二)ApplicationContext继承体系:

 

下面介绍一下ApplicationContext初始化容器的过程:

测试代码:

@Test
    public void testApplicationContext(){
        ApplicationContext context = new ClassPathXmlApplicationContext(".\\HelloSpring\\src\\Beans.xml");
        HelloWorld obj = context.getBean("helloWorld",HelloWorld.class);
        obj.getMessage();
    }

调用refresh方法:

复制代码
org.springframework.context.support
public class ClassPathXmlApplicationContext extends AbstractXmlApplicationContext {
    public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, @Nullable ApplicationContext parent) throws BeansException {
    super(parent);
    this.setConfigLocations(configLocations);
    if (refresh) {
    //开始调用模板方法
        this.refresh();
    }
  }
}
复制代码

上层抽象类的AbstractApplicationContext的refresh方法.

复制代码
// 完成IoC容器的创建及初始化工作
    @Override
    public void refresh() throws BeansException, IllegalStateException {
        synchronized (this.startupShutdownMonitor) {
            // Prepare this context for refreshing.
            // STEP 1: 刷新预处理
            prepareRefresh();

            // Tell the subclass to refresh the internal bean factory.
            // STEP 2:
            //         a) 创建IoC容器(DefaultListableBeanFactory)
            //        b) 加载解析XML文件(最终存储到Document对象中)
            //        c) 读取Document对象,并完成BeanDefinition的加载和注册工作
            ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

            // Prepare the bean factory for use in this context.
            // STEP 3: 对IoC容器进行一些预处理(设置一些公共属性)
            prepareBeanFactory(beanFactory);

            try {
                // Allows post-processing of the bean factory in context subclasses.
                // STEP 4: 
                postProcessBeanFactory(beanFactory);

                // Invoke factory processors registered as beans in the context.
                // STEP 5: 调用BeanFactoryPostProcessor后置处理器对BeanDefinition处理
                invokeBeanFactoryPostProcessors(beanFactory);

                // Register bean processors that intercept bean creation.
          //注册
BeanPostProcessor后置处理器
// STEP 6:找到BeanPostProcessor的实现,排序后注册到容器
                registerBeanPostProcessors(beanFactory);

                // Initialize message source for this context.
                // STEP 7: 初始化一些消息源(比如处理国际化的i18n等消息源)
                initMessageSource();

                // Initialize event multicaster for this context.
                // STEP 8: 初始化应用事件广播器
                initApplicationEventMulticaster();

                // Initialize other special beans in specific context subclasses.
                // STEP 9: 初始化一些特殊的bean
                onRefresh();

                // Check for listener beans and register them.
                // STEP 10: 注册一些监听器
                registerListeners();

                // Instantiate all remaining (non-lazy-init) singletons.
                // STEP 11: 实例化剩余的单例bean(非懒加载方式)
                // 注意事项:Bean的IoC、DI和AOP都是发生在此步骤
                finishBeanFactoryInitialization(beanFactory);

                // Last step: publish corresponding event.
                // STEP 12: 完成刷新时,需要发布对应的事件
          //初始化生命周期处理器,调用生命周期处理器的onfresh方法,发布ContextRefreshedEvent事件 JMX相关处理
          
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(); } }
复制代码

几个关键类的介绍:

AbstractBeanDefinitionReader:实现BeanDefinitionReader接口的 bean 定义读取器的抽象基类。提供通用属性,例如要处理的 bean 工厂和用于加载 bean 类的类加载器。

XmlBeanDefinitionReader: XML bean 定义的 bean 定义阅读器。将实际的 XML 文档读取委托给BeanDefinitionDocumentReader接口的实现。通常应用于DefaultListableBeanFactory或GenericApplicationContext 。此类加载一个 DOM 文档并将 BeanDefinitionDocumentReader 应用到它。文档阅读器将向给定的 bean 工厂注册每个 bean 定义,并与后者的BeanDefinitionRegistry接口实现对话。

DefaultBeanDefinitionDocumentReader:BeanDefinitionDocumentReader接口的默认实现,该接口根据“spring-beans”DTD 和 XSD 格式(Spring 的默认 XML bean 定义格式)读取 bean 定义。所需 XML 文档的结构、元素和属性名称在此类中进行了硬编码。 (当然,如果需要生成这种格式,可以运行转换)。 <beans>不需要是 XML 文档的根元素:该类将解析 XML 文件中的所有 bean 定义元素,而不考虑实际的根元素。

BeanDefinitonParserDelegate:用于解析 XML bean 定义的有状态委托类。供主解析器和任何扩展BeanDefinitionParsers或BeanDefinitionDecorators使用。该类可以通过解析<bean>标签,获取BeanDefinition对象。

注册Bean的调用链,中间调用过程需要结合ApllicationContext继承体系看。然后结合代码本地debug一下,对于途中的函数,可以重点看下细节。

特别解释: 进入AbstractXmlApplicationContext类后就到了一个到转折点,  由XmlBeanDefinitionReader开始进行读取文件信息和注册BeanDefinition. 随后的过程就和xmLBeanFactory注册BeanDefinition的过程基本相同了.

 

两个扩展接口的说明:

满足于对 Bean 对象扩展的两个接口,其实也是 Spring 框架中非常具有重量级的两个接口:BeanFactoryPostProcessor 和 BeanPostProcessor,也几乎是大家在使用 Spring 框架额外新增开发自己组建需求的两个必备接口。
BeanFactoryPostProcessor,是由Spring框架组建提供的容器扩展机制,允许在Bean对象注册后但未实例化之前,对Bean的定义信息BeanDefinition执行修改操作
BeanPostProcessor,也是Spring提供的扩展机制,不过BeanPostProcessor是在Bean对象实例化之后修改Bean对象,也可以替换Bean对象。这部分与后面要实现的 AOP 有着密切的关系。

初始化与销毁的说明:

在 spring.xml 配置中添加 init-method、destroy-method 两个注解,在配置文件加载的过程中,把注解配置一并定义到 BeanDefinition 的属性当中。这样在 initializeBean 初始化操作的工程中,就可以通过反射的方式来
调用配置在Bean定义属性当中的方法信息了。另外如果是接口实现的方式,那么直接可以通过Bean对象调用对应接口定义的方法即可,((InitializingBean) bean).afterPropertiesSet(),两种方式达到的效果是一样的。
除了在初始化做的操作外,destroy-method和DisposableBean接口的定义,都会在Bean对象初始化完成阶段,执行注册销毁方法的信息到DefaultSingletonBeanRegistry类中的disposableBeans属性里,
这是为了后续统一进行操作。这里还有一段适配器的使用,因为反射调用和接口直接调用,是两种方式。所以需要使用适配器进行包装,下文代码讲解中参考 DisposableBeanAdapter的具体实现 -关于销毁方法需要在虚拟机执行关闭之前
进行操作,所以这里需要用到一个注册钩子的操作,如:Runtime.getRuntime().addShutdownHook(new Thread(() -> System.out.println("close!"))); 这段代码你可以执行测试,
另外你可以使用手动调用 ApplicationContext.close 方法关闭容器。

ApllicationContext的bean初始化流程:

 

 
posted @   小兵要进步  阅读(52)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 【杭电多校比赛记录】2025“钉耙编程”中国大学生算法设计春季联赛(1)

侧边栏
点击右上角即可分享
微信分享提示