7.refresh-invokeBeanFactoryPostProcessor方法解析(中)

【接上文】

  • @Import
    ConfigurationClassParser#311
processImports(configClass, sourceClass, getImports(sourceClass), filter, true);

ConfigurationClassParser#processImports

//在解析时,入栈,解析结束后,出栈,通过检查栈中是否有当前类,判断是否有循环依赖
private final ImportStack importStack = new ImportStack();

//记录所有的ImportBeanDefinitionRegistrar
private final Map<ImportBeanDefinitionRegistrar, AnnotationMetadata> importBeanDefinitionRegistrars = new LinkedHashMap<>();

private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
			Collection<SourceClass> importCandidates, Predicate<String> exclusionFilter,
			boolean checkForCircularImports) {
    if (importCandidates.isEmpty()) {
        return;
    }
    if (checkForCircularImports && isChainedImportOnStack(configClass)) {
        this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));
    } else {
        //在解析时,入栈,解析结束后,出栈,通过检查栈中是否有当前类,判断是否有循环依赖
        this.importStack.push(configClass);
        try {
            for (SourceClass candidate : importCandidates) {
                //处理@ImportSelector
                if (candidate.isAssignable(ImportSelector.class)) {
                    //处理实现了ImportSelector接口的类
                    ...具体过程下文分析,看到整体脉络即可
                }
                //处理实现了ImportBeanDefinitionRegistrar接口的类
                else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
                    //处理实现了ImportBeanDefinitionRegistrar接口的类
                    ...具体过程下文分析,看到整体脉络即可
                }
                else {
                    // Candidate class not an ImportSelector or ImportBeanDefinitionRegistrar ->
                    // process it as an @Configuration class
                    //两个都不满足,作为一个配置类解析
                    this.importStack.registerImport(
                            currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
                    processConfigurationClass(candidate.asConfigClass(configClass), exclusionFilter);
                }
            }
        } catch (BeanDefinitionStoreException ex) {
            throw ex;
        } catch (Throwable ex) {
            throw new BeanDefinitionStoreException(
                    "Failed to process import candidates for configuration class [" +
                    configClass.getMetadata().getClassName() + "]", ex);
        } finally {
            this.importStack.pop();
        }
    }
}

ConfigurationClassParser#getImports

/**
 * Returns {@code @Import} class, considering all meta-annotations.
 */
private Set<SourceClass> getImports(SourceClass sourceClass) throws IOException {
    Set<SourceClass> imports = new LinkedHashSet<>();
    Set<SourceClass> visited = new LinkedHashSet<>();
    //递归查询所有注解以及注解的注解是否包含@Import
    collectImports(sourceClass, imports, visited);
    return imports;
}

/**
 * Recursively collect all declared {@code @Import} values. Unlike most
 * meta-annotations it is valid to have several {@code @Import}s declared with
 * different values; the usual process of returning values from the first
 * meta-annotation on a class is not sufficient.
 * <p>For example, it is common for a {@code @Configuration} class to declare direct
 * {@code @Import}s in addition to meta-imports originating from an {@code @Enable}
 * annotation.
 * @param sourceClass the class to search
 * @param imports the imports collected so far
 * @param visited used to track visited classes to prevent infinite recursion
 * @throws IOException if there is any problem reading metadata from the named class
 */
private void collectImports(SourceClass sourceClass, Set<SourceClass> imports, Set<SourceClass> visited)
        throws IOException {
    //记录是否已经扫描过这个类,如果扫描过就不重复添加,防止重复或者死循环
    if (visited.add(sourceClass)) {
        for (SourceClass annotation : sourceClass.getAnnotations()) {
            String annName = annotation.getMetadata().getClassName();
            //对于非@Import注解,递归查找其内部是否包含@Import注解
            if (!annName.equals(Import.class.getName())) {
                collectImports(annotation, imports, visited);
            }
        }
        //添加@Import注解里面的所有配置类
        imports.addAll(sourceClass.getAnnotationAttributes(Import.class.getName(), "value"));
    }
}

通过getImports方法,采集相关的@Import里面的类。

对ImportSelector的处理

