starter原理是什么?怎么实现的?
starter其实就是将需要依赖的jar包集合在里面,然后自动配置。这里主要用到的是springboot的自动配置功能。自己完全可以实现一个starter。
www.jianshu.com/p/bbf439c8a…
springboot的自动配置是怎么实现的?
- 启动类上都会加@SpringBootApplication,这个注解其实可以拆分为三个注解 SpringBootConfiguration,EnableAutoConfiguration,ComponentScan,其中EnableAutoConfiguration就是用于启动自动配置的;
- @Import提供,其导入的AutoConfigurationImportSelector的selectImports()方法通过SpringFactoriesLoader.loadFactoryNames()扫描所有具有META-INF/spring.factories的jar包。这一步将所有需要自动配置的类注入到spring容器。也就拿到了所有需要自动配置的类。
- 具体的XxxxAutoConfiguration是怎么拿到application.properties中我们的配置呢,例如:DataSourceAutoConfiguration
Configuration(
proxyBeanMethods = false
)
@ConditionalOnClass({DataSource.class, EmbeddedDatabaseType.class})
@EnableConfigurationProperties({DataSourceProperties.class})
@Import({DataSourcePoolMetadataProvidersConfiguration.class, DataSourceInitializationConfiguration.class})
public class DataSourceAutoConfiguration
@EnableConfigurationProperties({DataSourceProperties.class})将DataSourceProperties注入到spring容器,下面看下DataSourceProperties
@ConfigurationProperties(
prefix = "spring.datasource"
)
public class DataSourceProperties implements BeanClassLoaderAware, InitializingBean
@ConfigurationProperties将prefix = "spring.datasource"的配置参数都加载到对应的属性中,然后spring就拿到了一个配置好了的DataSourceProperties,再通过DataSourceAutoConfiguration把这些属性构造成DataSource所需要的所有bean注入到spring容器中。
Springboot 是如何省去web.xml?
当实现了Servlet3.0规范的容器(比如tomcat7及以上版本)启动时,通过SPI扩展机制自动扫描所有已添加的jar包下的META-INF/services/javax.servlet.ServletContainerInitializer中指定的全路径的类,并实例化该类,然后回调META-INF/services/javax.servlet.ServletContainerInitializer文件中指定的ServletContainerInitializer的实现类的onStartup方法。 如果该类存在@HandlesTypes注解,并且在**@HandlesTypes**注解中指定了我们感兴趣的类,所有实现了这个类的onStartup方法将会被调用。
下面看SpringServletContainerInitializer的代码,主要是调用WebApplicationInitializer的实现类的onStartup方法
@HandlesTypes(WebApplicationInitializer.class)
public class SpringServletContainerInitializer implements ServletContainerInitializer
WebApplicationInitializer的Springboot实现类SpringBootServletInitializer
public void onStartup(ServletContext servletContext) throws ServletException {
// Logger initialization is deferred in case an ordered
// LogServletContextInitializer is being used
this.logger = LogFactory.getLog(getClass());
WebApplicationContext rootAppContext = createRootApplicationContext(
servletContext);
if (rootAppContext != null) {
servletContext.addListener(new ContextLoaderListener(rootAppContext) {
@Override
public void contextInitialized(ServletContextEvent event) {
// no-op because the application context is already initialized
}
});
}
else {
this.logger.debug("No ContextLoaderListener registered, as "
+ "createRootApplicationContext() did not "
+ "return an application context");
}
}
protected WebApplicationContext createRootApplicationContext(
ServletContext servletContext) {
SpringApplicationBuilder builder = createSpringApplicationBuilder();
builder.main(getClass());
ApplicationContext parent = getExistingRootWebApplicationContext(servletContext);
if (parent != null) {
this.logger.info("Root context already created (using as parent).");
servletContext.setAttribute(
WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, null);
builder.initializers(new ParentContextApplicationContextInitializer(parent));
}
builder.initializers(
new ServletContextApplicationContextInitializer(servletContext));
builder.contextClass(AnnotationConfigServletWebServerApplicationContext.class);
builder = configure(builder);
builder.listeners(new WebEnvironmentPropertySourceInitializer(servletContext));
SpringApplication application = builder.build();
if (application.getAllSources().isEmpty() && AnnotationUtils
.findAnnotation(getClass(), Configuration.class) != null) {
application.addPrimarySources(Collections.singleton(getClass()));
}
Assert.state(!application.getAllSources().isEmpty(),
"No SpringApplication sources have been defined. Either override the "
+ "configure method or add an @Configuration annotation");
// Ensure error pages are registered
if (this.registerErrorPageFilter) {
application.addPrimarySources(
Collections.singleton(ErrorPageFilterConfiguration.class));
}
return run(application);
}
run方法就是我们@springapplication的main方法的run一样,一般我们用外置tomcat启动需要自定义一个ServletInitializer,这样就能找到主类了。
public class ServletInitializer extends SpringBootServletInitializer {
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(Bootstrap.class);
}
}
下面我们看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;
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);
//这个地方启动spring容器,并且启动了webSevlet容器内置tomcat,并将dispatchserverlet注册到容器中了,具体怎么注入的看DispatcherServletAutoConfiguration
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;
}
refreshContext(context)启动spring容器,并且启动了webSevlet容器内置tomcat,并将dispatchsevlet注册到容器中了,具体怎么注入的看DispatcherServletAutoConfiguration。dispatchsevlet绑定到tomcat是在创建tomcat实例的时候,也是在onRefresh()方法中。
总结一下:
- 外部tomcat启动
- 回调ServletContainerInitializer.onStartup(ServletContext)
- 调用所有WebApplicationInitializer的实现(SpringBootServletInitializer)
- 调用SpringApplication.run启动spring容器
- 将DispatcherServletRegistrationBean注册到spring容器,注意该类是ServletContextInitializer子类,onStartup方法中将servlet注册到ServletContext
- spring容器启动后,onRefresh方法中createWebServer(),该方法会调用所有的ServletContextInitializer的回调方法onStartup,从而将dispatchServlet注册到ServletContext。具体调用链:
onRefresh()-->createWebServer()-->getSelfInitializer().onStartup(servletContext)-->selfInitialize()-->getServletContextInitializerBeans()-->new ServletContextInitializerBeans(getBeanFactory()) --> addServletContextInitializerBeans
内部tomcat启动就是直接从第4步开始。
blog.csdn.net/choubayan43…
www.cnblogs.com/hello-shf/p…
blog.csdn.net/mnicsm/arti…
SpringBoot怎么将application.properties加载进内存的?
在容器创建之前会发布的ApplicationEnvironmentPreparedEvent,ConfigFileApplicationListener处理事件的时候会把application.yml配置信息加载进来