1. SpringMVC执行流程
浅探SpringMVC中HandlerExecutionChain之handler、interceptor
2. IOC启动流程
参考
spring的启动是建筑在servlet容器之上的,所有web工程的初始位置就是web.xml,它配置了servlet的上下文(context)和监听器(Listener)。 web.xml中定义了 上下文监听器 ContextLoaderListener和前端控制器DispatcherServlet。
- 在启动项目时会触发ContextLoaderListener监听器的
contextInitialized()
上下文初始化方法。 - 然后ContextLoaderListener会调用父类 ContextLoader 的
initWebApplicationContext()
方法。 这是对ApplicationContext的初始化方法,也就是到这里正是进入了springIOC的初始化。 initWebApplicationContext()
方法的主要任务有三个:- 执行
createWebApplicationContext(servletContext)
方法即是完成创建WebApplicationContext工作 configureAndRefreshWebApplicationContext
方法用来 创建spring容器并初始化spring bean实例, 这个方法于封装ApplicationContext数据并且初始化所有相关Bean对象。 在这个方法中会调用refresh()
方法来实例化bean。- 最后完成ApplicationContext创建之后就是将其放入ServletContext中。
- 执行
总结1
-
首先,对于一个web应用,其部署在web容器中,web容器提供其一个全局的上下文环境,这个上下文就是ServletContext,其为后面的spring IoC容器提供宿主环境;
-
其 次,在web.xml中会提供有
contextLoaderListener
。在web容器启动时,会触发容器初始化事件,此时contextLoaderListener
会监听到这个事件,其contextInitialized()
方法会被调用,执行上下文初始化方法。 -
contextInitialized()
方法会调用父类 ContextLoader 的initWebApplicationContext()
方法。在这个方法中,spring会初始 化一个启动上下文,这个上下文被称为根上下文,即WebApplicationContext
,这是一个接口类,确切的说,其实际的实现类是XmlWebApplicationContext
。这个就是spring的IoC容器,其对应的Bean定义的配置由web.xml中的 context-param标签指定。 -
initWebApplicationContext()
方法的主要任务有三个:- 执行
createWebApplicationContext(servletContext)
方法即是完成创建WebApplicationContext工作 configureAndRefreshWebApplicationContext()
方法用来加载spring配置文件中的Bean实例。 这个方法于封装ApplicationContext数据并且初始化所有相关Bean对象。 configureAndRefreshWebApplicationContext()方法中调用最终初始化Bean的==refresh()==方法- 最后完成ApplicationContext创建之后,spring以
WebApplicationContext.ROOTWEBAPPLICATIONCONTEXTATTRIBUTE
为属性Key,将其存储到ServletContext中,便于获取。
- 执行
refresh()方法
public void refresh() throws BeansException, IllegalStateException {
synchronized(this.startupShutdownMonitor) {
this.prepareRefresh();
ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();
this.prepareBeanFactory(beanFactory);
try {
this.postProcessBeanFactory(beanFactory);
this.invokeBeanFactoryPostProcessors(beanFactory);
this.registerBeanPostProcessors(beanFactory);
this.initMessageSource();
this.initApplicationEventMulticaster();
this.onRefresh();
this.registerListeners();
this.finishBeanFactoryInitialization(beanFactory);
this.finishRefresh();
} catch (BeansException var9) {
if (this.logger.isWarnEnabled()) {
this.logger.warn("Exception encountered during context initialization - cancelling refresh attempt: " + var9);
}
this.destroyBeans();
this.cancelRefresh(var9);
throw var9;
} finally {
this.resetCommonCaches();
}
}
}
prepareRefresh();
刷新前的预处理obtainFreshBeanFactory();
创建了一个BeanFactoryprepareBeanFactory();
BeanFactory的预准备工作invokeBeanFactoryPostProcessors();
执行BeanFactory的后置处理器方法registerBeanPostProcessors();
注册BeanPostProcessor(Bean的后置处理器)initMessageSource();
初始化MessageSource组件initApplicationEventMulticaster();
初始化事件派发器onRefresh();
子类重写AbstractApplicationContext.onRefresh()这个方法,在容器刷新的时候可以自定义逻辑;registerListeners();
将所有项目里面的ApplicationListener注册到容器中finishBeanFactoryInitialization(beanFactory);
初始化所有剩下的单实例bean,这个方法里有初始化bean操作,即bean的生命周期finishRefresh();
完成
初始化Servlet
-
初始化Spring Web MVC使用的Web上下文,并且指定父容器为WebApplicationContext 。
contextLoaderListener
监听器初始化完毕后,开始初始化web.xml中配置的Servlet,这里是DispatcherServlet。DispatcherServlet上下文在初始化的时候会建立自己的IoC上下文,用以持有spring mvc相关的bean。在建立DispatcherServlet自己的IoC上下文时,会利用WebApplicationContext.ROOTWEBAPPLICATIONCONTEXTATTRIBUTE
先从ServletContext中获取之前的根上下文(即WebApplicationContext)作为自己上下文的parent上下文。 -
初始化DispatcherServlet使用的策略,如HandlerMapping、HandlerAdapter等。
protected void initStrategies(ApplicationContext context) {
initMultipartResolver(context);
initLocaleResolver(context);
initThemeResolver(context);
initHandlerMappings(context);
initHandlerAdapters(context);
initHandlerExceptionResolvers(context);
initRequestToViewNameTranslator(context);
initViewResolvers(context);
initFlashMapManager(context);
}
- 这样每个servlet 就持有自己的上下文,即拥有自己独立的bean空间,同时各个servlet共享相同的bean,即根上下文(第2步中初始化的上下文)定义的那些 bean。
3. Spring中Bean的创建过程
如下面的代码,doCreateBean()
方法实现了Bean生命周期的前三个阶段
// 忽略了无关代码
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
throws BeanCreationException {
// Instantiate the bean.
BeanWrapper instanceWrapper = null;
if (instanceWrapper == null) {
// 实例化阶段!
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
// Initialize the bean instance.
Object exposedObject = bean;
try {
// 属性赋值阶段!
populateBean(beanName, mbd, instanceWrapper);
// 初始化阶段!
exposedObject = initializeBean(beanName, exposedObject, mbd);
}
}
1. 实例化 createBeanInstance()
容器根据相应bean定义的BeanDefintion取得实例化信息,结合不同的实例化策略以及不同的bean定义类型,获得实例化完成的对象实例,以BeanWrapper对构造完成的对象实例进行包裹,返回相应的BeanWrapper实例。
容器在内部实现的时候,采用“策略模式(Strategy Pattern)”来决定采用何种方式初始化bean实例。
容器根据相应bean定义的BeanDefintion取得实例化信息,结合CglibSubclassingInstantiationStrategy以及不同的bean定义类型,就可以返回实例化完成的对象实例。
但是,返回方式上有些“点缀”。不是直接返回构造完成的对象实例,而是以BeanWrapper对构造完成的对象实例
进行包裹,返回相应的BeanWrapper实例。
使用BeanWrapper对bean实例操作很方便,可以免去直接使用Java反射API操作对象实例的烦琐。
2. 属性赋值 populateBean()
为什么实例化返回相应的BeanWrapper实例,而不是原对象实例?
- BeanWrapper定义继承了org.springframework.beans.PropertyAccessor接口,可以以统一的
方式对对象属性进行访问; - BeanWrapper定义同时又直接或者间接继承了PropertyEditorRegistry和TypeConverter接口 ,便于设置对象属性值、 转换类型
3. 设置相关依赖
-
使用BeanFactory容器,采用Aware接口设置相关依赖
当对象实例化完成并且相关属性以及依赖设置完成之后, Spring容器会检查当前对象实例是否实现了一系列的以Aware命名结尾的接口定义。如果是,则将这些Aware接口定义中规定的依赖注入给当前对象实例。
主要是
org.springframework.beans.factory.
包下的接口:BeanNameAware
、BeanClassLoaderAware
、BeanFactoryAware
等 -
使用ApplicationContext,则采用BeanPostProcesser方式设置相关依赖
主要是
org.springframework.context.
包下的接口,如:ResourceLoaderAware
、ApplicationEventPublisherAware
、MessageSourceAware
、ApplicationContextAware
BeanPostProcesser:
public interface BeanPostProcessor { //初始化之前执行 Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException; //初始化之后执行 Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException; }
BeanPostProcessor的两个方法中都传入了原来的对象实例的引用,这为我们扩展容器的对象实例化过程中的行为提供了极大的便利 。也可以自定义BeanPostProcessor 。
4. 初始化 initializeBean()
指定自定义的对象初始化操作,有两种方式:
- 实现
InitializingBean
接口 :在对象实例化过程调用过“BeanPostProcessor的前置处理”之后,会接着检测当前对象是否实现了InitializingBean接口,如果是,则会调用其afterPropertiesSet()方法进一步调整对象实例的状态。 - 使用< bean>的
init-method
属性: 通过init-method,系统中业务对象的自定义初始化操作可以以任何方式命名,而不再受制于InitializingBean的afterPropertiesSet()。
5. 销毁
与InitializingBean和init-method用于对象的自定义初始化相对应, DisposableBean和destroy-method为对象提供了执行自定义销毁逻辑的机会。
https://www.jianshu.com/p/1dec08d290c1
4. SpringBoot启动流程
参考:
1. 启动
Spring Boot程序有一个入口,就是main方法。main里面调用SpringApplication.run()启动整个Spring Boot程序
2. 构造SpringApplication实例
SpringBoot启动的时候,不论调用什么方法,都会构造一个SpringApplication的实例,然后调用这个实例的run方法,这样就表示启动SpringBoot。
在run方法调用之前,也就是构造SpringApplication的时候会进行初始化的工作,初始化的时候会做以下几件事:
-
this.webApplicationType = WebApplicationType.deduceFromClasspath();
判断 应用程序 类型: SpringBoot将应用程序分为三种类型:
Reactive
(响应式web类型) 、Servlet
(web类型)、None
(非web类型)。 -
this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
从spring.factories文件中找出所有的初始化器, 创建并初始化ApplicationContextInitializer列表 -
this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
从spring.factories文件中找出所有的应用程序监听器, 创建并初始化 ApplicationListener列表 列表 -
this.mainApplicationClass = this.deduceMainApplicationClass();
找出运行的主类(main class)
3. 执行run()方法
- 获取并启动 事件监听器 SpringApplicationRunListener
SpringApplicationRunListeners listeners = this.getRunListeners(args);
listeners.starting();
- 创建并配置当前Spring Boot应用将要使用的Environment
ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
this.configureIgnoreBeanInfo(environment);
-
构造Spring容器(ApplicationContext)
-
context = this.createApplicationContext();
根据应用程序类型创建上下文 -
this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
准备上下文 -
this.refreshContext(context);
刷新上下文, 这个是spring启动的代码该方法会调用 AbstractApplicationContext - >refresh()方法,该方法就是IOC容器启动的代码。和SSM项目中IOC容器启动不一样的地方是,refresh()方法中的 onRefresh() 方法会创建内置容器,如tomcat。
-
this.afterRefresh(context, applicationArguments);
上下文刷新完毕
-
-
listeners.started(context);
遍历执行SpringApplicationRunListener的finished()方法
5. SpringBoot自动配置原理
具体看这篇文章《Spring Boot自动配置原理、实战》。
注解 @EnableAutoConfiguration, @Configuration, @ConditionalOnClass 就是自动配置的核心,首先它得是一个配置文件,其次根据类路径下是否有这个类去自动配置。
1. 配置文件
Spring Boot有一个全局配置文件:application.properties或application.yml。 我们的各种属性都可以在这个文件中进行配置,最常配置的比如:server.port、logging.level.* 等等。
那么问题来了:这些配置是如何在Spring Boot项目中生效的呢?
2. 加载自动配置类到容器
Spring Boot关于自动配置的源码在spring-boot-autoconfigure-x.x.x.x.jar中:
Spring Boot的启动类上有一个@SpringBootApplication
注解,这个注解是Spring Boot项目必不可少的注解。 @SpringBootApplication
是一个复合注解或派生注解, 它包括@SpringBootConfiguration
、@EnableAutoConfiguration和@ComponentScan
。
@EnableAutoConfiguration注解也是一个派生注解,其中的关键功能由==@Import==提供,其导入的AutoConfigurationImportSelector的selectImports()方法通过SpringFactoriesLoader.loadFactoryNames()扫描所有具有META-INF/spring.factories的jar包。 扫描所有具有META-INF/spring.factories的jar包。spring-boot-autoconfigure-x.x.x.x.jar里就有一个这样的spring.factories文件。
AutoConfigurationImportSelector
源码如下
这个spring.factories文件也是一组一组的key=value的形式,其中有一个KV对,key是EnableAutoConfiguration类的全类名,而它的value是一个以AutoConfiguration结尾的类名的列表,这些类名以逗号分隔,如下图所示:
这个@EnableAutoConfiguration注解通过@SpringBootApplication被间接的标记在了Spring Boot的启动类上。在SpringApplication.run(…)的内部就会执行selectImports()方法,找到所有JavaConfig自动配置类的全限定名对应的class,然后将所有自动配置类加载到Spring容器中。
3. 自动配置生效
每一个AutoConfiguration结尾自动配置类都是在某些条件之下才会生效的,这些条件的限制在Spring Boot中以注解的形式体现,常见的条件注解有如下几项:
@ConditionalOnBean:当容器里有指定的bean的条件下。
@ConditionalOnMissingBean:当容器里不存在指定bean的条件下。
@ConditionalOnClass:当类路径下有指定类的条件下。
@ConditionalOnMissingClass:当类路径下不存在指定类的条件下。
@ConditionalOnProperty:指定的属性是否有指定的值,比如@ConditionalOnProperties(prefix=”xxx.xxx”, value=”enable”, matchIfMissing=true),代表当xxx.xxx为enable时条件的布尔值为true,如果没有设置的情况下也为true。
以ServletWebServerFactoryAutoConfiguration
为例
在ServletWebServerFactoryAutoConfiguration类上,有一个@EnableConfigurationProperties注解:开启配置属性,而它后面的参数是一个ServerProperties类,这就是习惯优于配置的最终落地点。
在这个类上,我们看到了一个非常熟悉的注解:@ConfigurationProperties,它的作用就是从配置文件中绑定属性到对应的bean上,而@EnableConfigurationProperties负责导入这个已经绑定了属性的bean到spring容器中。那么所有其他的和这个类相关的属性都可以在全局配置文件中定义,也就是说,真正“限制”我们可以在全局配置文件中配置哪些属性的类就是这些XxxxProperties类,它与配置文件中定义的prefix关键字开头的一组属性是唯一对应的。
至此,我们大致可以了解。在全局配置的属性如:server.port等,通过@ConfigurationProperties注解,绑定到对应的XxxxProperties配置实体类上封装为一个bean,然后再通过@EnableConfigurationProperties注解导入到Spring容器中。
而诸多的XxxxAutoConfiguration自动配置类,就是Spring容器的JavaConfig形式,作用就是为Spring 容器导入bean,而所有导入的bean所需要的属性都通过xxxxProperties的bean来获得。
4.总结
- Spring Boot启动的时候会通过@EnableAutoConfiguration注解找到META-INF/spring.factories配置文件中的所有自动配置类,并对其进行加载
- 而这些自动配置类都是以AutoConfiguration结尾来命名的,它实际上就是一个JavaConfig形式的Spring容器配置类
- XxxxProperties类是通过@ConfigurationProperties注解与全局配置文件中对应的属性进行绑定的。
- 名字以AutoConfiguration结尾的自动配置类能通过Properties类获取到这些属性如:server.port
6. Mybatis执行流程
public static void addStudent(){
Student stu = new Student(3,"lc",19,"1");
//1
InputStream stream = Student.class.getClassLoader().getResourceAsStream("conf.xml");
//2
SqlSessionFactory ssf=new SqlSessionFactoryBuilder().build(stream);
//3
SqlSession session=ssf.openSession();
//4
StudentMapper studentMapper = session.getMapper(StudentMapper.class);
//5
studentMapper.addStudent(stu);
session.commit();
session.close();
}
[执行流程] https://blog.youkuaiyun.com/qq_38270106/article/details/93398694 :
-
加载Mybatis配置文件(两种方式:使用类加载器加载, 或使用Resources类加载),将配置文件里的信息解析成一个IO流。
-
构建sqlSession的工厂:新建一个SqlSessionFactoryBuilder对象,并调用build()方法,将IO流传入其中,将解析到的信息包成一个Configuration对象,使用这个对象创建一个SqlSessionFactory并返回。
-
创建能执行映射文件中sql的sqlSession:使用SqlSessionFactory的operSession()方法创建sqlSession,其中openSession方法中,
- 根据Configuration对象获取Environment信息, transactionFactory 使用该环境产生事务,
- 根据Configuration对象调用 Executor 执行器,默认是Simple Executor 。
在构建sqlSession过程中,需要创建 transaction和executor用于后续执行操作。
-
jdk动态代理生成mapper接口的代理对象
-
通过第4步返回的代理对象,执行相应的方法。
- 先判断sql语句的类型
- 如果是查询语句,使用CachingExecutor的query方法去查二级缓存,没有二级缓存的话,去查一级缓存 localCache,一级缓存也没有, 走queryFromDatabase方法查数据库 ,查到后放入一级缓存。