springboot之条件注解底层探秘

编程


我们来看下spring-boot的启动关键步骤

1.程序的启动点

//1.入口

SpringApplication.run(SampleTomcatJspApplication.class, args);

//2.跟着入口一直看下去,我们就到了这个地方

public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {

return new SpringApplication(primarySources).run(args);

}

到这里的话,我们就看到了一个实际上是创建一个SpringApplication对象然后执行run方法。我们先看一下创建对应的时候都是做了什么事情

2. SpringApplication的构造过程

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {

//资源加载器赋值

this.resourceLoader = resourceLoader;

Assert.notNull(primarySources, "PrimarySources must not be null");

this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));

//获取对应的web应用类型

this.webApplicationType = WebApplicationType.deduceFromClasspath();

//设置ApplicationContextInitializer,该接口也被成为回调函数

setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));

//设置监听器(参考观察者模式)

setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));

//获取main函数对应的类名称

this.mainApplicationClass = deduceMainApplicationClass();

}

在构造函数中,主要有三个方法,我们要注意一下,第一个是获取web应用的类型,这个决定了我们后面容器的创建。ApplicationContextInitializer的设置,这个是一个扩展点,主要在容器刷新之前,我们可以做一些环境设置。最后一个就是监听器的注册也是一个扩展点


3. SpringApplication的run方法

SpringApplication创建以后,到现在我们能够判断出当前的web应用是那种类型。那么我们继续看SpringApplication对象中的run方法

       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);

//1.创建容器

context = createApplicationContext();

exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,

new Class[] { ConfigurableApplicationContext.class }, context);

prepareContext(context, environment, listeners, applicationArguments, printedBanner);

//2.刷新容器

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;

其他的内容不是不重要,而是我们现在关注点是在于条件注解在容器创建过程中,到底是在什么地方使用的。所以我们先关注在容器的创建和刷新的过程,看下这个两个过程都做了什么事情

3.1 创建ConfigurableApplicationContext

protected ConfigurableApplicationContext createApplicationContext() {

Class<?> contextClass = this.applicationContextClass;

if (contextClass == null) {

try {

switch (this.webApplicationType) {

case SERVLET:

contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS);

break;

case REACTIVE:

contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);

break;

default:

contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);

}

}

catch (ClassNotFoundException ex) {

throw new IllegalStateException(

"Unable create a default ApplicationContext, please specify an ApplicationContextClass", ex);

}

}

return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);

}

主要根据我们获取到的web应用类型,来创建对应的容器。跟踪代码可以知道web类型这里是SERVLET,所以对应的创建的容器就是AnnotationConfigServletWebServerApplicationContext。下面我们就看下AnnotationConfigServletWebServerApplicationContext这个类的无参构造函数里面到底做了个什么

3.2 实例化AnnotationConfigServletWebServerApplicationContext

public AnnotationConfigServletWebServerApplicationContext() {

// AnnotatedBeanDefinitionReader 初始化BeanDefinition读取类

this.reader = new AnnotatedBeanDefinitionReader(this);

//ClassPathBeanDefinitionScanner 初始BeanDefinition扫描类

this.scanner = new ClassPathBeanDefinitionScanner(this);

}

以前看过spring 源码的同学是不是对这个两个类很熟悉。我们先不关心扫描类,我们来看下AnnotatedBeanDefinitionReader都干了什么。

3.2.1 AnnotatedBeanDefinitionReader创建

public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry, Environment environment) {

Assert.notNull(registry, "BeanDefinitionRegistry must not be null");

Assert.notNull(environment, "Environment must not be null");

this.registry = registry;

//看着这个地方是不是和条件注解有点关系了?

this.conditionEvaluator = new ConditionEvaluator(registry, environment, null);

//注册注释后置处理器(这个是关键点)

AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);

}

代码看到这里就和我们今天的主题有点关系了。ConditionEvaluator这个类就是用来评估条件注解标注的类。换句通俗的话说就是他的行为结果决定了类是否可以应该被注入。

AnnotationConfigUtils.registerAnnotationConfigProcessors() 这个太重要了,重要到我们不得不去里面瞄一眼里面都是有什么东西

public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors(

BeanDefinitionRegistry registry, @Nullable Object source) {

DefaultListableBeanFactory beanFactory = unwrapDefaultListableBeanFactory(registry);

if (beanFactory != null) {

if (!(beanFactory.getDependencyComparator() instanceof AnnotationAwareOrderComparator)) {

beanFactory.setDependencyComparator(AnnotationAwareOrderComparator.INSTANCE);

}

if (!(beanFactory.getAutowireCandidateResolver() instanceof ContextAnnotationAutowireCandidateResolver)) {

beanFactory.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver());

}

}

