溪源的Java笔记—Spring

溪源的Java笔记—Spring

前言

在现如今的Java开发中有一个不得不提的工具,那就是Spring,SpringJava EE编程领域的一个轻量级开源框架, 是为了解决企业级编程开发中的复杂性,实现敏捷开发的应用型框架 。本期博客带领大家去了解Spring相关的知识点。

线程并发与线程安全可参考我的博客:溪源的Java笔记—线程并发与线程安全

正文

Spirng

SpringJava EEJava企业应用)编程领域的一个轻量级开源框架, 是为了解决企业级编程开发中的复杂性,实现敏捷开发的应用型框架 。
所谓敏捷开发是指以用户的需求进化为核心,采用迭代、循序渐进的方法进行软件开发方式。

Spring常用的七大模块
Spring Core
Spring Core封装包是框架的最基础部分,提供IOC控制反转和DI依赖注入特性:

  • IOC控制反转:一种编程思想,借助容器来管理对象之间的依赖关系。
  • DI依赖注入DI是实现IOC的一种手段,其内部是通过Java的反射机制来实现的。

Spring Context
Spring Context构建于Core封装包基础的上下文封装包,提供了一种框架式的对象访问方法,Spring 上下文包括企业服务,例如JNDI、电子邮件、国际化、校验等。
JNDI提供统一的客户端API,通过不同的访问提供者接口JNDI服务供应接口(SPI)的实现.

Spring Dao
Spring Dao对数据访问进行封装:

  • Dao中来管理异常处理和不同数据库提供商抛出的异常。
  • 简化了错误处理,并极大的降低了需要编写的异常代码数量,比如打开和关闭连接。

Spring ORM
Spring ORM提供了对象关系映射,包括MybatisHibernate等。这些对象关系映射框架都遵从Spring的通用事务和Dao的层次结构。

Spring AOP
Spring AOP声明式事务,是一种OOP的延伸,用于给不存在继承关系的对象之间引用一个公共行为。

Spring Web
Spring Web,Spring中的Web提供了基础的针对WEB开发的集成特性。

Spring Web MVC
Spring Web MVC是一个Model-View-Controller Web框架,它是基于前端控制器Servel并发处理http请求并进行展示。

Spring IOC容器

在这里插入图片描述

Spring注册Bean到IOC容器的流程:

  1. 读取Bean的配置信息
  2. 根据Bean注册表实例化Bean
  3. Bean实例放在Spring容器中
  4. 应用程序通过Bean缓存池(内部事通过HashMap来实现的)来使用Bean

Spring Bean的作用域:

  • 单例模式(默认)Spring创建Bean的原则是不等bean创建完成就将beanFactory提早放到缓存中,如果其他bean依赖这个bean可以直接使用,这种三级缓存的机制很好地解决了循环依赖的问题。

  • 多例模式(原型模式):每次使用时都会创建新的Bean实例,Bean调用setAllowCircularReferences(false)来禁止循环依赖,否则出现循环依赖会直接抛异常。

  • Request模式:一次 request 一个实例, 当前 Http 请求结束,该 bean 实例也将会被销毁.

  • Session模式:在一次 Http Session 中,容器会返回该 Bean 的同一实例。而对不同的 Session 请求则会创建新的实例,该 bean 实例仅在当前 Session 内有效。

  • global Session模式(不常使用):在一个全局的 Http Session 中,容器会返回该 Bean 的同一个实例,仅在 使用portlet context 时有效。

xml配置文件中添加配置属性scope= prototype使用,或者使用注解@Scope("prototype")

