Spring Boot扩展:深入分析 IoC 容器

Spring IoC 容器是整个框架中最核心、最关键的一部分内容,也是许多面试考察中的重要内容。 今天我将从 Spring 接口源码入手,与大家一块学习下容器相关的内容。 今天的内容包括三部分,第一部分是 Spring 中 BeanFactory 接口及其子类(接口)的梳理;第二部分是对 ApplicationContext 接口及其子类实现的梳理; 最后一部分从应用的角度介绍容器的使用、配置、定制化等内容。

01-BeanFactory

官方文档中,对 BeanFactory 定义或介绍为:

The root interface for accessing a Spring bean container. This interface is implemented by objects that hold a number of bean definitions, each uniquely identified by a String name.

这个定义,简单、明确的指明了 BeanFactory 的功能,它负责管理 Bean,并通过 String 类型的名称来区分不同的 Bean。 BeanFactory 定义了 Spring 框架中 Bean 容器应当具备的最基本的能力。 这些能力可以分为如下几类:

boolean containsBean(String name);
复制代码
 T getBean(Class requiredType) throws BeansException;
 T getBean(Class requiredType, Object... args) throws BeansException;
Object getBean(String name) throws BeansException;
 T getBean(String name, Class requiredType) throws BeansException;
Object getBean(String name, Object... args) throws BeansException;
 ObjectProvider getBeanProvider(Class requiredType);
 ObjectProvider getBeanProvider(ResolvableType requiredType);
复制代码
Class<?> getType(String name) throws NoSuchBeanDefinitionException;
Class<?> getType(String name, boolean allowFactoryBeanInit) throws NoSuchBeanDefinitionException;
复制代码
boolean isPrototype(String name) throws NoSuchBeanDefinitionException;
boolean isSingleton(String name) throws NoSuchBeanDefinitionException;
复制代码
boolean isTypeMatch(String name, Class<?> typeToMatch) throws NoSuchBeanDefinitionException;
boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException;
复制代码

01.1-盘点 BeanFactory 的众多子类(接口)

BeanFactory 接口的子类(接口),都在某些特定方面增强了它的功能,例如:

BeanFactory getParentBeanFactory();
boolean containsLocalBean(String name);
复制代码
 Map getBeansOfType(@Nullable Class type) throws BeansException; 
 Map getBeansOfType(@Nullable Class type, boolean includeNonSingletons, boolean allowEagerInit) throws BeansException;
Map getBeansWithAnnotation(Class<? extends Annotation> annotationType) throws BeansException;
int getBeanDefinitionCount();
String[] getBeanDefinitionNames();
复制代码
Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName)
			throws BeansException;
Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
			throws BeansException;
 NamedBeanHolder resolveNamedBean(Class requiredType) throws BeansException;
Object resolveBeanByName(String name, DependencyDescriptor descriptor) throws BeansException;
Object resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName) throws BeansException;
Object resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName,
			@Nullable Set autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException;