Set<BeanDefinitionHolder> beanDefs = new LinkedHashSet<>(8);

// 看这个是啥,用于处理@Configuration注解相关的,我们的文章的答案就在这个类里面

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));

}

//同理这个就是用来处理@Autowired等

if (!registry.containsBeanDefinition(AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME)) {

RootBeanDefinition def = new RootBeanDefinition(AutowiredAnnotationBeanPostProcessor.class);

def.setSource(source);

beanDefs.add(registerPostProcessor(registry, def, AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME));

}

// Check for JSR-250 support, and if present add the CommonAnnotationBeanPostProcessor.

if (jsr250Present && !registry.containsBeanDefinition(COMMON_ANNOTATION_PROCESSOR_BEAN_NAME)) {

RootBeanDefinition def = new RootBeanDefinition(CommonAnnotationBeanPostProcessor.class);

def.setSource(source);

beanDefs.add(registerPostProcessor(registry, def, COMMON_ANNOTATION_PROCESSOR_BEAN_NAME));

}

// Check for JPA support, and if present add the PersistenceAnnotationBeanPostProcessor.

if (jpaPresent && !registry.containsBeanDefinition(PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME)) {

RootBeanDefinition def = new RootBeanDefinition();

try {

def.setBeanClass(ClassUtils.forName(PERSISTENCE_ANNOTATION_PROCESSOR_CLASS_NAME,

AnnotationConfigUtils.class.getClassLoader()));

}

catch (ClassNotFoundException ex) {

throw new IllegalStateException(

"Cannot load optional framework class: " + PERSISTENCE_ANNOTATION_PROCESSOR_CLASS_NAME, ex);

}

def.setSource(source);

beanDefs.add(registerPostProcessor(registry, def, PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME));

}

if (!registry.containsBeanDefinition(EVENT_LISTENER_PROCESSOR_BEAN_NAME)) {

RootBeanDefinition def = new RootBeanDefinition(EventListenerMethodProcessor.class);

def.setSource(source);

beanDefs.add(registerPostProcessor(registry, def, EVENT_LISTENER_PROCESSOR_BEAN_NAME));

}

if (!registry.containsBeanDefinition(EVENT_LISTENER_FACTORY_BEAN_NAME)) {

RootBeanDefinition def = new RootBeanDefinition(DefaultEventListenerFactory.class);

def.setSource(source);

beanDefs.add(registerPostProcessor(registry, def, EVENT_LISTENER_FACTORY_BEAN_NAME));

}

return beanDefs;

}

这个方法重要吧,日常的注解能够被使用。这个地方就是处理他们的入口,我们直接来看ConfigurationClassPostProcessor#postProcessBeanFactory(),跟踪这个方法,会进入到processConfigBeanDefinitions(),在processConfigBeanDefinitions()中有段代码

do {

parser.parse(candidates);//这地方就是解析注解的地方,一路跟踪下去

parser.validate();

}

while (!candidates.isEmpty());

跟踪此方法,最后会进入到这个地方

protected void processConfigurationClass(ConfigurationClass configClass, Predicate<String> filter) throws IOException {

// 通过 conditionEvaluator来判断是否能够注入类。这个就是我们条件注解能够生效的原因

if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) {

return;

}

ConfigurationClass existingClass = this.configurationClasses.get(configClass);

if (existingClass != null) {

if (configClass.isImported()) {

if (existingClass.isImported()) {

existingClass.mergeImportedBy(configClass);

}

// Otherwise ignore new imported config class; existing non-imported class overrides it.

return;

}

else {

// Explicit bean definition found, probably replacing an import.

// Let"s remove the old one and go with the new one.

this.configurationClasses.remove(configClass);

this.knownSuperclasses.values().removeIf(configClass::equals);

}

}

// Recursively process the configuration class and its superclass hierarchy.

SourceClass sourceClass = asSourceClass(configClass, filter);

do {

sourceClass = doProcessConfigurationClass(configClass, sourceClass, filter);

}

while (sourceClass != null);

this.configurationClasses.put(configClass, configClass);

}

以上是 springboot之条件注解底层探秘 的全部内容, 来源链接: utcz.com/z/516045.html

回到顶部