Spring笔记
什么是Spring?
应该从容器和生态方面作答
什么是开闭原则:
给系统做功能扩展时,不应该修改已经写好的代码。即对扩展开放,对修改关闭
为什么要使用容器?
容器的核心功能是用于统一规范,只要符合规范的对象都可以放在容器中统一管理
Autowired和Resource的区别?
两者类似于JDBC和JPA的关系,Autowired是spring提供的注解,默认按照类型注入。Resource是j2ee提供的,默认按照名称注入
Spring中有哪些核心模块?
Spring并不是一个模块,而是一堆模块的集合,例如 Spring Core(核心类库,提供IOC功能),AOP(提供aop功能),MVC(web应用支持)等
Spring AOP的理解
能够提供哪些与业务无关的功能,降低模块的耦合度,有利于代码的扩展和维护。SpringAOP是基于动态代理的,如果要代理的对象实现了某个接口,则会通过JDK的动态代理去创建代理对象,如果没有实现指定接口,则使用CGlib动态代理生成一个被代理对象的子类作为代理对象。当然,SpringAOP也继承了AspectJ
Spring AOP 和 AspectJ AOP 的区别?
AOP是一种思想,这两者就是它的实现。SpringAOP基于动态代理,属于运行时增强,而AspectAOP基于字节码操作,属于编译时增强。
对Spring IOC的理解
对象生命周期管理权的转移,用于解耦,它是整个Spring的基础核心
Bean的创建过程?
Bean的创建过程大致为:根据配置文件或注解生成BeanDefinition —> 执行 BeanFactory 的后置处理器(钩子,对BeanDefinition做修改或增强) —> 实例化对象 —> 填充属性(populate方法) —> 设置Aware接口 —>执行 Bean 创建的前置处理器 —> 执行 init-method 方法 —> 执行 Bean 创建的后置处理器 —> 创建完成
设置 Aware 接口有什么用?
Bean在创建时,有时可能需要知道一些关于容器的信息,例如可能需要知道BeanName,或者容器中其他的Bean,总之就是需要知道和容器相关的信息,但是Bean中并没有这些信息(这个Bean就是你需要交给Spring管理的类,你的类肯定没有容器相关的信息),此时就可以让该Bean实现某个 Aware 接口,例如实现 BeanNameAware 接口,该接口有一个 setBeanName(String name) 的方法,当Spring创建该Bean时,就会自动调用该接口并传入BeanName。这些Aware接口在业务开发中基本用不到,但如果要写一些BeanFactoryPostProcessor 或者 BeanPostProcessor 则很有可能需要实现Aware接口,因为这些PostProcessor其实也是Bean,也需要放到BeanFactory中才能生效。
使用@autowired注解也能实现 Aware 接口的功能,两者有什么区别?
对Aware接口的支持属于原始Spring容器本身的生命周期的过程,而对autowired注解的支持则需要添加特定的bean后置处理器实现的扩展功能,如果没有该后置处理器,则autowired也不会生效,下文中有对autowired注解原理做解释
BeanFactory 和 FactoryBean 的区别?
BeanFactory实际上就是整个IOC容器,它是一个顶层接口,也是IOC最基础的接口,它注意提供各种 getBean() 的方法重载。FactoryBean 主要也是提供 getObject() 的方法,两者实质上都是可以获取Bean实例的。区别在于,FactoryBean本质上是BeanFactory管理的一个Bean,只不过它比较特殊,当调用BeanFactory的getBean()方法时,并不是直接得到FactoryBean,而是会自动调用FactoryBean的getObject()得到Bean。如果FactoryBean中getSingolon()方法设置返回true,Spring在首次调用getObject()方法后会将得到的对象缓存到 factoryBeanObjectCache 集合(注意,不是三级缓存)中,下次直接从这里面拿。不过 FactoryBean 与 @Bean 注解的功能相同,已经被淘汰
如何解决循环依赖的问题?
核心在于要将对象的实例化(创建空对象)和初始化(填充属性,执行init-method)分开。需要注意的是,这里的实例化指的是一个完整的new对象的过程,而非只是开辟内存赋零值的过程,它也是执行了init方法的,而后面的初始化实际上指的是根据Autowire自动注入的那些字段的初始化
AbstractApplicationContext#refresh()中的关键方法:
prepareRefresh(): 创建BeanFactory的准备工作,例如获取Environment(系统参数、用户指定参数等),创建监听器和时间的集合对象
obtainFreshBeanFactory(): 创建新的BeanFactory(DefaultListableBeanFactory),它内部也包含很多重要的方法:
customizeBeanFactory(beanFactory): 用于在创建完BeanFactory之后进行定制修改,例如修改是否支持循环依赖等
loadBeanDefinitions(beanFactory): 使用xml配置文件或者注解配置的Bean会被解析为BeanDefinitions,它就好比是建造大楼的蓝图,该方法的作用就是加载它。这里使用了适配器模式,即虽然beanFactory传递给了AbstractXmlApplicationContext,但其内部实际又传递给了 XmlBeanDefinitionReader 对象,真正发挥功能的是后面这个
源码分析
以下记录于:https://www.bilibili.com/video/BV1P44y1N7QG
ClassPathXmlApplicationContext 的继承关系如下:
面试中常问的ApplicationContext和BeanFactory的关系为:ApplicationContext实现了BeanFactory接口并扩展了很多其他的功能,例如国际化(通过实现MessageSource接口实现)、获取系统参数(通过继承DefaultResourceLoader抽象类实现)、消息的订阅与发送(通过实现ApplicationEventPublisher接口实现),下面是消息订阅与发送的一个例子:
package com.example.demo;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.event.EventListener;
public class DemoApplication {
public static void main(String[] args) {
// 创建一个可以根据 @Configuration 注解生成 BeanDefinition 的BeanFactory
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(Messager.class);
Sender sender = (Sender) applicationContext.getBean("sender");
sender.send();
}
}
@Configuration
class Messager {
// 使用 @Bean 注解的方法得到的返回值会被Spring容器管理
@Bean
public Sender sender() {
return new Sender();
}
@Bean
public Receiver receiver() {
return new Receiver();
}
}
// 这里顺便可以演示 Aware 接口的用法:向Bean中注入容器内部对象
class Sender implements ApplicationContextAware {
private ApplicationContext applicationContext;
public void send() {
applicationContext.publishEvent(new ApplicationEvent("bingo") {
@Override
public Object getSource() {
return super.getSource();
}
});
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}
class Receiver {
@EventListener
public void receive(ApplicationEvent event) {
System.out.println("receive msg: " + event.getSource()); // 当调用 sender.send() 方法后,这个方法会自动执行,需要注意的是,SpringBoot启动时其他的一些Bean发送的消息也会被该方法监听
}
}
BeanFactory是Spring最原始的容器,它的功能很简单,仅仅是在Bean的生命周期过程中加上了很多钩子,它并不能去解析xml加载Bean,也不能识别Autowired等注解,它能做的仅仅是你手动创建一个BeanDefinition注册进BeanFactory后,它根据该BeanDefinition去创建对象加入到容器中。
如果要实现解析xml、解析各种注解,就需要向BeanFactory中添加各种各样的后置处理器。
例如,为了实现解析 @Configuration 的注解(该注解的功能是将其内部使用@Bean的方法返回值存放到BeanFactory中),需要向BeanFactory添加特定的 BeanFactoryPostProcesser,它能使得BeanFactory具备扫描@Configuration注解的功能,并自动生成 BeanDefinition。
为了实现 @Autowired 注解的功能,也需要向BeanFactory中添加特定的 BeanPostProcesser,它使得Bean在创建过程中,能够扫描内部被 @Autowired 注解标记的属性并填充
Autowired 会先根据类型查找,如果有多个同类型的bean存在,则再按照名称进行查找
如果一个属性既加了 Autowired 注解 也加了 Resource 注解,则会按照添加的后置处理器顺序来解析,例如如果先加的是 Autowired 后置处理器,则会使用Autowired注解
各种ApplicationContext的实现(例如 ClassPathXmlApplicationContext)就在实现了BeanFactory基本的功能外增加了很多后置处理器和扩展的功能。
public static void main(String[] args) {
// 创建一个最简单的BeanFactory的实现类
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
// 为了向 beanFactory 中添加由xml配置的 BeanDefinition 对象,可以使用XmlBeanDefinitionReader去解析xml文件,其他获取 BeanDefinition 的方式同理
XmlBeanDefinitionReader xmlBeanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
xmlBeanDefinitionReader.loadBeanDefinitions("xxx.xml"); // 指定xml文件路径
}
使用BeanFactory创建一个web容器
package com.example.demo;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.boot.autoconfigure.web.servlet.DispatcherServletRegistrationBean;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext;
import org.springframework.boot.web.servlet.server.ServletWebServerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.DispatcherServlet;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;
public class DemoApplication {
public static void main(String[] args) {
// 创建一个可以根据 @Configuration 注解生成 BeanDefinition 并且具有 Web server 环境的BeanFactory
AnnotationConfigServletWebServerApplicationContext applicationContext = new AnnotationConfigServletWebServerApplicationContext(Server.class);
}
}
@Configuration
class Server {
// 向Spring容器中添加一个 servlet 容器
@Bean
public ServletWebServerFactory servletWebServerFactory() {
return new TomcatServletWebServerFactory();
}
// 向Spring容器中添加一个 dispatcher,所有的请求都会先通过该 dispatcher 处理并分发
@Bean
public DispatcherServlet dispatcherServlet() {
return new DispatcherServlet();
}
// 将dispatcher注册进 servlet 容器,也是通过一个具有该功能的类实现的,将该类添加到Spring容器
@Bean
public DispatcherServletRegistrationBean registrationBean(DispatcherServlet dispatcherServlet) {
return new DispatcherServletRegistrationBean(dispatcherServlet, "/");
}
// 添加一个Controller,如果 @Bean 的name参数以 / 开头,则会自动解析为url地址
@Bean("/bingo")
public Controller controller1() {
return new Controller() {
@Override
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
response.getWriter().print("bingo");
return null;
}
};
}
}
模板方法模式
假如我要从头实现BeanFactory的功能时:
class BeanFactory{
public Object getBean() {
Object bean = instantBean(); // bean 实例化
initBean(bean); // bean 初始化
return bean;
}
}
这是这个流程的核心代码逻辑,但如果后续需要在实例化Bean或者初始化Bean前后添加一些功能,例如支持Autowired注解,则需要重新修改代码,一个好的方案是,提供一个PostProcesser接口,并在BeanFactory中创建一个该接口的集合,在getBean()方法中调用该集合中的函数,总之,就是先创建好一个流程模板,将动态的部分抽取成一个接口:
class BeanFactory{
// 这里以 initBean() 方法的前/后置处理器举例
private List<InitPostProcesses> initPostProcesses;
public Object getBean() {
Object bean = instantBean(); // bean 实例化
// 执行所有的init前置处理器
for (InitPostProcesses initPostProcess : initPostProcesses) {
bean = initPostProcess.beforeInit(bean);
}
initBean(bean); // bean 初始化
// 执行所有的init后置处理器
for (InitPostProcesses initPostProcess : initPostProcesses) {
bean = initPostProcess.afterInit(bean);
}
return bean;
}
interface InitPostProcesses {
Object beforeInit(Object bean);
Object afterInit(Object bean);
}
public static void main(String[] args) {
BeanFactory beanFactory = new BeanFactory();
demoApplication.initPostProcesses.add(new InitPostProcesses() {
@Override
public Object beforeInit(Object bean) {
// bean初始化前的操作
return bean;
}
@Override
public Object afterInit(Object bean) {
// 代理可以在这里实现
return bean;
}
});
demoApplication.getBean();
}
}
Autowired注解如何生效的?
这里以Autowired注解实现方式举例,其他Spring的一些功能也差不多是这样实现的
Autowired注解生效的本质是因为添加了 AutowiredAnnotationBeanPostProcessor 后处理器,它实际上是在bean实例化后的一个后置处理器:
下面的代码创建了一个基本的BeanFactory,并向里面注册了一个Bean对象,该Bean对象属性中有Autowired注解,但由于此时没有添加任何后置处理器,所以BeanFactory并不能识别该注解,所以b属性为null
package com.example.demo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.support.GenericApplicationContext;
public class DemoApplication {
public static void main(String[] args) {
# GenericApplicationContext也是一个ApplicationContext,但它相对其他的ApplicationContext更干净,它不会自动添加额外的后置处理器
GenericApplicationContext applicationContext = new GenericApplicationContext();
applicationContext.registerBean("a", A.class);
applicationContext.registerBean("b", B.class); // 注意,这里只是将b对象放到容器中,并不是给a对象中的b属性赋值
applicationContext.refresh();
A a = (A) applicationContext.getBean("a");
a.printB(); // 此时打印为 null,因为 BeanFactory 中现在没有任何后置处理器能处理 @Autowired 注解
}
}
class A {
@Autowired
private B b;
public void printB() {
System.out.println(b);
}
}
class B {
}
解决方法是添加一个AutowiredAnnotationBeanPostProcessor后置处理器,此时A中的b属性就有值了:
...
GenericApplicationContext applicationContext = new GenericApplicationContext();
applicationContext.registerBean("autowired", AutowiredAnnotationBeanPostProcessor.class); // 添加能够解析Autowired注解的后置处理器,对于这种特殊的bean,不指定beanName也行
applicationContext.registerBean("a", A.class);
...
其他的一些 BeanFactory 后置处理器解析注解,例如:
-
ConfigurationClassPostProcessor:用于解析 @ComponentScan、@Bean、@Import、@ImportResource等注解
-
CommonAnnotationBeanPostProcessor:@Resource、@PostConstruct等注解
一种@autowired失效的情况
class A implements BeanFactoryPostProcessor {
private B b;
@Autowired
public void setB(B b) {
System.out.println("set B");
this.b = b;
}
public void printB() {
System.out.println(b);
}
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
System.out.println("执行 postProcessBeanFactory");
}
public static void main(String[] args) {
GenericApplicationContext applicationContext = new GenericApplicationContext();
applicationContext.registerBean("autowired", AutowiredAnnotationBeanPostProcessor.class); // 添加能够解析Autowired注解的后置处理器
applicationContext.registerBean("b", B.class); // 注意,这里只是将b对象放到容器中,并不是给a对象中的b属性赋值
applicationContext.registerBean("a", A.class);
applicationContext.refresh();
A a = (A) applicationContext.getBean("a");
a.printB(); // ”执行 postProcessBeanFactory“ 打印了,但是 b 却为null,没有注入进去
}
}
上例中,我确实在 applicationContext 中添加了解析 autowired 注解的bean后置处理器,但a对象中的b属性并没有被成功注入,问题的原因在于A类实现了 BeanFactoryPostProcessor 接口,只要将该接口去掉,b就能被成功注入。
这是因为:Spring容器在启动过程中,会优先将 BeanFactory 后置处理器先加载,然后才会加载 Bean 后置处理器。而当一个Bean实现了BeanFactoryPostProcessor接口,它就是BeanFactory 后置处理器。而Autowired后置处理器属于 Bean 后置处理器,也就是说它在加载 Autowired 后置处理器之前就已经将 A 创建好并放到Spring容器中了,所以它内部的Autowired就不会生效。
同理,如果在 @Configuration 标注的类中,使用 @Bean 标记了一个返回 BeanFactory 后置处理器的类,那么这个@Configuration 标注的类就会被先创建好才能调用其内部的创建BeanFactory后置处理器的方法,所以该类中,其他添加了Autowired或者PostConstruct注解的方法就不会生效
三种初始化Bean方法的执行顺序
Bean的初始化(区别于实例化)有三种方法:
-
@PostConstruct 注解的方法
-
实现 InitializingBean 接口的方法
-
@Bean(initMethod=‘init’) 注解的方法(@Bean注解作用于@Configuration注解的类中)
它们三者依照上述排序顺序执行,原因是它们分别属于下图中的三个方法
销毁的方法同理
单例对象中注入其他scope属性失效问题
假设我有两个类:A类是singleton模式的,它内部有一个 B 类的属性b,而B类是一个prototype/request/session/application模式的
@Component
@Scope("singleton")
class A {
@Autowired
private B b;
}
@Component
@Scope("prototype")
class B {
}
这种写法下是有问题的,我希望在A实例对象中每次得到的B对象都是不同的,但是由于bean只会执行一次初始化,所以每次得到的b对象仍然是相同的。解决方法有:
1. 给A对象中加上懒加载属性(本质是在调用b时spring自动创建一个代理对象)
@Component
@Scope("singleton")
class A {
@Lazy
@Autowired
private B b;
}
2. 给B类的Scope注解加一个代理属性(也是基于代理实现的)
@Component
@Scope(value = "prototype", proxyMode = ScopedProxyMode.TARGET_CLASS)
class B {
}
3. 不直接给A中注入B,而是注入B的ObjectFactory
@Component
@Scope("singleton")
class A {
@Autowired
private ObjectFactory<B> b;
public B getB() {
return b.getObject();
}
}
4. 直接在A中注入applicationContext,获取b时直接调用其getBean() 方法
AOP
AspectJ
AspectJ不属于Spring,它是一个独立的实现AOP的库。可以在需要增强的类上加@Aspect注解,并提供切面代码,然后使用AspectJ的编译器(ajc)来编译该类(Maven中也有它的插件,加上后会自动编译),其原理是直接修改class文件,在字节码文件中直接修改被代理的方法。
当有些方法不能创建动态代理时,例如静态方法,则可以使用这种方法来创建代理方法
另外,如果一个代理方法内部也调用当前类中的另一个代理方法,则动态代理也会有问题:
# 以下伪代码表示,MyClass下有两个方法a、b,它们都是会被代理的方法
class MyClass:
def a():
# 此时调用b()方法其实并不会去调用代理b方法,因为其本质是调用 this.b()
b()
def b():
...
这种情况下,就可以使用AspectJ来处理。
与AspectJ相似的还有一种agent代理的方法,它是在类加载过程中对被代理的方法进行修改(注意,class文件并没有修改,而是将class文件加载到方法区时才修改)
JDK 代理
只能使用接口代理,其本质上是对接口的代理,与被代理类无关
JDK代理实质上是使用ASM直接生成代理类的字节码
public class DemoApplication {
public static void main(String[] args) {
// 要被代理的类
C c = new C();
// 生成一个接口的代理对象,有三个参数:
// classloader: 创建代理类的本质是创建一个实现了某接口的类,那么该类也是需要类加载器来加载的,所以要指定classloader
// classes:要代理的接口,可以同时代理多个接口
// InvocationHandler:对被代理的接口中每个方法进行拦截
// 注意:实际上生成接口的代理对象与真正的被代理类是无关的,仅仅是在方法拦截过程中,可能需要使用到被代理方法
I interfaceProxy = (I) Proxy.newProxyInstance(
DemoApplication.class.getClassLoader(),
new Class[]{I.class},
new InvocationHandler() {
// 被拦截的方法也有三个参数
// 创建出的代理对象、被拦截的方法、方法传递进来的参数
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("before...");
Object res = method.invoke(c, args); // 反射调用
System.out.println("after...");
return res;
}
});
interfaceProxy.call(); // 此时就会执行代理方法,注意,是使用代理类去调用方法
}
}
interface I {
void call();
}
class C implements I {
@Override
public void call() {
System.out.println("call c");
}
}
优点是被代理类可以是final修饰的
上述方法中会利用反射去调用被代理的方法,实际上反射调用的效率较低,所以当一个代理方法被反射调用16次后,jdk默认就会生成一个新的代理对象,将代理方法整体转换为该对象的一个静态方法,后续再调用代理方法时会直接调用该静态方法来提高效率
Cglib
是使用创建子类的方法方式实现代理,所以被代理类和被代理方法不能被final修饰
public static void main(String[] args) {
// 要被代理的类
C c = new C();
C c_proxy = (C) Enhancer.create(C.class, new MethodInterceptor() {
// 生成的代理对象、被代理方法、方法参数、一种提供非反射调用的方法
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("before...");
Object res = method.invoke(c, args); // 使用反射方式调用
// Object res = proxy.invoke(c, args); // 非反射调用
// Object res = proxy.invokeSuper(obj, args); // 非反射调用,使用这种方式甚至不用创建被代理对象
System.out.println("after...");
return res;
}
});
c_proxy.call();
}
cglib避免反射调用的方式和jdk动态代理不同,其大致思路为 为每个被代理对象再创建一个代理类,该类继承FastClass类,其内部会根据被代理类中每个方法的方法签名生成一个 MethodProxy 对象,当使用methodProxy.invoke() 或 .invokeSuper() 时,就能根据其内部保存的方法签名找到被代理对象中对应的那个方法(实际还会将签名转换为一个index进行索引),从而直接调用。大致过程就是这样,具体我也没细看。
Spring使用的代理
动态代理只能代理能被重写的方法,对于 static、final、private 的方法都无效
先解释几个概念:
-
切点(Pointcut):一个表达式,例如 “(* xxx.xxx.method(..))",用于表示需要拦截哪些方法
-
通知(Advice):在切点执行的代码,又分为:前置通知、后置、返回、异常、环绕。Spring内部会将所以类型的通知都转换为环绕通知,体现了适配器模式的思想(将一套接口转换为另一套符合某场景的接口),多个通知调用组成一个调用链,体现了责任链模式
-
切面(Aspect):用于封装切点和通知的类,另外还有一种切面成为 Advisor,它也是一个类,但只包含一个切面和一个通知。Aspect在生效前会被拆解为多个Advisor
Spring中使用ProxyFactory类创建代理对象,其内部创建的代理对象分为3种情况:
-
ProxyFactory类中属性 proxyTargetClass=false时,如果被代理类实现了接口,则使用JDK代理实现
-
该属性为false时,如果被代理类没用实现接口,则使用cglib
-
该属性为true时,总是使用cglib
Spring代理的创建时机
bean的创建过程大致有三步:创建 -> ( 1* ) 属性填充 -> 初始化 ( 2* ),Spring创建代理对象的时机有两个,即带 * 的位置,它们的区别在于:
-
当对象没用循环依赖时,会在初始化之后才创建代理对象
-
当有循环依赖时,则在 属性填充前创建,并将创建完成的代理对象放入二级缓存
需要注意的一点是,无论代理如何实现,原始对象在属性填充或者初始化都不会调用任何代理方法,代理只有在从容器中获取bean后,调用bean的被代理方法才会生效
也就是说,上述流程中,不管是属性填充前还是初始化后,其实操作的都仍然还是原始对象。而代理对象是没有属性填充和初始化流程的。这就说明,如果直接获取代理对象中,属性填充的值是获取不到的,但为什么程序也不会出问题呢?是因为一般程序不会直接获取对象的属性,而是调用属性的get方法,而代理对象的get方法或其他方法都是会在内部真正去调用原始对象中的方法的,所以是可以拿到属性填充或初始化的值的。
Springboot
启动流程分为两步:1)创建SpringApplication 2)调用SpringApplication的run方法
注:在第一个阶段并没有创建Spring容器,而是创建一个SpringApplication对象(区分ApplicationContext才是容器),在第二个阶段才创建
创建SpringApplication
过程又分为5步:
-
从各个途径获得 Bean Definition 源:例如创建 SpringApplication 对象时需要传入一个主类(就算Springboot启动主程序的那个类),该类一般包含或间接包含了 @ComponentScan 这一注解,该注解将由特定的beanfactory 后置处理器解析得到更多的 BeanDefinition 对象。注:不是生成BeanDefinition对象,而是告诉后续生成的Beanfactory去哪可以生成BeanDefinition对象
-
推断应用类型:根据应用所依赖的一些包来推断应用是否为web应用,或者是否为Servlet应用
-
创建 ApplicationContext 初始化器:添加一些回调方法,当后续Spring容器创建后会调用这些初始化方法
-
创建监听器:也是一些回调方法,监听后续Springboot启动过程中触发的事件
-
推断主类:即获取Springboot启动类
SpringApplication的run方法
-
创建一个事件发布器 SpringApplicationRunListeners,它内部有很多发布事件的方法,在Springboot启动的各个阶段就可以调用对应的方法发布事件
-
保存参数:启动Springboot时可以在main()方法中添加一些参数,或者在命令行启动时添加的一些参数
-
3-7步都和环境变量有关。将系统环境变量、properties、yml等配置的变量封装成为Environment对象。当然也能手动向PropertySources的数组中添加新的变量来源。PropertySources的数组中来源的顺序就是找变量的顺序
-
获取上述变量时,有些使用驼峰命令法有些使用分隔符,为了统一风格,在PropertySources的数组头中加上一个虚拟源,将所有的变量命名都修改为一致的状态
-
添加变量源后处理器,可以向PropertySources的数组中动态添加其他源,使得程序可以更灵活加载各种形式的源
-
将上述环境变量中的一些值赋值给Springboot内置变量
-
输出Springboot的banner信息,就是每次springboot启动时控制台输出的那个很大的Spring。这一步你就可以控制banner源和输出位置
-
创建容器
-
准备容器:就算执行创建SpringApplication第3步的回调方法
-
创建 BeanDefinition 对象:这个过程会根据 BeanDefinition 源来创建不同的加载类,例如 如果bean definition 来源于xml文件,则会创建一个 XmlBeanDefinitionReader来读取xml文件并创建BeanDefinition对象,如果使用的是注解,则会创建 AnnotatedBeanDefinitionReader 来读取注解
-
refresh 容器:执行各种后置处理器,创建单例bean
-
执行Runner:Runner也是一类Bean,只不过它们需要实现一些特定接口,在Springboot启动后会执行接口中的方法,就算一个钩子函数,这些钩子函数能得到第2步中保存的一些参数
设计模式
包括但不限于:
-
工厂方法模式:FactoryBean(注意不是BeanFactory)
-
单例模式
-
代理模式
-
装饰器模式:与代理模式类似,只不过代理模式使用的是被代理类调用方法,二装饰器模式直接使用装饰器类调用方法,Spring中,以Wapper或Decorator结尾的
-
观察者模式:各种各样的监听器
-
模板方法模式:BeanFactory.getBean() 就提供了一个获取Bean的模板,中间穿插了很多钩子函数用于扩展
-
策略模式:Spring会根据不同的情况更换动态代理(jdk还是cglib),这种根据不同条件选择不同的实现方法的方法就算策略模式