Spring笔记

一、谈谈你对 Spring 的理解

  Spring 是一个开源框架,为简化企业级应用开发而生。Spring 是一个 IOC 和 AOP 容器框架。

  其中 IoC (Inversion of Control)是控制反转的意思,这是一种面向对象编程的设计思想。在传统的 java 开发模式中,当需要一个对象时,我们要主动去创建它并进行属性赋值,然后才能去使用这个对象。

  而在 spring 开发模式中,Spring 容器使用了工厂模式为我们创建了所需要的对象,不需要我们自己创建了,直接调用 spring 提供的对象就可以了,这是控制反转的思想。简单来理解其实就是把获取依赖对象的方式,交由 IoC 容器来实现,由“主动创建”变为“被动获取”。

  说到IOC 就不得不说DI(Dependency Injection),DI是依赖注入的意思,它是IOC实现的一种方式。Spring 使用 JavaBean对象的 set 方法或者带参数的构造方法为我们在创建所需对象时将其属性自动设置所需要的值的过程,就是依赖注入的思想。

   AOP (Aspect-Oriented Programming),意思是面向切面编程,是对传统 OOP(Object-Oriented Programming, 面向对象编程) 的补充。就是把可重用的功能提取出来,然后将这些通用功能在合适的时候织入到应用程序中,比如事务管理、权限控制、日志记录、性能统计等。AOP 底层是动态代理,如果是接口采用 JDK 动态代理,如果是类采用 CGLIB 方式实现动态代理。


二、Spring 中常用的设计模式

(1)代理模式——Spring 中两种代理方式,若目标对象实现了若干接口,Spring 使用 JDK 的 java.lang.reflect.Proxy 类代理。若目标对象没有实现任何接口,Spring 使用 CGLIB 库生成目标类的子类。

(2)单例模式——在 Spring 的配置文件中设置 Bean 默认为单例模式。

(3)模板方式模式——用来解决代码重复的问题。 比如:RestTemplate、JmsTemplate、JpaTemplate

(4)工厂模式——在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用同一个接口来 指向新创建的对象。Spring 中使用 BeanFactory 来创建对象的实例。


三、介绍一下 Spring bean 的生命周期、注入方式和作用域

生命周期:

  •  调用构造器 或者是通过工厂的方式创建 Bean 对象
  •  给 bean 对象的属性注入值

  • 调用初始化方法进行初始化, 初始化方法是通过 init-method 来指定的.

  •  使用

  •  IOC 容器关闭时, 销毁 Bean 对象. 

注入方式:

  1) 通过 setter 方法注入

  2) 通过有参的构造方法注入

作用域:

 

  在默认情况下, Spring 的 ApplicationContext 容器在启动时,会自动实例化所有 singleton 的 Bean 并缓存在容器中 。 虽然启动时会多花费一些时间,但是有这些好处:
* 对 Bean 提前进行实例化操作会及早发现一些潜在的配置问题。
* Bean 以缓存的方式保存,当运行时调用该 Bean 时就无须再次实例化了,因此提高运行效率 。
  在默认情况下, Spring 容器在启动时不实例化 prototype 的 Bean ,此外, Spring 容器将 prototype 的 Bean 交给调用者后,就不再负责管理它的生命周期了。


四、Spring如何管理事务?

  Spring为事务管理提供了一致的编程模板,在高层次上建立了统一的事务抽象。也就是说,不管是选择哪种 ORM 技术( MyBatis、Hibernate、JPA还是Spring JDBC) ,Spring都可以让用户以统一的编程模型进行事务管理。

Spring支持两种事务编程模型:

  1) 编程式事务

   Spring提供了 TransactionTemplate 模板,利用该模板我们可以通过编程的方式实现事务管理,而无需关注资源获取、复用、释放、事务同步及异常处理等操作。相对于声明式事务来说,这种方式相对麻烦一些,但是好在更为灵活,我们可以将事务管理的范围控制的更为精确。(可以在方法内部中使用,可以把方法内部的代码 按照逻辑分成单元提交。编程式事务可以迅速的处理完成并释放资源,在同样的资源下实现了高可用高并发,效率会比声明式事务快N多倍。)

  2) 声明式事务 

  Spring事务管理的亮点在于声明式事务管理,它允许我们通过声明的方式,在IOC配置中指定事务的边界和事务属性,Spring会自动在指定的事务边界上应用事务属性。相对于编程式事务来说,这种方式十分的方便,只需要在需要做事务管理的方法上,增加@Transactional注解,以声明事务特征即可。(当它标注在类上时,代表这个类所有公共(public)非静态的方法都将启用事务功能;当它标注在方法上时,代表这个方法将启用事务功能。)

  在@Transactional注解上,我们可以使用 isolation 属性声明事务的隔离级别,使用 propagation 属性声明事务的传播机制。

@Transactional的属性:

 事务失效的场景

  1)在一个`@Transactional`注解的方法中调用本类中的其他方法,被调用的方法上的`@Transactional`注解是不!会!生!效!的!但是加上也并不会报错;

解释:这是因为Spring默认基于代理技术实现对`@Transactional`注解的支持。Spring事务是通过AOP实现的,会在运行时为`@Transactional`注解的方法动态生成一个代理对象,并在代理对象上添加事务的相关逻辑。而调用被代理对象的非代理方法,是直接调用该对象的方法,无法触发代理逻辑,因而也就无法开启新的事务。(相当于this.methodB(),this不是代理对象)

1. 可以考虑通过将`@Transactional`注解放到被调用的方法外部;

