Spring

简介

Spring是一个轻量级的IOC和AOP容器框架,是为Java应用程序提供基础性服务的一套框架,目的是简化企业应用程序的开发,使开发者只需关心业务需求,将对象之间的依赖关系交由框架处理,减低组件的耦合性。

核心模块

  1. Spring Core:核心类库,所有功能都依赖与它,主要组件是BeanFactory,提供IOC(控制反转)和DI(依赖注入)服务。
  2. Spring Contenxt:提供上下文信息,框架式Bean访问方式,以及企业级功能(JNDI、定时任务、添加国际化的使用、事件传播等)。
  3. Spring AOP: 提供面向切面的编程功能。
  4. Spring Web:提供基本面向Web的综合特性,提供对常见框架(Struts2,SpringMVC)的支持,能够管理这些框架,将Spring资源注入其中,也能在这些框架前后添加拦截器。
  5. Spring MVC :提供面向Web应用的MVC三层架构(Model-View-Controller)。
  6. Spring Dao:操作数据库模块,对JDBC的抽象封装,简化数据库访问异常处理,并能统一管理JDBC事务。
  7. Spring ORM:对现有ORM(Object Relational Mapping对象关系映射)框架的支持。

核心组件

SpringAOP

JDK动态代理

CGLIB动态代理

spring启动流程

配置形式:5.2.12.RELEASE

  1. 启动代码
1
ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("applicationConfig.xml");
  1. ClassPathXmlApplicationContext内部
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//先加载ContextClosedEvent类
static {
// Eagerly load the ContextClosedEvent class to avoid weird classloader issues
// on application shutdown in WebLogic 8.1. (Reported by Dustin Woods.)
//急切地加载ContextClosedEvent类以避免奇怪的类加载器问题
//weblogic8.1中的应用程序关闭。(达斯汀·伍兹报道)
ContextClosedEvent.class.getName();
}
//实际调用构造方法
public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, @Nullable ApplicationContext parent) throws BeansException {
super(parent);//初始化父类
//设置AbstractRefreshableConfigApplicationContext中的配置文件目录configLocations数组
setConfigLocations(configLocations);

if (refresh) {
refresh(); //IOC真正初始化
}
}

image-20210401145150319

  1. 初始化AbstractApplicationContext 父类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public AbstractApplicationContext(@Nullable ApplicationContext parent) {
this();
setParent(parent);
}
//this();无参构造
public AbstractApplicationContext() {
//在这之前会初始化必要实例字段,如
//logger(日志记录)
//id(容器id),displayName(展示名) 两者值一样,都是
//ObjectUtils.identityToString(this);
//初始化BeanFactoryPostProcessor集合(空ArrayList),
//原子boolean类active、closed
//初始化监听器集合(LinkedList)等

//初始化资源解析器
this.resourcePatternResolver = getResourcePatternResolver();
}

protected ResourcePatternResolver getResourcePatternResolver() {
//Ant模式通配符的ResourceLoader子类资源查找器
return new PathMatchingResourcePatternResolver(this);
}


  1. 调用refresh()正真初始化IOC
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
@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.
//初始化BeanFactory:销毁存在的并新建新的
//调用loadBeanDefinitions方法使用XmlBeanDefinitionReader加载Bean定义,
//initBeanDefinitionReader方法通过读取配置文件configLocations数组,解析xml文件,
//通过beanDefinitionRegistry将BeanDefinition存放到
//DefaultListableBeanFactory类的Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256)中;
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

// Prepare the bean factory for use in this context.
//为Bean工厂添加类加载器,表达式解析器(StandardBeanExpressionResolver)、
//资源编辑器(ResourceEditorRegistrar)、自动装配、
//Bean工厂早期后置处理器(用以将内部bean检测为ApplicationListeners。)等
prepareBeanFactory(beanFactory);

try {
// Allows post-processing of the bean factory in context subclasses.
//提供给想要实现BeanPostProcessor的三方框架使用的。谁要使用谁就去实现。作用是在BeanFactory
//准备工作完成后做一些定制化的处理,一般结合BeanPostProcessor接口的实现类一起使用,
//注入一些重要资源(类似Application的属性和ServletContext的属性)
postProcessBeanFactory(beanFactory);

// Invoke factory processors registered as beans in the context.
//在BeanFactory标准初始化之后执行BeanFactoryPostProcessor的方法,即BeanFactory的后置处理器:
invokeBeanFactoryPostProcessors(beanFactory);

// Register bean processors that intercept bean creation.
//注册Bean后置处理器
registerBeanPostProcessors(beanFactory);

// Initialize message source for this context.
//初始化MessageSource组件,负责国际功能
initMessageSource();

// Initialize event multicaster for this context.
//初始化事件派发器,在注册监听器时会用到
initApplicationEventMulticaster();

// Initialize other special beans in specific context subclasses.
//留给子容器、子类重写这个方法,在容器刷新的时候可以自定义逻辑
onRefresh();

// Check for listener beans and register them.
//查找所有的ApplicationListener监听器并注册到广播器中
registerListeners();

// Instantiate all remaining (non-lazy-init) singletons.
//初始化所有剩下的单实例bean,核心方法是preInstantiateSingletons(),会调用getBean()方法创建对象;
finishBeanFactoryInitialization(beanFactory);

// Last step: publish corresponding event.
//完成其他工作如清除缓存、发布BeanFactory容器刷新完成事件等,IOC容器初始化结束
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.
//设置active状态为false
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();
}
}
}

