黑马程序员Spring视频教程,深度讲解spring5底层原理

ApplicationContext功能
四个父接口
MessageSource提供翻译功能,国际化能力
ResourcePatternResolver提供通配符匹配资源的能力
ApplicationEventPublisher提供发布事件的能力
EnvironmentCapable提供读取配置文件能力
BeanFactory实现:DefaultListableBeanFactory,bean的定义
后处理器:
①Bean工厂后处理器:负责bean的定义
②Bean后处理器:负责Bean的实例生成
默认是延迟创建单例对象
学到了什么:
a. beanFactory 不会做的事
- 1. 不会主动调用 BeanFactory 后处理器
- 2. 不会主动添加 Bean 后处理器
- 3. 不会主动初始化单例
- 4. beanFactory 还不会解析 ${ } 与 #{ }
ApplicationContext实现:
AnnotationApplicationContext:内部有工具类,自动加载后处理器
AnnotationConfigServletWebServerApplicationContext:较为经典的容器,基于java配置类来创建,用于创建web环境。
①内嵌容器Tomcat
②dispatcherService
③DispatcherServletRegistrationBean
Bean的生命周期
1 Spring bean生命周期各个阶段:
构造/实例化->依赖注入->初始化->销毁
bean工厂后处理器,补充bean,configuration定义
bean后处理器:生命周期各个阶段的扩展
@PostConstruct
注解是 Java EE和Spring框架中的一种标准注解,它用于在对象创建后执行特定的初始化操作。具体来说,@PostConstruct
注解的作用是标记一个方法,在对象构造完成后(即依赖注入完成后),容器会自动调用这个方法,以执行一些初始化任务。
2 模板方法
并不能确定的部分抽象成接口,在特定的时机调用抽象方法。有静有动
静:固定的步骤
动:不确定的逻辑
Bean后处理器
1,AutowiredAnnotationBeanPostProcessor用于解析AutoWired注解和Value注解
CommonAnnotationBeanPostProcessor
用于解析@Resource @PostConstruct @PreDestroy
ConfigurationPropertiesBindingPostProcessor解析@ConfigurationProperties
2,AutowiredAnnotationBeanPostProcessor工作流程
调用postProcessPropertie方法
①先找到某个类型中标注了Autowired的方法或成员变量,即InjectionMetadata
②调用InjectionMetadata来进行注入,注入时按照类型查找值。
- 得到成员变量和方法信息之后,把它们封装成DependencyDescriptor
- 调用beanFactory的doResolveDependcy方法
BeanFactory后处理器:
1,常见后处理器
① ConfigurationClassPostProcessor用于解析@ComponentScan @Bean @Import @ImportResource
②,MapperScanner解析@Mapper
2, 组件扫描后处理器实现
先找包,后找这个包里面的类的字节码,找到加了Component注解的类,进行注册。
3,@Bean注解的解析
① 获取配置类的字节码文件
② 获取配置类中被@Bean标注的方法信息
③ 遍历方法,根据每个方法生成BeanDefinition,方法名则是Bean的名字
④ 对Bean注解的一些属性进行解析
4,MapperScan
① 对对应的包进行筛选出接口
② 根据接口类名,SqlsessionFactory 来创建一个个MapperFactoryBean
③ 再往容器里注册这个MapperFactoryBean
Aware接口,Innitialzing
1,Aware接口,Innitialzing初始化接口属于内置功能,不会失效Aware可以注入名字例如容器名或者bean名字,Innitialzing则可以进行初始化。
2,Autowired,PostConstructor功能失效
refresh执行顺序:
1,找到所有BeanFactory后处理器,
2 添加Bean后处理器
3 初始化所有单例
3.1 依赖注入扩展
3.2 初始化扩展
失效原因总结:在执行第一步时,这个配置类中有BeanFactory后处理器工厂方法,需要优先创建这个配置类,再执行工厂方法,添加这个后处理器,但此时还没有创建Bean后处理器,无法解析这个类的Autowired,Value注解。这个配置类已经在添加Bean后处理器前被初始化,后续也不会再初始化。
Spring bean的初始化和销毁顺序
扩展功能即PostContruct的初始化
接口方式的初始化
@Bean注解指定的初始化
销毁顺序也是一样的
Scope
1,Request域:请求来了创建bean,请求结束销毁bean
Session域:当过了一段时间,浏览器没有发来请求,session的bean对象销毁。
application域:
2,@prototype失效
解决方案:
① 使用@Lazy注解,生成代理对象每次调用对象的任意方法,都会创建新的对象
② 注入ObjectFactory对象
③通过容器来获取对象
aop实现原理:
① aop之proxy增强,通过方法重写的方式,
在运行期间直接生成代理类的字节码,
- aop之jdk增强:生成的代理类和目标类属于同级别关系,实现了同一个接口,不能强制转换
- aop之cglib增强:生成的代理类和目标类是父子关系,就要求不能有final
② aop之ajc增强,class被改写,和Spring没什么关系,通过aspect编译器,在编译阶段改写类
③aop之agent增强 在类加载阶段改写类
jdk代理内部实现原理:
①设计一个接口Invokationhandler回调抽象操作
②将接口的方法对象作为参数传给invoke,知道目标对象要调用哪个方法
③ invoke内部根据这个方法参数再来反射调用
jdk代理源码:
直接生成字节码,利用asm技术
jdk反射优化:生成一个方法的代理类来优化反射直接调用
cglib代理
通过methodInterceptor接口来进行回调,不同点,生成方法代理。
方法代理methodProxy.invoke(target,args):
结合目标类使用,内部没有反射。
methodProxy,invokeSuper(proxy,args)
结合代理使用,内部无反射。
内部会生成一个FastClass
记录目标类的方法或者代理类原始方法编号
根据index知道它调用的是哪个方法,用目标类直接正常调用,避免反射调用。
与jdk对比,jdk代理一开始没用反射优化
②一个代理类会用两个FastClass来消除反射,一个是配合目标类,一个是配合代理类
Spring选择代理
设置切点
设置通知
1,创建代理对象的选择:
- proxyTargetClass=False,目标实现了接口,代理用jdk实现
- proxyTargetClass=False,目标没有实现接口,代理用cglib实现
- proxyTargetClass=True,总是使用cglib实现
切点实现
①,aspectj的匹配方法,切点表达式只能匹配方法,如果注解加在类上,则没有办法找到。
②MethodMatcher接口,用来执行方法匹配。
Aspect切面(高级切面)和Advisor切面
AnnotationAwareAspectJAutoProxyCreator:后处理器
依赖注入之前会调用它的功能,初始化之后。
1,findEnableAdvisors方法作用:根据目标类型去找到这个目标类型相匹配的所有切面,返回切面集合
2,WrapIfnecessary()用于判断是否有必要为这个类创建代理,并且返回这个代理
3,代理创建时机
- 初始化之后(没有循环依赖)例如Bean1不依赖Bean2,但是Bean2依赖Bean1,Bean1代理创建时机就是在初始化之后
- 依赖注入之前(存在循环依赖),Bean1,Bean2相互依赖
- 依赖注入和初始化不应该增强,作用于原始对象,而非代理对象
4, 切面顺序控制@Order
order注解加在切面类上,不能加在方法上
5,高级切面转换为低级切面(创建代理时)
- 对切面类所有方法进行解析
- 遍历方法,检查所有方法对应的注解
- 从注解中拿到切点表达式,利用切点表达式创建切点对象
- 根据注解类型的不同选择不同的通知实现
- 根据切点和通知创建低级的切面advisor
统一转换成环绕通知(代理类执行方法时)
1适配器设计模式:把一套接口转换成另一套接口。
创建Methodinvocation
最外层创建一个环绕通知
2,调用链Methodinvocation实现,在通知类里面调用invocation的proceed方法,责任链模式
动态通知调用
带了参数绑定
DispatcherServlet
1,AnnotationConfigServletWebServiceApplication 支持内置Tomcat容器
2,DispatcherServlet创建是由Spring框架创建,初始化是由Tomcat服务器初始化,默认是在第一次请求时初始化。
3,通过将一些值写入配置文件解决硬编码问题。
DispatcherServlet初始化做了什么事
- 初始化了一堆组件,如果没有提供组件那么有默认的实现。
DispatcherServlet组件
1,RequestMappingHandlerMapping:先到当前容器找到所有控制器类,记录下所有的方法
初始化时记录路径和方法的映射关系
2,RequestMappingHandlerAdapter
- 调用控制器方法,需要参数解析器。
- 各种不同的注解,像RequestBody等,需要不同类型的参数解析器
- 返回值类型解析器
3,自定义参数解析器
4,自定义返回值解析器
参数解析器
获取方法参数的信息,例如注解,还有参数名,参数类型
- a. 每个参数处理器能干啥
1) 看是否支持某种参数
2) 获取参数的值
- b. 组合模式在 Spring 中的体现
- c. @RequestParam, @CookieValue 等注解中的参数名、默认值, 都可以写成活的, 即从 ${ } #{ }中获取
组合模式:逐一调用每个参数解析器,直到找到合适的解析器
对象绑定和类型转换
两套偏底层的类型转换接口
Spring实现
jdk自带 Formatter
1,高层接口实现:
TypeConverter:类型转换
SimpleTypeConverter(转换目标,转换目标类型)
BeanWrapperImpl给类属性赋值,并且可以进行类型转换
DirectFieldAcessor,跟上一个一样,但是直接走的成员变量。
DataBinder:类属性绑定,支持两种绑定方式,默认情况走get,Set方法
2,网络环境下的数据绑定
ServletRequestDataBinder:网络情况下的数据绑定。
ServletRequestParameterPropertyValues封装请求数据。
3,绑定器工厂,扩展功能
字符串“1996|11|01”转换成日期需要转换器
流程:
①通过initBinder注解
- 收集InitBinde方法信息,包括方法对象,方法本身
- 绑定工厂,创建binder的时候会回调这个方法,补充:底层调用的是PropertyEditor接口
用ConversationService转换
当两种都有的情况下,会优先采用initBinder的方式,后面采用ConversationService方式,最后采用默认的方式
@ControllerAdvice增强功能
@ExcetionHandler:处理异常
@ModelAttribute:补充模型数据
Initbinder初始化时机:
@InitBinder 的来源有两个
1. @ControllerAdvice 中 @InitBinder 标注的方法,由 RequestMappingHandlerAdapter 在初始化时解析并记录
2. @Controller 中 @InitBinder 标注的方法,由 RequestMappingHandlerAdapter 会在控制器方法首次执行时解析并记录
a. Method 对象的获取利用了缓存来进行加速
b. 绑定器工厂的扩展点(advice 之一), 通过 @InitBinder 扩展类型转换器
控制器方法执行流程
HandlerAdapter最重要的功能:执行控制器方法
HandlerMethod需要
Bean是哪个Controller

