美文网首页Java 杂谈程序员Spring Boot
Spring IOC基于注解启动分析

Spring IOC基于注解启动分析

作者: 神易风 | 来源:发表于2019-03-02 23:51 被阅读20次

    Spring 基于注解启动

    主要有两个Class实现注解启动

    • AnnotationConfigApplicationContext
    • AnnotationConfigWebApplicationContext

    我们以AnnotationConfigApplicationContext 为研究对象


    AnnotationConfigApplicationContext.png

    引入Spring 最小依赖

            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-context</artifactId>
                <version>${spring.version}</version>
            </dependency>
    

    编写器启动代码

     public static void main(String[] args) {
            AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
            applicationContext.register(BeanConfig.class);
            applicationContext.refresh();
            Date date = applicationContext.getBean("date",Date.class);
            System.out.println(date);
        }
    

    AnnotationConfigApplicationContext 构造函数

        public AnnotationConfigApplicationContext() {
                    //负责注册Class ,读取器
            this.reader = new AnnotatedBeanDefinitionReader(this);
                   //负责扫描指定类路径下的Class,注册bean
            this.scanner = new ClassPathBeanDefinitionScanner(this);
        }
    

    AnnotatedBeanDefinitionReader 构造方法

        public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry) {
            this(registry, getOrCreateEnvironment(registry));
        }
    
        public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry, Environment environment) {
            Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
            Assert.notNull(environment, "Environment must not be null");
            this.registry = registry;
                   //初始化ConditionEvaluator
            this.conditionEvaluator = new ConditionEvaluator(registry, environment, null);
                     
            /** 在給定的注册表中註冊所有相关的post processors
             *  判断容器是否已经存在给定注册表的bean,如果没有注册bean,并将bean放入容器中
             *  把所有的处理处理器列出来
             * ConfigurationClassPostProcessor 內部管理的配置注解处理器
             * AutowiredAnnotationBeanPostProcessor  内部管理@Autowired 的处理器
             * RequiredAnnotationBeanPostProcessor  @Required的处理器
             * CommonAnnotationBeanPostProcessor  JSR-250注解处理器 ,先判断是否支持jsr,如果支持注册
             * PersistenceAnnotationBeanPostProcessor JPA管理 先使用类加载器查找是否存在,如果有这个包则注册
             * EventListenerMethodProcessor  @EventListener的处理器
             * DefaultEventListenerFactory 管理EventListenerFactory处理器
             */
            AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
        }
    

    ConditionEvaluator 这个对象干什么,点击进去

        public ConditionEvaluator(@Nullable BeanDefinitionRegistry registry,
                @Nullable Environment environment, @Nullable ResourceLoader resourceLoader) {
    
            this.context = new ConditionContextImpl(registry, environment, resourceLoader); 
        }
            //ConditionContextImpl 实现了ConditionContext接口,ConditionEvaluator静态内部类
            public ConditionContextImpl(@Nullable BeanDefinitionRegistry registry,
                    @Nullable Environment environment, @Nullable ResourceLoader resourceLoader) {
    
                this.registry = registry;
                this.beanFactory = deduceBeanFactory(registry);
                this.environment = (environment != null ? environment : deduceEnvironment(registry));
                this.resourceLoader = (resourceLoader != null ? resourceLoader : deduceResourceLoader(registry));
                this.classLoader = deduceClassLoader(resourceLoader, this.beanFactory);
            }
    

    可以知道ConditionEvaluator使用外部传参的方法初始化了Spring容器顶级对象
    BeanFactory,Environment,ResourceLoader,ClassLoader。在将这些传给ConditionContextImpl为接下来的解析@Conditional注解做好准备

    ClassPathBeanDefinitionScanner构造函数

        public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry) {
            this(registry, true);
        }
    
        public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters) {
            this(registry, useDefaultFilters, getOrCreateEnvironment(registry));
        }
    
        public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters,
                Environment environment) {
            this(registry, useDefaultFilters, environment,
                    (registry instanceof ResourceLoader ? (ResourceLoader) registry : null));
        }
    
        public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters,
                Environment environment, @Nullable ResourceLoader resourceLoader) {
            Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
            this.registry = registry;
            if (useDefaultFilters) {
                registerDefaultFilters();
            }
            setEnvironment(environment);
            setResourceLoader(resourceLoader);
        }
    
        protected void registerDefaultFilters() {
            this.includeFilters.add(new AnnotationTypeFilter(Component.class));
            ClassLoader cl = ClassPathScanningCandidateComponentProvider.class.getClassLoader();
            try {
                this.includeFilters.add(new AnnotationTypeFilter(
                        ((Class<? extends Annotation>) ClassUtils.forName("javax.annotation.ManagedBean", cl)), false));
                logger.debug("JSR-250 'javax.annotation.ManagedBean' found and supported for component scanning");
            }
            catch (ClassNotFoundException ex) {
            }
            try {
                this.includeFilters.add(new AnnotationTypeFilter(
                        ((Class<? extends Annotation>) ClassUtils.forName("javax.inject.Named", cl)), false));
                logger.debug("JSR-330 'javax.inject.Named' annotation found and supported for component scanning");
            }
            catch (ClassNotFoundException ex) {
                // JSR-330 API not available - simply skip.
            }
        }
    
    

    绕了地球几圈了,其实就是将Spring 顶级接口 Environment,ResourceLoader赋值,使用默认注解过滤器,首先将@Component加入List中,判断当前环境是否支持JSR-250JSR-330,相应加入过滤器中。也就是这个扫描器默认只扫描@Component或者JSR-250JSR-330的标记的Class。

    applicationContext.register(BeanConfig.class)

        public void register(Class<?>... annotatedClasses) {
            Assert.notEmpty(annotatedClasses, "At least one annotated class must be specified");
            this.reader.register(annotatedClasses); //调用 刚刚初始化读取器
        }
                                                |
    ============================AnnotatedBeanDefinitionReader 读取器代码======================================================================================================
           public void register(Class<?>... annotatedClasses) {
            for (Class<?> annotatedClass : annotatedClasses) {
                registerBean(annotatedClass);
            }
         }
    
        public void registerBean(Class<?> annotatedClass) {
            doRegisterBean(annotatedClass, null, null, null);
        }
            
        /**
        *从给定的bean解析Class给定的注解,执行相应的初始化,保存到Spring容器中
        */
        <T> void doRegisterBean(Class<T> annotatedClass, @Nullable Supplier<T> instanceSupplier, @Nullable String name,
                @Nullable Class<? extends Annotation>[] qualifiers, BeanDefinitionCustomizer... definitionCustomizers) {
    
            //根据Class的Annotated 得出元数据 AnnotationMetadata
            AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(annotatedClass);
           /**
            *  判断注册的Class 是否包含@Conditional注解,如果有获取全部value,放入List中
            *  排序后,遍历所有的Conditiion的实现,使用反射获取对象,执行matches方法,
            * 如果发现有返回false,中断循环直接返回true,
            */
            if (this.conditionEvaluator.shouldSkip(abd.getMetadata())) { //如果 @Conditional条件不满足,不进行注册
                return;
            }
    
            abd.setInstanceSupplier(instanceSupplier);
            //解析Class是否有@Scope,解析@Scope注解返回ScopeMetadata对象,没有直接返回空
            ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(abd);
            abd.setScope(scopeMetadata.getScopeName());
           //判断注解上Value是否有值,有就使用这个作为BeanName,没有则取类名 
            String beanName = (name != null ? name : this.beanNameGenerator.generateBeanName(abd, this.registry));
            //继续解析AnnotationMetadata的@Lazy,@Primary,@DependsOn,@Role,@Description的注解,放入结果放入对象的属性中
            AnnotationConfigUtils.processCommonDefinitionAnnotations(abd);
            //这个类只是BeanDefinition 包装类
            BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(abd, beanName);
            //是否需要代理类,如果是则修改内部属性,重新生成BeanDefinition 对象
            definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
          //调用DefaultListableBeanFactory.registerBeanDefinition的方法,做一些安全性校验再,将definitionHolder  放入register容器中
            BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry);
        }
    

    这个方法就是将注册的Bean,解析Class上的注解,初始化注解数据,做相应处理,转化成BeanDefinition ,放入Spring 容器中保存起来。
    我们看下BeanDefinition是怎么实现注册到Spring的容器中,主要由DefaultListableBeanFactory.registerBeanDefinition来实现

    public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
                throws BeanDefinitionStoreException {
    
            Assert.hasText(beanName, "Bean name must not be empty");
            Assert.notNull(beanDefinition, "BeanDefinition must not be null");
    
            if (beanDefinition instanceof AbstractBeanDefinition) {
                try {
                    //对beanDefinition 进行校验判断MethodOverrides不能为空,必须拥有工厂方法
                    ((AbstractBeanDefinition) beanDefinition).validate();
                }
                catch (BeanDefinitionValidationException ex) {
                    throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
                            "Validation of bean definition failed", ex);
                }
            }
    
            BeanDefinition oldBeanDefinition;
    
            oldBeanDefinition = this.beanDefinitionMap.get(beanName); 
            if (oldBeanDefinition != null) {
                 //这个方法是判断是否允许出现重名bean,并且是不同的定义bean,是否可以覆盖前者
                if (!isAllowBeanDefinitionOverriding()) {
                    throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
                            "Cannot register bean definition [" + beanDefinition + "] for bean '" + beanName +
                            "': There is already [" + oldBeanDefinition + "] bound.");
                }
                else if (oldBeanDefinition.getRole() < beanDefinition.getRole()) {
                    // e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE
                    if (this.logger.isWarnEnabled()) {
                        this.logger.warn("Overriding user-defined bean definition for bean '" + beanName +
                                "' with a framework-generated bean definition: replacing [" +
                                oldBeanDefinition + "] with [" + beanDefinition + "]");
                    }
                }
                else if (!beanDefinition.equals(oldBeanDefinition)) {
                    if (this.logger.isInfoEnabled()) {
                        this.logger.info("Overriding bean definition for bean '" + beanName +
                                "' with a different definition: replacing [" + oldBeanDefinition +
                                "] with [" + beanDefinition + "]");
                    }
                }
                else {
                    if (this.logger.isDebugEnabled()) {
                        this.logger.debug("Overriding bean definition for bean '" + beanName +
                                "' with an equivalent definition: replacing [" + oldBeanDefinition +
                                "] with [" + beanDefinition + "]");
                    }
                }
                this.beanDefinitionMap.put(beanName, beanDefinition);
            }
            else {
                //调用alreadyCreated.isEmpty(),alreadyCreated Set对象,保存已经创建beanName
               //文档中表示created,跟这里注册应该不是同一个行为,这个要看到后面才知道什么意思
                if (hasBeanCreationStarted()) {
                    synchronized (this.beanDefinitionMap) {//更新数据
                        this.beanDefinitionMap.put(beanName, beanDefinition);
                        List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);
                        updatedDefinitions.addAll(this.beanDefinitionNames);
                        updatedDefinitions.add(beanName);
                        this.beanDefinitionNames = updatedDefinitions;
                        if (this.manualSingletonNames.contains(beanName)) {
                            Set<String> updatedSingletons = new LinkedHashSet<>(this.manualSingletonNames);
                            updatedSingletons.remove(beanName);
                            this.manualSingletonNames = updatedSingletons;
                        }
                    }
                }
                else { 
                    //Spring  beanDefinition 容器,一个Map转载
                    this.beanDefinitionMap.put(beanName, beanDefinition);
                  //保存beanName,主要用于记录每个bean注册顺序
                    this.beanDefinitionNames.add(beanName);
                  //删除单例,注册成一个普通bean
                    this.manualSingletonNames.remove(beanName);
                }
                this.frozenBeanDefinitionNames = null;
            }
    
            if (oldBeanDefinition != null || containsSingleton(beanName)) { 
                //更新Spring容器里beanName
                resetBeanDefinition(beanName);
            }
        }
    

    将beanDefinition注册到Spring容器中,并没有太多复杂的逻辑,只是做一些安全性的检查。

    BeanDefinition

    一个BeanDefinition描述了一个bean的实例,包括属性值,构造方法参数值和继承自它的类的更多信息。BeanDefinition仅仅是一个最简单的接口,主要功能是允许BeanFactoryPostProcessor 例如PropertyPlaceHolderConfigure 能够检索并修改属性值和别的bean的元数据(译注)
    Spring 容器beanDefinition主要分为RootBeanDefinition,AnnotatedGenericBeanDefinition这两种

    • RootBeanDefinition Spring Factory中的特定bean
    • AnnotatedGenericBeanDefinition 用户自定义bean

    Spring 启动流程总结

    AnnotationConfigApplicationContext 初始化.png

    这些BeanDefinition只是放入到Spirng 容器中,并没有进行任何初始化对象的操作,真正的IOC操作都在refresh(),这个方法有空再进行分析。

    相关文章

      网友评论

        本文标题:Spring IOC基于注解启动分析

        本文链接:https://www.haomeiwen.com/subject/zepfuqtx.html