复制代码
protected  T doGetBean(
        String name, @Nullable Class requiredType, @Nullable Object[] args, boolean typeCheckOnly)
        throws BeansException {

    String beanName = transformedBeanName(name);
    Object beanInstance;

    // Eagerly check singleton cache for manually registered singletons.
    Object sharedInstance = getSingleton(beanName);
    if (sharedInstance != null && args == null) {
        /** 对于已经初始化过的单例,直接取出 */
        beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, null);
    }

    else {
        // Fail if we're already creating this bean instance:
        // We're assumably within a circular reference.
        if (isPrototypeCurrentlyInCreation(beanName)) {
            throw new BeanCurrentlyInCreationException(beanName);
        }

        /**
        * 如果当前容器中不包含 containsBeanDefinition(beanName) == false
        * 则尝试从父容器中取
        * 根据父容器的类型,调用不同的接口
        */
        BeanFactory parentBeanFactory = getParentBeanFactory();
        if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
            // Not found -> check parent.
            String nameToLookup = originalBeanName(name);
            if (parentBeanFactory instanceof AbstractBeanFactory) {
                return ((AbstractBeanFactory) parentBeanFactory).doGetBean(
                        nameToLookup, requiredType, args, typeCheckOnly);
            }
            else if (args != null) {
                // Delegation to parent with explicit args.
                return (T) parentBeanFactory.getBean(nameToLookup, args);
            }
            else if (requiredType != null) {
                // No args -> delegate to standard getBean method.
                return parentBeanFactory.getBean(nameToLookup, requiredType);
            }
            else {
                return (T) parentBeanFactory.getBean(nameToLookup);
            }
        }
        /**
        * 当前容器中没有、父容器中也没找到,则尝试创建(实例化)
        */
        if (!typeCheckOnly) {
            markBeanAsCreated(beanName);
        }

        StartupStep beanCreation = this.applicationStartup.start("spring.beans.instantiate")
                .tag("beanName", name);
        try {
            if (requiredType != null) {
                beanCreation.tag("beanType", requiredType::toString);
            }
            /**
            * 取得 BeanName 对应的 BeanDefinition 对象
            */
            RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
            checkMergedBeanDefinition(mbd, beanName, args);

            /**
            * 是否有依赖项,若有,则先创建依赖项
            */
            // Guarantee initialization of beans that the current bean depends on.
            String[] dependsOn = mbd.getDependsOn();
            if (dependsOn != null) {
                for (String dep : dependsOn) {
                    /** 循环依赖的一种情况,发现后抛异常 */
                    if (isDependent(beanName, dep)) {
                        throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                                "Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
                    }
                    registerDependentBean(dep, beanName);
                    try {
                        /** 最终又到 doGetBean */
                        getBean(dep);
                    }
                    catch (NoSuchBeanDefinitionException ex) {
                        throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                                "'" + beanName + "' depends on missing bean '" + dep + "'", ex);
                    }
                }
            }
            /** 所有的依赖都创建完毕后,创建当前 Bean */
            // Create bean instance.
            if (mbd.isSingleton()) {
                /** 这个过程在之前分析创建 Bean 的过程时分析过 */
                sharedInstance = getSingleton(beanName, () -> {
                    try {
                        return createBean(beanName, mbd, args);
                    }
                    catch (BeansException ex) {
                        // Explicitly remove instance from singleton cache: It might have been put there
                        // eagerly by the creation process, to allow for circular reference resolution.
                        // Also remove any beans that received a temporary reference to the bean.
                        destroySingleton(beanName);
                        throw ex;
                    }
                });
                beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
            }
            else if (mbd.isPrototype()) {
                // It's a prototype -> create a new instance.
                Object prototypeInstance = null;
                try {
                    beforePrototypeCreation(beanName);
                    /** 过程与创建单例 Bean 类似,不同在于前后的 beforeXXX 和 afterXXX */
                    prototypeInstance = createBean(beanName, mbd, args);
                }
                finally {
                    afterPrototypeCreation(beanName);
                }
                beanInstance = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
            }
            else {
                /** request、session、application 等类型的作用范围在这里处理,用户自定义的作用范围也在这里处理 */
                String scopeName = mbd.getScope();
                if (!StringUtils.hasLength(scopeName)) {
                    throw new IllegalStateException("No scope name defined for bean '" + beanName + "'");
                }
                Scope scope = this.scopes.get(scopeName);
                if (scope == null) {
                    throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
                }
                try {
                    /** 与 prototype 类似 */
                    Object scopedInstance = scope.get(beanName, () -> {
                        beforePrototypeCreation(beanName);
                        try {
                            return createBean(beanName, mbd, args);
                        }
                        finally {
                            afterPrototypeCreation(beanName);
                        }
                    });
                    beanInstance = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
                }
                catch (IllegalStateException ex) {
                    throw new ScopeNotActiveException(beanName, scopeName, ex);
                }
            }
        }
        catch (BeansException ex) {
            beanCreation.tag("exception", ex.getClass().toString());
            beanCreation.tag("message", String.valueOf(ex.getMessage()));
            cleanupAfterBeanCreationFailure(beanName);
            throw ex;
        }
        finally {
            beanCreation.end();
        }
    }
    /** 根据 requiredType 的情况,确定是否需要做类型转换 */
    return adaptBeanInstance(name, beanInstance, requiredType);
}
复制代码

