第十次JAVA实习生面试题打卡

Posted Code_BinBin

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了第十次JAVA实习生面试题打卡相关的知识,希望对你有一定的参考价值。

springboot自动配置原理是什么?

1、在springboot的启动过程中,有一个步骤是创建上下文,如果不记得可以看下面的代码:

public ConfigurableApplicationContext run(String... args) 
		StopWatch stopWatch = new StopWatch();
		stopWatch.start();
		ConfigurableApplicationContext context = null;
		Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
		configureHeadlessProperty();
		SpringApplicationRunListeners listeners = getRunListeners(args);
		listeners.starting();
		try 
			ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
			ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
			configureIgnoreBeanInfo(environment);
			Banner printedBanner = printBanner(environment);
			context = createApplicationContext();
			exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
					new Class[]  ConfigurableApplicationContext.class , context);
            //此处完成自动装配的过程
			prepareContext(context, environment, listeners, applicationArguments, printedBanner);
			refreshContext(context);
			afterRefresh(context, applicationArguments);
			stopWatch.stop();
			if (this.logStartupInfo) 
				new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
			
			listeners.started(context);
			callRunners(context, applicationArguments);
		
		catch (Throwable ex) 
			handleRunFailure(context, ex, exceptionReporters, listeners);
			throw new IllegalStateException(ex);
		

		try 
			listeners.running(context);
		
		catch (Throwable ex) 
			handleRunFailure(context, ex, exceptionReporters, null);
			throw new IllegalStateException(ex);
		
		return context;
	

2、在prepareContext方法中查找load方法,一层一层向内点击,找到最终的load方法

//prepareContext方法
	private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
			SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) 
		context.setEnvironment(environment);
		postProcessApplicationContext(context);
		applyInitializers(context);
		listeners.contextPrepared(context);
		if (this.logStartupInfo) 
			logStartupInfo(context.getParent() == null);
			logStartupProfileInfo(context);
		
		// Add boot specific singleton beans
		ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
		beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
		if (printedBanner != null) 
			beanFactory.registerSingleton("springBootBanner", printedBanner);
		
		if (beanFactory instanceof DefaultListableBeanFactory) 
			((DefaultListableBeanFactory) beanFactory)
					.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
		
		if (this.lazyInitialization) 
			context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
		
		// Load the sources
		Set<Object> sources = getAllSources();
		Assert.notEmpty(sources, "Sources must not be empty");
        //load方法完成该功能
		load(context, sources.toArray(new Object[0]));
		listeners.contextLoaded(context);
	


	/**
	 * Load beans into the application context.
	 * @param context the context to load beans into
	 * @param sources the sources to load
	 * 加载bean对象到context中
	 */
	protected void load(ApplicationContext context, Object[] sources) 
		if (logger.isDebugEnabled()) 
			logger.debug("Loading source " + StringUtils.arrayToCommaDelimitedString(sources));
		
        //获取bean对象定义的加载器
		BeanDefinitionLoader loader = createBeanDefinitionLoader(getBeanDefinitionRegistry(context), sources);
		if (this.beanNameGenerator != null) 
			loader.setBeanNameGenerator(this.beanNameGenerator);
		
		if (this.resourceLoader != null) 
			loader.setResourceLoader(this.resourceLoader);
		
		if (this.environment != null) 
			loader.setEnvironment(this.environment);
		
		loader.load();
	

	/**
	 * Load the sources into the reader.
	 * @return the number of loaded beans
	 */
	int load() 
		int count = 0;
		for (Object source : this.sources) 
			count += load(source);
		
		return count;
	

3、实际执行load的是BeanDefinitionLoader中的load方法,如下:

	//实际记载bean的方法
	private int load(Object source) 
		Assert.notNull(source, "Source must not be null");
        //如果是class类型,启用注解类型
		if (source instanceof Class<?>) 
			return load((Class<?>) source);
		
        //如果是resource类型,启动xml解析
		if (source instanceof Resource) 
			return load((Resource) source);
		
        //如果是package类型,启用扫描包,例如@ComponentScan
		if (source instanceof Package) 
			return load((Package) source);
		
        //如果是字符串类型,直接加载
		if (source instanceof CharSequence) 
			return load((CharSequence) source);
		
		throw new IllegalArgumentException("Invalid source type " + source.getClass());
	

