2020-9-23
源码调试
Spring Boot的 容器启动
版本: (v2.1.15.RELEASE)
org.springframework.boot.SpringApplication::run
- 实例自己
new SpringApplication(primarySources).run(args);执行 run方法
实例 run方法
public ConfigurableApplicationContext run(String... args) {//args是启动参数, 同 main参数
StopWatch stopWatch = new StopWatch();//计时工具 (用于启动计时间)
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
configureHeadlessProperty();
//事件分发器(整个springboot 应用的事件,例如 starting, contextPrepared ,failed ..)
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();//分发starting事件
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);//封装启动参数为对象
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);//封装环境配置为对象
configureIgnoreBeanInfo(environment);//configureIgnoreBeanInfo 跳过搜索BeanInfo类
Banner printedBanner = printBanner(environment);//打印 Banner
context = createApplicationContext();//创建 ApplicationContext (Spring容器) 生成Bean实例的工厂, 并且管理容器中的Bean; <!>这里面也创建了Servlet容器(tomcat)
exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
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;
}org.springframework.context.support.AbstractApplicationContext.refresh()
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");
// 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);
StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");
// Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
registerBeanPostProcessors(beanFactory);
beanPostProcess.end();
// 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();
contextRefresh.end();
}
}
}粗略的流程代码
BeanFactory 和 ApplicationContext 实例是: org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext
-
创建servlet 容器
org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.createWebServer() -
注册 BeanDefinition 最好在这打断点
org.springframework.beans.factory.support.DefaultListableBeanFactory.registerBeanDefinition(String, BeanDefinition)
加载data jpa(Hibernate)的过程
利用spring 中的SPI(Service Provider Interface)机制, 也就是约定在工程的 META-INFO目录下配置相关的文件, 比如spring.factories文件, 在项目启动的时候, 去扫描各个jar包及工程目录下META-INFO文件夹下的spring.factories 文件, 根据里面的配置, 加载相关的类, 这就是spring boot的核心装配思想.
有了上面的思路, 再来看spring data jpa是怎么自动装配进来的; 我们找到 spring-boot-autoconfigure这个jar包中的 spring.factories , 可以看到很多配置;
自动装配类
其自动装配类在: org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration
值得注意 org.springframework.boot.autoconfigure.orm.jpa.HibernateProperties,这个Bean,它就是 yaml hibernate的配置内容
@ConfigurationProperties("spring.jpa.hibernate")
public class HibernateProperties {
......配置入口
最终入口配置在 org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaConfiguration
class HibernateJpaConfiguration extends JpaBaseConfiguration {
HibernateJpaConfiguration(DataSource dataSource, JpaProperties jpaProperties,
ConfigurableListableBeanFactory beanFactory, ObjectProvider<JtaTransactionManager> jtaTransactionManager,
ObjectProvider<TransactionManagerCustomizers> transactionManagerCustomizers,
HibernateProperties hibernateProperties,
ObjectProvider<Collection<DataSourcePoolMetadataProvider>> metadataProviders,
ObjectProvider<SchemaManagementProvider> providers,
//通过ObjectProvider 注入, hibernate 物理命名策略,和隐式命名策略, 默认null
ObjectProvider<PhysicalNamingStrategy> physicalNamingStrategy,
ObjectProvider<ImplicitNamingStrategy> implicitNamingStrategy,
ObjectProvider<HibernatePropertiesCustomizer> hibernatePropertiesCustomizers) {
super(dataSource, jpaProperties, jtaTransactionManager, transactionManagerCustomizers);
this.hibernateProperties = hibernateProperties;
this.defaultDdlAutoProvider = new HibernateDefaultDdlAutoProvider(providers);
this.poolMetadataProvider = new CompositeDataSourcePoolMetadataProvider(metadataProviders.getIfAvailable());
this.hibernatePropertiesCustomizers = determineHibernatePropertiesCustomizers(
physicalNamingStrategy.getIfAvailable(), implicitNamingStrategy.getIfAvailable(), beanFactory,
hibernatePropertiesCustomizers.orderedStream().collect(Collectors.toList()));
}
......
关键在它的父类org.springframework.boot.autoconfigure.orm.jpa.JpaBaseConfiguration 创建了 jpaVendorAdapter JPA适配器
@Bean
@ConditionalOnMissingBean
public JpaVendorAdapter jpaVendorAdapter() {
AbstractJpaVendorAdapter adapter = createJpaVendorAdapter();//实现在 org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter
adapter.setShowSql(this.properties.isShowSql());
adapter.setDatabase(this.properties.determineDatabase(this.dataSource));
adapter.setDatabasePlatform(this.properties.getDatabasePlatform());
adapter.setGenerateDdl(this.properties.isGenerateDdl());
return adapter;
}
动态表/列名? 使用Hibernate 命名策略
The PhysicalNamingStrategy will be applied regardless of whether the attribute explicitly specified the column name or whether we determined that implicitly. The ImplicitNamingStrategy would only be applied if an explicit name was not given. So, it all depends on needs and intent.
无论该属性是显式指定列名还是隐式确定列名, 都将应用 PhysicalNamingStrategy; ImplicitNamingStrategy仅在未提供明确名称的情况下适用
hibernate 标准实现是: org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl .. 什么都没做.
so 动态表/列名, 在Spring boot 只需要定义个
PhysicalNamingStrategybean 即可
关于 ObjectProvider
可以理解为ObjectFactory 的变体, 它就是继承了 ObjectFactory
关于 FactoryBean 参考笔记: 关于 FactoryBean
// 返回指定类型的bean, 如果容器中不存在, 抛出NoSuchBeanDefinitionException异常
// 如果容器中有多个此类型的bean, 抛出NoUniqueBeanDefinitionException异常
T getObject(Object... args) throws BeansException;
// 如果指定类型的bean注册到容器中, 返回 bean 实例, 否则返回 null
@Nullable
T getIfAvailable() throws BeansException;
// 如果不可用或不唯一(没有指定primary)则返回null; 否则, 返回对象;
@Nullable
T getIfUnique() throws BeansException;作用是用于Bean 构造参数依赖的注入, 只需要定义泛型即可, Bean构造依赖时由Spring 自动包装为ObjectProvider<?>
例:
public FooService(ObjectProvider<FooRepository> repositoryProvider) {
this.repository = repositoryProvider.getIfUnique();
}这里只需要定义 FooRepository 即可, FooService 构造依赖时 由Spring 自动包装为ObjectProvider<FooRepository> 注入
Spring EL 表达式 关键对象
ExpressionParser
表示解析器, 默认实现是org.springframework.expression.spel.standard包中的SpelExpressionParser类, 使用parseExpression方法将字符串表达式转换为Expression对象, 对于ParserContext接口用于定义字符串表达式是不是模板, 及模板开始与结束字符
EvaluationContext
表示上下文环境, 默认实现是org.springframework.expression.spel.support包中的StandardEvaluationContext类, 使用setRootObject方法来设置根对象, 使用setVariable方法来注册自定义变量, 使用registerFunction来注册自定义函数等等;
Expression
表示表达式对象, 默认实现是org.springframework.expression.spel.standard包中的SpelExpression, 提供getValue方法用于获取表达式值, 提供setValue方法用于设置对象值;
关于线程上下文类加载器 Thread.currentThread().getClassLoader()
双亲委派模型并不能解决所有的类加载器问题, 比如,Java 提供了很多服务提供者接口( ServiceProviderInterface, SPI), 允许第三方为这些接口提供实现;
常见的 SPI 有 JDBC, JNDI, JAXP 等, 这些SPI的接口由核心类库提供, 却由第三方实现, 这样就存在一个问题:SPI 的接口是 Java 核心库的一部分, 是由BootstrapClassLoader加载的;
SPI实现的Java类一般是由AppClassLoader来加载的; BootstrapClassLoader是无法找到 SPI 的实现类的, 因为它只加载Java的核心库; 它也不能代理给AppClassLoader, 因为它是最顶层的类加载器; 也就是说, 双亲委派模型并不能解决这个问题;
类似这样的注解 @PreAuthorize("@el.check('menu:list')") 是调用Spring 容器里Bean名称是el的check方法;
值得看的一篇文博客
自动配置机制
实质上就是导入jar包,spring-boot启动的时候会找到 starter jar包中的resources/META-INF/spring.factories文件,根据spring.factories文件中的配置,找到需要自动配置的类。
2.7.x 后弃用从spring.factories加载自动配置类
通过spring.factories 加载自动配置类的方式成为过去式; 需要被自动加载的类写在META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports文件里,每一行是配置类的全类名。
为了向后兼容,spring.factories 加载自动配置类的方式依旧可用
踩坑指南 源码调试
过滤器链入口
tomcat 入口点
org.apache.catalina.core.ApplicationFilterChain的internalDoFilter 方法, tomcat 的入口点,
持有springSecurityFilterChain,类名是:org.springframework.web.filter.DelegatingFilterProxy
spring web 入口点
在 org.springframework.security.web.FilterChainProxy::doFilterInternal 处理 “热拔插” 得到”虚拟过滤链对象”VirtualFilterChain
过滤链调试
最终org.springframework.security.web.VirtualFilterChain::doFilter 责任链调用~ (需要调试Spring过滤链, 最好在这打断点)
Spring MVC的 servlet 入口
servlet 入口类
org.springframework.web.servlet.DispatcherServlet
分派逻辑方法 protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception
通过 handlerMappings列表 获取到HandlerMapping它包含对应的Controller的一系列信息
[参考笔记: DispatcherServlet 处理流程 ][./N_SpringMVC_src.md]