Bean的生命周期

  1. Bean的实例化:由BeanFactory读取Bean定义文件,并生成各个实例。
  2. IOC依赖注入:执行Bean的属性依赖注入。
  3. BeanName:执行BeanNameAwaresetBeanName(), 此处传递的就是 Spring 配置文件中 Beanid
  4. BeanFactory:执行BeanFactoryAwaresetBeanFactory()
  5. 传入Spring上下文:执行ApplicationContextAware接口的setApplicationContext()
  6. 初始化预处理BeanPostProcessorspostProcessBeforeInitialization()
  7. 构造:如果BeanSpring配置文件中配置了init-method属性会自动调用其配置的初始化方法。
  8. 加工构造后BeanPostProcessorsprocessAfterInitialization(),如果是单例模式会将Bean的实例放在Bean的缓存池中。
  9. 自动清理阶段:执行DisposableBeandestroy()
  10. 自配置清理:如果这个BeanSpring配置中配置了destroy-method属性,会自动调用其配置的销毁方法。

Spring常用注解

1.自动注入(也叫Spring自动装配)

  • @Resources: 默认是byname
  • @Autowired: 默认是bytype ,如果存在多个相同类型的,通过变量名可以与Beanid或者name进行匹配
  • @Autowired+@Qualifier(“name”): 可以实现等同 @Resources的效果

2.声明Bean定义
在这里插入图片描述

3.@Configuration:用于定义配置类,可替换xml配置文件,被注解的类内部包含有一个或多个被@Bean注解的方法

  • @EnableAutoConfiguration ,如果使用@Configuration的话需要在启动类添加这个注解,用于扫描配置类。
  • 等同于在spring配置文件中 <context :annotation-config>

4.@Constroller与@RestConstroller的区别

  • @Constroller使用了视图解析器
  • @RestConstroller没有使用视图解析器,不能打开jsp

5.@ComponentScan 对Bean进行扫描

  • 等同于在Spring配置文件中 <context:component-scan base-package="包名">

6.@Bean和@Component的区别
相同点:两者的结果都是为Spring容器注册Bean.
不同点@Component 通常是通过类路径扫描来自动侦测以及自动装配到Spring容器中。
    @Bean 注解通常是我们在标有该注解的方法中定义产生这个bean的逻辑。

AOP面向切面编程

应用场景:需要多个不具有继承关系的对象引入一个公共行为
使用方法

  • XML配置文件中 加入<aop:aspect-autoproxy>开启AOP支持,并在XML中定义切面
  • 使用@Aspect注解定义切面,启动类加上@EnableAspectJAutoProxy(proxyTargetClass =true)来开启AOP支持

Spring AOP的实现方式

  • JDK动态代理 :用于实现接口的类的动态代理

  • Cglib动态代理:用于没实现接口的类的动态代理

动态代理简单来说是通过一个代理类来根据不同场景选择不同的实现类。可以参考设计模式中的代理模式。

JDK动态代理

  • 类对象必须实现接口
  • JDK动态代理,背后是借助Java多态的特性,因为JDK动态代理生成的class文件已经继承了Proxy,而Java是单继承的,不能继承目标对象,只能实现目标对象,所以是基于JDK动态代理是基于接口的。

JDK动态代理主要涉及两个类:

  • InvocationHandler:是一个接口,通过实现该接口定义横切逻辑,并通过反射机制调用目标类的代码,动态将横切逻辑和业务逻辑编制在一起。
  • Proxy: 利用 InvocationHandler 动态创建 一个符合某一接口的实例,生成目标类的代理对象。

Cglib动态代理

  • Cglib是一个强大的高性能,高质量的代码生成类库, 可以在运行期扩展 Java 类与实现 Java 接口,CgLib 封装了asm,可以再运行期动态生成新 的 class

使用AOP主要的应用场景:

  1. Authentication 权限检查
  2. Caching 缓存
  3. Context passing 内容传递
  4. Error handling 错误处理
  5. Lazy loading 延迟加载
  6. Debugging 调试
  7. logging, tracing, profiling and monitoring日志记录,跟踪,优化,校准
  8. Performance optimization性能优化,效率检查
  9. Persistence 持久化
  10. Resource pooling资源池
  11. Synchronization同步
  12. Transactions 事务管理

循环依赖
循环依赖指的是Spring Bean之间相互依赖的情况。

解决循环依赖的前置条件:

  • 出现循环依赖的Bean必须要是单例
  • 依赖注入的方式不能全是构造器注入的方式

