技术干货Java技能图谱

7.spring5源码分析-BeanFactoryPostPro

2020-03-18  本文已影响0人  求索

其实你找不到错误不代表错误不存在,同样你看不到技术比你牛的人并不代表世界上没有技术比你牛的人。

spring 中提供两种bean扩展实现BeanFactoryPostProcessor 和BeanPostProcessor。

BeanFactoryPostProcessor

BeanFactory的后置处理器,它的主要功能是参与BeanFactory的建造,通过BeanFactoryPostProcessor可以实现 @ComponentScans扫包、Properties配置加载等功能。下图是spring几个基本后置处理器。

7.BeanFactoryPostProcessor.png
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
        int registryId = System.identityHashCode(registry);
        if (this.registriesPostProcessed.contains(registryId)) {
            throw new IllegalStateException(
                    "postProcessBeanDefinitionRegistry already called on this post-processor against " + registry);
        }
        if (this.factoriesPostProcessed.contains(registryId)) {
            throw new IllegalStateException(
                    "postProcessBeanFactory already called on this post-processor against " + registry);
        }
        this.registriesPostProcessed.add(registryId);
        processConfigBeanDefinitions(registry);
    }

该方法做了两件事。一保障processConfigBeanDefinitions一次处理;二、调用方法processConfigBeanDefinitions 。

public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
    List<BeanDefinitionHolder> configCandidates = new ArrayList<>();
    String[] candidateNames = registry.getBeanDefinitionNames();
    for (String beanName : candidateNames) {
        BeanDefinition beanDef = registry.getBeanDefinition(beanName);
        if (ConfigurationClassUtils.isFullConfigurationClass(beanDef) ||
                ConfigurationClassUtils.isLiteConfigurationClass(beanDef)) {
            logger.debug("has already been processed as a configuration class: " );
        } else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
            configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
        }
    }
    //省略排序、非空判断、环境准备等
    // Parse each @Configuration class
    ConfigurationClassParser parser = new ConfigurationClassParser(/*省略*/);
       //省略
    do {
        parser.parse(candidates);
        parser.validate();
        Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());
        configClasses.removeAll(alreadyParsed);
        // Read the model and create bean definitions based on its content
        if (this.reader == null) {
            this.reader = new ConfigurationClassBeanDefinitionReader( /*省略*/);
        }
        this.reader.loadBeanDefinitions(configClasses);
        alreadyParsed.addAll(configClasses);
         //省略
        for (String candidateName : newCandidateNames) {
            BeanDefinition bd = registry.getBeanDefinition(candidateName);
            if (ConfigurationClassUtils.checkConfigurationClassCandidate(bd, this.metadataReaderFactory) && !alreadyParsedClasses.contains(bd.getBeanClassName())) {
                candidates.add(new BeanDefinitionHolder(bd, candidateName));
            }
        }
        candidateNames = newCandidateNames;
    }
    while (!candidates.isEmpty());
       // ... 
}

通过源码分析可以看的核心代码分为三部分,以是判断是否为配置类

ConfigurationClassUtils.checkConfigurationClassCandidate
//具体实现:
// full:完成匹配,识别注解 @Configuration
public static boolean isFullConfigurationCandidate(AnnotationMetadata metadata) {
    return metadata.isAnnotated(Configuration.class.getName());
}

// 候选匹配 
// 优先判断注解 @Component、@ComponentScan、@Import、@ImportResource
// 最后判断 @Bean 注解
public static boolean isLiteConfigurationCandidate(AnnotationMetadata metadata) {
    // 接口中的注解不考虑
    if (metadata.isInterface()) {
        return false;
    } 
    // 任意candidateIndicators注解标记的类
    // candidateIndicators具体值有:Component、ComponentScan、Import、ImportResource
    for (String indicator : candidateIndicators) {
        if (metadata.isAnnotated(indicator)) {
            return true;
        }
    }
    // Finally, let's look for @Bean methods...
    return metadata.hasAnnotatedMethods(Bean.class.getName());
}
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
    Properties mergedProps = mergeProperties();
    // Convert the merged properties, if necessary.
    convertProperties(mergedProps);
    // Let the subclass process the properties.
    processProperties(beanFactory, mergedProps);
}

