栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

SpringBoot核心注解原理

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

SpringBoot核心注解原理

SpringBoot的启动类代码

@SpringBootApplication
public class MyblogApplication {
    public static void main(String[] args) {
        SpringApplication.run(MyblogApplication.class, args);
    }
}

首先在启动类这段代码中,我们点进 @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
@EnableAutoConfiguration
@ComponentScan

首先我们来说 @SpringBootConfiguration @EnableAutoConfiguration这两个注解

@SpringBootConfiguration 点进去之后

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@documented
@Configuration
public @interface SpringBootConfiguration {
}

除了元注解外,就只有一个 @Configuration,也就是说或这个注解相当于@Configuration,所以这两个注解作用是一样的,什么作用呢?就是能够注册一些额外的Bean,并且导入一些额外的配置。@Configuration还有一个作用就是把该类变成一个配置类,不需要进行额外的XML配置。所以@SpringBootConfiguration相当于@Configuration。

继续看 @EnableAutoConfiguration,官网说这个注解是 让Spring自动去进行一些配置

点进去看

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@documented
@Inherited
@AutoConfigurationPackage
@import(AutoConfigurationimportSelector.class)
public @interface EnableAutoConfiguration {
}

可以看到它是由@AutoConfigurationPackage 和 @import(AutoConfigurationimportSelector.class) 组成,@AutoConfigurationPackage,作用是:让包中类以及子包中的类能够被自动扫描到Spring容器中。自动配置?它是如何配置的呢?和@import(AutoConfigurationimportSelector.class)相关

从@import(AutoConfigurationimportSelector.class) ,我们点进去看

public class AutoConfigurationimportSelector implements DeferredimportSelector, BeanClassLoaderAware,
		ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
	@Override
	public String[] selectimports(Annotationmetadata annotationmetadata) {
		if (!isEnabled(annotationmetadata)) {
			return NO_importS;
		}
		AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationmetadata);
		return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
	}



	protected List getCandidateConfigurations(Annotationmetadata metadata, AnnotationAttributes attributes) {
		List configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
				getBeanClassLoader());
		Assert.notEmpty(configurations, "No auto configuration classes found in meta-INF/spring.factories. If you "
				+ "are using a custom packaging, make sure that file is correct.");
		return configurations;
	}

}

这个AutoConfigurationimportSelector  类中有一个方法  selectimports(Annotationmetadata annotationmetadata) ,它可以帮助扫描那些类自动去添加到程序当中。

里面还有一个方法是 getCandidateConfigurations(),它的作用是引入系统已经加载好的一些类,到底是哪些类呢?

"No auto configuration classes found in meta-INF/spring.factories. If you "
      + "are using a custom packaging, make sure that file is correct."

从这句话中,可以看到 这个类会去找一个目录为 meta-INF/spring.factories,也就是说它会帮你加载让你去使用,也就是在meta-INF/spring.factories目录装配的,它在哪呢?

 点进去看

可以发现它搬我们配置了很多类的全路径,比如WebMvc

 可以看到它已经帮我们引入了进来,拿几个过来看

org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration,
org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,
org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration,
org.springframework.boot.autoconfigure.jdbc.JndiDataSourceAutoConfiguration,
org.springframework.boot.autoconfigure.jdbc.XADataSourceAutoConfiguration,

@EnableAutoConfiguration主要作用就是帮你自动去配置,但并不是所有都是创建好的,是根据你的程序去进行决定。那我们继续来看

接着看@ComponentScan注解

@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
		@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })

扫描包,放入Spring容器,它在SpringBoot做了什么呢?

它帮助我们做了一个排除策略,在这里它结合@SpringBootConfiguration去使用,为什么是排除?因为不可能一上来就全部加载,内存有限。

总结@SpringBootApplication:就是说 它已经把很多东西准备好,具体是否使用取决于我们的程序或者说配置,那我们用不用?继续看这一行代码

public class MyblogApplication {
    public static void main(String[] args) {
        SpringApplication.run(MyblogApplication.class, args);
    }
}

来看run方法有没有用到哪些自动配置的东西,比如说内置tomcat,

我们点进去 SpringApplication

class SpringApplication{
    public ConfigurableApplicationContext run(String... args) {
        //计时器
		StopWatch stopWatch = new StopWatch();
		stopWatch.start();
		ConfigurableApplicationContext context = null;
		Collection 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);
            //预刷新context
			prepareContext(context, environment, listeners, applicationArguments, printedBanner);
            //刷新context
			refreshContext(context);
            //刷次之后的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;
	}
}

我们关注的就是  refreshContext(context);  刷新context,点进去看

	private void refreshContext(ConfigurableApplicationContext context) {
		if (this.registerShutdownHook) {
			try {
				context.registerShutdownHook();
			}
			catch (AccessControlException ex) {
				// Not allowed in some environments.
			}
		}
		refresh((ApplicationContext) context);
	}