AbstractBeanFactory 预留了三个抽象方法由子类实现,containsBeanDefinition、getBeanDefinition 和 createBean。

02-ApplicationContext

ApplicationContext 的接口定义如下:

public interface ApplicationContext extends EnvironmentCapable, ListableBeanFactory, HierarchicalBeanFactory,
		MessageSource, ApplicationEventPublisher, ResourcePatternResolver { ... }
复制代码

从接口定义上看,ApplicationContext 继承了 ListableBeanFactory 和 HierarchicalBeanFactory,因此具备它们所具有的能力(这里需要注意的是 ApplicationContext 并没有重复实现 BeanFactory 的功能,而是将请求代理给内部持有的 BeanFactory)。 GenericApplicationContext 中就将对容器的请求,代理给了 DefaultListableBeanFactory 这一 BeanFactory 实现。 除此之外,ApplicationContext 继承了 EnvironmentCapable 表明它具有操纵 Environment 的能力(Environment getEnvironment();); 继承了 ResourcePatternResolver 表明它能够解析、加载资源文件(getResources、getResource); 继承了 MessageSource 表明它能够解析消息,处理参数化、国际化等; 继承了 ApplicationEventPublisher 表明它能够发布相关事件(publishEvent)。

ApplicationContext 自身只定义了少数几个方法,例如 getId、getApplicationName、getDisplayName、getAutowireCapableBeanFactory 等。 ApplicationContext 的直接子接口包括两个:ConfigurableApplicationContext 和 WebApplicationContext。

接下来,我来完成前面留下的一个任务,即与大家一块学习下 AbstractApplicationContext 对 ConfigurableApplicationContext#refresh 方法的实现。

@Override
public void refresh() throws BeansException, IllegalStateException {
    synchronized (this.startupShutdownMonitor) {
        StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");

        /**
        * 主要是设置 active/closed 状态位,startDate 统计信息,
        * 初始化 PropertySource、验证 required property 等
        */
        // Prepare this context for refreshing.
        prepareRefresh(); 

        /**
        * 刷新内部的 BeanFactory,实际是 DefaultListableBeanFactory 对象。
        * 如果存在旧的 BeanFactory,则关闭后,重新创建、配置并加载 BeanDefinition
        */
        // Tell the subclass to refresh the internal bean factory.
        ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

        /**
        * 对 BeanFactory 进行预处理,包括:
        * 1. 设置类加载器
        * 2. 设置 BeanPostProcessor:ApplicationContextAwareProcessor、ApplicationListenerDetector 和 LoadTimeWeaverAwareProcessor 等。
        * 3. 注入 environment、systemProperties 等对象
        * 这里是官网对比 ApplicationContext 与 BeanFactory 优劣时的一项内容 “Automatic BeanPostProcessor registration” 
        * ApplicationContext 相比 BeanFactory 会做更多的事,开发时更推荐使用 ApplicationContext,除非你有非用 BeanFactory 不可的理由。
        */
        // Prepare the bean factory for use in this context.
        prepareBeanFactory(beanFactory);

        try {
            // Allows post-processing of the bean factory in context subclasses.
            postProcessBeanFactory(beanFactory);
            /**
            * 记录启动时间
            */
            StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");
            // Invoke factory processors registered as beans in the context.
            invokeBeanFactoryPostProcessors(beanFactory);

            /**
            * 注册 BeanPostProcessor
            */
            // Register bean processors that intercept bean creation.
            registerBeanPostProcessors(beanFactory);
            beanPostProcess.end();

            // Initialize message source for this context.
            initMessageSource();

            // Initialize event multicaster for this context.
            initApplicationEventMulticaster();

            // Initialize other special beans in specific context subclasses.
            onRefresh();

            // Check for listener beans and register them.
            registerListeners();

            /**
            * 这里会触发容器预实例化所有的单例 Bean 对象
            */
            // Instantiate all remaining (non-lazy-init) singletons.
            finishBeanFactoryInitialization(beanFactory);
            
            /**
            * 容器的初始化完成,通知其他关注容器事件的模块,容器初始化(刷新)完成
            */
            // Last step: publish corresponding event.
            finishRefresh();
        }
        catch (BeansException 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();
            contextRefresh.end();
        }
    }
}
复制代码