//处理@ImportSelector
if (candidate.isAssignable(ImportSelector.class)) {
    //处理@ImportSelector
    // Candidate class is an ImportSelector -> delegate to it to determine imports
    Class<?> candidateClass = candidate.loadClass();
    ImportSelector selector = ParserStrategyUtils.instantiateClass(candidateClass, ImportSelector.class,
            this.environment, this.resourceLoader, this.registry);
    Predicate<String> selectorFilter = selector.getExclusionFilter();
    //查看是否有过滤器
    if (selectorFilter != null) {
        exclusionFilter = exclusionFilter.or(selectorFilter);
    }
    //如果是DeferredImportSelector,则用deferredImportSelectorHandler处理
    if (selector instanceof DeferredImportSelector) {
        this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);
    }
    else {
        //迭代进行selectImports中bean的解析
        String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
        Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames, exclusionFilter);
        processImports(configClass, currentSourceClass, importSourceClasses, exclusionFilter, false);
    }
}

获取ImportSelect实例的时候用到了ParserStregyUtils这个类,大概看下。

package org.springframework.context.annotation;
...省略引包代码
/**
 * Common delegate code for the handling of parser strategies, e.g.
 * {@code TypeFilter}, {@code ImportSelector}, {@code ImportBeanDefinitionRegistrar}
 * 处理解析策略的通用代理类
 */
abstract class ParserStrategyUtils {

	/**
	 * Instantiate a class using an appropriate constructor and return the new
	 * instance as the specified assignable type. The returned instance will
	 * have {@link BeanClassLoaderAware}, {@link BeanFactoryAware},
	 * {@link EnvironmentAware}, and {@link ResourceLoaderAware} contracts
	 * invoked if they are implemented by the given object.
     * 使用合适的构造方法实例化一个类,返回的实例如果实现了aware接口将会调用对应的set方法。
	 * @since 5.2
	 */
	@SuppressWarnings("unchecked")
	static <T> T instantiateClass(Class<?> clazz, Class<T> assignableTo, Environment environment,
			ResourceLoader resourceLoader, BeanDefinitionRegistry registry) {

		Assert.notNull(clazz, "Class must not be null");
		Assert.isAssignable(assignableTo, clazz);
		if (clazz.isInterface()) {
			throw new BeanInstantiationException(clazz, "Specified class is an interface");
		}
		ClassLoader classLoader = (registry instanceof ConfigurableBeanFactory ?
				((ConfigurableBeanFactory) registry).getBeanClassLoader() : resourceLoader.getClassLoader());
		T instance = (T) createInstance(clazz, environment, resourceLoader, registry, classLoader);
        //注入aware接口的相关属性
		ParserStrategyUtils.invokeAwareMethods(instance, environment, resourceLoader, registry, classLoader);
		return instance;
	}

	private static Object createInstance(Class<?> clazz, Environment environment,
			ResourceLoader resourceLoader, BeanDefinitionRegistry registry,
			@Nullable ClassLoader classLoader) {
        //取构造方法
		Constructor<?>[] constructors = clazz.getDeclaredConstructors();
        //存在带参构造方法  
        //这里看了下大部分的ImportSelector的实现类都是没有带参构造方法的
		if (constructors.length == 1 && constructors[0].getParameterCount() > 0) {
			try {
				Constructor<?> constructor = constructors[0];
                //解析参数类型并设置对应的值
				Object[] args = resolveArgs(constructor.getParameterTypes(),
						environment, resourceLoader, registry, classLoader);
				return BeanUtils.instantiateClass(constructor, args);
			}
			catch (Exception ex) {
				throw new BeanInstantiationException(clazz, "No suitable constructor found", ex);
			}
		}
		return BeanUtils.instantiateClass(clazz);
	}
    
    //设置参数
	private static Object[] resolveArgs(Class<?>[] parameterTypes,
			Environment environment, ResourceLoader resourceLoader,
			BeanDefinitionRegistry registry, @Nullable ClassLoader classLoader) {

			Object[] parameters = new Object[parameterTypes.length];
			for (int i = 0; i < parameterTypes.length; i++) {
				parameters[i] = resolveParameter(parameterTypes[i], environment,
						resourceLoader, registry, classLoader);
			}
			return parameters;
	}
    
    /**
    * 根据参数类型返回对应的值
    * Environment
    * ResourceLoader
    * Registry
    * ClassLoader
    */
	@Nullable
	private static Object resolveParameter(Class<?> parameterType,
			Environment environment, ResourceLoader resourceLoader,
			BeanDefinitionRegistry registry, @Nullable ClassLoader classLoader) {

		if (parameterType == Environment.class) {
			return environment;
		}
		if (parameterType == ResourceLoader.class) {
			return resourceLoader;
		}
		if (parameterType == BeanFactory.class) {
			return (registry instanceof BeanFactory ? registry : null);
		}
		if (parameterType == ClassLoader.class) {
			return classLoader;
		}
		throw new IllegalStateException("Illegal method parameter type: " + parameterType.getName());
	}
    