Spring在创建Bean的时候默认是按照自然排序来进行创建的,IOC容器先创建A,再创建B

  • A、B均采用setter方式相互注入
  • A采用setter方式获取B,B采用构造器方式获取A

以上两种情况的循环依赖时可以解决循环依赖,其他情况都会异常抛出。

解决循环依赖的流程:

  1. 当A完成了实例化并添加进了Bean的缓存池(一级缓存)中。
  2. 为A进行属性注入了,在注入时发现A依赖了B,会去实例化B。
  3. 在创建B的时候,因为A已经放在Bean的缓存池(一级缓存)当中了,所以无论B采用的setter方式还是构造器方式都可以获取A。

这里需要注意的是:

  • 如果A采用的是构造器方式,创建A时发现依赖于B,于是会先去创建B,但是B又依赖于A,并且缓存没有A,所以会直接因为循环依赖,导致启动异常。
  • @Autowired@Resources实际上都是setter方式注入依赖。

AOP在解决循环依赖的问题上,会使用三级缓存的方式去完成:

  • singletonObject:一级缓存,这里的bean是已经创建完成的,一旦进行getBean操作时,我们第一时间就会寻找一级缓存
  • earlySingletonObjects:二级缓存,该缓存所获取到的bean是提前曝光出来的,是还没创建完成的。
  • singletonFactories:三级缓存为早期曝光对象工厂,这个工厂的目的在于延迟对实例化阶段生成的对象的代理,只有真正发生循环依赖的时候,才去提前生成代理对象。

知识点:

  1. 添加单例缓存,当bean被创建完以后进行的操作。这时候说明bean已经创建完,删除二三级缓存,直接留下一级缓存,并且注册该bean
  2. 二级缓存可以提前曝光被其他Bean所引用,它可以解決循环依赖。但是二级缓存无法解决AOP+循环依赖的问题,因为不可能每次执行singleFactory.getObject()方法都给我产生一个新的代理对象,所以还要借助另外一个缓存来保存产生的代理对象。
  3. 三级缓存是用来来保存产生的代理对象,但它并没有所谓性能上的“提升”。

Spring MVC

  • 模型(Model)代表数据控制器。数据的读取,插入,更新都是由模型来负责。
  • 视图(View)是展示给用户的最终页面。视图负责将数据以用户友好的形式展现出来。
  • 控制器(Controller)是模型,视图以及其他任何处理 HTTP 请求所必须的资源之前的中介
    在这里插入图片描述

SpringMVC处理请求的的控制器

  • DispatcherServlet前端控制器:接收用户请求
  • HandlerAdapter处理器适配器:选择合适的处理器,并且调用相应功能处理方法
  • ViewResolver视图解析器:用于视图解析

用户发送请求和返回响应的流程:

  1. 发送请求 至DispatcherServlet
  2. 映射处理器 获取处理器映射至 DispatcherServet
  3. HandlerAdapter进行处理器适配
  4. 调用处理器相应功能处理方法 (获得 ViewmodelDispatcherServlet ) 这里涉及Controller
  5. ViewResolver 接收View 进行视图解析
  6. Model加入到View 中进行视图渲染
  7. DispatcherServlet返回响应

Spring 事务管理

声明式事务与编程式事务

  • 声明式事务管理使业务代码不受污染,一个普通的POJO对象,只要加上注解就可以获得完全的事务支持。
  • 声明式事务管理它的最细粒度只能作用到方法级别,而编程式事务那样可以作用到代码块级别。

Spring对事物的支持
Spring并不直接管理事务,而是提供了多种事务管理器,他们将事务管理的职责委托给Hibernate或者JTA等持久化机制所提供的相关平台框架的事务来实现。
常见的有:

  • Jdbc事务mybayis通常使用这个方式进行事务管理
  • Hibernate事务Hibernate的事务管理方式
  • Java持久化API事务JPA就是基于这种方式
  • Java原生API事务 :使用以上多种事务