2. 或者将被调用的方法抽取到另一个类中并对该类使用`@Transactional`注解来规避这个问题。

3. 使用 AopContext.currentProxy() 来获取当前的代理对象,然后再去调用其他方法。注意:还需要在启动类上添加 @EnableAspectJAutoProxy(exposeProxy = true)从而解决需要在代理对象内部访问被代理对象引用的问题。

  2)@Transactional注解的方法必须是公共方法,就是必须是public修饰符!!!

  3)@Transactional注解可以标注在类和方法上,也可以标注在定义的接口和接口方法上。但不建议将其用在接口上,原因是接口本身定义了抽象方法的签名和规范,而对于事务的定义和实现是与方法的具体实现相关的。因此,将@Transactional注解放在接口上并不能真正地定义或实现事务,只会对具体实现方法产生影响,容易混淆。通常建议将@Transactional注解仅用于具体实现方法或实现类上。(在方法上的@Transactional注解会覆盖掉类上的@Transactional。)

  4)@Transactional注解保证的是每个方法处在一个事务,如果有try一定在catch中抛出运行时异常(否则无法回滚数据)

注意:Spring 默认遇到运行时异常或 Error才回滚事务;其他异常不会触发回滚事务(编译异常)。如果想发生编译异常也会回滚代码,需要指定要回滚的异常:@Transactional(rollbackFor = Exception.class)


五、Spring的事务传播方式有哪些?

  当我们调用一个业务方法时,它的内部可能会调用其他的业务方法,以完成一个完整的业务操作。这种业务方法嵌套调用的时候,如果这两个方法都是要保证事务的,那么就要通过 Spring的事务传播机制 控制当前事务如何传播到被嵌套调用的业务方法中。 Spring在TransactionDefinition接口中规定了7种类型的事务传播行为,它们规定了事务方法和事务方法发生嵌套调用时如何进行传播,如下所示:

  • REQUIRED:PROPAGATION_REQUIRED是默认的事务传播行为。如果一个方法被标记为PROPAGATION_REQUIRED,并且当前存在事务,那么这个方法会加入到当前事务中执行;如果当前没有事务,则会开启一个新的事务。
  • REQUIRE_NEW:它不管是否存在事务,它都会新开启一个事务来执行,新老事务相互独立的,外部事务抛出异常,并不会影响内部事务的一个正常提交
  • NESTED:如果当前存在事务,就嵌套当前事务中去执行,如果当前没有事务,那么就新建一个事务,类似 REQUIRE_NEW这个样一个传播行为
  • SUPPORTS:表示支持当前事务; 如果当前事务不存在,就以非事务的方式去执行
  • NOT_SUPPORT:当一个方法被标记为PROPAGATION_NOT_SUPPORTED时,它表示该方法不支持在事务环境中运行。如果当前存在事务,那么在执行这个方法之前,当前事务会被挂起,方法在没有事务的环境下执行,执行完毕后,再恢复之前被挂起的事务。
  • MANDATORY:支持当前事务; 如果当前事务不存在,则抛出异常。【强制事务
  • NEVER:PROPAGATION_NEVER表示方法绝对不能在事务环境中运行,如果当前存在事务,那么调用这个方法会抛出异常【强制非事务

Spring的事务传播级别一般是不需要去定义的,默认就是 REQUIRED,除非在嵌套的事务情况下,需要去重点了解。


如图,methodA()调用methodB()那么这两个方法都显示了开启事务,那么methodB()是开启一个新的事务呢?还是继续在methodA()这个事务里面去执行?就取决于事务传播的一个行为。


六、Spring中@Autowired和@Resource的区别

在Spring框架中,如果在Service层中需要注入其他依赖的对象,通常我们都会使用@Autowired或者@Resource注解,但是它们是有区别的,比如@Autowired跟Spring框架强耦合了, 如果换成其他框架,@Autowired就没作用了。而@Resource是JSR-250提供的,它是Java标准,绝大部分框架都支持。

主要区别如下:

1)包含的属性不同

@Autowired只包含一个参数:required,表示是否开启自动注入,默认是true。而@Resource包含七个参数,其中最重要的两个参数是:name 和 type。

2)@Autowired默认按byType自动装配,而@Resource默认byName自动装配。

@Autowired如果要使用byName,需要使用另一个注解 @Qualifier 一起配合。而@Resource如果指定了name,则用byName自动装配,如果指定了type,则用byType自动装配。

3) 注解应用的位置不同

@Autowired能够用在:构造器、方法、参数、成员变量和注解上,而@Resource只能用在:类、成员变量和方法上。

4)装配顺序不同

  • @Autowired的装配顺序如下:

@Autowired默认先按byType进行匹配,如果发现找到多个bean,则又按照byName方式进行匹配,如果还有多个,则报出异常。

  • @Resource的装配顺序如下:
  1. 如果同时指定了name和type,查找name和type唯一匹配的Bean,找的到自动装配,找到多个则报出异常。
  2. 若只是指定了@Resource注解的name,则按name后的名字去bean元素里查找有与之相等的name属性的bean。【不会再按type去查找】
  3. 若只指定@Resource注解的type属性,则从上下文中找到类型匹配的唯一bean进行装配,找不到或者找到多个,都会抛出异常。【不会再按name去查找】
  4. 若既不指定name属性,也不指定type属性,则自动按byName方式进行查找。如果没有找到符合的bean,则回退为一个原始类型进行查找,如果找到就注入。【推荐】

 

posted @ 2021-10-14 17:56  danielzzz  阅读(98)  评论(0编辑  收藏  举报