注解加载

spring启动流程

参考链接

三级缓存

ceef00b5c21677e9c0ec4bfbe400a3c5

参考链接0

参考链接1

参考链接2

参考链接3

参考链接4

BeanWrapper及类型转换

BeanWrapper其实就是一个Bean的包装器,它对Bean包装的目的是为了能操纵Bean中的属性,所以它同时需要具备获取、设置Bean中的属性能力,所以它也必须是一个属性访问器(PropertyAccessor),另外为了将各种不同类型的配置数据绑定到Bean的属性上,那么它还得具备属性转换的能力,因为它还得是一个类型转换器(TypeConverter)。

Spring中将类型转换的功能都委托给了一个TypeConverterDelegate,这个委托类在进行类型转换时会有两套方案:

  1. PropertyEditor,这是Spring最初提供的方案,扩展了java中的PropertyEditor(java原先提供这个接口的目的更多是为了进行图形化编程)。
  2. Spring后来提供的一个进行类型转换的ConversionService体系,用来取代PropertyEditor,因为PropertyEditor有很大的局限性,只能进行String->Object的转换。

20200331082247233

参考链接

Spring容器加载Bean流程

  1. ResourceLoader从配置文件中加载Spring配置信息,并使用Resource表示这个配置文件的资源。

  2. XXXBeanDefinitionReader(XXX可以是Xml、Properties、Annotated根据不同配置类型选择),读取并解析Resource所指向的配置文件,生成BeanDefinition对象(用于存储配置元信息:创建对象所需要的必要信息)。如xml配置中一个< bean >< /bean >标签话就对应一个BeanDefinition对象。所有的BeanDefinition对象会被注册到BeanDefinitionRegistry(底层使用key-value存储,如下)。

    image-20210315141623519

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    public class SimpleBeanDefinitionRegistry extends SimpleAliasRegistry implements BeanDefinitionRegistry {

    /** Map of bean definition objects, keyed by bean name */
    private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<String, BeanDefinition>(64);
    //……
    }

    public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory
    implements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable {
    //……
    /** Map of bean definition objects, keyed by bean name. */
    private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);
    //……
    }

    public class GenericApplicationContext extends AbstractApplicationContext implements BeanDefinitionRegistry {

    //直接操作DefaultListableBeanFactory的方法
    private final DefaultListableBeanFactory beanFactory;

    }
  3. 容器扫描BeanDefinitionRegistry中的BeanDefinition,利用反射识别出实现BeanFactoryPostProcessor工厂后置处理接口的Bean。并调用工厂后置处理器对BeanDefinition进行加工处理。

    注:BeanFactoryPostProcessor是容器启动阶段Spring提供的一个扩展点,主要负责对注册到BeanDefinationRegistry中BeanDefination进行一定程度上的修改与替换。主要完成以下两项工作:

    • 将配置文件中的占位符”${xx}”替换为最终配置值。

    • 通过Java反射机制找出所有实现java.beans.PropertyEditor接口的Bean,并自动将它们注册到Spring容器的属性编辑器注册表中(PropertyEditorRegistry)。

  4. Spring容器从BeanDefinitionRegistry中取出加工后的BeanDefinition,调用InstantiationStrategy类将其实例化。

  5. 在实例化Bean时,Spring容器会使用BeanWrapper对Bean进行封装。(BeanWrapper提供了很多以Java反射机制操作Bean的方法,它将结合该Bean的BeanDefinition以及容器中属性编辑器,完成Bean属性的设置工作)

  6. 最后利用容器中注册的Bean后处理器(实现BeanPostProcessor接口的Bean)对已经完成属性设置工作的Bean进行后续加工,直接装配出一个准备就绪的Bean。

参考链接

Bean实例化执行流程。

20210222011843360

参考链接

参考链接

@Resource与@Autowried区别

相同

  • 都可以标注在字段或属性的setter方法上,都可以实现对象注入。