HandlerMethod需要
. bean即是哪个Controller
. method即是Controller 中的哪个方法ServletlnvocableHandlerMethod需要
. WebDataBinderFactory负责对象绑定、类型转换
. ParameterNameDiscoverer负责参数名解析,模型数据加入ModelAndViewContainer,如果指定未名称,那么将对象首字母小写作为名称放入容器
·HandlerMethodArgumentResolverComposite负责解析参数· HandlerMethodReturnValueHandlerComposite负责处理返回值
@ModelAttribute
加在方法上,默认名字是返回类型的第一个字母小写,值是返回值,那么需要RequestMappingHandlerAdapter,ModelFactory对@ModelAttribute进行额外解析
返回值处理器
解析返回值,加入ModelAndViewContainer
MessageConverter
将对象转换为json,或者将json转换为对象
多个MessageConverter
先看响应要求
再看request要求
如果没有特殊要求,则根据列表顺序
ControllerAdvice之ResponsebodyAdvice
返回点扩展
Tomcat异常处理
@ExceptionHandler注解,处理控制器方法出现的异常,加一些返回值处理器。
学到了什么
a. ExceptionHandlerExceptionResolver 能够重用参数解析器、返回值处理器,实现组件重用
b. 能够支持嵌套异常
BsiicErrorController
MVC处理流程
Boot启动流程
SpringApplication构造方法做了哪些事:
- 获取Bean Definition源
xml文
配置文件
- 演示推断应用类型
非Web应用程序
Web的reactive程序
Web的Servlet程序
- 演示ApplicationContext初始化器
做扩展,补充BeanDefiniton
- 演示监听器与事件
- 演示主类推断
main方法运行所在类
run方法执行流程
1,发布事件
run 方法内获取事件发布器 (得到 SpringApplicationRunListeners) 的过程, 对应步骤中
1.获取事件发布器
发布 application starting 事件1️⃣
发布 application environment 已准备事件2️⃣
发布 application context 已初始化事件3️⃣
发布 application prepared 事件4️⃣
发布 application started 事件5️⃣
发布 application ready 事件6️⃣
这其中有异常,发布 application failed 事件7️⃣
*/
两种runner,都是在Springboot启动的最后流程进行回调。
commandlineRunner:来自main方法的参数,
ApplicationRunner:经过封装的参数。
3
准备好环境对象,配置环境来源,把命令行的来源加入
4
往EnvironmentPropertiesSoure中加入ConfigurationPropertiesSource,统一下划线,驼峰命名等风格。
5,
增加EnvironmentPostProcessor接口,增加来源环境
6,
数据绑定,将配置文件的值绑定到特定的类中
8,创建Spring容器
9,调用一些初始化器对Spring容器进行功能增强,发布事件。
10,得到所有beandefiniton源
发布事件
11,调用applicationContext的bean后处理器,bean工厂后处理器,初始化每个单例。
12,调用runner,调用所有实现了ApplicationRunner接口或CommandlineRunner接口的bean
发布running事件
Tomcat内嵌容器
1,创建Tomcat对象
2,创建项目文件夹
3,创建Tomcat项目,在Tomcat中称为Context
4,编程添加Servlet
5,启动Tomcat
6,创建连接器,设置监听接口
自动配置类
1, @Import:导入第三方配置类
本项目配置类优先
@ConditionalOnmissingbean
2,AopAutoConfiguration
通过注解的方式进行嵌套判断