一、SpringBootApplication注解学习
1、SpringBoot启动方法
在启动类的入口函数上,添加了@SpringBootApplication注解。
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication app = new SpringApplication(Application.class);
app.setDefaultProperties(PropertiesUtils.getInstance().getProperties());
app.run(args);
}
}
2、SpringBootApplication注解
该注解是一个组合注解,即@SpringBootApplication = @SpringBootConfiguration(@Configuration) + @EnableAutoConfiguration + @ComponentScan。代码如下:
@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 {
@AliasFor(annotation = EnableAutoConfiguration.class, attribute = "exclude")
Class<?>[] exclude() default {};
@AliasFor(annotation = EnableAutoConfiguration.class, attribute = "excludeName")
String[] excludeName() default {};
@AliasFor(annotation = ComponentScan.class, attribute = "basePackages")
String[] scanBasePackages() default {};
@AliasFor(annotation = ComponentScan.class, attribute = "basePackageClasses")
Class<?>[] scanBasePackageClasses() default {};
}
3、SpringBootConfiguration注解
该注解本质上是一个@Configuration注解,除了具有@Configuration注解的功能外,还表明该注解的类是一个SpringBoot应用。
@Configuration注解注解的作用,主要是定义配置类,可以替换XML配置文件,被注解的类内部包含有一个或多个被@Bean注解的方法,这些方法将会被AnnotationConfigApplicationContext或AnnotationConfigWebApplicationContext类进行扫描,并用于构建bean定义,初始化Spring容器。其他具体用法请参考相关内容学习,此处不再展开。
注解代码如下:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {
}
4、ComponentScan注解
@ComponentScan注解会自动扫描指定包(如果不指定,那么默认扫描当前类路径以及子路径下的所有类)下的全部标有@Component(包括@Service、@Repository、@Controller等子注解)的类,并注册成bean。代码不再贴出。
5、EnableAutoConfiguration注解
@EnableAutoConfiguration注解用于自动载入应用程序所需要的所有Bean,这依赖于SpringBoot在类路径中的查找。主要依靠了@Import注解实现该功能。
@SuppressWarnings("deprecation")
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(EnableAutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
Class<?>[] exclude() default {};
String[] excludeName() default {};
}
通过上面代码可以发现包括了两个关键注解@AutoConfigurationPackage和@Import。其中@AutoConfigurationPackage用于把包下所有被注解的类注册到AutoConfigurationPackages类中。@Import注解是实现自动注入的核心代码,下面专门介绍。
5、Import注解
@Import注解的作用是提供了注入类的几种实现方法,主要有以下几种:
1. 声明一个bean
2. 导入@Configuration注解的配置类
3. 导入ImportSelector的实现类
4. 导入ImportBeanDefinitionRegistrar的实现类
在@EnableAutoConfiguration注解中使用的是@Import的第三种方法,即ImportSelector实现方法。该方法的详细使用方法请自行查阅相关文档或博客。该方法的核心代码是ImportSelector接口的selectImports方法。在@EnableAutoConfiguration注解中该方法的具体实现是在AutoConfigurationImportSelector类中完成,即在@EnableAutoConfiguration注解中通过@import注解引入EnableAutoConfigurationImportSelector类,EnableAutoConfigurationImportSelector类又继承了AutoConfigurationImportSelector类,并在该类中实现了selectImports方法,该方法中实现了相关类的自动加。代码如下:
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
try {
AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
.loadMetadata(this.beanClassLoader);
AnnotationAttributes attributes = getAttributes(annotationMetadata);
List<String> configurations = getCandidateConfigurations(annotationMetadata,
attributes);
configurations = removeDuplicates(configurations);
configurations = sort(configurations, autoConfigurationMetadata);
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = filter(configurations, autoConfigurationMetadata);
fireAutoConfigurationImportEvents(configurations, exclusions);
return configurations.toArray(new String[configurations.size()]);
}
catch (IOException ex) {
throw new IllegalStateException(ex);
}
}
上述代码中selectImports方法实现了相关类的自动加载,具体加载实现逻辑后续模块单独介绍,该方法的调用时机请参考
二、方法执行过程
1、入口
public static void main(String[] args) {
SpringApplication app = new SpringApplication(Application.class);
app.setDefaultProperties(PropertiesUtils.getInstance().getProperties());
app.run(args);
}
2、对象构建过程
public SpringApplication(Object... sources) {
initialize(sources);
}
2.1、构造函数-初始化方法
该方法主要做了四件事,分别是:
- 判断是否是web环境
- 初始化ApplicationContextInitializer相关实例
- 初始化ApplicationListener相关实例
- 判断是否是main方法应用(启动)
@SuppressWarnings({ "unchecked", "rawtypes" })
private void initialize(Object[] sources) {
if (sources != null && sources.length > 0) {
this.sources.addAll(Arrays.asList(sources));
}
this.webEnvironment = deduceWebEnvironment();
setInitializers((Collection) getSpringFactoriesInstances(
ApplicationContextInitializer.class));
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = deduceMainApplicationClass();
}
2.1.1 判断是否是web环境
private boolean deduceWebEnvironment() {
for (String className : WEB_ENVIRONMENT_CLASSES) {
if (!ClassUtils.isPresent(className, null)) {
return false;
}
}
return true;
}
2.1.2 判断是否是main方法应用(启动)
private Class<?> deduceMainApplicationClass() {
try {
StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
for (StackTraceElement stackTraceElement : stackTrace) {
if ("main".equals(stackTraceElement.getMethodName())) {
return Class.forName(stackTraceElement.getClassName());
}
}
}
catch (ClassNotFoundException ex) {
// Swallow and continue
}
return null;
}
2.1.3 初始化实例对象
主要是初始化了SpringApplication类的下列属性:
- private List<ApplicationContextInitializer<?>> initializers;
- private List<ApplicationListener<?>> listeners;
初始化initializers和listeners对象过程中,核心方法的代码如下:
private <T> Collection<? extends T> getSpringFactoriesInstances(Class<T> type,
Class<?>[] parameterTypes, Object... args) {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
// Use names and ensure unique to protect against duplicates
Set<String> names = new LinkedHashSet<String>(
SpringFactoriesLoader.loadFactoryNames(type, classLoader));
List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
classLoader, args, names);
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
后续,根据反射创建实例对象不再继续分析。
3、run方法运行过程
3.1、 run方法代码如下:
/**
* Run the Spring application, creating and refreshing a new
* {@link ApplicationContext}.
* @param args the application arguments (usually passed from a Java main method)
* @return a running {@link ApplicationContext}
*/
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
FailureAnalyzers analyzers = null;
configureHeadlessProperty();
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(
args);
ConfigurableEnvironment environment = prepareEnvironment(listeners,
applicationArguments);
Banner printedBanner = printBanner(environment);
context = createApplicationContext();
analyzers = new FailureAnalyzers(context);
prepareContext(context, environment, listeners, applicationArguments,
printedBanner);
refreshContext(context);
afterRefresh(context, applicationArguments);
listeners.finished(context, null);
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass)
.logStarted(getApplicationLog(), stopWatch);
}
return context;
}
catch (Throwable ex) {
handleRunFailure(context, listeners, analyzers, ex);
throw new IllegalStateException(ex);
}
}
其中,StopWatch对象用于执行代码的耗时检测。configureHeadlessProperty方法,是设置系统属性java.awt.headless,这里设置为true,表示运行在服务器端,在没有显示器和鼠标键盘的模式下工作,模拟输入输出设备功能。这些代码与主流程关系不大,且代码比较简单,需要详细了解,可以自行查看和学习。
3.2、 getRunListeners(args)方法
该方法主要是获取运行时监听器EventPublishingRunListener(SpringApplicationRunListener的实现类)的封装对象。实际上,就是获取SpringApplicationRunListeners对象,该对象是一个SpringApplicationRunListener的集合,该类改造了SpringApplicationRunListener接口中的一些方法(实现批处理功能,本质上就是添加for循环处理,可以理解成包装类)。其中,SpringApplicationRunListener接口主要用于监听SpringApplication的run方法。通过SpringFactoriesLoader加载SpringApplicationRunListener(一个或多个),SpringApplicationRunListener的实现类必须声明一个接收SpringApplication实例和String[]数组的公有构造方法,每次SpringApplication的run方法执行,都会创建一个新的SpringApplicationRunListener实例。该方法的代码如下:
private SpringApplicationRunListeners getRunListeners(String[] args) {
Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances(
SpringApplicationRunListener.class, types, this, args));
}
在该方法中,通过getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args)方法实现了SpringApplicationRunListener类型的对象加载,内部还是通过读取spring-boot.jar中META-INF/spring.factories文件中的配置,并筛选出符合要求的对象集合(EventPublishingRunListener对象),底层是通过SimpleApplicationEventMulticaster实现了事件广播等功能,具体用法,另外章节专门分析。
3.3、 应用参数封装
ApplicationArguments applicationArguments = new DefaultApplicationArguments(
args);
该代码主要是实现参数的封装,把启动时的系统参数封装成了ApplicationArguments对象(真正的对象DefaultApplicationArguments),即它持有着args参数,就是main函数传进来的参数。具体内容,可以查看源码继续深入学习。
3.4、 prepareEnvironment方法
该方法准备运行的环境,比如开发环境dev,测试环境test,还是生产环境prd,然后根据环境解析不同的配置文件。具体代码如下:
private ConfigurableEnvironment prepareEnvironment(
SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments) {
// Create and configure the environment
ConfigurableEnvironment environment = getOrCreateEnvironment();
configureEnvironment(environment, applicationArguments.getSourceArgs());
listeners.environmentPrepared(environment);
if (!this.webEnvironment) {
environment = new EnvironmentConverter(getClassLoader())
.convertToStandardEnvironmentIfNecessary(environment);
}
return environment;
}
其中,getOrCreateEnvironment方法会创建ConfigurableEnvironment对象,如果是web程序,那么创建一个StandardServletEnvironment对象并返回,如果不是web程序,那么创建一个StandardEnvironment对象并返回。
configureEnvironment(environment, applicationArguments.getSourceArgs());
listeners.environmentPrepared(environment);
上述是环境配置代码,第一行是配置环境参数、Profile等。第二行是把该环境配置到上述得到的监听器中,即发布一个ApplicationEnvironmentPreparedEvent事件,其实底层最终是通过事件广播器,依次调用每个ApplicationListener对象的onApplicationEvent方法,该功能将来单独分析学习。
if (!this.webEnvironment) {
environment = new EnvironmentConverter(getClassLoader())
.convertToStandardEnvironmentIfNecessary(environment);
}
return environment;
上述是检查当前环境是否是Web环境,如果不是就返回StandardEnvironment对象,如果是就原样返回。
3.5、 printBanner方法
该方法主要是默认或者自定义系统启动时的打印字样,不属于主要流程,本处不做深入学习,将来单独分析(里面涉及了一些设计模式的应用,值得学习)。
3.6、 createApplicationContext方法
代码如下:
protected ConfigurableApplicationContext createApplicationContext() {
Class<?> contextClass = this.applicationContextClass;
if (contextClass == null) {
try {
contextClass = Class.forName(this.webEnvironment
? DEFAULT_WEB_CONTEXT_CLASS : DEFAULT_CONTEXT_CLASS);
}
catch (ClassNotFoundException ex) {
throw new IllegalStateException(
"Unable create a default ApplicationContext, "
+ "please specify an ApplicationContextClass",
ex);
}
}
return (ConfigurableApplicationContext) BeanUtils.instantiate(contextClass);
}
该方法主要是创建ConfigurableApplicationContext对象。当applicationContextClass属性为null时,会根据是否是web环境分别创建org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext或者org.springframework.context.annotation.AnnotationConfigApplicationContext对象。如果applicationContextClass属性不为null时,根据该Class创建对应的对象,并强转成ConfigurableApplicationContext对象。
3.7、 FailureAnalyzers对象
该对象用于分析故障并提供可以显示给用户的诊断信息。并在handleRunFailure方法中进行异常分析处理。
3.8、 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
context.getBeanFactory().registerSingleton("springApplicationArguments",
applicationArguments);
if (printedBanner != null) {
context.getBeanFactory().registerSingleton("springBootBanner", printedBanner);
}
// Load the sources
Set<Object> sources = getSources();
Assert.notEmpty(sources, "Sources must not be empty");
load(context, sources.toArray(new Object[sources.size()]));
listeners.contextLoaded(context);
}
其中,context.setEnvironment(environment)是为上下午设置环境;postProcessApplicationContext(context);是为设置上下文的beanNameGenerator和resourceLoader(如果SpringApplication有的话);applyInitializers(context)是为了对上下文进行初始化,底层其实是调用ApplicationContextInitializer对象的initialize方法;listeners.contextPrepared(context),该方法和之前的environmentPrepared方法时类似的,只不过EventPublishingRunListener的contextPrepared方法是个空实现而已。
if (this.logStartupInfo) {
logStartupInfo(context.getParent() == null);
logStartupProfileInfo(context);
}
上述代码,是实现日志打印功能。
context.getBeanFactory().registerSingleton("springApplicationArguments",applicationArguments);
上述代码,是向上下文的beanFactory中注册一个singleton的实例bean,即ApplicationArguments对象。
if (printedBanner != null) {
context.getBeanFactory().registerSingleton("springBootBanner", printedBanner);
}
上述代码,是向上下文的beanFactory中注册一个singleton的实例bean,即PrintedBanner对象。
Set<Object> sources = getSources();
Assert.notEmpty(sources, "Sources must not be empty");
load(context, sources.toArray(new Object[sources.size()]));
上述代码,作用是注册启动类的bean定义,即调用SpringApplication.run(Application.class, args);的类。SpringApplication的load方法内会创建BeanDefinitionLoader的对象,并调用它的load()方法,最终通过BeanDefinitionLoader类的load方法实现类的注册,该方法中又通过this.annotatedReader.register(source);实现。其中,sources参数代表的即为调用SpringApplication.run(Application.class, args);的类。
listeners.contextLoaded(context);
@Override
public void contextLoaded(ConfigurableApplicationContext context) {
for (ApplicationListener<?> listener : this.application.getListeners()) {
if (listener instanceof ApplicationContextAware) {
((ApplicationContextAware) listener).setApplicationContext(context);
}
context.addApplicationListener(listener);
}
this.initialMulticaster.multicastEvent(
new ApplicationPreparedEvent(this.application, this.args, context));
}
上述代码中,调用contextLoaded方法,底层是调用了EventPublishingRunListener类的contextLoaded方法,该方法中通过创建ApplicationPreparedEvent事件对象,并广播出去,也就是调用ApplicationListener的onApplicationEvent方法。
3.9、 refreshContext(context)方法
代码如下:
private void refreshContext(ConfigurableApplicationContext context) {
refresh(context);
if (this.registerShutdownHook) {
try {
context.registerShutdownHook();
}
catch (AccessControlException ex) {
// Not allowed in some environments.
}
}
}
/**
* Refresh the underlying {@link ApplicationContext}.
* @param applicationContext the application context to refresh
*/
protected void refresh(ApplicationContext applicationContext) {
Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);
((AbstractApplicationContext) applicationContext).refresh();
}
@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();
}
}
}
通过上述代码,可以看出来,最终是调用了AbstractApplicationContext类(真正的实现类是EmbeddedWebApplicationContext)的refresh方法。该方法其实就是spring项目启动的一个核心方法,这里暂时不展开分析。其中,refresh方法中的onRefresh方法,最终调用了EmbeddedWebApplicationContext类的onRefresh方法,代码如下:
@Override
protected void onRefresh() {
super.onRefresh();
try {
createEmbeddedServletContainer();
}
catch (Throwable ex) {
throw new ApplicationContextException("Unable to start embedded container",
ex);
}
}
其中,createEmbeddedServletContainer方法实现了内嵌服务器的创建。
3.9、 listeners.finished(context, null)方法
该方法底层是调用了EventPublishingRunListener类的finished方法。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/68923.html