接下来,我会特别介绍下 ApplicationContextAwareProcessor 这个 BeanPostProcessor 实现。 它与 AbstractAutowireCapableBeanFactory 关系比较密切。 AbstractAutowireCapableBeanFactory 中定义了两个集合,ignoredDependencyTypes 和 ignoredDependencyInterfaces,并实现了 ConfigurableListableBeanFactory#ignoreDependencyType 和 ConfigurableListableBeanFactory#ignoreDependencyInterface 两个方法,分别向两个集合中添加元素。 这两个集合仅在 AbstractAutowireCapableBeanFactory#isExcludedFromDependencyCheck 方法中使用,主要作用是判断某个特定的 property 是否应当在依赖检查中被排除。

protected boolean isExcludedFromDependencyCheck(PropertyDescriptor pd) {
    return (AutowireUtils.isExcludedFromDependencyCheck(pd) ||    /** 忽略 CGLIB 定义的 property */
            this.ignoredDependencyTypes.contains(pd.getPropertyType()) || /** 忽略 ignoredDependencyTypes 集合中包含的类型 */
            AutowireUtils.isSetterDefinedInInterface(pd, this.ignoredDependencyInterfaces)); /** 如果 property 的设置方法在 ignoredDependencyInterfaces 中有定义,则忽略 */
}
复制代码

isExcludedFromDependencyCheck 方法被两个地方调用:

  1. AbstractAutowireCapableBeanFactory#unsatisfiedNonSimpleProperties,在 populateBean 阶段调用 AbstractAutowireCapableBeanFactory#autowireByName 和 AbstractAutowireCapableBeanFactory#autowireByType 是被调用。
  2. AbstractAutowireCapableBeanFactory#filterPropertyDescriptorsForDependencyCheck,在 populateBean 阶段被调用。

通过上面的介绍可以得出,这两个集合在填充类属性(即解析依赖并注入依赖)时使用。

ApplicationContextAwareProcessor 是一个 BeanPostProcessor,简单来说就是 Bean 容器在创建、实例化一个托管 Bean 时,会在一定的时机调用其中的回调方法。 ApplicationContextAwareProcessor 负责处理多个 Aware 接口,在 Bean 实例化后,调用它的 setXXX 方法:

03-如何创建并使用 IoC 容器?

基于 Spring IoC 容器构建的应用一般包括两部分,第一部分是业务对象,一般是指托管在容器中的 Java 类; 第二部分是配置元信息,这部分信息告诉 Spring IoC 容器如何实例化、配置、装配托管的 Bean。 配置元信息可以有多种形式:

如无特殊说明,本节中涉及的 IoC 容器指 ApplicationContext 接口的不同实现。

03.1-容器的实例化和使用

/** 使用 XML 作为配置文件 */
final ApplicationContext context = new ClassPathXmlApplicationContext("services.xml", "daos.xml");

/** 使用 Java 类作为配置文件 */
final ApplicationContext context = new AnnotationConfigApplicationContext(Config.class);

