Spring 框架源码解读8

作者: 想54256 | 来源:发表于2020-04-18 18:50 被阅读0次

    title: Spring 框架源码解读8
    date: 2020/04/18 16:17


    本节内容 & 思考题

    1. 实现 Spring 中包扫描 @Component 注解
    <context:component-scan base-package="com.dist.ars.aop.test"/>
    
    1. 实现注解版实例工厂(@Configuration + @Bean 注解实现)

    猜猜 @ComponentScan 和 xml 中的包扫描,Spring 是怎样实现的呢?

    实现配置文件包扫描

    1、@Component 注解

    @Target({ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    public @interface Component {
    
        String value() default "";
    }
    

    2、修改 json 文件格式

    {
      "beans": [
        {
          "name": "appleFactory",
          "className": "cn.x5456.summer.AppleFactory"
        },
        {
          "name": "initAnnotationBeanPostProcessor",
          "className": "cn.x5456.summer.InitAnnotationBeanPostProcessor"
        }
      ],
      "componentScanPackages": [
        "cn.x5456.summer"
      ]
    }
    

    3、修改 JsonBeanFactoryImpl

    public class JsonBeanFactoryImpl extends ListableBeanFactoryImpl {
    
        public JsonBeanFactoryImpl(String filePath) {
            this.loadBeanDefinitions(filePath);
        }
    
        public JsonBeanFactoryImpl(String filePath, BeanFactory parentBeanFactory) {
            super(parentBeanFactory);
            this.loadBeanDefinitions(filePath);
        }
    
        private void loadBeanDefinitions(String filePath) {
            String json = FileUtil.readUtf8String(filePath);
            Map<String, Object> configMap = JsonUtils.toMap(json, String.class, Object.class);
    
            List<Map<String, String>> beanDefinitionList = (List<Map<String, String>>) configMap.get("beans");
            if (ObjectUtil.isNotEmpty(beanDefinitionList)) {
                for (Map<String, String> map : beanDefinitionList) {
                    BeanDefinition bd = BeanUtil.mapToBeanIgnoreCase(map, DefaultBeanDefinition.class, true);
                    super.registerBeanDefinition(bd.getName(), bd);
                }
            }
    
            // 读取包扫描路径
            List<String> scanPackageNames = (List<String>) configMap.get("componentScanPackages");
            if (ObjectUtil.isNotEmpty(scanPackageNames)) {
                for (String packageName : scanPackageNames) {
                    Set<Class<?>> classes = ClassUtil.scanPackage(packageName);
                    for (Class<?> clazz : classes) {
                        // 判断是否具有 @Component 注解,并且本身不是注解
                        Component component = AnnotationUtil.getAnnotation(clazz, Component.class);
                        if (ObjectUtil.isNotNull(component) && !clazz.isAnnotation()) {
                            DefaultBeanDefinition bd = new DefaultBeanDefinition();
        
                            String beanName = StrUtil.isNotBlank(component.value()) ? component.value() : StrUtil.lowerFirst(clazz.getSimpleName());
                            bd.setName(beanName);
                            bd.setClassName(clazz.getName());
        
                            // TODO: 2020/4/18 参数列表
        
                            super.registerBeanDefinition(beanName, bd);
                        }
                    }
                }
            }
    
            // 向 beanPostProcessors 中添加后置处理器
            for (BeanPostProcessor beanPostProcessor : super.getBeansOfType(BeanPostProcessor.class).values()) {
                super.addBeanPostProcessor(beanPostProcessor);
            }
        }
    }
    

    使用 BeanDefinitionRegistryPostProcessor 实现对配置类中 @Bean 注解的扫描

    1、@Bean

    @Documented
    @Retention(RetentionPolicy.RUNTIME)
    @Target({ElementType.METHOD})
    public @interface Bean {
    
        /**
         * 组件名称
         */
        String value() default "";
    
        /**
         * 初始化方法
         */
        String initMethod() default "";
    
        /**
         * 销毁方法
         */
        String destroyMethod() default "";
    
    }
    

    2、新增 BeanDefinitionRegistry#registerBeanDefinition

    public interface BeanDefinitionRegistry {
    
        /**
         * 注册 bd 到 bf
         */
        void registerBeanDefinition(String name, BeanDefinition beanDefinition);
    
        /**
         * 根据 bdName 获取 bd
         */
        BeanDefinition getBeanDefinition(String name);
    }
    

    3、ConfigurationClassPostProcessor

    public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPostProcessor {
    
        @Override
        public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
            ListableBeanFactory bf = (ListableBeanFactory) registry;
    
            for (String bdName : bf.getBeanDefinitionNames()) {
                BeanDefinition classBeanDefinition = registry.getBeanDefinition(bdName);
                String className = classBeanDefinition.getClassName();
                Class<?> clazz = ReflectUtils.getType(className);
    
                // 找到类上携带 @Configuration 的
                Configuration configuration = AnnotationUtil.getAnnotation(clazz, Configuration.class);
                if (ObjectUtil.isNotEmpty(configuration)) {
                    // 循环找方法中包含 @Bean 注解的
                    for (Method method : clazz.getMethods()) {
                        if (method.isAnnotationPresent(Bean.class)) {
                            BeanDefinition bdDef = new DefaultBeanDefinition();
    
                            Bean bean = method.getAnnotation(Bean.class);
                            String beanName = ObjectUtil.isNotEmpty(bean.value()) ? bean.value() : method.getName();
    
                            bdDef.setName(beanName);
                            bdDef.setFactoryBean(classBeanDefinition.getName());
                            bdDef.setFactoryMethod(method.getName());
                            bdDef.setInitMethod(bean.initMethod());
                            bdDef.setDestroyMethod(bean.destroyMethod());
    
                            // TODO: 2020/4/18 属性列表
    
                            registry.registerBeanDefinition(beanName, bdDef);
                        }
                    }
                }
            }
        }
    }
    

    Spring 5.0

    怎么放进去的

    image image image image image image image

    然后调用了这个方法

    ConfigurationClassPostProcessor image image image

    工厂方法怎么执行的

    这部分我在 xml 文件配置工厂方法那里没有讲,因为我觉得 Spring 还是比较清晰的。

    image image image

    思考题答案

    image

    相关文章

      网友评论

        本文标题:Spring 框架源码解读8

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