Создание не простое, пожалуйста, указывайте автора в начале перепечатки: Nuggets@小西子 + ссылка на источник~
Если вы хотите узнать больше об исходном коде Spring,Нажмите, чтобы перейти к остальной части построчного анализа серии Spring
Введение
В предыдущей статье мы говорили об использованииcontext:component-scan
тег, после регистрации сканера, сканер может поместить целевой пакет в соответствующие условия фильтра (по умолчанию он регистрирует@Component
аннотированныйAnnotationTypeFilter
) класс, инкапсулированный вbeanDefinition
и зарегистрировался наIOC
в контейнере.
И для@Configuration
,@Bean
Поддержка аннотаций, в прошлой статье как раз вкратце рассказывалось о том, что он зарегистрировалConfigurationClassPostProcessor
изbeanDefinition
прибытьIOC
В контейнере речь пойдет в основном об этой статьеConfigurationClassPostProcessor
Принцип работы.
2. ОBeanPostProcessor
иBeanFactoryPostProcessor
Spring
При проектировании было оставлено много точек расширения, и эти зарезервированные точки расширения можно будет использовать позжеSpring
/ Когда пользователям нужно добавить новые функции, им не нужно переходить к основному процессу. Эти конкретные точки расширения можно условно разделить на две категории:
BeanFactoryPostProcessor
:BeanFactoryPostProcessor
и его подчиненные интерфейсы, этот тип точки расширения в основном используется вSpring
связанный с контейнеромвремя называется.
BeanPostProcessor
:BeanPostProcessor
и его подчиненные интерфейсы, этот тип точки расширения в основном используется вbean
вызывается в течение всего жизненного цикла.
и нашConfigurationClassPostProcessor
:
public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPostProcessor, xxx {}
public interface BeanDefinitionRegistryPostProcessor extends BeanFactoryPostProcessor {
void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException;
}
может видеть нашConfigurationClassPostProcessor
СлишкомBeanFactoryPostProcessor
производный класс.
три,BeanFactoryPostProcessor
время вызова
Мы возвращаемся к моменту запуска контейнераrefresh()
метод (конкретноAbstractApplicationContext#refresh
):
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
prepareRefresh();
// 上两篇已经讲过了,这里是xml解析的入口
// Tell the subclass to refresh the internal bean factory
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// 这里对beanFactory做了一些预处理,感兴趣的同学可以去看下,逻辑不复杂
// Prepare the bean factory for use in this context.
prepareBeanFactory(beanFactory);
// 为子类预留的一个钩子方法
// Allows post-processing of the bean factory in context subclasses.
postProcessBeanFactory(beanFactory);
// !!! 调用BeanFactoryPostProcessor,这一篇我们主要讲这里!!!
// Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory);
// skip ...
}
}
Видно, что вbeanFactory
Когда все будет готово, у нас есть очевидный метод для вызоваBeanFactoryPostProcessor
, давайте нажмем, чтобы увидеть логику:
protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());
// skip ...
}
Как видите, мы делегировали конкретную логикуPostProcessorRegistrationDelegate
Чтобы разобраться, вот на самом деле, чтобы зарегистрироватьсяPostProcessor
Логика вызова объединена в класс, давайте продолжим:
public static void invokeBeanFactoryPostProcessors(
ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessorPostProcessors) {
// 保存所有调用过的PostProcessor的beanName
Set<String> processedBeans = new HashSet<>();
// 如果这个beanFactory也是一个BeanDefinitionRegistry的话,我们也需要调用
// BeanFactoryPostProcessor的子接口BeanDefinitionRegistryPostProcessor的方法
// 正常都是会走这个分支的,因为我们默认的DefaultListableBeanFactory实现了BeanDefinitionRegistry接口
if (beanFactory instanceof BeanDefinitionRegistry) {
BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
// 这两个list主要用来分别收集BeanFactoryPostProcessor和BeanDefinitionRegistryPostProcessor
List<BeanFactoryPostProcessor> regularPostProcessors = new ArrayList<>();
List<BeanDefinitionRegistryPostProcessor> registryProcessors = new ArrayList<>();
// 首先我们先把传入的BeanFactoryPostProcessor实例分类-beanFactory初始化的时候
// 会注册一下实例到AbstractApplicationContext#beanFactoryPostProcessors这个列表,
// 这个列表的值也是这个方法的第二个入参
for (BeanFactoryPostProcessor postProcessor : beanFactoryPostProcessors) {
if (postProcessor instanceof BeanDefinitionRegistryPostProcessor) {
BeanDefinitionRegistryPostProcessor registryProcessor =
(BeanDefinitionRegistryPostProcessor) postProcessor;
// 对于已经注册到spring的beanFactoryPostProcessors的registryProcessor,直接调用
// 优先级最高
registryProcessor.postProcessBeanDefinitionRegistry(registry);
registryProcessors.add(registryProcessor);
}
else {
regularPostProcessors.add(postProcessor);
}
}
// 一个中间容器,用来保存当前需要调用的registryProcessor
List<BeanDefinitionRegistryPostProcessor> currentRegistryProcessors = new ArrayList<>();
// 接下来我们需要先把我们注册的beanDefinition中实现了BeanDefinitionRegistryPostProcessor接口的类的名称找出来,
// 注意,这里不会直接实例化这个bean(主要是这些PostProcessor需要按顺序初始化和调用,先调用的PostProcessor是可能对后初始化的bean造成影响的)
String[] postProcessorNames =
beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
// 我们找出所有实现了PriorityOrdered接口的PostProcessor
for (String ppName : postProcessorNames) {
if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
// 这里直接创建了这个registryProcessor的实例,并且加入当前需要处理的容器
// beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class)
// 逻辑之后讲bean生命周期的时候会系讲,这里先略过
currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
// 标记为已处理
processedBeans.add(ppName);
}
}
// 对所有实现了PriorityOrdered接口的PostProcessor排序
sortPostProcessors(currentRegistryProcessors, beanFactory);
// 加入registryProcessors列表
registryProcessors.addAll(currentRegistryProcessors);
// 调用BeanDefinitionRegistryPostProcessor的方法,我们的ConfigurationClassPostProcessor的逻辑就是在这里被调用的
invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
// 清除currentRegistryProcessors
currentRegistryProcessors.clear();
// 接下来处理实现了Ordered接口的BeanDefinitionRegistryPostProcessor
// 逻辑就不讲了,跟上面是一样的
postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
for (String ppName : postProcessorNames) {
if (!processedBeans.contains(ppName) && beanFactory.isTypeMatch(ppName, Ordered.class)) {
currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
processedBeans.add(ppName);
}
}
sortPostProcessors(currentRegistryProcessors, beanFactory);
registryProcessors.addAll(currentRegistryProcessors);
invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
currentRegistryProcessors.clear();
// 接下来处理普通的,没实现排序接口的BeanDefinitionRegistryPostProcessor
// 需要主要的是,这里有一个循环,主要原因是BeanDefinitionRegistryPostProcessor#postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry)方法是传入了一个BeanDefinitionRegistry的
// 这意味着我们可以在(实际上正常实现这个接口就是为了注册beanDefinition的)这个PostProcessor注册新的beanDefinition
// 而新注册的beanDefinition对应的类也是可能实现BeanDefinitionRegistryPostProcessor接口的,所以这里需要循环处理,知道不会注册新的BeanDefinitionRegistryPostProcessor为止
boolean reiterate = true;
while (reiterate) {
reiterate = false;
postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
for (String ppName : postProcessorNames) {
if (!processedBeans.contains(ppName)) {
// 如果还有未处理的BeanDefinitionRegistryPostProcessor,则实例化它们
// 并且把标记需要在循环一次,因为之后新的BeanDefinitionRegistryPostProcessor的调用可能注册新的BeanDefinitionRegistryPostProcessor
currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
processedBeans.add(ppName);
reiterate = true;
}
}
// 排序、收集、调用一条龙
sortPostProcessors(currentRegistryProcessors, beanFactory);
registryProcessors.addAll(currentRegistryProcessors);
invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
currentRegistryProcessors.clear();
}
// 所有的BeanDefinitionRegistryPostProcessor都调用完了,接下来要调用BeanFactoryPostProcessor的逻辑了
// 由于BeanDefinitionRegistryPostProcessor是继承BeanFactoryPostProcessor的,所以这里registryProcessors也需要调用
invokeBeanFactoryPostProcessors(registryProcessors, beanFactory);
invokeBeanFactoryPostProcessors(regularPostProcessors, beanFactory);
}
else {
// 如果当前beanFactory没有实现BeanDefinitionRegistry接口,则这里只需要调用BeanFactoryPostProcessor的逻辑就行了
invokeBeanFactoryPostProcessors(beanFactoryPostProcessors, beanFactory);
}
// 接下来是处理注册了beanDifinition而没有实例化的BeanFactoryPostProcessor的逻辑,这里逻辑和处理BeanDefinitionRegistryPostProcessor基本是一样的,都是按顺序初始化(PriorityOrdered->Ordered->None),然后排序,调用的流程
// 只是没有了循环的逻辑,因为从设计上来讲,BeanFactoryPostProcessor#postProcessBeanFactory里不应当负责beanDefinition的注册的逻辑的,所以也不会产生新的beanDifinition,所以这里就不需要循环处理了。
// 你当然可以在postProcessBeanFactory逻辑里把beanFactory强转成BeanDefinitionRegistry并且注册一些BeanFactoryPostProcessor的beanDefinition,导致这里解析不全,可是,我们为什么要跟自己过不去呢?
}
можно увидеть,invokeBeanFactoryPostProcessors
Хотя кода больше, логика не сложная.
Мы первыми с этим разобралисьBeanDefinitionRegistryPostProcessor
логика, а порядок обработки таков:
- созданный экземпляр
registryPostProcessor
--> только зарегистрированыbeanDefinition
, который еще не создал экземплярregistryPostProcessor
- для созданного экземпляра
registryPostProcessor
, добавив их вAbstractApplicationContext#beanFactoryPostProcessors
последовательное выполнение списка - для неустановленного
registryPostProcessor
, реализуемый согласноPriorityOrdered
Интерфейс --> реализованOrdered
Интерфейс --> Порядок нереализованного интерфейса сортировки Создание экземпляров, сортировка и выполнение пакетами. После выполнения одного пакета создайте экземпляр, отсортируйте и выполните следующий пакет. - Наконец, обработайте те, которые не реализуют интерфейс сортировки.
registryPostProcessor
, должна быть циклическая обработка, чтобы гарантироватьregistryPostProcessor
только что зарегистрированный в логикеregistryPostProcessor
изbeanDefinition
также реализовано.
Далее мы имеем дело сBeanFactoryPostProcessor
логика, эта логика иBeanDefinitionRegistryPostProcessor
Логика обработки в основном такая же, но нет необходимости делать логику четвертого цикла.
во время звонкаinvokeBeanDefinitionRegistryPostProcessors
иinvokeBeanFactoryPostProcessors
Как именно это достигается?
private static void invokeBeanDefinitionRegistryPostProcessors(
Collection<? extends BeanDefinitionRegistryPostProcessor> postProcessors, BeanDefinitionRegistry registry) {
for (BeanDefinitionRegistryPostProcessor postProcessor : postProcessors) {
postProcessor.postProcessBeanDefinitionRegistry(registry);
}
}
private static void invokeBeanFactoryPostProcessors(
Collection<? extends BeanFactoryPostProcessor> postProcessors, ConfigurableListableBeanFactory beanFactory) {
for (BeanFactoryPostProcessor postProcessor : postProcessors) {
postProcessor.postProcessBeanFactory(beanFactory);
}
}
Видно, что на самом деле это простой вызов цикла, тогдаAbstractApplicationContext#refresh
серединаinvokeBeanFactoryPostProcessors
Логика здесь остановится.
Четыре,ConfigurationClassPostProcessor
Принцип работы
Мы уже говорили раньше,@Configuration
,@Bean
Функция аннотации заключается вConfigurationClassPostProcessor
поддержите, тогда посмотримConfigurationClassPostProcessor
логика.
Во-первых, давайте вспомнимConfigurationClassPostProcessor
когда ты зарегистрировалсяIOC
Контейнер, давайте разберем его сноваcontext:component-scan
При помечении, после создания сканера и сканирования класса, некоторые публичные компоненты зарегистрированы:
// ComponentScanBeanDefinitionParser#parse
public BeanDefinition parse(Element element, ParserContext parserContext) {
String basePackage = element.getAttribute(BASE_PACKAGE_ATTRIBUTE);
basePackage = parserContext.getReaderContext().getEnvironment().resolvePlaceholders(basePackage);
String[] basePackages = StringUtils.tokenizeToStringArray(basePackage,
ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
// 创建扫描器并扫描
ClassPathBeanDefinitionScanner scanner = configureScanner(parserContext, element);
Set<BeanDefinitionHolder> beanDefinitions = scanner.doScan(basePackages);
// 注册组件
registerComponents(parserContext.getReaderContext(), beanDefinitions, element);
return null;
}
protected void registerComponents(
XmlReaderContext readerContext, Set<BeanDefinitionHolder> beanDefinitions, Element element) {
// ...
// 是否开启注解,默认就是开启的
boolean annotationConfig = true;
if (element.hasAttribute(ANNOTATION_CONFIG_ATTRIBUTE)) {
annotationConfig = Boolean.parseBoolean(element.getAttribute(ANNOTATION_CONFIG_ATTRIBUTE));
}
if (annotationConfig) {
// 这里注册了一写支持注解的属性
Set<BeanDefinitionHolder> processorDefinitions =
AnnotationConfigUtils.registerAnnotationConfigProcessors(readerContext.getRegistry(), source);
// ...
}
// ...
}
существуетAnnotationConfigUtils#registerAnnotationConfigProcessors
, мы положилиConfigurationClassPostProcessor
упаковано вbeanDefinition
и зарегистрировал его:
// 如果不存在beanName为AnnotationConfigUtils#CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME
// 的beanDefinition,则注册一个新的
if (!registry.containsBeanDefinition(CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)) {
RootBeanDefinition def = new RootBeanDefinition(ConfigurationClassPostProcessor.class);
def.setSource(source);
beanDefs.add(registerPostProcessor(registry, def, CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME));
}
так какConfigurationClassPostProcessor
реализуетсяBeanDefinitionRegistryPostProcessor
интерфейс, то мы объединяемAbstractApplicationContext#invokeBeanFactoryPostProcessors
Логика может знать,beanFactory
После инициализации он сначала вызоветConfigurationClassPostProcessor
изpostProcessBeanDefinitionRegistry
метод, давайте посмотрим на логику этого метода:
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
// skip ...
processConfigBeanDefinitions(registry);
}
Продолжайте следить:
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
List<BeanDefinitionHolder> configCandidates = new ArrayList<>();
// 拿到当前registry中所有已经注册的beanDefinition的名称
String[] candidateNames = registry.getBeanDefinitionNames();
// 过滤所有的beanDefinition,把未被处理过的配置类收集起来
for (String beanName : candidateNames) {
BeanDefinition beanDef = registry.getBeanDefinition(beanName);
// 如果beanDefinition中有这个ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE属性,说明这个beanDefinitiony是一个配置类,且已经被处理过了,不需要再处理
if (beanDef.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE) != null) {
// only log
}
// !!!判断当前beanDefinition是不是一个配置类,如果是,收集起来!!!
else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
// 如果是一个需要处理的配置类,加入列表
configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
}
}
// 完全没找到待处理的ConfigurationClass,就直接返回了
if (configCandidates.isEmpty()) {
return;
}
// 排序
configCandidates.sort((bd1, bd2) -> {
int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition());
int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition());
return Integer.compare(i1, i2);
});
// 这里主要是初始化一些工具类,环境变量之类的
SingletonBeanRegistry sbr = null;
if (registry instanceof SingletonBeanRegistry) {
sbr = (SingletonBeanRegistry) registry;
if (!this.localBeanNameGeneratorSet) {
BeanNameGenerator generator = (BeanNameGenerator) sbr.getSingleton(
AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR);
if (generator != null) {
this.componentScanBeanNameGenerator = generator;
this.importBeanNameGenerator = generator;
}
}
}
if (this.environment == null) {
this.environment = new StandardEnvironment();
}
// 创建一个ConfigurationClassParser
ConfigurationClassParser parser = new ConfigurationClassParser(
this.metadataReaderFactory, this.problemReporter, this.environment,
this.resourceLoader, this.componentScanBeanNameGenerator, registry);
Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates);
Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size());
do {
// !!!委托给Parser来解析这些配置类!!!
// 这里主要是把配置类上的注解信息解析封装成ConfigurationClass对象
// 如果是配置类导入(入import/componentScan)的普通类(非配置类),将会在这里生成beanDefinition并注册
parser.parse(candidates);
// 校验扫描出来的beanDefinitionu是否合法,这里其实主要是校验
// 1.proxyBeanMethods=true的情况下配置类是否可以重新(非final,需要生成cglib代理类)
// 2.@Bean修饰的方法是否可以重写(非final,需要生成cglib代理类)
parser.validate();
Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());
configClasses.removeAll(alreadyParsed);
// 初始化一个ConfigurationClassBeanDefinitionReader
if (this.reader == null) {
this.reader = new ConfigurationClassBeanDefinitionReader(
registry, this.sourceExtractor, this.resourceLoader, this.environment,
this.importBeanNameGenerator, parser.getImportRegistry());
}
// !!!把封装好的ConfigurationClass对象委托给BeanDefinitionReader处理!!!
// 通过配置类加载注册beanDefinition
this.reader.loadBeanDefinitions(configClasses);
alreadyParsed.addAll(configClasses);
candidates.clear();
// 处理完ConfigurationClass后,可能会注册新的配置类,这里就是收集这些新注册的配置类的
if (registry.getBeanDefinitionCount() > candidateNames.length) {
String[] newCandidateNames = registry.getBeanDefinitionNames();
Set<String> oldCandidateNames = new HashSet<>(Arrays.asList(candidateNames));
Set<String> alreadyParsedClasses = new HashSet<>();
for (ConfigurationClass configurationClass : alreadyParsed) {
alreadyParsedClasses.add(configurationClass.getMetadata().getClassName());
}
for (String candidateName : newCandidateNames) {
// 只有新注册的beanDefinition才需要处理
if (!oldCandidateNames.contains(candidateName)) {
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());
// skip ...
}
Благодаря приведенному выше исходному коду мы в целом знаем процесс обработки классов конфигурации.Далее мы сосредоточимся на нескольких важных деталях.
1. Определите, является ли класс классом конфигурации
processConfigBeanDefinitions
В методе мы в основном имеем дело с логикой класса конфигурации, так что же такое класс конфигурации?
Некоторые студенты могут захотеть сказать, разве это не просто, просто@Configuration
Аннотированный модифицированный класс!
Это утверждение верно, но неполно, так что давайте посмотримConfigurationClassUtils#checkConfigurationClassCandidate
Как судить о классе - это класс конфигурации в методе:
public static boolean checkConfigurationClassCandidate(
BeanDefinition beanDef, MetadataReaderFactory metadataReaderFactory) {
String className = beanDef.getBeanClassName();
// 首先如果这个bean是通过factoryMethod来注册的,那它就不是一个配置类
if (className == null || beanDef.getFactoryMethodName() != null) {
return false;
}
AnnotationMetadata metadata;
// skip ... 这里跳过了一些获取AnnotationMetadata的逻辑,我们只需要知道
// 这个AnnotationMetadata能拿到类上的所有注解的信息就可以了
// 获取类上@Configuration注解的属性
Map<String, Object> config = metadata.getAnnotationAttributes(Configuration.class.getName());
// 有@Configuration注解且注解的proxyBeanMethods=true(这个是默认值)
// 这里解释一下这个proxyBeanMethods属性,这个属性我之前也没有注意,看注解是说如果这个属性为true
// 则这个配置类里被@Bean注解修饰的方法会被spring代理,使我们通过方法调用的时候获取到的实例也是属于spring管理的。
if (config != null && !Boolean.FALSE.equals(config.get("proxyBeanMethods"))) {
// 注意这里在beanDefinition中塞入了CONFIGURATION_CLASS_ATTRIBUTE这个属性
// 外面是通过判断这个是否有属性来确定某个beanDefinition是否是配置类
beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_FULL);
}
// 有@Configuration 或者 isConfigurationCandidate(metadata)
else if (config != null || isConfigurationCandidate(metadata)) {
beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_LITE);
}
else {
return false;
}
// 获取排序值
Integer order = getOrder(metadata);
if (order != null) {
beanDef.setAttribute(ORDER_ATTRIBUTE, order);
}
return true;
}
Мы видим, что класс имеет@Configuration
Класс действительно принадлежит к классу конфигурации, однако не существует@Configuration
, пока
isConfigurationCandidate
метод возвращаетtrue
, также думайте, что этот класс является классом конфигурации, давайте взглянем на этот метод:
public static boolean isConfigurationCandidate(AnnotationMetadata metadata) {
// Do not consider an interface or an annotation...
if (metadata.isInterface()) {
return false;
}
// 类上是否有被candidateIndicators列表中任一注解修饰?
for (String indicator : candidateIndicators) {
if (metadata.isAnnotated(indicator)) {
return true;
}
}
try {
// 类中是否包含@Bean注解修饰的方法
return metadata.hasAnnotatedMethods(Bean.class.getName());
}
catch (Throwable ex) {
return false;
}
}
тогда этоConfigurationClassUtils#candidateIndicators
Какие аннотации включены?
private static final Set<String> candidateIndicators = new HashSet<>(8);
static {
candidateIndicators.add(Component.class.getName());
candidateIndicators.add(ComponentScan.class.getName());
candidateIndicators.add(Import.class.getName());
candidateIndicators.add(ImportResource.class.getName());
}
Тогда мы знаем, что когда класс@Configuration
,@Component
,@ComponentScan
,@Import
,@ImportResource
Модифицированный, или в классе@Bean
При аннотировании модифицированного методаspring
Думайте об этом классе как о классе конфигурации (с@Component
В случае с аннотациями мы можем в основном думать, что большинствоbeanDefinition
будет затронуто этоPostProcessor
иметь дело с).
2. Проанализируйте информацию о конфигурации в классе конфигурации.
a.ConfigurationClass
структура
Мы только что видели,spring
Информация класса конфигурации анализируется и инкапсулируется вConfigurationClass
В объекте, то давайте сначала посмотрим на структуру этого класса:
final class ConfigurationClass {
// 配置类的注解信息
private final AnnotationMetadata metadata;
private final Resource resource;
@Nullable
private String beanName;
// 当前类是哪个配置类导入的
private final Set<ConfigurationClass> importedBy = new LinkedHashSet<>(1);
// 这个配置类中被@Bean注解标记的方法
private final Set<BeanMethod> beanMethods = new LinkedHashSet<>();
// 配置类上的@ImportResource 注解中的消息,配置文件地址-对应处理器Class
private final Map<String, Class<? extends BeanDefinitionReader>> importedResources =
new LinkedHashMap<>();
// 配置类上的@Import 注解导入的类,如果是实现了ImportBeanDefinitionRegistrar接口,将会封装到这里
private final Map<ImportBeanDefinitionRegistrar, AnnotationMetadata> importBeanDefinitionRegistrars =
new LinkedHashMap<>();
// 这里重新了equals和hashCode方法,只要配置类的全类名相等这边就认为两个对象一致了。
@Override
public boolean equals(@Nullable Object other) {
return (this == other || (other instanceof ConfigurationClass &&
getMetadata().getClassName().equals(((ConfigurationClass) other).getMetadata().getClassName())));
}
@Override
public int hashCode() {
return getMetadata().getClassName().hashCode();
}
}
b.ConfigurationClassParser#processConfigurationClass
Обработка записи класса конфигурации
ConfigurationClassParser#parse
После простой инкапсуляции в методе он перейдет кConfigurationClassParser#processConfigurationClass
, посмотрим непосредственно на логику этого метода:
private final Map<ConfigurationClass, ConfigurationClass> configurationClasses = new LinkedHashMap<>();
protected void processConfigurationClass(ConfigurationClass configClass, Predicate<String> filter) throws IOException {
// 这里是判断@Condition那些,看是否需要跳过
if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) {
return;
}
// 看一下这个配置类是否已经解析过了,configurationClasses是一个Map
// 这里相当于是通过配置类的类名去获取配置类的封装信息的
ConfigurationClass existingClass = this.configurationClasses.get(configClass);
if (existingClass != null) {
// 如果已经解析过,会做一些处理,这里可能会直接返回,即本次就不再解析了
// 这里的处理逻辑不太重要,我们不看了
}
// 这里把configClass又包装成了一个SourceClass, 这个filter的值默认是 DEFAULT_EXCLUSION_FILTER,意思就是这两个包内的类在解析的时候会被排除
// private static final Predicate<String> DEFAULT_EXCLUSION_FILTER = className ->(className.startsWith("java.lang.annotation.") || className.startsWith("org.springframework.stereotype."));
SourceClass sourceClass = asSourceClass(configClass, filter);
// 处理配置类时,处理完当前类之后,还会往上处理它的父类,直到父类是Object就不再处理了
// 这个循环就是做这个作用的
do {
// 处理配置类的逻辑,配置类中的信息将会被封装到configClass
sourceClass = doProcessConfigurationClass(configClass, sourceClass, filter);
}
while (sourceClass != null);
// 把解析后的配置类信息储存到configurationClasses,这里这个key可以认为就是一个类名
this.configurationClasses.put(configClass, configClass);
}
Реальная логика обработки все еще тамdoProcessConfigurationClass
, продолжаем:
protected final SourceClass doProcessConfigurationClass(
ConfigurationClass configClass, SourceClass sourceClass, Predicate<String> filter)
throws IOException {
if (configClass.getMetadata().isAnnotated(Component.class.getName())) {
// 如果配置类被@Component修饰,先处理内部类
processMemberClasses(configClass, sourceClass, filter);
}
// 这里是处理配置类上的@PropertySources注解的
// 简单来说就是把properties文件中的内容加载到内存中的Environment中了
// 我们不细看
for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(
sourceClass.getMetadata(), PropertySources.class,
org.springframework.context.annotation.PropertySource.class)) {
if (this.environment instanceof ConfigurableEnvironment) {
processPropertySource(propertySource);
}
else {
logger.info("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() +
"]. Reason: Environment must implement ConfigurableEnvironment");
}
}
// 这里开始是处理类上的@ComponentScan注解的逻辑
Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(
sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
if (!componentScans.isEmpty() &&
!this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
// 循环处理每一个注解(可以用@ComponentScans包装多个注解-jdk<java8,或者直接打上多个@ComponentScan注解-jdk>=java8)
for (AnnotationAttributes componentScan : componentScans) {
// 委托给componentScanParser处理,这里处理完之后返回了一批已注册的BeanDefinition
// 这里的parse逻辑其实就是创建了一个扫描器并且进行扫描,毕竟这个注解就是做这个事的。
// 我们之后会看一下
Set<BeanDefinitionHolder> scannedBeanDefinitions =
this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();
if (bdCand == null) {
bdCand = holder.getBeanDefinition();
}
if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
// 如果扫描出来的类是配置类,需要走一遍解析配置类的逻辑
// 实际上是一个递归
parse(bdCand.getBeanClassName(), holder.getBeanName());
}
}
}
}
// 处理@Import注解
processImports(configClass, sourceClass, getImports(sourceClass), filter, true);
// 处理@ImportResource注解
AnnotationAttributes importResource =
AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);
if (importResource != null) {
String[] resources = importResource.getStringArray("locations");
// 这个值默认是 BeanDefinitionReader.class
Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader");
for (String resource : resources) {
String resolvedResource = this.environment.resolveRequiredPlaceholders(resource);
// 把@ImportResource注解上的信息封装到configClass
configClass.addImportedResource(resolvedResource, readerClass);
}
}
// 处理有@Bean注解的方法
// 这里找到类里所有有@Bean注解修饰的方法
Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
for (MethodMetadata methodMetadata : beanMethods) {
// 封装成BeanMethod并且也放入configClass
configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
}
// 由于java8之后接口也可以有默认方法(default修饰的方法)
// 这里会找到所有接口中被@Bean修饰的非抽象的方法,也封装成BeanMethod放入configClass
processInterfaces(configClass, sourceClass);
// 如果有父类的话,会返回父类的sourceClass,继续在外层循环中解析
// 并且把解析的信息封装到当前这个configClass
if (sourceClass.getMetadata().hasSuperClass()) {
String superclass = sourceClass.getMetadata().getSuperClassName();
if (superclass != null && !superclass.startsWith("java") &&
!this.knownSuperclasses.containsKey(superclass)) {
this.knownSuperclasses.put(superclass, configClass);
// Superclass found, return its annotation metadata and recurse
return sourceClass.getSuperClass();
}
}
// 没有父类了就处理完了
return null;
}
Так как аннотаций для обработки много, логика здесь относительно сложная, поэтому давайте поговорим о них по порядку.
в. Обращение@Component
аннотация
ВходитьdoProcessConfigurationClass
метод, мы сначала будем иметь дело с@Component
аннотация:
if (configClass.getMetadata().isAnnotated(Component.class.getName())) {
// 如果配置类被@Component修饰,先处理内部类
processMemberClasses(configClass, sourceClass, filter);
}
Давайте взглянемprocessMemberClasses
Логика:
private void processMemberClasses(ConfigurationClass configClass, SourceClass sourceClass,
Predicate<String> filter) throws IOException {
// 获取所有的内部类
Collection<SourceClass> memberClasses = sourceClass.getMemberClasses();
if (!memberClasses.isEmpty()) {
List<SourceClass> candidates = new ArrayList<>(memberClasses.size());
// 循环处理每个内部类
for (SourceClass memberClass : memberClasses) {
// 如果内部类也是一个配置类,且内部类与当前类不一致(其实我也不知道为什么会有这种情况?)
// 则加入待处理的配置类列表
if (ConfigurationClassUtils.isConfigurationCandidate(memberClass.getMetadata()) &&
!memberClass.getMetadata().getClassName().equals(configClass.getMetadata().getClassName())) {
candidates.add(memberClass);
}
}
// 对待处理的配置类排序
OrderComparator.sort(candidates);
// 循环按顺序处理每个配置类
for (SourceClass candidate : candidates) {
// 这里是判断是否有循环import的配置类的,如果有循环导入会直接报错
if (this.importStack.contains(configClass)) {
this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));
}
else {
this.importStack.push(configClass);
try {
// 把每一个内部类也需要处理一下,这里其实又回到上层了,是一个递归
processConfigurationClass(candidate.asConfigClass(configClass), filter);
}
finally {
this.importStack.pop();
}
}
}
}
}
Как видите, обработка класса конфигурации@Component
Annotation, по сути, состоит в том, чтобы получить все внутренние классы в классе конфигурации, и разобрать в нем класс конфигурации, то есть вызватьprocessConfigurationClass
метод.
г. Обработка@ComponentScan
аннотация
@ComponentScan
Роль состоит в том, чтобы сканировать в классе@Component
Обратите внимание, эта функция не имеет отношения к намcontext:component-scan
Этикетка работает так же? На самом деле их функции на этапе сканирования действительно одинаковы. Как видите, мы имеем дело с@ComponentScan
При аннотировании он делегируетсяcomponentScanParser
Обработанный:
Set<BeanDefinitionHolder> scannedBeanDefinitions =
this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
Давайте посмотрим на этоComponentScanAnnotationParser#parse
Логика:
public Set<BeanDefinitionHolder> parse(AnnotationAttributes componentScan, final String declaringClass) {
// 第一行就创建了一个扫描器Scanner
ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(this.registry,
componentScan.getBoolean("useDefaultFilters"), this.environment, this.resourceLoader);
// skip ... 中间就是解析了@ComponentScan注解中的信息,并通过这些配置信息配置scanner的属性
// 这些信息其实跟自定义标签context:component-scan中的属性/子标签是对应的,有兴趣的同学可以看一下我的上一篇博客
// 新增了一个ExcludeFilter,意思就是扫描的时候就不需要处理当前类了-毕竟当前类已经在处理了。
scanner.addExcludeFilter(new AbstractTypeHierarchyTraversingFilter(false, false) {
@Override
protected boolean matchClassName(String className) {
return declaringClass.equals(className);
}
});
// 扫描器扫描~
return scanner.doScan(StringUtils.toStringArray(basePackages));
Ха-ха, видите ли, вот сканер создал, а потом просканировал, логика следует за намиcontext:component-scan
Процесс сканирования такой же, и здесь он не рассматривается.
Следует отметить, что сканер сканируетbeanDifinition
, если это также класс конфигурации, он вызоветparse
Метод анализирует этот класс конфигурации и, наконец, переходит кprocessConfigurationClass
методОбрабатывать:
for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();
if (bdCand == null) {
bdCand = holder.getBeanDefinition();
}
if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
// 递归解析扫描出来的配置类
parse(bdCand.getBeanClassName(), holder.getBeanName());
}
}
е. Обработка@Import
аннотация
@Import
Аннотации обычно используются для представления третьих лиц.jar
упаковать класс вspring
контейнер, конечно@bean
тоже имеет эту функцию, но@Import
и@Bean
Разница в том,@Import
используется для модификации класса,spring
полно@EnableXxx
Аннотация через@Import
Функция реализована, теперь посмотрим на процесс ее обработки:
processImports(configClass, sourceClass, getImports(sourceClass), filter, true);
getImports()
используется для сбора всех@import
Для импортированных классов давайте взглянем на эту логику коллекции:
private Set<SourceClass> getImports(SourceClass sourceClass) throws IOException {
// 用来保存import导入的类
Set<SourceClass> imports = new LinkedHashSet<>();
// 用来标记哪些类是已经处理过的
Set<SourceClass> visited = new LinkedHashSet<>();
// 递归收集
collectImports(sourceClass, imports, visited);
return imports;
}
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
if (!annName.equals(Import.class.getName())) {
// 则继续递归收集这个注解上的注解(有点绕...)
collectImports(annotation, imports, visited);
}
}
// 把当前类上的所有@Import注解的value属性(即import的Class<?>)封装成sourceClass
// 并且加入收集到的容器
imports.addAll(sourceClass.getAnnotationAttributes(Import.class.getName(), "value"));
}
}
Логика здесь может быть немного окольной, но ее функция состоит в том, чтобы поместить@Import
Информация об аннотациях собирается, а аннотации к аннотациям собираются рекурсивно, например, наша@EnableAsync
аннотация:
@Import(AsyncConfigurationSelector.class)
public @interface EnableAsync {}
@Configuration
@EnableAsync
public class Test {}
Мы собираемTest
Вверх@Import
При аннотировании информации введите в первый разcollectImports
метод,sourceClass=Test
, на этот раз помимо сбораTest
класс@Import
В дополнение к аннотационной информации вы также получитеTest
Другие аннотации к классу, например здесь@EnableAsync
аннотировать, а затем поставить@EnableAsync
Информация о классе аннотации какsourceClass
(которыйsourceClass=EnableAsync
) продолжать звонитьcollectImports
рекурсивная коллекция методов@Import
Аннотационная информация, на этот разEnableAsync
Вверх@Import(AsyncConfigurationSelector.class)
Собирается аннотационная информация.
Идем дальше, смотрим на коллекцию@Import
Как обрабатывается ~ после импортированного класса:
private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
Collection<SourceClass> importCandidates, Predicate<String> exclusionFilter,
boolean checkForCircularImports) {
// 这里把循环导入的处理逻辑和异常处理逻辑去掉了
// 循环每一个@Import导入的类
for (SourceClass candidate : importCandidates) {
if (candidate.isAssignable(ImportSelector.class)) {
// 如果实现了ImportSelector接口
// 这里把这个ImportSelector实例化了
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);
}
// 这里判断是否是延迟导入,如果是延迟导入的话,会在parse方法中,所有配置类都处理完之后再处理,有兴趣的同学可以自己看一下
// 具体代码在ConfigurationClassParser#parse(java.util.Set<org.springframework.beans.factory.config.BeanDefinitionHolder>)
if (selector instanceof DeferredImportSelector) {
this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);
}
else {
// 如果是一个普通的ImportSelector
String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames, exclusionFilter);
// 调用ImportSelector.selectImports方法,将获取到的类名作为参数递归调用当前方法
// 也就是说这个ImportSelector接口和@Import注解实现的功能是一样的
// 估计@Import是spring支持注解之后对ImportSelector接口做的注解版吧
processImports(configClass, currentSourceClass, importSourceClasses, exclusionFilter, false);
}
}
else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
// 如果导入的类实现了ImportBeanDefinitionRegistrar接口
// 这里也会把这个类先实例化
Class<?> candidateClass = candidate.loadClass();
ImportBeanDefinitionRegistrar registrar =
ParserStrategyUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class,
this.environment, this.resourceLoader, this.registry);
// 然后加入到当前配置类的属性中了
configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
}
else {
// 如果导入的类既没实现ImportSelector接口,又没实现ImportBeanDefinitionRegistrar接口
// 则认为是一个普通的配置类,进行配置类的处理逻辑
processConfigurationClass(candidate.asConfigClass(configClass), exclusionFilter);
}
}
}
Как видите, для импортированных классов есть три логики обработки:
- Достигнуто
ImportSelector
Импорт класса интерфейса- перечислить
ImportSelector.selectImports
метод, который рекурсивно вызывает метод, в данный момент обрабатывающий импортированный класс, с полученным именем класса в качестве параметра - ты мог бы так сказать
@Import
версия интерфейса
- перечислить
- выполнить
ImportBeanDefinitionRegistrar
интерфейс- создан как
ImportBeanDefinitionRegistrar
После объекта поставьте текущийconfigClass
середина -
ImportBeanDefinitionRegistrar
Интерфейс имеет входящийBeanDefinitionRegistry
изregisterBeanDefinitions
метод, нетрудно догадаться, что этот интерфейс можно использовать для регистрацииbeanDefinition
Да, этот метод должен быть только вызван.
- создан как
- общий класс
- Как обычный класс конфигурации, рекурсивно вызываемый
processConfigurationClass
методОбработка
- Как обычный класс конфигурации, рекурсивно вызываемый
е. Обработка@ImportResource
аннотация
@ImportResource
функция импортаspring
изxml
Файлы конфигурации, обычно используемые для доступа к некоторым более старым, используютxml
определениеbean
сторонняя библиотека.
AnnotationAttributes importResource =
AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);
if (importResource != null) {
String[] resources = importResource.getStringArray("locations");
Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader");
for (String resource : resources) {
String resolvedResource = this.environment.resolveRequiredPlaceholders(resource);
// 这里是直接把配置文件路径和处理类类型放入了configClass对象
configClass.addImportedResource(resolvedResource, readerClass);
}
}
г. лечение@Bean
аннотация
@Bean
Аннотации могут быть наиболее часто используемыми аннотациями в этом наборе аннотаций, и мы часто используем их, чтобы представить некоторые классы сторонних библиотек, чтобы сделатьspring
управлять. Давайте взглянемparse
Поэтапная логика его обработки:
// 收集当前类里所有有@bean注解的方法
Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
for (MethodMetadata methodMetadata : beanMethods) {
// 封装成BeanMethod对象,加入configClass
configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
}
// 处理当前类的接口,接口是可能有有默认实现(jdk>=1.8)的@Bean修饰方法
processInterfaces(configClass, sourceClass);
в коллекции@Bean
Метод модификации аннотации на самом деле очень прост, мы можем получить его через отражение, ноspring
Для того, чтобы определить последовательность этих методов обработки, использованиеasm
Методы байт-кода для получения порядка, в котором методы объявляются в классе:
private Set<MethodMetadata> retrieveBeanMethodMetadata(SourceClass sourceClass) {
AnnotationMetadata original = sourceClass.getMetadata();
// 获取所有被@Bean修饰的方法
Set<MethodMetadata> beanMethods = original.getAnnotatedMethods(Bean.class.getName());
if (beanMethods.size() > 1 && original instanceof StandardAnnotationMetadata) {
// Try reading the class file via ASM for deterministic declaration order...
// Unfortunately, the JVM's standard reflection returns methods in arbitrary
// order, even between different runs of the same application on the same JVM.
// 这里注释是说由于jvm返回的方法列表顺序不能保证,这里尝试使用asm字节码技术拿到方法在类中的声明顺序,以此来为这些被@Bean修饰的方法排序
try {
AnnotationMetadata asm =
this.metadataReaderFactory.getMetadataReader(original.getClassName()).getAnnotationMetadata();
Set<MethodMetadata> asmMethods = asm.getAnnotatedMethods(Bean.class.getName());
// 排序
if (asmMethods.size() >= beanMethods.size()) {
Set<MethodMetadata> selectedMethods = new LinkedHashSet<>(asmMethods.size());
for (MethodMetadata asmMethod : asmMethods) {
for (MethodMetadata beanMethod : beanMethods) {
if (beanMethod.getMethodName().equals(asmMethod.getMethodName())) {
selectedMethods.add(beanMethod);
break;
}
}
}
if (selectedMethods.size() == beanMethods.size()) {
// All reflection-detected methods found in ASM method set -> proceed
beanMethods = selectedMethods;
}
}
}
catch (IOException ex) {
logger.debug("Failed to read class file via ASM for determining @Bean method order", ex);
// No worries, let's continue with the reflection metadata we started with...
}
}
return beanMethods;
}
Давайте посмотрим на логику обработки интерфейса:
private void processInterfaces(ConfigurationClass configClass, SourceClass sourceClass) throws IOException {
for (SourceClass ifc : sourceClass.getInterfaces()) {
// 获取接口上所有被@Bean修饰的方法
Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(ifc);
for (MethodMetadata methodMetadata : beanMethods) {
if (!methodMetadata.isAbstract()) {
// A default method or other concrete method on a Java 8+ interface...
// 只有不是抽象的方法才封装成BeanMethod加入configClass
configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
}
}
// 递归处理
processInterfaces(configClass, ifc);
}
}
h.parse
Краткое описание метода
можно увидеть,parser.parse(candidates)
Логика обработки довольно сложна. Этот метод в основном обрабатывает аннотации, которые мы обычно используем, и он содержит много логики рекурсивных вызовов. Я также прочитал его много раз, прежде чем понял. Заинтересованные студенты могут захотеть прочитать больше несколько раз.
3. Зарегистрируйтесь, настроив загрузку классовbeanDefinition
После анализа класса конфигурации нам нужно загрузить регистрацию через информацию о классе конфигурации.beanDefinition
:
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(
registry, this.sourceExtractor, this.resourceLoader, this.environment,
this.importBeanNameGenerator, parser.getImportRegistry());
}
// 加载注册beanDefinition
this.reader.loadBeanDefinitions(configClasses);
alreadyParsed.addAll(configClasses);
Пойдем прямоloadBeanDefinitions
Логика:
public void loadBeanDefinitions(Set<ConfigurationClass> configurationModel) {
TrackedConditionEvaluator trackedConditionEvaluator = new TrackedConditionEvaluator();
for (ConfigurationClass configClass : configurationModel) {
// 循环解析
loadBeanDefinitionsForConfigurationClass(configClass, trackedConditionEvaluator);
}
}
private void loadBeanDefinitionsForConfigurationClass(
ConfigurationClass configClass, TrackedConditionEvaluator trackedConditionEvaluator) {
if (trackedConditionEvaluator.shouldSkip(configClass)) {
// skip ...
}
if (configClass.isImported()) {
// 如果是导入的配置类,先把自己的beanDefinition注册到spring
// 里面就是一个简单的beanDefinition封装注册的流程,我们就不看了
registerBeanDefinitionForImportedConfigurationClass(configClass);
}
for (BeanMethod beanMethod : configClass.getBeanMethods()) {
// 从beanMethod注册beanDefinition
loadBeanDefinitionsForBeanMethod(beanMethod);
}
// 从导入的配置文件注册beanDefinition
loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());
// 从导入的ImportBeanDefinitionRegistrar注册beanDefinition
loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
}
Как видите, загружаем регистрациюbeanDefinition
Логика вполне ясна, в основном это инкапсулировать нашConfigurationClass
Информация в объекте загружается одна за другой. Давайте рассмотрим каждый из них один за другим.
пропускBeanMethod
Загрузить регистрациюbeanDefinition
BeanMethod
Объект находится в классе конфигурации, который@Bean
Метод, модифицированный аннотацией, инкапсулирован, рассмотрим логику его обработки:
private void loadBeanDefinitionsForBeanMethod(BeanMethod beanMethod) {
// 我这里把代码简化了一下,因为一个beanDefinition的封装过程,无非是把@Bean注解中的信息获取封装一遍
// 那些信息的封装我们在讲解xml标签的时候已经讲过了,这些属性都是一一对应的
// 这里我只把我们需要额外关注的地方写出来了 -- 即我们一般意义上说的@Bean的实现原理
ConfigurationClass configClass = beanMethod.getConfigurationClass();
MethodMetadata metadata = beanMethod.getMetadata();
// skip ...
// 新建一个ConfigurationClassBeanDefinition
ConfigurationClassBeanDefinition beanDef = new ConfigurationClassBeanDefinition(configClass, metadata);
// skip ...
// !!!关键点!!!
if (metadata.isStatic()) {
// 如果是静态的@Bean方法,需要设置beanClass/beanClassName,用于在bean初始化时调用
if (configClass.getMetadata() instanceof StandardAnnotationMetadata) {
beanDef.setBeanClass(((StandardAnnotationMetadata) configClass.getMetadata()).getIntrospectedClass());
}
else {
beanDef.setBeanClassName(configClass.getMetadata().getClassName());
}
// 设置FactoryMethodName
beanDef.setUniqueFactoryMethodName(methodName);
}
else {
// 如果是非静态的@Bean方法,还需要设置工厂类的beanName
beanDef.setFactoryBeanName(configClass.getBeanName());
// 设置FactoryMethodName
beanDef.setUniqueFactoryMethodName(methodName);
}
// skip ...
// 注册beanDefinition
this.registry.registerBeanDefinition(beanName, beanDefToRegister);
}
Как видите, наш@Bean
созданныйbeanDefinition
, с обычнымbeanDefinition
Отличие в том, что он установленfactoryMethodName
, то есть он говорит, что с помощьюxml
в путиfactory-bean
,factory-method
функция этикетки для достиженияbean
создано!
б. пройти@ImportedResource
Регистрация загрузки информации аннотацииbeanDefinition
@ImportedResource
Аннотация содержит путь к импортируемому файлу, а также файлReader
информация, поэтому мы загружаем регистрациюbeanDefinition
, но и из этой информации:
private void loadBeanDefinitionsFromImportedResources(
Map<String, Class<? extends BeanDefinitionReader>> importedResources) {
// reader实例缓存
Map<Class<?>, BeanDefinitionReader> readerInstanceCache = new HashMap<>();
// 循环处理<配置文件路径-reader>
importedResources.forEach((resource, readerClass) -> {
// 如果注解配置的Reader是默认的(我们一般其实也不改)
if (BeanDefinitionReader.class == readerClass) {
if (StringUtils.endsWithIgnoreCase(resource, ".groovy")) {
// 如果文件名是.groovy结尾,则使用GroovyBeanDefinitionReader
// 说实话我也第一次知道还可以用groovy脚本来做spring的配置文件
// 后面我去看了一下BeanDefinitionReader这个接口的实现类,发现还一个
// PropertiesBeanDefinitionReader,感兴趣的同学可以去研究一下
readerClass = GroovyBeanDefinitionReader.class;
}
else {
// 默认情况下我们使用XmlBeanDefinitionReader
// 有没有点眼熟这个类?xml配置解析的时候就是用的它呀
readerClass = XmlBeanDefinitionReader.class;
}
}
// 先从缓存拿
BeanDefinitionReader reader = readerInstanceCache.get(readerClass);
if (reader == null) {
try {
// 拿不到就新建一个,配置的reader类必须有一个只有BeanDefinitionRegistry参数的构造器
reader = readerClass.getConstructor(BeanDefinitionRegistry.class).newInstance(this.registry);
// Delegate the current ResourceLoader to it if possible
if (reader instanceof AbstractBeanDefinitionReader) {
AbstractBeanDefinitionReader abdr = ((AbstractBeanDefinitionReader) reader);
abdr.setResourceLoader(this.resourceLoader);
abdr.setEnvironment(this.environment);
}
readerInstanceCache.put(readerClass, reader);
}
catch (Throwable ex) {
throw new IllegalStateException(
"Could not instantiate BeanDefinitionReader class [" + readerClass.getName() + "]");
}
}
// 使用reader从文件加载bean
reader.loadBeanDefinitions(resource);
});
}
По умолчанию (в большинстве случаев мы сами не настраиваемBeanDefinitionReader
) Создайте одинXmlBeanDefinitionReader
для анализа нагрузки, определенной в нашем файле конфигурацииbean
Да, это с намиxml
Содержимое секции парсинга такое же, поэтому здесь она обсуждаться не будет.
в. пройти@Import
Аннотированный импортImportBeanDefinitionRegistrar
регистрация загрузки классаbeanDefinition
когда@Import
Импортированный класс имеет реализациюImportBeanDefinitionRegistrar
Когда мы будем взаимодействовать, мы соберем класс и не будем работать с ним до этого момента:
private void loadBeanDefinitionsFromRegistrars(Map<ImportBeanDefinitionRegistrar, AnnotationMetadata> registrars) {
registrars.forEach((registrar, metadata) ->
// 直接调用registerBeanDefinitions方法
registrar.registerBeanDefinitions(metadata, this.registry, this.importBeanNameGenerator));
}
Логика обработки также очень проста, то есть вызывать напрямуюImportBeanDefinitionRegistrar.registerBeanDefinitions
метод, проводитьbeanDefinition
Регистрация.
В этом месте, по сути, весьConfigurationClassPostProcessor
Мы закончили с логикой.spring
это через этоPostProcessor
предусмотрено@Bean
,@Import
и т.д. общийIOC
Поддержка аннотаций.
Пять, начало чистой аннотацииspring
Мы говорили только чтоConfigurationClassPostProcessor
Поддержка аннотаций, но этот компонент действительно разрешен к веснеxml
в файле конфигурацииcontext:component-scan
Он был введен при внедрении метки, и пусть студенты его вспомнят (логика поиска класса обработки через пользовательскую метку здесь повторяться не будет, а заинтересованные студенты могут прочитать мой последний пост в блоге):
public BeanDefinition parse(Element element, ParserContext parserContext) {
// 获取标签上配置并处理的base-package属性
String basePackage = element.getAttribute(BASE_PACKAGE_ATTRIBUTE);
// 处理占位符
basePackage = parserContext.getReaderContext().getEnvironment().resolvePlaceholders(basePackage);
// 最终获取到的是一个数组 - 因为我们配置的时候是可以配置多个的
String[] basePackages = StringUtils.tokenizeToStringArray(basePackage,
ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
// 获取一个扫描器 - 这个东西很重要,我们以后还会看到
ClassPathBeanDefinitionScanner scanner = configureScanner(parserContext, element);
// 嗯,扫描器进行扫描,看来就是这个方法会扫描那些注解了
Set<BeanDefinitionHolder> beanDefinitions = scanner.doScan(basePackages);
// !!!注册一些组件!!! 就是这里注入的ConfigurationClassPostProcessor
registerComponents(parserContext.getReaderContext(), beanDefinitions, element);
return null;
}
protected void registerComponents(
XmlReaderContext readerContext, Set<BeanDefinitionHolder> beanDefinitions, Element element) {
// skip ...
// 默认是true
if (annotationConfig) {
// 注意这个AnnotationConfigUtils.registerAnnotationConfigProcessors
Set<BeanDefinitionHolder> processorDefinitions =
AnnotationConfigUtils.registerAnnotationConfigProcessors(readerContext.getRegistry(), source);
// skip ...
}
// skip ...
}
public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors(
BeanDefinitionRegistry registry, @Nullable Object source) {
// skip ...
if (!registry.containsBeanDefinition(CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)) {
// 就是里注册了一个ConfigurationClassPostProcessor
RootBeanDefinition def = new RootBeanDefinition(ConfigurationClassPostProcessor.class);
def.setSource(source);
beanDefs.add(registerPostProcessor(registry, def, CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME));
}
// skip ...
}
Как видите, мы разбираемcontext:component-scan
Когда лейбл, наконец, называетсяAnnotationConfigUtils.registerAnnotationConfigProcessors
Метод регистрирует пакет компонентов, поддерживающих аннотации с помощью spring, включаяConfigurationClassPostProcessor
.
Затем возникает проблема, так как я хочу определить ее через аннотации, объявитьbean
, то почему я должен иметьxml
файл, но и для разбораxml
середина
context:component-scan
А этикетки? Есть ли чистый способ аннотации, который позволил бы мне начатьspring
Шерстяная ткань?
Конечно, да, но в настоящее время мы должны использоватьAnnotationConfigApplicationContext
начатьspring
.
1. ИспользуйтеAnnotationConfigApplicationContext
запускатьspring
Сначала определим бизнес-класс:
@Data
@Service
public class MyAnnoClass {
public String username = "xiaoxizi";
}
затем используйтеAnnotationConfigApplicationContext
запускать:
@Test
public void test() {
applicationContext = new AnnotationConfigApplicationContext("com.xiaoxizi.spring");
MyAnnoClass myAnnoClass = applicationContext.getBean(MyAnnoClass.class);
System.out.println(myAnnoClass);
}
результат операции:
MyAnnoClass(username=xiaoxizi)
spring
Старт прошел успешно, и текущие результаты соответствовали нашим ожиданиям.MyAnnoClass
действительно былоspring
удалось.
2. AnnotationConfigApplicationContext
Чистый старт аннотацииspring
Принципиальный анализ
Без лишних слов, давайте проанализируем междуAnnotationConfigApplicationContext
Поддержка чистого запуска аннотацийspring
принцип.
Давайте сначала посмотримAnnotationConfigApplicationContext
конструктор:
public AnnotationConfigApplicationContext(String... basePackages) {
// 调用自己的无参构造器
this();
// 这个scan,看着就像扫描的意思啊
scan(basePackages);
// 这个refresh就是我们常说的spring启动的核心流程了
refresh();
}
// 看一下无参构造器
public AnnotationConfigApplicationContext() {
// 创建一个AnnotatedBeanDefinitionReader
this.reader = new AnnotatedBeanDefinitionReader(this);
// 创建一个ClassPathBeanDefinitionScanner
this.scanner = new ClassPathBeanDefinitionScanner(this);
}
Как видите, наше творениеAnnotationConfigApplicationContext
объект, создать
AnnotatedBeanDefinitionReader
иClassPathBeanDefinitionScanner
. подожди, этоClassPathBeanDefinitionScanner
Это кажется немного знакомым? это не наш анализcontext:component-scan
Сканер, который создавался при маркировке? Затем конструктор вызываетscan
Разве логика метода не...?
@Override
public void scan(String... basePackages) {
Assert.notEmpty(basePackages, "At least one base package must be specified");
// 使用扫描器扫描
this.scanner.scan(basePackages);
}
Ты угадал, одноклассник, просканируй соответствующий пакет через сканер (╹▽╹). иcontext:component-scan
Этикетки обрабатываются таким же образом.
Итак, пока что мы можемbasePackage
под одеялом@Component
Декорированный класс, в котором зарегистрирован пакет сканированияspring
Да, но мы, кажется, еще не в состоянии справиться с этим@Bean
Дождитесь этикетки, ведь я не видел, куда вкалыватьConfigurationClassPostProcessor
.
В этот момент нужно оглянуться.AnnotationConfigApplicationContext
В конструкторе есть только одно действие, с которым мы не знакомы, а именно созданиеAnnotatedBeanDefinitionReader
, то давайте посмотрим на логику построения этого класса:
public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry) {
this(registry, getOrCreateEnvironment(registry));
}
public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry, Environment environment) {
this.registry = registry;
this.conditionEvaluator = new ConditionEvaluator(registry, environment, null);
// !!! 注册支持注解的组件 !!!
AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
}
Ладно, дело раскрыто, оказалосьAnnotatedBeanDefinitionReader
Когда он будет создан, он будет называтьсяAnnotationConfigUtils.registerAnnotationConfigProcessors
Метод регистрирует компонент, поддерживающий аннотацию с помощью spring, среди которых естьConfigurationClassPostProcessor
. Так что мы можем с радостью начать использовать чистые аннотации.spring
Ла.
6. Резюме
Этот пост в блоге отxml
Метод синтаксического анализа говорит о чистом методе аннотации для началаspring
, и в пареConfigurationClassPostProcessor
Анализ исходного кода , понятьspring
Как поддерживать аннотации.
уже,spring
в процессе запускаbeanDefinition
Загрузка, парсинг и регистрация завершены, об этом мы поговорим позже.spring
Процесс запуска, включая создание экземпляра класса singleton, жизненный цикл и т. д.
В целом написание поста в блоге кажется довольно многословным, и, по оценкам, мало кто его читает, но старайтесь придерживаться его самостоятельно.Процесс написания также является хорошей сортировкой знаний.Писать сложно много вещей, когда вы не знаете много об этом.Таким образом, вы можете заставить себя посмотреть на исходный код более подробно и понять логику.
Создание не простое, пожалуйста, указывайте автора в начале перепечатки: Nuggets@小西子 + ссылка на источник~
Если вы хотите узнать больше об исходном коде Spring,Нажмите, чтобы перейти к остальной части построчного анализа серии Spring
٩(* ఠO ఠ)=3⁼³₌₃⁼³₌₃⁼³₌₃du ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла. . .
Вот блогер-новичок Сяо Сизи, большие парни видели это, пожалуйста, поставьте лайк в левом верхнем углу~~