4、下面方法将用来判断是否资源的类型,是使用groovy加载还是使用注解的方式

	private int load(Class<?> source) 
        //判断使用groovy脚本
		if (isGroovyPresent() && GroovyBeanDefinitionSource.class.isAssignableFrom(source)) 
			// Any GroovyLoaders added in beans DSL can contribute beans here
			GroovyBeanDefinitionSource loader = BeanUtils.instantiateClass(source, GroovyBeanDefinitionSource.class);
			load(loader);
		
        //使用注解加载
		if (isComponent(source)) 
			this.annotatedReader.register(source);
			return 1;
		
		return 0;
	

5、下面方法判断启动类中是否包含@Component注解,但是会神奇的发现我们的启动类中并没有该注解,继续更进发现MergedAnnotations类传入了一个参数SearchStrategy.TYPE_HIERARCHY,会查找继承关系中是否包含这个注解,@SpringBootApplication–>@SpringBootConfiguration–>@Configuration–>@Component,当找到@Component注解之后,会把该对象注册到AnnotatedBeanDefinitionReader对象中

private boolean isComponent(Class<?> type) 
   // This has to be a bit of a guess. The only way to be sure that this type is
   // eligible is to make a bean definition out of it and try to instantiate it.
   if (MergedAnnotations.from(type, SearchStrategy.TYPE_HIERARCHY).isPresent(Component.class)) 
      return true;
   
   // Nested anonymous classes are not eligible for registration, nor are groovy
   // closures
   return !type.getName().matches(".*\\\\$_.*closure.*") && !type.isAnonymousClass()
         && type.getConstructors() != null && type.getConstructors().length != 0;


	/**
	 * Register a bean from the given bean class, deriving its metadata from
	 * class-declared annotations.
	 * 从给定的bean class中注册一个bean对象,从注解中找到相关的元数据
	 */
	private <T> void doRegisterBean(Class<T> beanClass, @Nullable String name,
			@Nullable Class<? extends Annotation>[] qualifiers, @Nullable Supplier<T> supplier,
			@Nullable BeanDefinitionCustomizer[] customizers) 

		AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(beanClass);
		if (this.conditionEvaluator.shouldSkip(abd.getMetadata())) 
			return;
		

		abd.setInstanceSupplier(supplier);
		ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(abd);
		abd.setScope(scopeMetadata.getScopeName());
		String beanName = (name != null ? name : this.beanNameGenerator.generateBeanName(abd, this.registry));

		AnnotationConfigUtils.processCommonDefinitionAnnotations(abd);
		if (qualifiers != null) 
			for (Class<? extends Annotation> qualifier : qualifiers) 
				if (Primary.class == qualifier) 
					abd.setPrimary(true);
				
				else if (Lazy.class == qualifier) 
					abd.setLazyInit(true);
				
				else 
					abd.addQualifier(new AutowireCandidateQualifier(qualifier));
				
			
		
		if (customizers != null) 
			for (BeanDefinitionCustomizer customizer : customizers) 
				customizer.customize(abd);
			
		

		BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(abd, beanName);
		definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
		BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry);
	

	/**
	 * Register the given bean definition with the given bean factory.
	 * 注册主类,如果有别名可以设置别名
	 */
	public static void registerBeanDefinition(
			BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
			throws BeanDefinitionStoreException 

		// Register bean definition under primary name.
		String beanName = definitionHolder.getBeanName();
		registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());

		// Register aliases for bean name, if any.
		String[] aliases = definitionHolder.getAliases();
		if (aliases != null) 
			for (String alias : aliases) 
				registry.registerAlias(beanName, alias);
			
		
	

//@SpringBootApplication
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters =  @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
		@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) )
public @interface SpringBootApplication 

//@SpringBootConfiguration
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration 

//@Configuration
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration 

当看完上述代码之后,只是完成了启动对象的注入,自动装配还没有开始,下面开始进入到自动装配。

6、自动装配入口,从刷新容器开始