追踪  refresh((ApplicationContext) context);

	public class SpringApplication {
    
	@Deprecated
	protected void refresh(ApplicationContext applicationContext) {
		Assert.isInstanceOf(ConfigurableApplicationContext.class, applicationContext);
		refresh((ConfigurableApplicationContext) applicationContext);
	}


    
	protected void refresh(ConfigurableApplicationContext applicationContext) {
		applicationContext.refresh();
	}
}

它调用了 refresh();点进去这个方法看

	protected void refresh(ConfigurableApplicationContext applicationContext) {
		applicationContext.refresh();
	}

追踪refresh()

来到了  ConfigurableApplicationContext 接口

public interface ConfigurableApplicationContext extends ApplicationContext, Lifecycle, Closeable {
    void refresh() throws BeansException, IllegalStateException;
}

实现该接口,重写该方法的几个类中,

点进去第一个看看

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

				// Register bean processors that intercept bean creation.
				registerBeanPostProcessors(beanFactory);

				// Initialize message source for this context.
				initMessageSource();

				// Initialize event multicaster for this context.
				initApplicationEventMulticaster();

				// Initialize other special beans in specific context subclasses.
				onRefresh();

				// Check for listener beans and register them.
				registerListeners();

				// Instantiate all remaining (non-lazy-init) singletons.
				finishBeanFactoryInitialization(beanFactory);

				// Last step: publish corresponding event.
				finishRefresh();
			}

			catch (BeansException ex) {
				if (logger.isWarnEnabled()) {
					logger.warn("Exception encountered during context initialization - " +
							"cancelling refresh attempt: " + ex);
				}

				// Destroy already created singletons to avoid dangling resources.
				destroyBeans();

				// Reset 'active' flag.
				cancelRefresh(ex);

				// Propagate exception to caller.
				throw ex;
			}

			finally {
				// Reset common introspection caches in Spring's core, since we
				// might not ever need metadata for singleton beans anymore...
				resetCommonCaches();
			}
		}
	}

发现,这个代码很熟悉,就是一个spring的bean的加载过程,解析SpringIOC加载过程的时候也含有这些方法,如果你看过Spring源码的话 ,应该知道这些方法都是做什么的。现在我们不关心其他的,我们来看一个方法叫做 onRefresh();方法

	
	protected void onRefresh() throws BeansException {
		// For subclasses: do nothing by default.
	}

找它的实现

 我们既然要找Tomcat那就肯定跟web有关,我们可以看到有个ServletWebServerApplicationContext

@Override
protected void onRefresh() {
   super.onRefresh();
   try {
      createWebServer();
   }
   catch (Throwable ex) {
      throw new ApplicationContextException("Unable to start web server", ex);
   }
}

可以看到 有一个  createWebServer() 方法,它是用来创建web容器的,而tomcat不是web容器,那它是怎么创建的呢?继续看 该方法 

private void createWebServer() {
		WebServer webServer = this.webServer;
		ServletContext servletContext = getServletContext();
		if (webServer == null && servletContext == null) {
			ServletWebServerFactory factory = getWebServerFactory();
			this.webServer = factory.getWebServer(getSelfInitializer());
			getBeanFactory().registerSingleton("webServerGracefulShutdown",
					new WebServerGracefulShutdownLifecycle(this.webServer));
			getBeanFactory().registerSingleton("webServerStartStop",
					new WebServerStartStopLifecycle(this, this.webServer));
		}
		else if (servletContext != null) {
			try {
				getSelfInitializer().onStartup(servletContext);
			}
			catch (ServletException ex) {
				throw new ApplicationContextException("Cannot initialize servlet context", ex);
			}
		}
		initPropertySources();
	}

中间一行中 factory.getWebServer(getSelfInitializer());他是通过工厂的方式创建的

追踪进去

@FunctionalInterface
public interface ServletWebServerFactory {

	
	WebServer getWebServer(ServletContextInitializer... initializers);

}

找它的实现类,要找tomcat如何创建,找tomcat相关的

 进去之后,找到创建tomcat的方法

@Override
	public WebServer getWebServer(ServletContextInitializer... initializers) {
		if (this.disableMBeanRegistry) {
			Registry.disableRegistry();
		}
		Tomcat tomcat = new Tomcat();
		File baseDir = (this.baseDirectory != null) ? this.baseDirectory : createTempDir("tomcat");
		tomcat.setbaseDir(baseDir.getAbsolutePath());
		Connector connector = new Connector(this.protocol);
		connector.setThrowonFailure(true);
		tomcat.getService().addConnector(connector);
		customizeConnector(connector);
		tomcat.setConnector(connector);
		tomcat.getHost().setAutoDeploy(false);
		configureEngine(tomcat.getEngine());
		for (Connector additionalConnector : this.additionalTomcatConnectors) {
			tomcat.getService().addConnector(additionalConnector);
		}
		prepareContext(tomcat.getHost(), initializers);
		return getTomcatWebServer(tomcat);
	}

该过程,就是我们要找的内置tomcat以及创建tomcat的过程。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/737226.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号