PropertyResourceConfigurer 主要做了三件事。

  1. 合并属性
  2. 转换类型
  3. 处理属性

BeanPostProcessor

Bean后置处理器,作用是在Bean对象在实例化和依赖注入完毕后,在显示调用初始化方法的前后添加我们自己的逻辑。注意是Bean实例化完毕后及依赖注入完成后触发的。接口的源码如下

public interface BeanPostProcessor {
    /**
     * 在 init-method 或 afterPropertiesSet 等初始化操作之前构造一个新实例返回
       */
    @Nullable
    default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }
    /**
     * 在 init-method 或 afterPropertiesSet 等初始化操作之后构造一个新实例返回
       */
    @Nullable
    default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }
}

类似修改bean配置信息、bean有效性验证等可以在postProcessBeforeInitialization实现; 一般AOP、代理等都在postProcessAfterInitialization阶段完成。

7.BeanPostProcessor.png

上图显示了几个BeanPostProcessor的实现:

public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
        if (bean != null) {
            Object cacheKey = getCacheKey(bean.getClass(), beanName);
            if (this.earlyProxyReferences.remove(cacheKey) != bean) {
                return wrapIfNecessary(bean, beanName, cacheKey);
            }
        }
        return bean;
}

通过 wrapIfNecessary 方法重新封装bean。wrapIfNecessary 方法包括核心代码:

  // Create proxy if we have advice.
        Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
        if (specificInterceptors != DO_NOT_PROXY) {
            this.advisedBeans.put(cacheKey, Boolean.TRUE);
            Object proxy = createProxy(
                    bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
            this.proxyTypes.put(cacheKey, proxy.getClass());
            return proxy;
        }

最后挑战到 createProxy 方法

protected Object createProxy(Class<?> beanClass, @Nullable String beanName,
            @Nullable Object[] specificInterceptors, TargetSource targetSource) {
        // ...
        ProxyFactory proxyFactory = new ProxyFactory();
        proxyFactory.copyFrom(this);
        if (!proxyFactory.isProxyTargetClass()) {
            //todo 设置代理类型
        }
        Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
        proxyFactory.addAdvisors(advisors);
        proxyFactory.setTargetSource(targetSource);
        // ...
        return proxyFactory.getProxy(getProxyClassLoader());
    }

从织入切面的方式上来看,存在三种织入方式:编译期织入、类加载期织入和运行期织入。编译期织入是指在Java编译期,采用特殊的编译器,将切面织入到Java类中;而类加载期织入则指通过特殊的类加载器,在类字节码加载到JVM时,织入切面;运行期织入则是采用CGLib工具或JDK动态代理进行切面的织入。

AspectJ采用编译期织入和类加载期织入的方式织入切面,是语言级的AOP实现,提供了完备的AOP支持。它用AspectJ语言定义切面,在编译期或类加载期将切面织入到Java类中。

AspectJ提供了两种切面织入方式,第一种通过特殊编译器,在编译期,将AspectJ语言编写的切面类织入到Java类中,可以通过一个Ant或Maven任务来完成这个操作;第二种方式是类加载期织入,也简称为LTW(Load Time Weaving)

核心代码:

public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
    if (bean instanceof LoadTimeWeaverAware) {
        LoadTimeWeaver ltw = this.loadTimeWeaver;
        if (ltw == null) {
            Assert.state(this.beanFactory != null,
                    "BeanFactory required if no LoadTimeWeaver explicitly specified");
            ltw = this.beanFactory.getBean(
                    ConfigurableApplicationContext.LOAD_TIME_WEAVER_BEAN_NAME, LoadTimeWeaver.class);
        }
        ((LoadTimeWeaverAware) bean).setLoadTimeWeaver(ltw);
    }
    return bean;
}

总结

上一篇 下一篇

猜你喜欢

热点阅读