Spring
2025-01-10 11:07:54 0 举报
AI智能生成
spring
作者其他创作
大纲/内容
AOP
代理模式
动态代理
jdk代理
jdk提供的,目标对象一定要实现接口
cglib 代理
基于继承,使用的时候要额外引入类库,目标类不能为final 目标方法不能为static/final ,只能是普通类,不能继承其他类
静态代理
代理对象和被代理对象之间是一种强耦合,代理对象必须知道被代理对象具体的变量或方法,从而进行调用,代理对象增多就要创建多个代理,工作量大并且不好维护
注入方式实现Aop
引入相关的Jar包 aspectj
配置文件
开启注解扫描
Aop注解扫描 @EnableAspectJAutoProxy(proxyTargetClass = true)
注解
前置通知(@Before)
在目标方法执行之前,执行切面逻辑;
后置通知(@AfterReturning)
在目标方法执行之后,执行切面逻辑;
环绕通知(@Around)
在目标方法执行之前和之后,执行切面逻辑;
可以通过proceedingJoinPoint来进行方法信息的获取
异常通知(@AfterThrowing)
在目标方法抛出异常时,执行切面逻辑;
最终通知(@After:最终通知)
在目标方法之后执行,总是会执行
引入通知(@DeclareParents)
向目标对象添加新的方法或属性。
Di依赖注入
由容器负责创建和管理对象,并将对象的依赖关系注入到需要依赖的组件中,
从而减少组件间的直接依赖,提高代码的可维护性、灵活性和可测试性。
从而减少组件间的直接依赖,提高代码的可维护性、灵活性和可测试性。
类型注入
@Autowired
自动装配,通过类型进行依赖注入。
可以用在构造方法、属性、方法上。spring提供,可以放在普通方法上,spring会实例化方法参数并执行一次方法
可以用在构造方法、属性、方法上。spring提供,可以放在普通方法上,spring会实例化方法参数并执行一次方法
@Inject
依赖注入,与@Autowired功能类似,也是通过类型进行注入。
需要引入javax.inject包。
需要引入javax.inject包。
名称注入
@Resource
通过名称进行依赖注入。可以通过指定name属性
或者默认按照字段名或方法名进行匹配注入。 jdk提供
或者默认按照字段名或方法名进行匹配注入。 jdk提供
@Value
通过指定的值进行依赖注入,可以用于注入
简单的值类型,如字符串、数字等。
简单的值类型,如字符串、数字等。
构造方法赋值
通过构造器来实例化bean,并将构造器参数注入到bean中。
Setter
普通字段
<property name="name" value="constName"/>
list
<property name="titleList">
<list>
<value>1</value>
<value>2</value>
</list>
</property>
<list>
<value>1</value>
<value>2</value>
</list>
</property>
map
<map>
<entry key="A" value="100"/>
</map>
<entry key="A" value="100"/>
</map>
property
对象
IOC(控制反转)
Bean
创建Bean的三种方式
构造器
setter方法
工厂方法
Bean的生命周期
实例化
加载xml
BeanDefinitionReader读取xml
解析xml
解析后,会将信息封装成BeanDefinition,之后放到beanDefinitionMap中,实例化 通过反射实现bean的实例化,实例化的时候在AbstractAutoWiredCapableBeanFactory 里面BeanWrapper 把实例化的对象在堆中开辟空间 但是 当前属性的值都是默认值 此时就到循环依赖提前暴露 对象创建的工作 循环依赖在此产生 暴露的是 只完成实例化 没完成初始化的操作
属性赋值
实例化后的对象会被封装在BeanWrapper中,然后通过BeanDefinition中以及BeanWrapper中属性信息完成依赖注入,此时堆中对象就有你xml里面配的属性了
初始化
aware接口
如果实现了aware接口,可以做一些初始化操作调用容器中其他初始化对象
例如applicationcontextaware 只需要在属性里有这个,不用关心如何注入,就可以在对象中拿到applicationcontext
BeanPostProcessor(后置处理器)
若实现了BeanPostProcessor接口,则要实现其中的before和after方法,可以在init-method前后做一些对对象的自定义操作
init-method
完成初始化
实现@PostConstruct注解实现初始化
InitializeBean方法初始化
invokeAwareMethods
invokeInitMethods
使用
销毁
容器关闭,自动调用bean的销毁方法
Bean工厂
BeanFactory
延迟加载:只有在需要使用Bean时才创建对象。
轻量级:相对于ApplicationContext,BeanFactory的资源消耗较少。
手动配置:需要手动配置和管理Bean的依赖关系。
ApplicationContext
自动装载:根据配置文件或注解自动装载Bean。
功能丰富:提供了更多的特性和扩展,如国际化支持、事件发布、AOP等。
更易于使用:相对于BeanFactory,ApplicationContext提供了更方便的配置和管理方式。
也是beanfactory,因为继承了beanfactory
循环依赖
如何解决循环依赖
一级缓存(singletonObjects)
用于存储完整生命周期的单例Bean
如果单例的Bean就必须经过一级缓存
会判断是不是完整的Bean,如果不是就会进入二级缓存查找
二级缓存(earlySingletonObjects)
保存早期仅进行实例化但没有赋值和初始化的Bean(正在创建当中的Bean)
三级缓存(singletonFactories)
主要在解决循环依赖时用到,大多用到二级与一级缓存,
第三级缓存存储了Bean的类名、属性、依赖关系等元数据信息,
当创建Bean实例过程,发现循环依赖的情况时,它会使用第三级缓存中的Bean定义信息来提前创建一个原始的、尚未完成初始化的Bean对象,然后将其放入二级缓存中
通过这种方式,Spring打破了循环依赖的等待条件,使得Bean的创建和初始化都能够继续进行
解决循环依赖流程
当A、B两个类发生循环引用时,在A完成实例化后,就使用实例化后的对象去创建一个对象工厂,并添加到三级缓存中,如果A被AOP代理,那么通过这个工厂获取到的就是A代理后的对象,
如果A没有被AOP代理,那么这个工厂获取到的就是A实例化的对象。当A进行属性注入时,因为在三个缓存级别中找不到B,会去创建B,同时B又依赖了A,所以创建B的同时又会去调用getBean(a)来获取需要的依赖,此时的getBean(a)会从缓存中获取,
如果A没有被AOP代理,那么这个工厂获取到的就是A实例化的对象。当A进行属性注入时,因为在三个缓存级别中找不到B,会去创建B,同时B又依赖了A,所以创建B的同时又会去调用getBean(a)来获取需要的依赖,此时的getBean(a)会从缓存中获取,
第一步,先获取到三级缓存中的工厂;
第二步,调用对象工工厂的getObject方法来获取到对应的对象,得到这个对象后将其注入到B中(也就是将尚未初始化完的BeanA注入到B中),此时A会从三级缓存移动到二级缓存。
紧接着B会走完它的生命周期流程,包括初始化、后置处理器等。当B创建完后,会将B再注入到A中,此时A再完成它的整个生命周期,A完成整个生命周期后,会存到一级缓存中。至此,循环依赖结束
不使用第三级缓存可以吗
如果要使用二级缓存解决循环依赖,意味着所有Bean在实例化后就要完成AOP代理,这样违背了Spring设计的原则,Spring在设计之初就是通过AnnotationAwareAspectJAutoProxyCreator这个后置处理器来在Bean生命周期的最后一步来完成AOP代理,因为,Aop 就是在BeanPostProcessor 中实现的,在引入三级缓存中,会在getEarlyBeanReference方法中 遍历BeanPostProcessor , 所以bean 被代理的时候必须实现三级缓存而不是在实例化后就立马进行AOP代理。
spring 要在初始化的最后一步完成Aop的代理 ,如果没有三级缓存就意味着:在实例化完成后 就得完成Aop的代理,就违背了Spring的设计原则
bean 的作用域
singleton
单例模式,使用 singleton 定义的 Bean 在 Spring 容器中只有一个实例,这也是 Bean 默认的作用域。
普通成员变量和静态变量都不是线程安全,通过threalocal都可以解决
单例模式,使用 singleton 定义的 Bean 在 Spring 容器中只有一个实例,这也是 Bean 默认的作用域。
普通成员变量和静态变量都不是线程安全,通过threalocal都可以解决
prototype
原型模式,每次通过 Spring 容器获取 prototype 定义的 Bean 时,容器都将创建一个新的 Bean 实例。
普通成员变量线程安全,静态变量线程不安全
原型模式,每次通过 Spring 容器获取 prototype 定义的 Bean 时,容器都将创建一个新的 Bean 实例。
普通成员变量线程安全,静态变量线程不安全
request
在一次 HTTP 请求中,容器会返回该 Bean 的同一个实例。而对不同的 HTTP 请求,会返回不同的实例,该作用域仅在当前 HTTP Request 内有效。
在一次 HTTP 请求中,容器会返回该 Bean 的同一个实例。而对不同的 HTTP 请求,会返回不同的实例,该作用域仅在当前 HTTP Request 内有效。
session
在一次 HTTP Session 中,容器会返回该 Bean 的同一个实例。而对不同的 HTTP 请求,会返回不同的实例,该作用域仅在当前 HTTP Session 内有效。
在一次 HTTP Session 中,容器会返回该 Bean 的同一个实例。而对不同的 HTTP 请求,会返回不同的实例,该作用域仅在当前 HTTP Session 内有效。
global Session 已废除
@SpringBootApplication
@EnabelAutoConfiguration
启动时自动导入AutoConfigurationImportSelector,会将所有符合条件的Configuration配置进行加载
@SpringBootConfiguration
等同于@Configuration
@ComponentScan
自动扫描&加载
run启动
服务构建
1.将资源加载器,主方法类记录到内存中
2.逐一判断对应的服务类是否存在,来确定web服务的类型
3.加载初始化类,读取所有META-INF/spring.factories文件中的注册初始化器(BootstrapRegistryInitializer)、上下文初始化器(ApplicationContexInitializer)和监听器(ApplicationListener)三类配置
4.通过运行栈stackTrace判断main方法所在的类
环境准备
1.创建一个启动上下文(BootStrapContext),并逐一调用刚刚加载的启动注册初始化器的一个初始化方法initialize
2.设置awt.headLess=true表示缺少显示器、键盘等设备也可正常启动
3.启动运行监听器(SpringApplicationRunListeners),同时发布启动事件,获取并加载spring.factories中的事件发布运行监听器,并且会将应用监听器也一并引入,以后想要在启动流程过程中加入自定义逻辑就只需要监听这些事件
4.通过prepareEnvironment方法组装启动参数,根据不同的web构造不同的环境。构造完毕后会加载环境变量、jvm系统属性到属性集合中,后期无需加载。
接下来会发布环境准备完成的事件,一些监听器收到信号会做相应处理。
接下来会发布环境准备完成的事件,一些监听器收到信号会做相应处理。
5.做忽略元数据加载、打印banner的操作。
容器创建
1.首先创建注解配置servlet容器,具体行为是创建beanfactory、用来解析一些注解的后置处理器和其他属性对象
2.调用prepareContext方法对容器中的属性进行初始化
填充容器
1.创建系统自带的Bean和用户自定义Bean并装配到容器里,通过启动tomcat和经历Bean的生命周期就可以使用一个完整的Bean了
2.发布启动事件完成的同时,会回调自定义Runner接口来处理定制化需求
SpringMVC
执行流程
客户端(浏览器)发送请求,直接请求到 DispatcherServlet
DispatcherServlet 根据请求信息调用 HandlerMapping ,解析请求对应的 Handler
解析到对应的 Handler (也就是我们平常说的 Cntroller 控制器)后,开始由 HandlerAdapter 适
配器处理。
配器处理。
HandlerAdapter 会根据 Handler 来调用真正的处理器开处理请求,并处理相应的业务逻辑
处理器处理完业务后,会返一个 ModelAndView 对象,Model 是返回的数据对象,View 是个逻辑上的 View。
ViewResolver 会根据逻辑 View 查找实际的 View
DispaterServlet 把返回的 Model 传给 Vew (视图染)。
把 view 返回给请者 (浏览器)
0 条评论
下一页