@Override
	public void refresh() throws BeansException, IllegalStateException 
		synchronized (this.startupShutdownMonitor) 
			// Prepare this context for refreshing.
			prepareRefresh();

			// Tell the subclass to refresh the internal bean factory.
			ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

			// Prepare the bean factory for use in this context.
			prepareBeanFactory(beanFactory);

			try 
				// Allows post-processing of the bean factory in context subclasses.
				postProcessBeanFactory(beanFactory);

				// Invoke factory processors registered as beans in the context.
                // 此处是自动装配的入口
				invokeBeanFactoryPostProcessors(beanFactory);
            

7、在invokeBeanFactoryPostProcessors方法中完成bean的实例化和执行

/**
	 * Instantiate and invoke all registered BeanFactoryPostProcessor beans,
	 * respecting explicit order if given.
	 * <p>Must be called before singleton instantiation.
	 */
	protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) 
        //开始执行beanFactoryPostProcessor对应实现类,需要知道的是beanFactoryPostProcessor是spring的扩展接口,在刷新容器之前,该接口可以用来修改bean元数据信息
		PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());

		// Detect a LoadTimeWeaver and prepare for weaving, if found in the meantime
		// (e.g. through an @Bean method registered by ConfigurationClassPostProcessor)
		if (beanFactory.getTempClassLoader() == null && beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) 
			beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
			beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
		
	

8、查看invokeBeanFactoryPostProcessors的具体执行方法

	public static void invokeBeanFactoryPostProcessors(
			ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) 

		// Invoke BeanDefinitionRegistryPostProcessors first, if any.
		Set<String> processedBeans = new HashSet<>();

		if (beanFactory instanceof BeanDefinitionRegistry) 
			BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
			List<BeanFactoryPostProcessor> regularPostProcessors = new ArrayList<>();
			List<BeanDefinitionRegistryPostProcessor> registryProcessors = new ArrayList<>();
			//开始遍历三个内部类,如果属于BeanDefinitionRegistryPostProcessor子类,加入到bean注册的集合,否则加入到regularPostProcessors
			for (BeanFactoryPostProcessor postProcessor : beanFactoryPostProcessors) 
				if (postProcessor instanceof BeanDefinitionRegistryPostProcessor) 
					BeanDefinitionRegistryPostProcessor registryProcessor =
							(BeanDefinitionRegistryPostProcessor) postProcessor;
					registryProcessor.postProcessBeanDefinitionRegistry(registry);
					registryProcessors.add(registryProcessor);
				
				else 
					regularPostProcessors.add(postProcessor);
				
			

			// Do not initialize FactoryBeans here: We need to leave all regular beans
			// uninitialized to let the bean factory post-processors apply to them!
			// Separate between BeanDefinitionRegistryPostProcessors that implement
			// PriorityOrdered, Ordered, and the rest.
			List<BeanDefinitionRegistryPostProcessor> currentRegistryProcessors = new ArrayList<>();

			// First, invoke the BeanDefinitionRegistryPostProcessors that implement PriorityOrdered.
            //通过BeanDefinitionRegistryPostProcessor获取到对应的处理类“org.springframework.context.annotation.internalConfigurationAnnotationProcessor”,但是需要注意的是这个类在springboot中搜索不到,这个类的完全限定名在AnnotationConfigEmbeddedWebApplicationContext中,在进行初始化的时候会装配几个类,在创建AnnotatedBeanDefinitionReader对象的时候会将该类注册到bean对象中,此处可以看到internalConfigurationAnnotationProcessor为bean名称,容器中真正的类是ConfigurationClassPostProcessor
			String[] postProcessorNames =
					beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
            //首先执行类型为PriorityOrdered的BeanDefinitionRegistryPostProcessor
            //PriorityOrdered类型表明为优先执行
			for (String ppName : postProcessorNames) 
				if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) 
                    //获取对应的bean
					currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
                    //用来存储已经执行过的BeanDefinitionRegistryPostProcessor
					processedBeans.add(ppName);
				
			
			sortPostProcessors(currentRegistryProcessors, beanFactory);
			registryProcessors.addAll(currentRegistryProcessors);
         

以上是关于第十次JAVA实习生面试题打卡的主要内容,如果未能解决你的问题,请参考以下文章

JAVA实习生第十二次面试题打卡——Redis篇

JAVA实习生第十一次面试题打卡——Redis篇

JAVA实习生第十一次面试题打卡——Redis篇

Java实习生每日10道面试题打卡!

Java实习生每日10道面试题打卡!

Java实习生每日面试题打卡——计算机网络篇