08-Java框架

nobility 发布于 2020-01-15 925 次阅读


框架

IOC和AOP

IOC就是控制反转,以前创建对象都是由自己控制,如果出现修改的需求,就必须到源代码中进行修改,不符合开闭原则,耦合度高,使用工厂模式虽然可以不对源代码进行修改,但是手动管理工厂又有些麻烦,也不太符合开闭原则;现在将控制权转移到Spring容器中,由容器根据配置文件去创建实例和依赖管理;通过依赖注入,将对象依赖属性进行注入,Spring的IOC注入方式有构造器注入、setter方法注入、根据注解注入

AOP就是面向切面编程,使用代理机制实现代码复用的目的,简单的说就是在方法执行前后添加一些代码,Spring的AOP是[动态代理](# 代理模式)的,如果要代理的对象实现了个接口,就使用JDK动态代理;没有实现接口,就使用CGlib动态代理

也可以使用AspectJ,Spring AOP中已经集成了AspectJ,Spring AOP是属于运行时增强,而AspectJ是编译时增强,Spring AOP基于代理,而AspectJ基于字节码操作,如果切面比较少,那么两者性能差异不大,切面多就使用AspectJ

SpringBean的生命周期

  1. 实例化对象,执行构造方法
  2. 注入属性,执行setter方法
  3. 若该类实现了BeanNameAware接口,会执行setBeanName()方法
  4. 若该类实现了BeanFactoryAware接口,会执行setBeanFactory()方法
  5. 若该类实现了ApplicationContextAware接口,会执行setApplicationContext()方法
  6. 若有一个类实现了BeanPostProcessor接口,会执行postProcessBeforeInitialization()方法
  7. 若该类实现了InitializingBean接口,会执行afterPropertiesSet()方法
  8. 若该类配置了init-method方法,会执行该初始化方法
  9. 若有一个类实现了BeanPostProcessor接口,会执行postProcessAfterInitialization()方法
  10. 执行该类自身的业务方法
  11. 若该类实现了DisposableBean接口,会执行destroy()方法
  12. 若该类配置destroy-method方法,会执行该销毁方法

Spring循环依赖

循环依赖在手动创建对象时没有任何问题,但是在IOC容器中创建时,就会存在创建死循环和AOP代理问题,用户在getBean时,如果单例池中有就直接从单例池中拿,没有就使用ObjectFactory创建,创建好后会放入单例池并返回

spring创建单例对象的步骤是,实例化对象、进行属性填充、执行bean的初始化方法,spring在创建bean时就给bean打上标签,如果递归调用回来发现还正在创建,就说明发生了循环依赖,spring通过三级缓存处理循环依赖,三级缓存分别

  • 一级缓存singletonObjects,缓存已经经历完整生命周期的bean
  • 二级缓存earlySingletonObjects,缓存未进行属性填充的原始对象
  • 三级缓存singletonFactories,缓存bean工厂ObjectFactory,这个bean工厂是用来生成原始对象进行AOP之后的代理对象

如果只有一级缓存,就会出现创建死循环问题,先实例化A对象,发现依赖B,就创建B对象,发现B又依赖A对象,但是由于A对象没有创建完成,所以一级缓存中并没有A对象,导致又去创建A,所以需要二级缓存,用来提前缓存没进行属性填充的A对象

如果只有二级缓存,就会出现AOP代理问题,因为spring的代理对象,是在BeanPostProcessor的后置处理中创建,所以在B又发现依赖A时,在二级缓存中获取到的A对象是未经过代理的,导致最后创建出来的A对象是代理对象,而B对象依赖的A对象却不是代理对象

所以如果有三级缓存,步骤就成了,先实例化A对象,先将A通过ObjectFactory提前曝光到三级缓存,发现依赖B,就创建B对象,发现B又依赖A对象,就从三级缓存中获取到A的ObjectFactory,通过ObjectFactory调用提前引用方法,提前创建出A的代理对象,在之后的BeanPostProcessor的后置处理中不再创建A的代理对象,而是直接从缓存中获取,之后创建出的A和B都是代理对象,并且相互引用的也是代理对象,从而解决循环依赖问题

Spring中的事务

Spring将[事务](# 数据库事务四大特性)抽象成3个接口,事务属性定义接口、事务管理器接口,事务状态接口,事务管理器接口有很多种实现(JPA、JDBC、分布式事务),大致流程就是,定义好事务属性,比如说回滚规则、隔离级别、传播行为,在通过事务管理器执行事务,改变事务的状态;也可以通过声明式事务,就是基于AOP,通过配置,自动提交或回滚

传播行为有

  • PROPAGATION_REQUIRED:必须,方法必须在事务中运行,如果没有就会创建一个新事务
  • PROPAGATION_SUPPORTS:忍受,方法既可以在事务中运行,也可以不在事务中运行
  • PROPAGATION_MANDATORY:强制,方法必须在事务中运行,否则就会抛出异常
  • PROPAGATION_REQUIRED_NEW:方法必须在自己的事务运行,如果已经有事务,在方法运行期间会将之前的事务挂起,自己重新开启一个事务
  • PROPAGATION_NEVER:方法不能在事务中运行,如果已经有事务,会抛出异常
  • PROPAGATION_NOT_SUPPORTS:方法不能事务中运行,如果已经有事务,在方法运行期间会将之前的事务挂起
  • PROPAGATION_NESTED:不管事务是否存在,都会新建一个事务

SpringMVC请求流程

  1. [客户端发送请求](# 浏览器输入URL经历的流程),到SpringMVC的中央处理器DispatcherServlet
  2. DispatcherServlet通过HandlerMapping处理映射器,查询出执行链,(就是拦截器、控制器)
  3. DispatcherServlet在通过HandlerAdapter处理适配器,执行对应的执行链,将执行结果并返回给DispatcherServlet
  4. DispatcherServlet通过视图解析器进行渲染,如果不是视图对象,就不需要视图解析器这步
  5. 之后再将内容响应给客户端

MyBatis中#和$的区别

  • #是预编译处理,$是字符串替换
  • Mybatis在处理#时,会将sql中的#替换为?号,调用PreparedStatement的set方法来赋值,Mybatis在处理$时,会直接替换成变量
  • 使用#可以防止SQL注入,提高系统安全性

MyBatis分页

Mybatis使用RowBounds对象进行分页,针对ResultSet结果集执行的内存分页,而不是物理分页,可以在sql内直接拼写带有物理分页的参数,来完成物理分页功能,也可以使用分页插件来完成物理分页

分页插件的基本原理,是使用Mybatis提供的插件接口,在插件的拦截方法内,拦截准将要执行的sql并重写,根据dialect方言,添加对应的物理分页语句和参数

MyBatis缓存

MyBatis整体分为三级缓存,分别是本地会话缓存、namespace缓存、第三方缓存,本地会话缓存是默认开启的

MyBatis一级缓存的生命周期和SqlSession一致,缓存共享范围是SqlSession,每个SqlSession都有一个Executor,Executor中又有一个LocalCache,当用户发起查询时, MyBatis会生成MappedStatement,先在LocalCache进行查询,如果缓存命中就直接返回,没有命中再查询数据库,并写入缓存,然后返回

如果想要多个SqlSession之间共享缓存,就需要开启二级缓存,会使用CachingExecutor装饰Executor,在查询一级缓存前,先在CachingExecutor进行二级缓存的查询

MyBatis留出一个Cache接口,所以就使用第三方写好的缓存组件,比如redis-cacheehcache-cache

SpringBoot自动配置原理

自动配置就是为了让第三方的配置类加载到IOC容器中

  • 这些配置类应该都是以用户的配置类优先,就是说用户配置了之后,底层的自动配置类就会失效
  • 而且也应该绑定配置文件,可以通过修改配置文件,进行修改配置

在启动类上面的SpringBootApplication注解,主要包含了SpringBootConfiguration、Configuration、EnableAutoConfiguration,EnableAutoConfiguration注解又包含AutoConfigurationPackage和Import注解,导入了一个注册类,再经过一层层的调用,最终会导入META-INF/spring.factories配置文件中的内容,内容就是自动配置类的全类名数组,再使用反射机制进行加载

此作者没有提供个人介绍
最后更新于 2020-01-15