    /**
    * 如果实现了aware接口的子接口则设置相应的属性参数
    */
	private static void invokeAwareMethods(Object parserStrategyBean, Environment environment,
			ResourceLoader resourceLoader, BeanDefinitionRegistry registry, @Nullable ClassLoader classLoader) {

		if (parserStrategyBean instanceof Aware) {
			if (parserStrategyBean instanceof BeanClassLoaderAware && classLoader != null) {
				((BeanClassLoaderAware) parserStrategyBean).setBeanClassLoader(classLoader);
			}
			if (parserStrategyBean instanceof BeanFactoryAware && registry instanceof BeanFactory) {
				((BeanFactoryAware) parserStrategyBean).setBeanFactory((BeanFactory) registry);
			}
			if (parserStrategyBean instanceof EnvironmentAware) {
				((EnvironmentAware) parserStrategyBean).setEnvironment(environment);
			}
			if (parserStrategyBean instanceof ResourceLoaderAware) {
				((ResourceLoaderAware) parserStrategyBean).setResourceLoader(resourceLoader);
			}
		}
	}

}

根据上面源码的分析,我们大致捋一下@ImportSelector的解析流程
0. 获取到@Import和@Imports中的value 进行遍历

  1. @Import中的value指定的类如果实现了ImportSelector 则继续解析@SelectImports中的bean A
  2. @Import中的value指定的类如果实现了DeferedImportSelector 则交给deferedImportSelectorHandler进行处理 B
  3. 如果@Import中的value指定的类不满足1 和 2 则作为配置类解析成bean
  4. 1中的A如果满足1 则继续迭代1 如果满足2 则继续迭代2 如果都不满足 则进行3

解析@Import的时候还有个分支就是继承了ImportBeanDefinitionRegistrar

// Candidate class is an ImportBeanDefinitionRegistrar ->
// delegate to it to register additional bean definitions
Class<?> candidateClass = candidate.loadClass();
ImportBeanDefinitionRegistrar registrar =
        ParserStrategyUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class,
                this.environment, this.resourceLoader, this.registry);
configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());

这个的处理就比较简单了,先调用ParserStrategyUtils.instantiateClass实例化然后直接加入到configClass的ImportBeanDefinitionRegistrar集合中,后续使用。

再补充下DeferedImportSelector实现类的处理源码分析。
入口在ConfigurationClassParser#577-579

//handler定义
private final DeferredImportSelectorHandler deferredImportSelectorHandler = new DeferredImportSelectorHandler();
if (selector instanceof DeferredImportSelector) {
    this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);
}

看下DeferredImportSelectorHandler的handle方法做了什么事情。

private class DeferredImportSelectorHandler {

    @Nullable
    private List<DeferredImportSelectorHolder> deferredImportSelectors = new ArrayList<>();
    
    /**
     * Handle the specified {@link DeferredImportSelector}. If deferred import
     * selectors are being collected, this registers this instance to the list. If
     * they are being processed, the {@link DeferredImportSelector} is also processed
     * immediately according to its {@link DeferredImportSelector.Group}.
     * @param configClass the source configuration class
     * @param importSelector the selector to handle
     */
    public void handle(ConfigurationClass configClass, DeferredImportSelector importSelector) {
        DeferredImportSelectorHolder holder = new DeferredImportSelectorHolder(configClass, importSelector);
        //通过@Import进到这个handle方法的  deferredImportSelectors 一定是new出来的 不走if分支
        if (this.deferredImportSelectors == null) {
            DeferredImportSelectorGroupingHandler handler = new DeferredImportSelectorGroupingHandler();
            handler.register(holder);
            handler.processGroupImports();
        }
        else {
            //这里只是简单的把holder加入到deferredImportSelectors里面
            this.deferredImportSelectors.add(holder);
        }
    }
    
    /**
     * 真正的处理方法。
     */
    public void process() {
        List<DeferredImportSelectorHolder> deferredImports = this.deferredImportSelectors;
        this.deferredImportSelectors = null;
        try {
            if (deferredImports != null) {
                DeferredImportSelectorGroupingHandler handler = new DeferredImportSelectorGroupingHandler();
                deferredImports.sort(DEFERRED_IMPORT_COMPARATOR);
                deferredImports.forEach(handler::register);
                handler.processGroupImports();
            }
        }
        finally {
            this.deferredImportSelectors = new ArrayList<>();
        }
    }
}

