Spring是个轻量级的框架,简化了应用的开发程序,提高开发人员的系统维护性,不过配置消息比较繁琐,所以后面才出选了SpringBoot的框架。
Spring的核心组件 : Spring Core 、 Spring Context 、 Spring Beans
IOC是Bean的一个容器,Context是每个Bean之间依赖的关系。正是因为各种Bean建立的关系我们才构成了IOC容器为我们服务,而Core就是维护、建立和维护所需要的的工具。
非安全的
- 原型Bean
对于原型Bean,每次创建一个新对象,也就是线程之间并不存在Bean共享,自然是不会有线程安全的问题。
- 单例Bean
对于单例Bean,所有线程都共享一个单例实例Bean,因此是存在资源的竞争。
如果单例Bean,是一个无状态Bean,也就是线程中的操作不会对Bean的成员执行查询以外的操作,那么这个单例Bean是线程安全的。比如Spring mvc 的 Controller、Service、Dao等,这些Bean大多是无状态的,只关注于方法本身。
?
但是如果Bean是有状态的 那就需要开发人员自己来进行线程安全的保证,最简单的办法就是改变bean的作用域 把 "singleton"改为’‘protopyte’ 这样每次请求Bean就相当于是 new Bean() 这样就可以保证线程的安全了。
- 有状态就是有数据存储功能
- 无状态就是不会保存数据 controller、service和dao层本身并不是线程安全的,只是如果只是调用里面的方法,而且多线程调用一个实例的方法,会在内存中复制变量,这是自己的线程的工作内存,是安全的。
- 所以其实任何无状态单例都是线程安全的。
Spring中的Bean默认是单例模式的,框架并没有对bean进行多线程的封装处理。
实际上大部分时间Bean是无状态的(比如Dao) 所以说在某种程度上来说Bean其实是安全的。
IOC:IOC就是控制反转,是一种思想而不是技术实现,A类对象中需要B类对象,传统是自己手动new就是主动控制,IoC思想就是交给IoC容器管理,不再自己手动new,容器管理好对象以及依赖关系,需要的时候从容器里拿就行。
?
IOC原理:就是在给每一个bean进行实例化后,都要对他的属性进行填充,大多数我们都是使用@Autowire直接的填充依赖注入的,在实例化和实例化后之间会调用一个MergedBeanDefinitionPostProcessor.postProcessMergedBeanDefinition一个寻找注入点的方法,然后在属性填充时候,首先是按照注入点的类型去寻找如果有多个选择@Qualifier注解寻找,如果还是有多个按照@Primary,@Priority,最后还是没有筛选出来一个唯一的注入就按照名字来查找。
?
AOP:是面向切面的编程,AOP就是可以将那些与业务不相关但是很多业务都要调用的代码抽取出来封装起来,减少重复代码,思想就是不侵入原有代码的情况下对功能进行增强。
?
AOP原理:SpringAOP是基于动态代理的,其中SpringAOP的底层的动态代理有两种方式,一种是对于实现接口的JDK Proxy方式,另外一种就是CgLib。
?
Aspect:表示切面,比如被@Aspect注解的类就是切面,可以在切面中去定义Pointcut、Advice等等。
Join point:表示连接点,表示一个程序在执行过程中的一个点,比如一个方法的执行,比如一个异常的处理,在Spring AOP中,一个连接点通常表示一个方法的执行。
Advice:表示通知,表示在一个特定连接点上所采取的动作。Advice分为不同的类型,后面详细讨论,在很多AOP框架中,包括Spring,会用Interceptor拦截器来实现Advice,并且在连接点周围维护一个Interceptor链。
Pointcut:表示切点,用来匹配一个或多个连接点,Advice与切点表达式是关联在一起的,Advice将会执行在和切点表达式所匹配的连接点上。
Introduction:可以使用@DeclareParents来给所匹配的类添加一个接口,并指定一个默认实现。
Target object:目标对象,被代理对象。
AOP proxy:表示代理工厂,用来创建代理对象的,在Spring Framework中,要么是JDK动态代理,要么是CGLIB代理。
Weaving:表示织入,表示创建代理对象的动作,这个动作可以发生在编译时期(比如Aspejctj),或者运行时,比如Spring AOP。
?
- 首先通过扫描包路径,通过ASM技术获取到类的信息,封装成BeanDefintion注册到Spring容器当中(这个操作其实就是BeanFactory的后置处理器操作)
- 然后就可以进行类的加载,进行类的实例化前操作、推断构造方法、实例化、实例化后、填充属性。(值得注意的是再实例化和实例化后之间有个BeanDefinction的后置处理器,这个后置处理器就是我们@Autowire的寻找注入点的迁前置操作。依赖注入就是再实例化后的操作)
- 最后就进行执行Aware回调、初始化前、初始化、初始化后(代理逻辑)操作
- 最后就是bean的销毁操作
循环依赖就是在创建A实例的时候里面包含着B属性实例,所以这个时候就需要去创建B实例,而创建B实例过程中也包含着A实例。 这样A实例还在创建的过程当中,所以就导致A和B实例都创建不出来。
- 一级缓存 就是我们所说的Bean存储的经过完整的生命周期的单例池
- 二级缓存 缓存未经过完整的声明周期的Bean
- 三级缓存 缓存的是ObjectFactory其实存储的是一个动态代理类的一个实现类的拉姆达表达式,执行这个拉姆达表达式就会生成这个类的代理类。
有了这三级缓存我们就来看一看,他是如何解决的。
如果出现了循环依赖的问题,我们在创建A的过程中,需要创建B。这个时候就需要将A放入三级缓存并且不会执行那个拉姆达表达式,所以这个时候需要去创建B在创建B时候需要去依赖A,就直接去三级缓存中查找,并且判断需不需要进行AOP处理,如果需要就执行那个拉姆达表达式得到代理对象,如果不需要就不需要执行,直接取出原始的对象。将取出的对象放入二级缓存中,因为这个时候A还未经过完整的生命周期所以不能放入一级缓存。这个时候其他需要依赖A对象的直接从二级缓存中去获取即可。当B创建完成,A继续执行生命周期,当A完成了属性的注入后,就可以放入一级缓存了。
- 客户端(浏览器)发送请求,直接请求到 DispatcherServlet。
- DispatcherServlet 根据请求信息调用并且找到对应的HandlerMapping,解析请求对应的 Handler。
- 然后利用对应的适配处理器,并且调用处理器开始执行,执行过程中包括解析参数,返回ModelAndView
- Model 是返回的数据对象,View 是个逻辑上的 View
- ViewResolver 会根据逻辑 View 查找实际的 View。
- DispaterServlet 把返回的 Model 传给 View(视图渲染)。
spring事务是在启动类上加一个@Configuration 和 @EnableTransactionManagement注解,然后再Service的方法上面加一个@Transactional注解就可以了。
Spring使用的事务隔离级别默认是使用MySQL数据库的隔离级别。
事务的传播机制:
?
8.1 当前不存在事务
- requiresd 新建一个事务
- requires_new 新建一个事务
- nested 新建一个事务
- mandatory 抛异常
?
8.2 当前存在事务
- requiresd 加入这个事务
- requires_new 把当前事务挂起,然后新建一个事务
- nested 记录一个SavePoint,创建一个事务作为当前事务的嵌套事务来运行外部主事务回滚的话,子事务也会回滚,而内部子事务可以单独回滚而不影响外部主事务和其他子事务。
- mandatory 加入该事务
- never 以非事务方式运行,如果当前存在事务,则抛出异常。
@configuration
@handmapping
@getmapping
@postmapping
@EnableTransactionManagement
@mapperScaner
@commpoent
@requestBody
@restcontroller
- ${}是properties文件的变量占位符,用于标签属性值和SQL内部,比如${driver}会被替换成com.mysql.jdbc.Driver。
- #{}是SQL的参数占位符,实际上会被替换成 ?
@SpringBootApplication下有三个注解:
- @SpringBootConfiguration:启用 SpringBoot 的配置类,相当于是Spring中的一个配置文件,比如说可以在这个类去注册bean,或者导入其他的配置类
- @ComponentScan:用于扫描启动类包下面的所有类的注解信息,并且注册
- @EnableAutoConfiguration:启用 SpringBoot 的自动配置机制,获取需要自动装配的所有配置类,读取所有的Start中的META-INF/spring.factories,按需加载到IOC容器中。
- 数据库引擎不支持事务
- 传播机制不支持事务,就是不以事务的情况运行
- 没有配置事务管理器数据源
- 没有被 Spring 管理
- 方法不是 public 的 @Transactional 只能用于 public 的方法上,否则事务不会失效,如果要用在非 public 方法上,可以开启 AspectJ 代理模式。
- 自身调用问题
- 异常被捕获了
- 异常的类型不匹配,默认是运行时的异常
?