spring是一个轻量级的java开发框架,解决企业应用开发的业务逻辑层和各层的耦合问题
属于低侵入式设计,代码的污染极低
Aop面向切面编程
IOC控制反转
对于主流的应用框架提供了集成的支持
这么问的话,就直接说Spring框架的好处就可以了。比如说Spring有以下特点:
轻量:Spring 是轻量的,基本的版本大约2MB。
控制反转:Spring通过控制反转实现了松散耦合,对象们给出它们的依赖,而不是创建或查找
依赖的对象们。
面向切面的编程**(AOP):**Spring支持面向切面的编程,并且把应用业务逻辑和系统服务分开。
容器:Spring 包含并管理应用中对象的生命周期和配置。
MVC**框架**:Spring的WEB框架是个精心设计的框架,是Web框架的一个很好的替代品。
事务管理:Spring 提供一个持续的事务管理接口,可以扩展到上至本地事务下至全局事务
(JTA)。
异常处理:Spring 提供方便的API把具体技术相关的异常(比如由JDBC,Hibernate or JDO抛
出的)转化为一致的unchecked 异常。
单例模式
:Spring 中的 Bean 默认情况下都是单例的。无需多说。
工厂模式
:工厂模式主要是通过 BeanFactory 和 ApplicationContext 来生产 Bean 对象。
代理模式
:最常见的 AOP 的实现方式就是通过代理来实现,Spring主要是使用 JDK 动态代理和 CGLIB 代理。
模板方法模式
:主要是一些对数据库操作的类用到,比如 JdbcTemplate、JpaTemplate,因为查询数据库的建立连接、执行查询、关闭连接几个过程,非常适用于模板方法。
AOP
aop是面向切面编程,用于将一些与业务无关的,但却对多个对象产生影响的公共行为和逻辑,抽取出来封装成一个模块,而这个模块就叫做切面。优点:减少系统中的重复代码,降低了模块间的耦合度,提高了系统的可维护性。主要用权限认证、日志、事务处理
aop的实现关键主要在于代理模式,分为JDK动态代理和CGLIB动态代理
JDK动态代理和CGLIB动态代理对比
JDK动态代理值提供接口的代理,不支持类的代理。核心InvocationHandler接口和Proxy类,InvocationHandler通过的是invoke()方法反射来代用目标类中的代码的。动态将逻辑和业务绑定在一起 。Proxy是利用InvocationHandler动态创建一个符合某一接口的实例,生成目标类的代理对象
如果代理类中没实现InvocationHandler接口,那么Aop会选择使用CGLIB来动态代理目标类。CGLIB是一个代码生成的类库,可以在运行时动态生成指定类的一个子类对象,并且覆盖其中特定的方法来实现的aop。CGLIB是通过继承的方式做的动态代理。所以如果某各类被 标记为final,那么他是无法使用CGLIB代理的
aop中切面、切点、连接点、通知之间的关系
1、切面:就是将公共行为封装成模块之后的类
2、切点:描述何时执行通知的规则
3、连接点:被切点匹配执行通知的位置
4、通知:是程序公共行为执行的代码操作
IOC
IOC是控制反转,指的是创建对象的控制权利的转移,以前创建对象的主动权是自己,而现在 是将主动权交给了spring去管理,并由容器根据配置文件去创建实例和管理各个实例之间的依赖关系,有利用功能的复用。DI依赖注入,和IOC是同一个概念的不同角度的描述,一个程序在运行时依赖IOC容器来动态注入对象 需要的外部资源
IOC就是 创建对象的时候不用在去new了,可以通过spring自动生产,使用java的反射机制,根据配置文件运行时动态去创建和管理,之后再去调用
spring的IOC是由三种注入方式:构造器注入、set方法注入、根据注解注入
1、singleton(单例模式):一个bean中只有一个实例 (默认)
2、prototype:一个bean的定义可以有多 个实例
3、request
4、session
5、global Session
客户端发起请求到dispatchservlet,dispatchservlet根据请求调用handlerMappering,解析出一个对应的handler,解析出的handler由handlerAdapter适配器去处理完成之后得到对应的moudleAndView,最后将view返回给客户
数据库不支持事务
注解放在了私有方法上
类内部调用
未捕获异常
多线程场景
传播行为(PROPAGATION) | 描述 |
---|---|
PROPAGATION_REQUIRED | 如果当前没有事务,就新建一个事务,如果已存在一个事务中,加入到这个事务中,这是最常见的选择。 |
PROPAGATION_SUPPORTS | 支持当前事务,如果没有当前事务,就以非事务方法执行。 |
PROPAGATION_MANDATORY | 使用当前事务,如果没有当前事务,就抛出异常。 |
PROPAGATION_REQUIRES_NEW | 新建事务,如果当前存在事务,把当前事务挂起。 |
PROPAGATION_NOT_SUPPORTED | 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。 |
PROPAGATION_NEVER | 以非事务方式执行操作,如果当前事务存在则抛出异常。 |
PROPAGATION_NESTED | 如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED 类似的操作 |
feign本身就包含了ribbon
feign自身是一个声明式的伪http客户端,写起来更加思路清晰和方便
feign是采用接口的注解编程方式
使用feign造成的问题:
一般在feign调用相应的service接口时候,第一次调用会因为超时而导致调用失败,所以需要设置超时时长
首先呢,beanFactory是一个factory工厂,factoryBean是一个特殊的bean接口
beanfactory是一个IOC的特殊工厂,是IOC的一个基本接口,他主要是用来管理和创建bean的实例 的,它提供了通过名字去创建bean的实例、判断此类是否在工厂中以及判断是否为单例模式
factorybean是一个特殊的bean接口,他主要有三个方法:getbean(),返回实现factorybean的类接口创建bean的实例。getbojecttype()返回该实例的类型,isSingleton()判断是否为单例模式
1、日志记录
2、权限验证
3、效率检查(个人在代码上,喜欢用注解+切面,实现校验,redis分布式锁等功能)
4、事务管理(spring 的事务就是用AOP实现的)
Spring Boot 底层实际上主要的有三个注解:
configuration
EnableAutoConfiguation
ComponentScan
读取 mate-inf下的 spring.factories 文件信息
约定大于配置 自动装配
Spring Bean的默认作用域为:singleton。它相比其他作用域的优点是系统开销小,Bean实例一旦创建成功便可重复使用。
prototype
request
session
global session
实例化,创建一个Bean对象
填充属性,为属性赋值
初始化
如果实现了xxxAware
接口,通过不同类型的Aware接口拿到Spring容器的资源
如果实现了BeanPostProcessor接口,则会回调该接口的postProcessBeforeInitialzation
和postProcessAfterInitialization
方法
如果配置了init-method
方法,则会执行init-method
配置的方法
销毁
容器关闭后,如果Bean实现了DisposableBean
接口,则会回调该接口的destroy
方法
如果配置了destroy-method
方法,则会执行destroy-method
配置的方法
###
使用context.getBean(A.class),获取容器内的单例A(若A不存在,就会走A这个Bean的创建流程),显然初次获取A是不存在的,因此走A的创建之路~
实例化A(注意此处仅仅是实例化),并将它放进缓存(此时A已经实例化完成,已经可以被引用了)
初始化A:@Autowired依赖注入B(此时需要去容器内获取B)
为了完成依赖注入B,会通过getBean(B)去容器内找B。但此时B在容器内不存在,就走向B的创建之路~
实例化B,并将其放入缓存。(此时B也能够被引用了)
初始化B,@Autowired依赖注入A(此时需要去容器内获取A)
此处重要:初始化B时会调用getBean(A)去容器内找到A,上面我们已经说过了此时候因为A已经实例化完成了并且放进了缓存里,所以这个时候去看缓存里是已经存在A的引用了的,所以getBean(A)能够正常返回
B初始化成功(此时已经注入A成功了,已成功持有A的引用了),return(注意此处return相当于是返回最上面的getBean(B)这句代码,回到了初始化A的流程中~)。
因为B实例已经成功返回了,因此最终A也初始化成功
到此,B持有的已经是初始化完成的A,A持有的也是初始化完成的B
我们开始三级缓存,首先通过A进入已经缓存,去寻找发现没有初始化的B(并且他使用了singlobject 提前曝光了自己) 在去二级缓找发现没有B,就去开启了创建B,
B开始从一级缓存寻找A发现没有A,但是鱿鱼我们的A刚刚提前曝光了自己,我们B可以获取到A(但是这个A并不是初始化的而是实例化的但是A可以被引用)我们B待着A去三级缓存最实例化了,现在我们的B是实例化的,我回到刚刚的A在去获取b发现可以获得一个实例化好的B,然后在去三级缓存最后把自己也成功了实例化了这个时候我们的A和B都已经实例化过来就不会出现循环依赖的问题了
这是别人总结的三级缓存
第一级缓存:用来保存实例化、初始化都完成的对象
第二级缓存:用来保存实例化完成,但是未初始化完成的对象
第三级缓存:用来保存一个对象工厂,提供一个匿名内部类,用于创建二级缓存中的对象
假设一个简单的循环依赖场景,A、B互相依赖。
A对象的创建过程:
创建对象A,实例化的时候把A对象工厂放入三级缓存
A注入属性时,发现依赖B,转而去实例化B
同样创建对象B,注入属性时发现依赖A,一次从一级到三级缓存查询A,从三级缓存通过对象工厂拿到A,把A放入二级缓存,同时删除三级缓存中的A,此时,B已经实例化并且初始化完成,把B放入一级缓存。
接着继续创建A,顺利从一级缓存拿到实例化且初始化完成的B对象,A对象创建也完成,删除二级缓存中的A,同时把A放入一级缓存
最后,一级缓存中保存着实例化、初始化都完成的A、B对象
因此,由于把实例化和初始化的流程分开了,所以如果都是用构造器的话,就没法分离这个操作,所以都是构造器的话就无法解决循环依赖的问题了。
不可以,主要是为了生成代理对象。
因为三级缓存中放的是生成具体对象的匿名内部类,他可以生成代理对象,也可以是普通的实例对象。
使用三级缓存主要是为了保证不管什么时候使用的都是一个对象。
假设只有二级缓存的情况,往二级缓存中放的显示一个普通的Bean对象,BeanPostProcessor
去生成代理对象之后,覆盖掉二级缓存中的普通Bean对象,那么多线程环境下可能取到的对象就不一致了。
谈到beans线程安全那么就看bean是否有多种状态,如果始终只有一种状态 就是线程安全的,否则需要自己去保证线程的安全,可以采用将singleton变为prototype
autowired是按照bytype注入的,是由spring提供的,resource是j2ee提供的,按照名字去注入的
如果一个接口有多个实现,那么采用Qualifier
什么时候spring事务不生效
innodb支持事务,mysiam不支持事务
私有和受保护的事务不生效
如果定义了事务注解被别的任务调用此时事务不生效
默认的异常是运行时异常所以要想事务注解生肖必须加上rollbackfor才生效