Transational注解
@Transational是一种声明式事务的一种方式,利用的是java的反射机制,实现动态代理,再借助泛型实现,能够忽略class的类型,基于AOP做事务上的操作。

  • Service类前加上@Transactional,声明这个service所有public方法需要事务管理。
  • @Transactional放在方法前面需要方法是public级别的
@Transactional(propagation = Propagation.SUPPORTS,isolation = Isolation.READ_COMMITTED,rollbackFor={
    
    BizException.class})

@Transactional参数说明:

  • propagation :用于指定传播行为
  • isolation:用于指定隔离级别
  • rollbackFor:当发生BizException进行事务回滚

事务的特点

  • 原子性:多条指令作为一个集体,要么都执行,要么都不执行
  • 一致性:比如 a=100 b=100, a、b之间交易 总和一定是200
  • 隔离性:一个事务不受其他事务的影响
  • 持久性:事务一旦提交,它对数据库的改变时永久性的

传播行为
场景:当一个事务去调用另一个事务时,用于定义事务的边界:

  1. Propagation.REQUIRED:表示当前方法必须运行在事务中,如果当前事务存在,方法将会在该事务中运行,否则,会启动一个新的事务。(默认)
  2. Propagation.SUPPORTS:表示当前方法不需要事务上下文,但是如果存在当前事务的话,那么该方法会在事务中运行。
  3. Propagation.MANDATORY:表示当前方法必须运行在事务中,如果事务不存在,则会抛出异常。
  4. PROPAGATION_REQUIRES_NEW:表示当前方法必须运行在自己的事务。一个新的事务将会被启动。如果存在当前事务,在该方法执行期间,当前事务会被挂起。如果使用JtaTransactionManager作为事务管理器,则需要访问TransactionManager对象。
  5. PROPAGATION_NOT_SUPPORTED:表示该方法不应该运行在事务中。如果存在当前事务,在该方法运行期间,当前事务将会被挂起。如果使用JtaTransactionManager作为事务管理器,则需要访问TransactionManager对象。
  6. PROPAGATION_NEVER:表示该方法不应该运行在事务上下文中。如果当前正有一个事务在运行,则会抛出异常。
  7. PROPAGATION_NESTED:表示如果当前已经存在一个事务,那么该方法将会在嵌套事务中运行。嵌套的事务可以独立于当前事务进行单独地提交或回滚。如果当前事务不存在,那么行为和Propagation.REQUIRED一致。但要注意当前事务管理器是否支持嵌套事务。

隔离级别
场景: 隔离级别定义一个事务可能受其他并发事务活动活动影响的程度。
mysql事务隔离级别 默认是 :可重复读
在这里插入图片描述

此外还有:DEFAULT使用数据库默认的事务隔离级别.

脏读:读取出来的数据是无效数据。( 脏读发生在一个事务读取了被另一个事务改写但尚未提交的数据时。如果这些改变在稍后被回滚了,那么第一个事务读取的数据就会是无效的)

幻读:读取数据时读取出来多条历史数据。( 当一个事务(T1)读取几行记录后,另一个并发事务(T2)插入了一些记录时,幻读就发生了。在后来的查询中,第一个事务(T1)就会发现一些原来没有的额外记录。)

不可重复读:数据由于锁机制,只能被读取一次。 (发生在一个事务执行相同的查询两次或两次以上,但每次查询结果都不相同时。这通常是由于另一个并发事务在两次查询之间更新了数据)

分布式事务
Java 事务编程接口(JTA)和 Java 事务服务 (JTS) 为 J2EE 平台提供了分布式事务服务。

Spring boot

在这里插入图片描述

SpringBoot是基于Spring4的条件注册的一套快速开发整合包, 实现了自动配置,降低了项目搭建的复杂度。

SpringBoot的优势

  • 可快速构建独立的Spring应用程序
  • 嵌入的 Tomcat,无需部署 WAR 文件
  • 简化了Maven配置
  • 自动配置Spring
  • 提供了actuator模块用于健康检查
  • 无需任何XML配置

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/weixin_40990818/article/details/109921826