目录
spring springmvc springboot springcloud区别
spring springmvc springboot springcloud区别
spring:控制反转(IOC)和面向切面(AOP)的容器框架。ioc是容器管理bean;aop公共提出来,比如鉴权、操作日志等等;
springMVC是spring的一部分,spring还包括springJDBC、springTest等;
springBoot秉承约定大于配置的原则,省去spring复杂的配置,基于spring的一套快速开发整合包,可以快速搭建一个spring服务;
springCloud是基于springBoot开发的微服务包,专注于服务管理。Eureka:服务注册、zuul:网关;security:安全控制(权限)、ribbon:负载均衡、hystrix:熔断器;feign:http客户端
springMVC 工作流
https://blog.csdn.net/floating_dreaming/article/details/89089214
1. 用户请求到DispatcherServlet(中央处理器)到HanderMapping(映射处理器)获取对应的Handler;
2. DispatcherServlet请求到对应适配器处理器HandlerAdapter,HandlerAdapter调用对应的后端处理器(Controller)处理相应业务逻辑,返回view给HandlerAdapter返回DispatcherServlet,
3. DispatcherServlet再到视图解析器ViewResolver中获取对应的view,DispatcherServlet根据Model渲染数据到view,再返回客户。
spring设计模式
1. 简单工厂:BeanFactory,配置bean的xml,加载xml到XmlBeanFactory factory中,factory.getBean(beanName)得到对应的bean。三级缓存中存放的就是它的map。意义:用它来注入依赖对象
2. 工厂模式:FactoryBean,getObject的时候,返回的不是FactoryBean对象,而是T.getObject的对象。比如jdbc,SqlSessionFactoryBean,返回的就是一个sqlSession的对象
3. 单例模式:spring默认注入的bean基本上都是单例。提供一个全局可以访问此类的唯一对象
4. 适配器模式: HandlerAdapter,类似于外界统一提供usb接口,用户如果需要typc-c,则需要一个usb转type-c的接口(适配器)。将类的接口转化为用户所期待的,使得不能在一起工作的类可以在一起工作。实现解藕,单一指责。
spring中HandlerAdapter就是一个很清晰的例子。如果新增一个Controller,则需要在DispatcherServlet中判断新增的Controller然后处理,则会增加很多if else,代码很难维护,并且需要修改spring源码(根本不可能),spring也不会穷举所有Controller,因为不同项目组使用是不一样的。所以提出了适配器,就是XXXController随意创建,如果现有HandlerAdapter适配器处理不了,则可以定义新的XXXHandlerAdapter(support方法:是否是XXXController对象,handle调用XXXController的处理方法),该适配器只要实现了implements HandlerAdapter,则在项目启动的时候,DispatcherServlet会把其放到List中,来一个请求会根据请求获取对应的HandlerAdapter(List依次support方法判断是不是)进行处理(调用handle)。
举例:https://blog.51cto.com/haolloyin/346128
简化spring代码:https://www.cnblogs.com/tongkey/p/7919401.html
5. 责任链模式:请求被传递,直到被处理,如果没处理传递给下一条处理器处理。
List, for循环依次每个filter进行处理。过滤器拦截器可以依次传递下去执行
6. 动态代理模式:aop,如果类实现了接口用jdk;如果没有实现接口,用gclib
Bean
作用域
1. 单例:容器启动时,唯一注入一个,在整个容器中只有一个;
2. 多例(原型):多个实例,在getBean或者注入其他class实例时,都会生成一个新的bean。每次访问的时候bean都不是一样的;
3. request:一个请求生命周期,对于每次HTTP请求,使用request定义的Bean都将产生一个新实例
4. session:一个会话周期,对于每次HTTP Session,使用session定义的Bean都会产生一个新实例
5. globalSession:每个全局的HTTP Session,使用session定义的Bean都将产生一个新实例
生命周期
https://www.jianshu.com/p/1dec08d290c1
https://www.cnblogs.com/javazhiyin/p/10905294.html
实例化(调用构造方法)
属性注入(setter)
aware相关(BeanNameAware.setBeanName -> BeanFactoryAware.setBeanFactory -> AppplicationContextAware.setAppplicationContext -> BeanPostProcessor.postProcessorBeforeInitionlization预处理 -> InitionlizationBean.afterPropertiesSet)
初始化
BeanPostProcessor.postProcessorAfterInitionlization -> bean可以销毁了? -> DisposableBean.destory()
销毁
结束
AOP代理 2种
https://www.jb51.net/article/191673.htm
静态代理:手动写的,运行前已存在;被代理类变、代理类也需要变,基本一一对应,难以维护
动态代理:自动生成该对象的代理对象,把该对象放在target中,然后该对象调用方法的时候会先调用invoke函数(jdk)或者intercept(gclib)函数。jdk、gclib两种方式
jdk、gclib区别:
1. jdk需要类实现接口,gclib不需要;
2. jdk是自带的,gclib需要引入第三方库,spring-core中就有gclib;
3. cglib是生成一个代理类的子类,所以代理类的static、final、private方法都不会被代理
spring中:
如果类实现了接口用jdk;如果没有实现接口,用gclib
spring循坏依赖
spring如何解决循环依赖
构造方法注入无法解决
setter注入可以解决(A依赖B,B依赖A),通过三级缓存。初始化一个对象时包括:实例化对象,然后填充属性,initBean。实例化也就是调用构造器(必须用构造器,所以构造器注入的无法解决)
实例化bean,这里A构造器,为半成品
然后属性B字段的时候,发现B没初始化,所以又去初始化B,
实例化B的时候,把A的半成品塞进去A字段属性,
递归B塞给A对象的B字段。
三级缓存是什么
三级缓存 map:singletonObjects(完全初始化的bean集合),earlySingletonObjects(半成品,叫做代理),singletonFactories(单例对象工厂)
半成品的bean,第一次放在singletonFactories中,第二次访问放到earlySingletonObjects,完成初始化好了放到singletonObjects。
为什么必须要三级缓存
如下图,如果类有aop,则最后得到的是该类的代理(因为该类被aop,所以每次访问该对象的时候都会先经过aop,所以最后得到的是这个对象+aop,也就是该对象的代理),所以和放在B中的A对象不是一个东西,所以需要三级缓存,singletonFactories会返回A的代理(如果A没有aop,则返回的是A)
那么earlySingletonObjects直接放代理不就得了,原因如下:
因为通过singletonFactories工厂获得对象代理比较耗时间,但是后续不一定能会用到,1%的概率用到,所以这里没必要生成代理,但是第二次访问了,则已经很耗时间的创建出代理了,所以直接放到earlySingletonObjects备用。比如A依赖B,B依赖C,C依赖B,A、C会放到singletonFactories中,但是不会被用到。
构造方法注入为什么无法解决循环依赖
spring实例化bean成办成品的时候依赖构造方法,所以...
Spring事务
https://www.cnblogs.com/mseddl/p/11577846.html
事务四个特性
原子性:不可分割
一致性:事务执行前后数据完整性保持一致,不能出现部分成功、部分失败
隔离性:一个事务执行,不应该受其他事务影响
持久性:一个事务执行结束后,都要持久化存储
spring事务配置方式(两种)
编程式:直接statement 执行sql,commit这样;代码级别,不推荐
声明式:xml配置或者注解,方法级别,推荐。
spring事务传播机制 7种
@Transactional(propagation=Propagation.REQUIRED)
解决事务嵌套的时候怎么处理,是合并还是挂起?
1. propagation_requested: 如果当前事务在一个事务中,则合并;如果上面没有事务,则新建一个事务执行;
2. propagation_reques_new:如果当前事务在一个事务中,则外事务挂起,等当前事务执行结束之后再接着执行外事务;如果上面没有事务,则新建一个事务执行;
3. propagetion_support:如果当前事务有外事务,合并;如果没有,非事务方式执行;
4. propagation_not_support:如果当前事务有外事务,挂起,非事务执行,结束后执行外事务,并且如果当前执行失败也不会影响外事务;
5. propagation_never: 如果当前事务有外事务,抛出异常;
6. propagation_mandatory: 如果当前事务无外事务,抛出异常;
7. propagation_nested: 可以保存状态保存点,如果失败各自回滚各自的,但是如果子事务处理不了异常,也会引起全部回滚
常引发安全性问题
1. 脏读:一个事务读了另一个事务未提交的数据;
2. 不可重复读:一个事务读了另一个事务update的数据,导致前后读的数据不一致;
3. 幻读:一个事务读了另一个事务insert的数据,导致前后读区数据不一致。
事务隔离级别 5种
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
isolation_default: 采用数据库设置的默认级别
isolation_read_uncommit: 未提交读,当前事务可以读其他未提交事务的数据,脏读、不可重复读、幻读都会发生;
isolation_read_commit: 提交读,当前事务只能提交其他事务已提交的数据,脏读可避免;oracle默认
isolation_repeatable_read: 可重复读,当前事务多次读相同字段值是一样,但幻读不可避免;mysql默认
isolation_serialization: 序列化,最慢,但是都可以避免,因为当前事务涉及到的数据都会锁住;
事务实现原理
aop动态代理,spring在加载类、方法时,针对@Transactional注解进行处理,前后增加配置信息,生成相应的代理,达到如果成功则commit,失败rollback。数据库底层的回滚通过binlog、redo log实现。