不同

  • @Autowired 注解是按类型装配依赖对象,默认情况下它要求依赖对象必须存在,如果允许null值,可以设置它required属性为false。
  • @Resource注解默认按名称装配。名称可以通过@Resource的name属性指定,如果没有指定,则默认取字段的名称作为bean名称寻找依赖对象。当注解标注在属性的setter方法上时,默认取属性名作为bean名称寻找依赖对象。 若设置了type属性则按照类型注入。
  • @Resources是JDK自带的,@Autowired是Spring提供的。

BeanFactory与FactoryBean区别

BeanFactory是IOC容器或对象工厂,FactoryBean是个Bean。在Spring中,所有的Bean都是由BeanFactory(也就是IOC容器)来进行管理的。但对FactoryBean而言,这个Bean不是简单的Bean,而是一个能生产或者修饰对象生成的工厂Bean,它的实现与设计模式中的工厂模式和修饰器模式类似

Spring中要获取FactoryBean实例的话需要在名字前面加&(注:BeanFactory.FACTORY_BEAN_PREFIX = “&”)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
/**。
* @author CZM
* @create 2021/4/3
*/
public class TestFactoryBean implements FactoryBean<Cat> {

@Override
public Cat getObject() throws Exception {
return new Cat();
}

@Override
public Class<?> getObjectType() {
return Cat.class;
}

}
public class Cat {

}

//配置文件
//<bean id = "cat" name="cat3,cat2" class="bean.Cat"></bean>
//<bean id = "factoryBean" name="factoryBean1,factoryBean2" class="bean.TestFactoryBean"></bean>


ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("applicationConfig.xml");
System.out.println(classPathXmlApplicationContext.getBean("cat"));
System.out.println(classPathXmlApplicationContext.getBean("factoryBean"));
//返回的是FactoryBean
System.out.println(classPathXmlApplicationContext.getBean("&factoryBean"));

//输出
//bean.Cat@598067a5
//bean.Cat@3c0ecd4b
//bean.TestFactoryBean@14bf9759

参考链接

Spring常见面试题总结

Spring源码解析

SpringMVC

img

参数解析绑定

  1. SpringMVC初始化时,RequestMappingHandlerAdapter类会把一些默认的参数解析器添加到argumentResolvers中。当SpringMVC接收到请求后首先根据url查找对应的HandlerMethod。
  2. 遍历HandlerMethod的MethodParameter数组。
  3. 根据MethodParameter的类型来查找确认使用哪个HandlerMethodArgumentResolver,遍历所有的argumentResolvers的supportsParameter(MethodParameter parameter)方法。如果返回true,则表示查找成功,当前MethodParameter,使用该HandlerMethodArgumentResolver。这里确认大多都是根据参数的注解以及参数的Type来确认。
  4. 解析参数,从request中解析出MethodParameter对应的参数,这里解析出来的结果都是String类型。
  5. 转换参数,把对应String转换成具体方法所需要的类型,这里就包括了基本类型、对象、List、Set、Map。

参考连接

spring与springmvc父子容器问题

SpringMVC执行流程

常见面试题参考

工作原理

MyBatis

二级缓存

参考链接

执行流程

Mybatis执行流程

  1. 读取 MyBatis 配置文件:mybatis-config.xml 为 MyBatis 的全局配置文件,配置了 MyBatis 的运行环境等信息,例如数据库连接信息。
  2. 加载映射文件。映射文件即 SQL 映射文件,该文件中配置了操作数据库的 SQL 语句,需要在 MyBatis 配置文件 mybatis-config.xml 中加载。mybatis-config.xml 文件可以加载多个映射文件,每个文件对应数据库中的一张表。
  3. 构造会话工厂:通过 MyBatis 的环境等配置信息构建会话工厂 SqlSessionFactory。
  4. 创建会话对象:由会话工厂创建 SqlSession 对象,该对象中包含了执行 SQL 语句的所有方法。
  5. Executor 执行器:MyBatis 底层定义了一个 Executor 接口来操作数据库,它将根据 SqlSession 传递的参数动态地生成需要执行的 SQL 语句,同时负责查询缓存的维护。
  6. MappedStatement 对象:在 Executor 接口的执行方法中有一个 MappedStatement 类型的参数,该参数是对映射信息的封装,用于存储要映射的 SQL 语句的 id、参数等信息。
  7. 输入参数映射:输入参数类型可以是 Map、List 等集合类型,也可以是基本数据类型和 POJO 类型。输入参数映射过程类似于 JDBC 对 preparedStatement 对象设置参数的过程。
  8. 输出结果映射:输出结果类型可以是 Map、 List 等集合类型,也可以是基本数据类型和 POJO 类型。输出结果映射过程类似于 JDBC 对结果集的解析过程。

image-20210506002152431

参考链接

参考链接

其他知识链接

参考链接

参考链接2

面试题

面试题2

深入理解

SpringBoot

