一、spring 概述
二、XML配置spring容器
三、Spring依赖注入类型
四、Spring依赖注入原理分析
五、Spring循环依赖解决方案
六、如何解决循环依赖
如何解决循环依赖
在日常的开发中,可能我们都会遇到循环依赖的问题。假设有一个商城,里面有用户Account
和订单Order
两个类。每个用户都有很多订单,用户购买时打折力度和订单的数量成反比。用户Account
和订单Order
之间的关联关系和代码如下。
查看关联关系以及代码,很明显它们之间构成相互依赖关系。那么,该如何消除循环依赖关系呢?当我们碰到问题无从下手时,不妨考虑一下是否可以通过“加一层”的方法进行解决。消除循环依赖的基本思路也是这样,通过在两个相互循环依赖的组件之间添加中间层,将循环依赖转变为间接依赖。有三种方法可以做到这一点,分别是提取中介者、转移业务逻辑和引入回调。
package com.ieening.acyclicdependency.original.order;
import java.math.BigDecimal;
import com.ieening.acyclicdependency.original.account.Account;
public class Order {
private BigDecimal chargeAmount;
private Account account;
public void setAccount(Account account) {
this.account = account;
}
public Order(Account account, BigDecimal chargeAmount) {
this.account = account;
this.chargeAmount = chargeAmount;
}
public BigDecimal getChargeAmount() {
return this.chargeAmount;
}
public BigDecimal pay() {
BigDecimal discount = new BigDecimal(1).subtract(this.account.getDiscountAmount());
BigDecimal paidAmount = this.chargeAmount.multiply(discount);
return paidAmount;
}
}
package com.ieening.acyclicdependency.original.account;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import com.ieening.acyclicdependency.original.order.Order;
public class Account {
private List<Order> orders;
public BigDecimal getDiscountAmount() {
// 根据账号下的订单数来模拟折扣力度
if (orders.size() > 5) {
return new BigDecimal(0.1);
} else {
return new BigDecimal(0.03);
}
}
public List<Order> getOrders() {
return this.orders;
}
public void createOrder(BigDecimal chargeAmount) {
Order order = new Order(this, chargeAmount);
if (orders == null) {
orders = new ArrayList<Order>();
}
orders.add(order);
}
public BigDecimal pay() {
BigDecimal total = new BigDecimal(0);
for (Order order : orders) {
total = total.add(order.pay());
}
return total;
}
}
1.1 提取中介者
提取中介者的核心思想是把两个相互依赖的组件中的交互部分抽象出来形成一个新的组件,而新组件同时包含着对原有两个组件的引用,这样就把循环依赖关系剥离出来并提取到一个专门的中介者组件中,关联关系如下图所示。中介者组件的实现非常简单,通过提供一个支付方法(pay
)来对循环依赖进行了剥离,该方法同时依赖于Account
和Order
对象,并实现了原有Order
中根据订单数量支付的业务逻辑。中介者PaymentMediator
类的实现代码如代码清单1-23
所示。
package com.ieening.acyclicdependency.escalate.mediator;
import java.math.BigDecimal;
import java.util.List;
import com.ieening.acyclicdependency.escalate.account.Account;
import com.ieening.acyclicdependency.escalate.order.Order;
public class PaymentMediator {
private Account account;
public PaymentMediator(Account account) {
this.account = account;
}
public BigDecimal pay(Order order) {
BigDecimal discount = new BigDecimal(1).subtract(this.account.getDiscountAmount());
return order.getChargeAmount().multiply(discount);
}
public BigDecimal pay(List<Order> orders) {
BigDecimal total = new BigDecimal(0);
for (Order order : orders) {
total = total.add(pay(order));
}
return total;
}
}
1.2 转移业务逻辑
转移业务逻辑方法的实现思路在于提取一个专门的业务组件来完成对折扣的计算。这样,Order
原本对Account
的依赖就转移到了对这个业务组件的依赖,而这个业务组件本身不需要依赖任何对象。实现转移业务逻辑后的关联关系如下图所示:
package com.ieening.acyclicdependency.demote.calculator;
import java.math.BigDecimal;
public class DiscountCalculator {
private Integer orderNums;
public DiscountCalculator(Integer orderNums) {
this.orderNums = orderNums;
}
public BigDecimal getDiscountAmount() {
if (orderNums.intValue() > 5) {
return new BigDecimal(0.1);
} else {
return new BigDecimal(0.03);
}
}
}
1.3 引入回调
引入回调的本质上是一种双向调用模式,也就是说,被调用方在被调用的同时也会调用对方。在实现上,我们可以提取一个用于计算折扣的业务接口,然后让Account
去实现这个接口。我们同样将这个接口命名为DiscountCalculator
,其中包含一个计算折扣的方法定义。这样,Order
在计算价格时只需要依赖这个业务接口,而不需要关心这个接口的具体实现类,如下图所示。
package com.ieening.acyclicdependency.callback.calculator;
import java.math.BigDecimal;
public interface DiscountCalculator {
public BigDecimal getDiscountAmount();
}
package com.ieening.acyclicdependency.callback.account;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import com.ieening.acyclicdependency.callback.calculator.DiscountCalculator;
import com.ieening.acyclicdependency.callback.order.Order;
public class Account implements DiscountCalculator {
private List<Order> orders;
public BigDecimal getDiscountAmount() {
if (orders.size() > 5) {
return new BigDecimal(0.1);
} else {
return new BigDecimal(0.03);
}
}
public List<Order> getOrders() {
return this.orders;
}
public void createOrder(BigDecimal chargeAmount) {
Order order = new Order(this, chargeAmount);
if (orders == null) {
orders = new ArrayList<Order>();
}
orders.add(order);
}
public BigDecimal pay() {
BigDecimal total = new BigDecimal(0);
for (Order order : orders) {
total = total.add(order.pay());
}
return total;
}
}
package com.ieening.acyclicdependency.callback.order;
import java.math.BigDecimal;
import com.ieening.acyclicdependency.callback.calculator.DiscountCalculator;
public class Order {
private BigDecimal chargeAmount;
private DiscountCalculator discounter;
public Order(DiscountCalculator discounter, BigDecimal chargeAmount) {
this.discounter = discounter;
this.chargeAmount = chargeAmount;
}
public BigDecimal getChargeAmount() {
return this.chargeAmount;
}
public BigDecimal pay() {
BigDecimal discount = new BigDecimal(1).subtract(this.discounter.getDiscountAmount());
BigDecimal paidAmount = this.chargeAmount.multiply(discount);
return paidAmount;
}
}
完整的代码请查看:learnSpring – acyclicdependency