可以看到handle方法并没有真正的处理@DeferedImportSelector,并没有调用process方法,process方法究竟是何时调用的呢?
还记得我们解析注解的入口方法吗?就是在那个方法的最后调用的,如下图:
ConfigurationClassParser#193
process方法调用位置
看来是先放到一个list中缓存起来等到所有的都解析完了再解析这部分内容,这也是ImportSelector和DeferedImportSelector区别的体现,也就是ImportSelector会优先解析注册,
DeferedImportSelector是在所有注解都解析完以后才进行的。

到此@Import流程的解析就完成了。

【小结】

整个@Import的解析流程就是

  1. 获取到@Import和@Imports中的value 进行遍历
  2. @Import中的value指定的类如果实现了ImportSelector 则先实例化然后继续解析@SelectImports中的bean A
  3. @Import中的value指定的类如果实现了DeferedImportSelector 则交给deferedImportSelectorHandler进行处理 B(放到一个list中,parse解析完成后再调用)
  4. @Import中的value指定的类如果实现了ImportBeanDefinitionRegistrar 则加入到configClass的ImportBeanDefinitionRegistrar集合中,后续使用。
  5. 如果123都不满足则作为配置类进行解析, 也就是调用processConfigurationClass(candidate.asConfigClass(configClass), exclusionFilter);
    对于1中解析出来的bean再进行判断 迭代走123的流程
  • @ImportResource

  • @Bean

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
以下是在CentOS 7上完成这些要求的具体步骤: 1. 安装BIND软件包: ``` yum install bind bind-utils -y ``` 2. 编辑主DNS服务器的配置文件`/etc/named.conf`,添加以下内容: ``` acl "trusted" { 192.168.1.0/24; localhost; }; options { directory "/var/named"; recursion yes; allow-query { trusted; }; forwarders { 8.8.8.8; }; }; zone "linux-xxx.com" IN { type master; file "linux-xxx.com.zone"; allow-update { none; }; }; zone "1.168.192.in-addr.arpa" IN { type master; file "1.168.192.in-addr.arpa.zone"; allow-update { none; }; }; ``` 3. 创建正向解析区域文件`/var/named/linux-xxx.com.zone`,添加以下内容: ``` $TTL 86400 @ IN SOA dns.linux-xxx.com. root.linux-xxx.com. ( 2021100101; Serial 3600; Refresh after 3 hours 900; Retry after 15 minutes 604800; Expire after 1 week 86400); Minimum TTL of 1 day @ IN NS dns.linux-xxx.com. dns.linux-xxx.com. IN A 192.168.1.学号 mail.linux-xxx.com. IN MX 10 192.168.1.180 study.linux-xxx.com. IN A 192.168.1.181 teach.linux-xxx.com. IN A 192.168.1.182 www.linux-xxx.com. IN A 192.168.1.183 ``` 4. 创建反向解析区域文件`/var/named/1.168.192.in-addr.arpa.zone`,添加以下内容: ``` $TTL 86400 @ IN SOA dns.linux-xxx.com. root.linux-xxx.com. ( 2021100101; Serial 3600; Refresh after 3 hours 900; Retry after 15 minutes 604800; Expire after 1 week 86400); Minimum TTL of 1 day @ IN NS dns.linux-xxx.com. 学号 IN PTR dns.linux-xxx.com. 180 IN PTR mail.linux-xxx.com. 181 IN PTR study.linux-xxx.com. 182 IN PTR teach.linux-xxx.com. 183 IN PTR www.linux-xxx.com. ``` 5. 启动并开机自启BIND服务: ``` systemctl start named systemctl enable named ``` 6. 编辑从DNS服务器的配置文件`/etc/named.conf`,添加以下内容: ``` options { directory "/var/named"; recursion yes; allow-query { any; }; }; zone "linux-xxx.com" IN { type slave; masters { 192.168.1.学号; }; file "slaves/linux-xxx.com.zone"; }; zone "1.168.192.in-addr.arpa" IN { type slave; masters { 192.168.1.学号; }; file "slaves/1.168.192.in-addr.arpa.zone"; }; ``` 7. 启动并开机自启BIND服务: ``` systemctl start named systemctl enable named ``` 8. 在其它虚拟机和Windows主机上配置主从DNS服务器的IP地址,测试解析是否正常。 注意:在上述步骤,需要将其的`linux-xxx.com`和`学号`替换为具体的域名和IP地址。在创建区域文件时,需要注意修改文件名和目录路径,以避免与已有文件重名。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

泽济天下

你的鼓励是我最大的动力。

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值