springboot自动装配原理
springboot启动类加@SpringBootApplication注解
@SpringBootApplication //启动自动装配功能
public class SpringbootApplication {}
@SpringBootApplication是个复合注解,引入了@EnableAutoConfiguration
//也就是@Configuration,将当前类申明为配置类
@SpringBootConfiguration
//启用自动配置
@EnableAutoConfiguration
//扫描当前包以及子包,将有@Component,@Controller,@Service,@Repository等注解的类注册到容器中
@ComponentScan(
excludeFilters = {@Filter(
type = FilterType.CUSTOM,
classes = {TypeExcludeFilter.class}
), @Filter(
type = FilterType.CUSTOM,
classes = {AutoConfigurationExcludeFilter.class}
)}
)
public @interface SpringBootApplication {}
@EnableAutoConfiguration引入了@Import
//@Import是Spring注解之一,用于在配置类中导入其他配置类或者普通的Java类
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {}
@Import导入了AutoConfigurationImportSelector类,AutoConfigurationImportSelector类实现了DeferredImportSelector接口
public class AutoConfigurationImportSelector implements DeferredImportSelector,
BeanClassLoaderAware, ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {}
重写了selectImports(),读取所有的spring.factories文件,过滤出AutoConfiguration类型的类
public String[] selectImports(AnnotationMetadata annotationMetadata) {
Enumeration urls = classLoader.getResources("META-INF/spring.factories");
}
spring.factories文件
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
省略
通过@ConditionalOn排除无效的自动配置类
@ConditionalOnClass({RabbitTemplate.class, Channel.class})
public class RabbitAutoConfiguration {}
spring整合mybatis
需要程序员手动将SqlSessionFactoryBean注入spring,由spring管理
@Configuration
public class MybatisConfiguration {
//创建SqlSessionFactoryBean对象,设置形参,Spring会自动去调用IOC容器中已有的数据源
@Bean
public SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource){
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(dataSource);
return sqlSessionFactoryBean;
}
}
springboot整合mybatis
mybatis-spring-boot-autoconfigure-2.2.2.jar,META-INF/spring.factories中
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
......,\org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration
springboot会自动装配需要的bean比如SqlSessionFactory,不需要程序员再一个个手动注入spring容器中
@ConditionalOnClass判断是否真正用到了mybatis,虽然可以自动装配SqlSessionFactory,但是只有当代码中真正用到mybatis时才会返回相应的bean
@ConditionalOnClass({ SqlSessionFactory.class, SqlSessionFactoryBean.class })
public class MybatisAutoConfiguration implements InitializingBean {
@Bean
@ConditionalOnMissingBean
public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {}
}
springboot启动原理?
9千字长文带你了解SpringBoot启动过程–史上最详细 SpringBoot启动流程-图文并茂
1,当启动springboot应用程序时,会先创建springApplication对象,在对象的构造方法中会进行某些参数初始化工作,主要是判断当前应用程序类型默认使用的是Servlet 容器,以及加载所有的初始化器和监听器,在这个过程中会加载整个应用程序中spring.factories文件,将文件内容放到缓存对象中,方便后续获取。
2,springApplication对象创建完成之后,开始执行run方法,来完成整个启动,启动过程中最主要的有两个方法,prepareContext和refreshContext,前面的处理逻辑包含了上下文对象的创建,banner的打印,实例化异常报告器等各种准备工作,方便后续来进行调用。
3,在prepareContext方法中完成上下文对象的初始化操作,包括属性值的设置,比如环境对象,其中load()将当前启动类作为一个beanDefintion注册到ioc容器中,方便后续在进行BeanFactoryPostProcessor调用执行的时候,找到对应的主类,来完成@SpringBootApplication,@EnableAutoConfiguration等注解解析工作。
4,在refreshContext方法中会进行整个容器刷新过程,来完成整个spring应用程序的启动,在自动装配过程中,在执行invokeBeanFactoryPostProcessors解析处理各种注解,包括@PropertySource,@ComponentScan,@Bean,@Import等注解,最主要的是@Import注解的解析
5,解析@Import注解的时候,会有一个getImports方法,从主类开始递归解析注解,把所有包含@Import的注解都解析到,调用deferredImportSelectorHandle中的process方法,来加载EnableAutoConfiguration
-----------------------创建springbootApplication对象--------------------
1,创建springbootApplication对象springboot容器初始化操作
2,获取当前应用的启动类型。
注1:通过判断当前classpath是否加载servlet类,返回servlet web启动方式。
注2:webApplicationType三种类型:
1.reactive:响应式启动(spring5新特性)
2.none:即不嵌入web容器启动(springboot放在外部服务器运行 )
3.servlet:基于web容器进行启动
3,读取springboot下的META-INFO/spring.factories文件,获取对应的ApplicationContextInitializer装配到集合
4,读取springboot下的META-INFO/spring.factories文件,获取对应的ApplicationListener装配到集合
5,mainApplicationClass,获取当前运行的主函数
------------------调用springbootApplication对象的run方法,实现启动,返回当前容器的上下文-------------------------
6,调用run方法启动
7,StopWatch stopWatch = new StopWatch(),记录项目启动时间
8,getRunListeners,读取META-INF/spring.factores,将SpringApplicationRunListeners类型存到集合中
监听器实现了SpringApplicationRunListener的类,用来加载配置文件
9,listeners.starting();循环调用starting方法
10,prepareEnvironment(listeners, applicationArguments);将配置文件读取到容器中读取多数据源:classpath:/,classpath:/config/,file:./,file:./config/底下。其中classpath是读取编译后的,file是读取编译的支持yml,yaml,xml,properties
11,Banner printedBanner = printBanner(environment);开始打印banner图,就是sprongboot启动最开头的图案
12,初始化AnnotationConfigServletWebServerApplicationContext对象
13,刷新上下文,调用注解,refreshContext(context);
14,创建tomcat
15,加载springmvc
16,刷新后的方法,空方法,给用户自定义重写afterRefresh()
17,stopWatch.stop();结束计时
18,使用广播和回调机制告诉监听者springboot容器已经启动化成功,listeners.started(context);
19,返回上下文
springboot启动原理详细步骤
1,运行 SpringApplication.run() 方法
@SpringBootApplication
public class App {
public static void main(String[] args) {
// 启动springboot
ConfigurableApplicationContext run = SpringApplication.run(App.class, args);
}
}
2,new 一个SpringApplication 对象,创建这个对象的构造函数做了一些准备工作
public static ConfigurableApplicationContext run(Class<?>[] primarySources,String[] args) {
return new SpringApplication(primarySources).run(args);
}
2.1,确定应用程序类型
在SpringApplication的构造方法内,首先会通过WebApplicationType.deduceFromClasspath()方法判断当前应用程序的容器,默认使用的是Servlet 容器,除了servlet之外,还有NONE 和 REACTIVE
static WebApplicationType deduceFromClasspath() {
if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)
&& !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
return WebApplicationType.REACTIVE;
}
for (String className : SERVLET_INDICATOR_CLASSES) {
if (!ClassUtils.isPresent(className, null)) {
return WebApplicationType.NONE;
}
}
return WebApplicationType.SERVLET;
}
2.2,加载所有的初始化器
从META-INF/spring.factories配置文件中加载初始化器
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
META-INF/spring.factories
# Initializers
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\
org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener
2.3,加载所有的监听器
加载监听器也是从META-INF/spring.factories 配置文件中加载的,与初始化器不同的是,监听器加载的是实现了ApplicationListener 接口的类
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
META-INF/spring.factories
# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.boot.autoconfigure.BackgroundPreinitializer
2.4,设置程序运行的主类
仅仅是找到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;
}
3,调用run()方法
3.1,开启计时器
// 实例化计时器
StopWatch stopWatch = new StopWatch();
// 开始计时
stopWatch.start();
3.2,将java.awt.headless设置为true
这里将java.awt.headless设置为true,表示运行在服务器端,在没有显示器器和鼠标键盘的模式下照样可以工作
private void configureHeadlessProperty() {
System.setProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS,
System.getProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS, Boolean.toString(this.headless)));
}
3.3,获取并启用监听器
创建所有 Spring 运行监听器并发布应用启动事件,启用监听器
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
3.4,设置应用程序参数
将执行run方法时传入的参数封装成一个对象
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
3.5,准备环境变量
准备环境变量,包含系统属性和用户配置的属性,将maven和系统的环境变量、yaml文件、property文件也加载进来了
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments) {
// Create and configure the environment
ConfigurableEnvironment environment = getOrCreateEnvironment();
configureEnvironment(environment, applicationArguments.getSourceArgs());
ConfigurationPropertySources.attach(environment);
listeners.environmentPrepared(environment);
bindToSpringApplication(environment);
if (!this.isCustomEnvironment) {
environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
deduceEnvironmentClass());
}
ConfigurationPropertySources.attach(environment);
return environment;
}
3.6,忽略bean信息
configureIgnoreBeanInfo(environment);
3.7,打印banner信息
Banner printedBanner = printBanner(environment);
打印效果
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.4.5)
3.8,创建应用程序的上下文
实例化应用程序的上下文, 调用 createApplicationContext() 方法,这里就是用反射创建上下文对象
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);
}
3.9,实例化异常报告器
异常报告器是用来捕捉启动过程抛出的异常使用的,当springboot应用程序在发生异常时,异常报告器会将其捕捉并做相应处理,在spring.factories 文件里配置了默认的异常报告器
Collection<SpringBootExceptionReporter> exceptionReporters =
getSpringFactoriesInstances(SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
META-INF/spring.factories
# Error Reporters
org.springframework.boot.SpringBootExceptionReporter=\
org.springframework.boot.diagnostics.FailureAnalyzers
3.10,准备上下文环境
将启动类作为配置类进行读取,主要是加载启动类上注解,将xml配置、注解的bean注册为BeanDefinition(bean的定义信息,在通过反射创建对象之前定义BeanDefinition,设置bean的属性信息)
BeanDefinition例如包含了以下属性值
<BeanDefinition id=? name=? classname=? depends-on=? initmethod=?>
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
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
if (printedBanner != null) {
beanFactory.registerSingleton("springBootBanner", printedBanner);
}
if (beanFactory instanceof DefaultListableBeanFactory) {
((DefaultListableBeanFactory) beanFactory)
.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
}
if (this.lazyInitialization) {
context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
}
// Load the sources
Set<Object> sources = getAllSources();
Assert.notEmpty(sources, "Sources must not be empty");
load(context, sources.toArray(new Object[0]));
listeners.contextLoaded(context);
}
3.11,实例化单例的beanName生成器
在 postProcessApplicationContext(context)方法里面。使用单例模式创建了BeanNameGenerator 对象,其实就是beanName生成器,用来生成bean对象的名称
3.12,执行初始化方法
执行2.2加载出来的所有初始化器,这些初始化器都实现了ApplicationContextInitializer接口的类
3.13,将启动参数注册到容器中
这里将启动参数以单例的模式注册到容器中,是为了以后方便拿来使用,参数的beanName 为 :springApplicationArguments
3.14,刷新上下文refresh
刷新上下文已经是spring的范畴了,自动装配和启动 tomcat就是在这个方法里面完成的,invokeBeanFactoryPostProcessor解析@Import加载所有的自动配置类
@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();
}
}
}
3.15,刷新上下文后置处理
afterRefresh 方法是启动后的一些处理,留给用户扩展使用,目前这个方法里面是空的
3.16,结束计时器
计时器会打印启动springboot的时长
3.17,发布上下文准备就绪事件
告诉应用程序,我已经准备好了,可以开始工作了
3.18,执行自定义的run方法
可以在启动完成后执行自定义的run方法,实现 ApplicationRunner 接口或实现 CommandLineRunner 接口
/**
* 自定义run方法的2种方式
*/
@Component
public class MyRunner implements ApplicationRunner, CommandLineRunner {
@Override
public void run(ApplicationArguments args) throws Exception {
System.out.println(" 我是自定义的run方法1,实现 ApplicationRunner 接口既可运行" );
}
@Override
public void run(String... args) throws Exception {
System.out.println(" 我是自定义的run方法2,实现 CommandLineRunner 接口既可运行" );
}
}
springboot的jar可以直接运行?
1,springboot提供了一个插件spring-boot-maven-plugin用于将程序打包成一个可执行jar
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
2,java -jar会找jar中mainfest文件,在里面找真正的启动类
3,jar启动main函数,创建一个LaunchedURLClassLoader来加载boot-lib下边的jar。
springboot内置Tomcat启动原理?
1,当我们添加了spring-boot-starter-web依赖时,在spring.factories中配置ServletWebServerFactoryAutoConfiguration配置类,通过@ConditionalOn相关注解选择到底应用哪个web容器,默认是tomcat
spring.factories:
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
//省略
org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration,\
@Import({ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class,
EmbeddedTomcat.class,
EmbeddedJetty.class,
EmbeddedUndertow.class})
public class ServletWebServerFactoryAutoConfiguration {}
2,SpringBoot启动的时候(SpringApplication.run)会创建Spring容器AnnotationConfigServletWebServerApplicationContext。
3,调用容器的refresh方法,加载ioc容器。解析spring.factories中自动配置类。
4,通过refresh里面调用getWebServerFactory方法。获得tomcat服务工厂。这个方法的作用就是去获取之前自动配置类当中配置的这个TomcatServletWebServerFactory。获取到之后就创建Bean,那么就有Bean实例了,之后返回。
@ConditionalOnClass({Servlet.class, Undertow.class, SslClientAuthMode.class})
@ConditionalOnMissingBean(
value = {ServletWebServerFactory.class},
search = SearchStrategy.CURRENT
)
static class EmbeddedTomcat {
@Bean
TomcatServletWebServerFactory tomcatServletWebServerFactory(ObjectProvider<TomcatConnectorCustomizer> connectorCustomizers, ObjectProvider<TomcatContextCustomizer> contextCustomizers, ObjectProvider<TomcatProtocolHandlerCustomizer<?>> protocolHandlerCustomizers) {
TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();
//省略
return factory;
}
}
5,调用getWebServer,这个方法就会启动内嵌的Tomcat。Tomcat挂起等待请求。
public WebServer getWebServer(ServletContextInitializer... initializers) {
//实例化 Tomcat,可以理解为 Server 组件
Tomcat tomcat = new Tomcat();
//启动tomcat并返回TomcatWebServer对象
return this.getTomcatWebServer(tomcat);
}
springboot核心注解?
1,@SpringBootApplication:这个注解标识了一个springboot工程,实际上他是一个复合注解。
2,@SpringBootConfiguration:这个注解实际就是一个@Configuration,表示启动类也是一个配置类
3,@EnableAutoConfiguration:向spring容器中导入了一个selector,用来加载classpath下springFactories中所定义的自动配置类,将这些自动加载到spring容器中。
4,@ConditionalOn也很关键,如果没有我们无法再自定义应用中进行订制开发
注解 | 含义 |
---|---|
@ConditionalOnBean | 当指定的bean在应用程序上下文存在时,才会启动被注解的组件或配置。用于基于其他bean的存在与否决定是否启动特定功能 |
@ConditionalOnMissingBean | 当指定bean在应用程序上下文不存在时,才会启动被注解的组件或配置。用于提供默认实现或避免重复创建bean |
@ConditionalOnClass | 当指定类位于类路径上时,才会启动被注解的组件或配置。用于根据类的可用性来决定是否启动某个特定功能 |
@ConditionalOnMissingClass | 当指定类不在类路径时,才会启动被注解的组件或配置。用于在某些类不可用时应用备用实现 |
@ConditionalOnExpression | 当指定表达式计算结果为true时,才会启动被注解的组件或配置。用于更复杂条件判断 |
@ConditionalOnProperty | 当指定属性满足条件时,才会启动被注解的组件或配置。用于基于配置属性的值来决定是否启动特定功能 |
springboot有哪些特性?
springboot是快速开发spring应用的一个脚手架,其设计目的是用来简化spring应用的初始搭建以及开发过程。
springboot提供了很多内置的starter结合自动配置,对主流框架无配置集成、开箱即用。
springboot简化了开发,采用JavaConfig的方式使用零xml的方式进行开发。
springboot内置web容器无需依赖外部web服务器,省略了web.xml,直接运行jar文件就可以启动web应用。
springboot帮我们管理常用的第三方依赖的版本,减少出现版本冲突的问题。
springboot自带的监控功能,可以监控应用程序的运行状况,或者内存、线程池、http请求统计等,同时还提供了优雅关闭应用程序等功能。
springboot读取配置文件原理?加载顺序?
通过事件监听的方式读取配置文件,ConfigFileApplicationListener
优先级:
resources/application.yml,
resources/config/application.yml,
resources/config/xxx/application.yml
classpath:application.yml
springboot默认日志框架?
logback
springboot为什么默认使用cglib动态代理?
1,虽然jdk动态代理在某些情况下可能比cglib动态代理略快,但差距通常不大。
2,jdk动态代理要求目标类必须实现一个接口,而cglib动态代理不需要。使得cglib动态代理更适用于没有接口的类,从而扩展了动态代理的适用范围。
3,springboot选择cglib动态代理可以使得类无需实现任何接口或继承特定的类,从而减少了对源代码的侵入性。
4,springboot默认提供了cglib的依赖,从而使应用程序使用cglib动态代理非常方便。
5,springboot设计上尽可能降低配置复杂度,使用cglib可以避免开发者在配置文件中显式声明要使用的代理类型。