/** 使用 Groovy 文件作为配置文件 */
final ApplicationContext context = new GenericGroovyApplicationContext("services.groovy", "daos.groovy");
复制代码
/** 使用容器 */
final Object foo = context.getBean("foo");
复制代码

03.2-盘点使用 Java 类作为配置文件时常用的注解

本节中,我将介绍使用 Java 类做为配置文件时常用的注解。 相信大部分人对这部分内容都比较熟悉,因而我不会过多的展开。你也可以选择跳过本节的内容。

@Autowired 是最常见的一个注解,它可以标注在构造器方法、setter 方法、其他方法,甚至可以直接注解在对象属性上。

AutowiredAnnotationBeanPostProcessor#findAutowiringMetadata

@Autowired 标注的地方,可以称之为依赖注入点(injection points)。 而且,默认是通过类型来进行注入的。注意看下面的示例:

@Autowired
private Bar[] bars;
复制代码

会收集容器中所有的 Bar 类型的 Bean,组合成数组,注入到 bars 中。 上述机制,除了数组类型,同样适用于 Set、Map 类型。

默认情况下,@Autowired 定义的注入点,默认是必需依赖;如果依赖非必需(或可选的),则可以通过 @Autowired(required = false)。 必需依赖和非必需依赖的区别在于,当依赖注入时,容器中找不到必需依赖时会抛异常,导致容器退出;而非必需依赖找不到时,只会忽略。 非必需依赖的另一种方式(Java 8 及以上版本),是通过 Optional 类,例如:

 @Autowired
public void setBar(Optional bar) {  }
复制代码

另外一种方式是 JSR-305 定义的 @Nullable:

 @Autowired
public void setBar(@Nullable Bar bar) {  }
复制代码

@Resource 与 @Autowired 作用一样,不同的是它由 JSR-250 定义,并且定义的注入点是按照 BeanName 注入的,而非按照类型。 @Inject 类似,由 JSR-330 定义,默认按照类型注入。

按照类型注入时,会遇到同类型多个候选对象的情况。 @Primary 的作用是在自动装配时,从众多候选者中指定一个优先级最高的候选者。 @Qualifier 的作用类似,用在自动装配时,不过能够提供粒度更细的控制。

前面提到的注解,注入的都是其他 Bean,即依赖类型为复合类型。 如果要注入的值是简单类型,例如字符串,可以使用 @Value。 它的数据来源可以是 Environment、SystemProperties 等中的变量,也可以是 @PropertySource 指定的外部配置文件中的变量。 而且,@Value 支持 SpEL 表达式。

JSR-250 还定义了 Bean 的生命周期回调 @PostConstruct 和 @PreDestroy,作用类似于 XML 配置中的 "init-method" 和 "destroy-method"。

Spring 中,Bean 的默认作用范围是 Singleton,可以通过 @Scope 修改它的实际作用范围。

@Component 及它的子类型 @Service、@Controller、@Repository、@Configuration 被称之为 "Stereotype Annotations"(样板注解)。 Spring 中提供了自动扫描或检测的机制,来发现上述样板注解标注的类,并创建对应的 Bean 对象。这个机制称为 "Component Scan"(组件扫描)。 组件扫描通过 @ComponentScan 来配置,作用与 XML 配置中的 一样。 组件扫描识别到的类,创建 Bean 对象时如何来生成 BeanName 是由 BeanNameGenerator 来完成的。 在 AnnotationConfigApplicationContext 中,默认使用的是 AnnotationBeanNameGenerator。

JSR-330 中定义了 @Named、@ManagedBean,与 @Component 作用一样。

@Bean 注解一般可以用在 @Configuration 或其他 @Component 类中,用来定义 Bean。

03.3-盘点 Spring IoC 容器预留给用户的扩展点

一般来说,用户不需要通过继承 ApplicationContext 实现的方式扩展容器的功能。 Spring IoC 容器在设计的时候,预留了扩展点给用户,以便用户实现特有的业务功能。 接下来,我将盘点一下 Spring 预留的扩展点。

