程序员社区

Spring 框架源码解读8


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

怎么放进去的

Spring 框架源码解读8插图
Spring 框架源码解读8插图1
Spring 框架源码解读8插图2
Spring 框架源码解读8插图3
Spring 框架源码解读8插图4
Spring 框架源码解读8插图5
Spring 框架源码解读8插图6

然后调用了这个方法

Spring 框架源码解读8插图7
ConfigurationClassPostProcessor
Spring 框架源码解读8插图8
Spring 框架源码解读8插图9
Spring 框架源码解读8插图10

工厂方法怎么执行的

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

Spring 框架源码解读8插图11
Spring 框架源码解读8插图12
Spring 框架源码解读8插图13

思考题答案

Spring 框架源码解读8插图14
赞(0) 打赏
未经允许不得转载:IDEA激活码 » Spring 框架源码解读8

一个分享Java & Python知识的社区