1.这2天学了一下spring boot,为了扫盲spring cloud,记录学习心得。
2. 第一天简单回答几个问题:
(1)spring boot是如何内嵌tomcat容器的。
解答:这个我没看代码也大概猜到了,因为以前看ClassPathXmlApplicationContext源码的时候,AbstractApplicationContext的refresh()方法是有很多预留的扩展方法的,不出所料,spring boot正式通过AnnotationConfigEmbeddedWebApplicationContext类实现onRefresh()的方式内嵌tomcat容器的
AnnotationConfigEmbeddedWebApplicationContext类实现onRefresh(),默认取得是TomcatEmbeddedServletContainer,可以切换到jetty 。
切换到jetty也很简单:
(2)spring boot是如何注册bean到容器里面的:
解答:一般情况下,DefaultListableBeanFactory或者ClassPathXmlApplicationContext注册bean到容器都是通过解析XML默认标签和自定义标签的方式注册bean到容器里面的,最终存到DefaultListableBeanFactory的Map<String, BeanDefinition> beanDefinitionMap =new ConcurrentHashMap<String, BeanDefinition>(256);对象中的,而springboot是通过
"org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext"注册bean到容器中的,具体源码如下:
核心代码: load(context, sources.toArray(new Object[sources.size()]));
2. 第一天简单回答几个问题:
(1)spring boot是如何内嵌tomcat容器的。
解答:这个我没看代码也大概猜到了,因为以前看ClassPathXmlApplicationContext源码的时候,AbstractApplicationContext的refresh()方法是有很多预留的扩展方法的,不出所料,spring boot正式通过AnnotationConfigEmbeddedWebApplicationContext类实现onRefresh()的方式内嵌tomcat容器的
@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();
}
}
}
AnnotationConfigEmbeddedWebApplicationContext类实现onRefresh(),默认取得是TomcatEmbeddedServletContainer,可以切换到jetty 。
@Override
protected void onRefresh() {
super.onRefresh();
try {
createEmbeddedServletContainer();
}
catch (Throwable ex) {
throw new ApplicationContextException("Unable to start embedded container",
ex);
}
}
/**
* Create a new {@link TomcatEmbeddedServletContainer} instance.
* @param tomcat the underlying Tomcat server
* @param autoStart if the server should be started
*/
public TomcatEmbeddedServletContainer(Tomcat tomcat, boolean autoStart) {
Assert.notNull(tomcat, "Tomcat Server must not be null");
this.tomcat = tomcat;
this.autoStart = autoStart;
initialize();
}
private synchronized void initialize() throws EmbeddedServletContainerException {
TomcatEmbeddedServletContainer.logger
.info("Tomcat initialized with port(s): " + getPortsDescription(false));
try {
addInstanceIdToEngineName();
// Remove service connectors to that protocol binding doesn't happen yet
removeServiceConnectors();
// Start the server to trigger initialization listeners
this.tomcat.start();
// We can re-throw failure exception directly in the main thread
rethrowDeferredStartupExceptions();
// Unlike Jetty, all Tomcat threads are daemon threads. We create a
// blocking non-daemon to stop immediate shutdown
startDaemonAwaitThread();
}
catch (Exception ex) {
throw new EmbeddedServletContainerException("Unable to start embedded Tomcat",
ex);
}
}
切换到jetty也很简单:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<!-- 排除默认的tomcat,引入jetty容器. -->
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- jetty 容器. -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jetty</artifactId>
</dependency>
(2)spring boot是如何注册bean到容器里面的:
解答:一般情况下,DefaultListableBeanFactory或者ClassPathXmlApplicationContext注册bean到容器都是通过解析XML默认标签和自定义标签的方式注册bean到容器里面的,最终存到DefaultListableBeanFactory的Map<String, BeanDefinition> beanDefinitionMap =new ConcurrentHashMap<String, BeanDefinition>(256);对象中的,而springboot是通过
"org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext"注册bean到容器中的,具体源码如下:
核心代码: load(context, sources.toArray(new Object[sources.size()]));
private ConfigurableApplicationContext createAndRefreshContext(
SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments) {
ConfigurableApplicationContext context;
// Create and configure the environment
ConfigurableEnvironment environment = getOrCreateEnvironment();
configureEnvironment(environment, applicationArguments.getSourceArgs());
listeners.environmentPrepared(environment);
if (isWebEnvironment(environment) && !this.webEnvironment) {
environment = convertToStandardEnvironment(environment);
}
if (this.bannerMode != Banner.Mode.OFF) {
printBanner(environment);
}
// Create, load, refresh and run the ApplicationContext
context = createApplicationContext();
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);
// 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);
// Refresh the context
refresh(context);
if (this.registerShutdownHook) {
try {
context.registerShutdownHook();
}
catch (AccessControlException ex) {
// Not allowed in some environments.
}
}
return context;
}