首先,最为人熟知或者最常见的扩展点是 BeanPostProccessor 接口。 它共定义了两个方法:

public interface BeanPostProcessor {
	@Nullable
	default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
		return bean;
	}

	@Nullable
	default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
		return bean;
	}
复制代码

这两个方法的调用时机发生在 AbstractAutowireCapableBeanFactory#initializeBean 中,即 Spring 创建 Bean 实例过程三阶段中的最后一个阶段。

Object wrappedBean = bean;
if (mbd == null || !mbd.isSynthetic()) {
    wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
}

try {
    invokeInitMethods(beanName, wrappedBean, mbd);
}
catch (Throwable ex) {  }
if (mbd == null || !mbd.isSynthetic()) {
    wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
}
复制代码

AppliationContext 实现类会自动的查找容器中所有 BeanPostProcessor 实现,并将它们注册到 BeanFactory 中。

// org.springframework.context.support.AbstractApplicationContext#refresh
registerBeanPostProcessors(beanFactory);

// org.springframework.context.support.PostProcessorRegistrationDelegate#registerBeanPostProcessors
BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
priorityOrderedPostProcessors.add(pp);

registerBeanPostProcessors(beanFactory, priorityOrderedPostProcessors);
复制代码

另外一个扩展点是 BeanFactoryPostProcessor 接口,它只定义了一个方法:

public interface BeanFactoryPostProcessor {
	void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;
}
复制代码

调用时机发生在 ApplicationContext 容器刷新时,即 AbstractApplicationContext#refresh 中。

// org.springframework.context.support.AbstractApplicationContext#refresh
invokeBeanFactoryPostProcessors(beanFactory);

// org.springframework.context.support.AbstractApplicationContext#invokeBeanFactoryPostProcessors
PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());
复制代码

PostProcessorRegistrationDelegate#invokeBeanFactoryPostProcessors 中,会先执行 BeanDefinitionRegistryPostProcessor(它是 BeanFactoryPostProcessor 的一个子类),再执行其他的 BeanFactoryPostProcessor。 ConfigurationClassPostProcessor 是 BeanDefinitionRegistryPostProcessor 的一个实现类,负责处理 @Configuration 标注的类。

再举个例子,PropertySourcesPlaceholderConfigurer 也实现了 BeanFactoryPostProcessor 接口,负责处理 BeanDefinition 中的“${...} ”占位符。 BeanFactoryPostProcessor 的调用时机发生在所有的单例 Bean 被创建之前,而且它操作的对象是 BeanDefinition。 所以,如果你有在 Spring 创建 Bean 之前做某些特定逻辑的需求,可以考虑实现自己的 BeanFactoryPostProcessor。

最后一个扩展点是 FactoryBean 接口,即工厂 Bean。 这个接口其实比较容易理解,它里面最重要的方法就是 FactoryBean#getObject 方法。 当通过 "&" + "beanName" 方式从容器中获取 Bean 时,获得的是 FactoryBean 对象(若有)。 如果仅通过 "beanName" 从容器中获取,当存在 BeanFactory 对象时,会调用它的 getObject 方法来创建一个对象。 当某个 Bean 的创建过程非常复杂时,可以通过自定义 FactoryBean 的方式来完成 Bean 的创建。

希望今天的内容能对你有所帮助。

展开阅读全文

页面更新:2024-05-09

标签:容器   子类   注解   接口   定义   对象   作用   类型   代码   方法

1 2 3 4 5

上滑加载更多 ↓
推荐阅读:
友情链接:
更多:

本站资料均由网友自行发布提供,仅用于学习交流。如有版权问题,请与我联系,QQ:4156828  

© CopyRight 2008-2024 All Rights Reserved. Powered By bs178.com 闽ICP备11008920号-3
闽公网安备35020302034844号

Top