启动流程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
public class SpringbootApplication {
public static void main(String[] args) {
//启动入口
SpringApplication.run(SpringbootApplication.class, args);
}
}

//调用SpringApplication的构造方法
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
//设置资源加载器
this.resourceLoader = resourceLoader;
//primarySources就是启动类class参数参数,null就抛出异常
Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
//推断WebApplicationType(枚举类),即当前启动环境,在classpath下搜索特定的类是否存在
this.webApplicationType = WebApplicationType.deduceFromClasspath();
// 获取启动加载器
this.bootstrappers = new ArrayList<>(getSpringFactoriesInstances(Bootstrapper.class));
//设置初始化器
//搜索META-INF\spring.factories文件配置的ApplicationContextInitializer的实现类
//主要是通过SpringFactoriesLoader的loadSpringFactories()方法,
//内部维护一个cache的Map缓存如下:
//static final Map<ClassLoader, Map<String, List<String>>> cache = new ConcurrentReferenceHashMap<>();
//将返回集合值存入initializers中
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
//搜索META-INF\spring.factories文件配置的ApplicationListenerr的实现类
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
//推断MainApplication的Class
this.mainApplicationClass = deduceMainApplicationClass();
}


WebApplicationType.deduceFromClasspath()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
static WebApplicationType deduceFromClasspath() {
//通过Class.forNam加载全路径类名对应的类
if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) &&
!ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null) &&
!ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
//该应用程序应作为反应式Web应用程序运行,并应启动嵌入式反应式Web服务器。
return WebApplicationType.REACTIVE;
}
for (String className : SERVLET_INDICATOR_CLASSES) {
if (!ClassUtils.isPresent(className, null)) {
//该应用程序不应作为Web应用程序运行,也不应启动嵌入式Web服务器。
return WebApplicationType.NONE;
}
}
//该应用程序应作为基于Servlet的Web应用程序运行,并应启动嵌入式Servlet Web服务器。
return WebApplicationType.SERVLET;
}

run方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
public ConfigurableApplicationContext run(String... args) {
//时间监控,主要用于记录任务所用时间
StopWatch stopWatch = new StopWatch();
//设置开始时间,
stopWatch.start();
//创建启动上下文对象
DefaultBootstrapContext bootstrapContext = createBootstrapContext();
ConfigurableApplicationContext context = null;
//置Headless属性,java.awt.headless是J2SE的一种模式用于在缺少显示屏、键盘或者鼠标时的系统配置,很多监控工具如jconsole 需要将该值设置为true,系统变量默认为true
configureHeadlessProperty();
//创建监视器EventPublishingRunListener并触发
SpringApplicationRunListeners listeners = getRunListeners(args);

listeners.starting(bootstrapContext, this.mainApplicationClass);
try {
//将输入参数转成DefaultApplicationArguments解析类
ApplicationArguments applicationArguments =
new DefaultApplicationArguments(args);
//根据webApplicationType创建相应ConfigurableEnvironment环境配置类
//并设置属性解析器的类型转换器,环境信息,发布环境准备好事件,profile信息,环境类类型转换等
ConfigurableEnvironment environment =
prepareEnvironment(listeners, bootstrapContext, applicationArguments);
//设置需要忽略的bean
configureIgnoreBeanInfo(environment);
//打印Banner信息即springboot控制台启动输出(在SpringBootBanner里)
Banner printedBanner = printBanner(environment);
//创建容器,根据WebApplicationType创建相应容器
//若为SERVLET则调用AnnotationConfigServletWebServerApplicationContext();
//创建AnnotatedBeanDefinitionReader和ClassPathBeanDefinitionScanner
context = createApplicationContext();
//给容器设置分步记录
context.setApplicationStartup(this.applicationStartup);
//准备容器,发布容器准备好事件,bootstrapContext关闭事件,开始日志记录,
//往工厂注册打印器,加载资源并发布加载完成事件等等
prepareContext(bootstrapContext, context, environment,
listeners, applicationArguments, printedBanner);
//调用refresh()刷新容器,详情参考sping的启动流程
refreshContext(context);
//刷新容器后提供的扩展接口
afterRefresh(context, applicationArguments);
// 结束计时器
stopWatch.stop();
if (this.logStartupInfo) {
//打印启动时间
new StartupInfoLogger(this.mainApplicationClass)
.logStarted(getApplicationLog(), stopWatch);
}
//发布容器启动完成事件ApplicationStartedEvent
listeners.started(context);
//调用ApplicationRunner,CommandLineRunner的run方法
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
handleRunFailure(context, ex, listeners);
throw new IllegalStateException(ex);
}

try {
//发布容器就绪事件ApplicationReadyEvent
listeners.running(context);
}
catch (Throwable ex) {
handleRunFailure(context, ex, null);
throw new IllegalStateException(ex);
}
return context;
}

启动流程链接

END