Spring框架是一个全面的、企业应用开发一站式的解决方案,贯穿表现层、业务层、持久层,主要包括以下七个模块:
Spring-core 提供了框架的基本组成部分,包括控制反转(Inversion of Control,IOC)和依赖注入(Dependency Injection,DI)功能。核心容器的主要组件是 BeanFactory,它是工厂模式的实现。BeanFactory 使用 IOC 模式将应用程序的配置和依赖性规范与实际的应用程序代码分开。
控制反转(Inversion of Control)指的是创建对象的控制权发生转移,以前创建对象的主动权和创建时机由应用程序把控,而现在这种权利转交给 IoC 容器。
在Spring中,ApplicationContext 接口表示 Spring IoC 容器,负责实例化、配置和装配 bean。容器通过读取配置元数据获取关于实例化、配置和组装对象的指令。配置元数据可以用 XML、 Java 注释或 Java 代码表示,它允许用户表达组成应用程序的对象以及这些对象之间丰富的相互依赖关系。ApplicationContext继承了BeanFactory接口。
依赖注入 (Dependency Injection) 是 IoC 的具体实现方式。在 Spring 中,我们可以通过构造函数注入、Setter 注入和接口注入等多种方式为对象注入依赖。DI的核心是DIP(依赖倒置原则),即:
也就是说,将对具体类的引用转换成对其接口的引用,具体类只引用接口(引用==依赖,接口==接口或抽象类)。事实上我们调用具体类的时候,只关心其提供的API而非实现,DIP则通过在设计和重构阶段在技术手段上保证了解耦。依赖注入有三种方式:
<!-- 这里反射默认调用的是无参构造方法,如果不存在无参构造方法就会报错 -->
<bean id = "user" class="实体类的路径(包含到类名)">
<!-- 如果成员变量是简单类型(基本类型+String)用value赋值;如果是自定义引用类型用ref赋值 -->
<property name="name" value="张三"/>
<property name="userDao" ref="ud"/>
</bean>
<bean id = "ud" class="UserDao的路径(包含到类名)" />
<!-- 这里反射调用的是有参构造方法,其中:
index 为构造方法的第几个参数进行配置
name 为构造方法的哪个名字的参数进行配置
index 和 name 可以配置任何一个或同时配置,但要求一旦配置必须正确(推荐优先使用index方式配置,防止没有源码造成name无法匹配到对应参数)
type 为该构造方法参数的类型
value 为该构造方法参数的值 ,用来指定基本值
ref 为该构造方法参数的值,用来指定引用其他bean的值
-->
<bean id = "user" class="实体类的路径(包含到类名)">
<constructor-arg index="0" type="java.lang.String" name="name" value="李四"></constructor-arg>
<constructor-arg index= "1" name="userDao" ref="ud"/>
</bean>
<bean id = "ud" class="UserDao的路径(包含到类名)" />
为了解决以来循环问题,Spring使用了三级缓存技术。在jdk源码中,三级缓存是这样定义的:
// 一级缓存Map 存放完整的Bean(流程跑完的)
private final Map<String, Object> singletonObjects = new ConcurrentHashMap(256);
// 二级缓存Map 存放不完整的Bean(只实例化完,还没属性赋值、初始化)
private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap(16);
// 三级缓存Map 存放一个Bean的lambda表达式(也是刚实例化完)
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap(16);
那么他们三个是如何解决循环依赖问题的呢?
设想一下没有三级缓存的循环依赖问题:ServiceA依赖ServiceB,ServiceB依赖ServiceA。ServiceA实例化,然后在属性填充的时候,发现依赖ServiceB。在Spring容器中找ServiceB,没有找到。ServiceA暂停属性注入,开始实例化ServiceB,然后在属性填充的时候,发现依赖ServiceA,于是又在Spring容器中找ServiceA,同样没有找到,这时候程序就出问题了。
加入了三级缓存后,实例化过程变成这样:ServiceA实例化,之后将实例化的不完整的实例ServiceA放入三级缓存。然后在属性填充的时候,发现ServiceA依赖ServiceB。在Spring容器中的一级缓存、二级缓存和三级缓存中找ServiceB,没有找到。ServiceA暂停属性注入,开始实例化ServiceB,同样将不完整的ServiceB实例放入三级缓存。然后在属性填充的时候,发现依赖ServiceA,于是又在Spring容器中找ServiceA,先找了一级缓存和二级缓存,没找到。在三级缓存中找到了不完整的实例ServiceA,然后将ServiceB从三级缓冲中移除,放入二级缓存,然后成功的对ServiceB进行了属性填充和初始化操作,然后从二级缓存移除,放入一级缓存。ServiceA继续属性注入,依次放入二级缓存和一级缓存。
分析上面场景时发现,要单纯解决循环依赖,其实只要有两级缓存就够了,那么为什么要用三级缓存呢?答案是因为Spring需要支持AOP。在Bean被AOP进行了切面代理之后,三级缓存中的singletonFactory获取到的对象实例是目标Bean的一个代理对象。每次获取到的都是新的代理对象,就破坏了Spring解决循环依赖问题的基础(所有的对象都是单例的)。而加入了二级缓存以后,代理对象也是只获取一次,然后放入二级缓存备用。
在Java应用程序中,事务可以使用 JDBC 或 Java Persistence API(JPA)进行管理。 Spring 框架支持声明式和编程式事务管理。
事务管理器主要有三个接口:
Spring 框架提供了许多实现 PlatformTransactionManager 接口的类, 其中包括:
可以根据的需要选择使用哪个事务管理器。
Spring 框架的事务传播机制用于定义在多个事务之间如何传播事务。例如,如果一个方法正在一个具有事务的上下文中执行,而该方法又调用另一个方法,那么应该如何处理事务? Spring 框架的事务传播机制定义了这种情况下的行为:
事务传播机制的默认值为 PROPAGATION_REQUIRED。这意味着如果一个方法在一个具有事务的上下文中执行,而该方法又调用另一个方法,则第二个方法将加入该事务。
在项目中,出现以下情况会使事务失效:
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。