Spring设计模式

Spring的设计模式

(1)工厂模式:Spring使用工厂模式,通过BeanFactory和ApplicationContext来创建对象

(2)单例模式:Bean默认为单例模式

(3)策略模式:例如Resource的实现类,针对不同的资源文件,实现了不同方式的资源获取策略

(4)代理模式:Spring的AOP功能用到了JDK的动态代理和CGLIB字节码生成技术

(5)模板方法:可以将相同部分的代码放在父类中,而将不同的代码放入不同的子类中,用来解决代码重复的问题。比如RestTemplate, JmsTemplate, JpaTemplate

模板方法模式的体现

HibernateTemplateJdbcTemplate 中,模板方法模式主要体现在它们定义了一套完整的操作流程,例如执行 SQL 语句、处理结果集等,而这些流程中的某些步骤(如 SQL 语句的具体内容、参数的设置等)是可以由子类或外部调用者来指定的。这种模式的好处是既保证了操作的一致性和安全性,又提供了足够的灵活性供开发者定制业务逻辑。

(6)适配器模式:Spring AOP的增强或通知(Advice)使用到了适配器模式,Spring MVC中也是用到了适配器模式适配Controller

(7)观察者模式:Spring事件驱动模型就是观察者模式的一个经典应用。

(8)桥接模式:可以根据客户的需求能够动态切换不同的数据源。比如我们的项目需要连接多个数据库,客户在每次访问中根据需要会去访问不同的数据库

(9) 装饰器模式:

适用场景:当需要扩展一个类的功能或者给类添加一个附加职责时,可以使用装饰器模式。例如,在项目中需要连接多个数据库,并且需要根据客户的需求动态切换不同的数据源时,装饰器模式就可以派上用场。通过为数据源对象添加不同的装饰器,可以实现在不修改原有数据源对象的前提下,动态地改变其行为或添加额外的功能。

工厂模式

用工厂代理创建对象,减小代码耦合

BeanFactroy采用的是延迟加载形式来注入Bean的,只有在使用到某个Bean时(调用getBean()),才对该Bean进行加载实例化

1
2
3
4
//文件名:bean.properties
circle=com.demo.Circle
rectangle=com.demo.Rectangle
square=com.demo.Square
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
//文件名:BeanFactory.java
package com.demo;

import java.io.IOException;
import java.io.InputStream;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

public class BeanFactory {
//配置对象(类比spring IOC容器中的Bean定义注册表)
private static final Properties props;
//保存创建好的对象的容器,与类名组成key-value对(类比spring IOC容器中的Bean缓存池)
private static Map<String, Object> beans;
static {
props = new Properties();
//通过类加载器读入配置文件
InputStream in = BeanFactory.class.getClassLoader().getResourceAsStream("bean.properties");
try {
//加载配置文件到配置对象
props.load(in);
//初始化容器
beans = new HashMap<>();
Enumeration<Object> keys = props.keys();
//循环遍历配置对象中的所有的类名(key)
while (keys.hasMoreElements()){
String key = keys.nextElement().toString();
//通过类名拿到全类名(value)
String beanPath = props.getProperty(key);
//利用全类名反射创建对象
Object value = Class.forName(beanPath).getDeclaredConstructor().newInstance();
//将对象放入容器中
beans.put(key, value);
}
} catch (IOException e) {
throw new ExceptionInInitializerError("初始化properties失败!程序终止!");
} catch (Exception e){
e.printStackTrace();
}
}
public static Object getBean(String beanName){
//从容器中获取对象
return beans.get(beanName);
}
}

IOC实现原理:

  1. 编写xml配置文件,一般为key-value结构(类别名-全类名)类别名用于调用对象
1
<bean id="circle" class="com.demo.Circle"/>
  1. 读取配置类的key和value,value为全类名,就可以利用反射机制将类创建出来
1
Object value = Class.forName(beanPath).getDeclaredConstructor().newInstance();
  1. 在Map中保存 类别名:反射创建的对象
1
beans.put(key, value);
  1. 实现getBean方法,本质就是取
1
2
3
4
public static Object getBean(String beanName){
//从容器中获取对象
return beans.get(beanName);
}

策略模式

当出现多种情况执行不同方法时,一直写if很不优雅,这样可以将业务功能分出来,写一个统一接口继承,这样有不同的实现类,直接调用不同的实现类方法,这样就是策略模式!

1
2
3
public interface NomalService {
void handle();
}
1
2
3
4
5
6
7
@Service("A")
public class AService implements NomalService {
@Override
public void handle() {
System.out.println("HandleMethod:A");
}
}
1
2
3
4
5
6
7
@Service("B")
public class BServcie implements NomalService {
@Override
public void handle() {
System.out.println("HandleMethod:B");
}
}
1
2
3
4
5
6
7
@Autowired
Map<String, NomalService> map;
@RequestMapping("/user/{name}")
public void handle(@PathVariable("name") String name)
{ //Spring策略模式
map.get(name).handle();
}

单例模式

在我们的系统中,有一些对象其实我们只需要一个,比如说:线程池、缓存、对话框、注册表、日志对象、充当打印机、显卡等设备驱动程序的对象。事实上,这一类对象只能有一个实例,如果制造出多个实例就可能会导致一些问题的产生,比如:程序的行为异常、资源使用过量、或者不一致性的结果。

使用单例模式的好处:

对于频繁使用的对象,可以省略创建对象所花费的时间,这对于那些重量级对象而言,是非常可观的一笔系统开销;
由于 new 操作的次数减少,因而对系统内存的使用频率也会降低,这将减轻 GC 压力,缩短 GC 停顿时间。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class Singleton {
private volatile static Singleton singleton;

public Singleton() {
}

public static Singleton getInstance(){
if (singleton == null){
synchronized (Singleton.class){
singleton = new Singleton();
}
}
return singleton;
}
}

使用volatile防止指令重排
创建一个对象,在JVM中会经过三步:

(1)为singleton分配内存空间

(2)初始化singleton对象

(3)将singleton指向分配好的内存空间(执行完这一步代表已经singleton不为空)

指令重排序是指:JVM在保证最终结果正确的情况下,可以不按照程序编码的顺序执行语句,尽可能提高程序的性能

在这三步中,第2、3步有可能会发生指令重排现象,创建对象的顺序变为1-3-2,会导致多个线程获取对象时,有可能线程A创建对象的过程中,执行了1、3步骤,线程B判断singleton已经不为空,获取到未初始化的singleton对象,就会报NPE异常

aHR0cHM6Ly9jZG4ubmxhcmsuY29tL3l1cXVlLzAvMjAyMC9wbmcvMTY5NDAyOS8xNTk0NTIyMzkwNzk3LTRkZjBkMDA4LTM3MmMtNDkxZi04YjlhLWY4NjBmODAzNzFhYi5wbmc

适配器模式

在实际业务中,我们遇到支付功能会首先创建一个统一接口,然后实现类去实现各种支付方式:比如支付宝,微信支付;这些可以手写实现;但是当引入第三方支付jar包,例如银联支付,这样不能修改他底层代码;我们只能使用适配器来实现!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
//统一的支付接口
public interface PaymentService {
/**
* 支付订单的方法
* @param orderId
*/
void pay(String orderId);
}
//阿里pay
public class AliPayService implements PaymentService {
@Override
public void pay(String orderId) {
System.out.println("支付宝支付:支付订单 " + orderId + " 成功!");
}
}

/**
* 具体支付服务类:微信支付
*/
public class WeChatPayService implements PaymentService {
@Override
public void pay(String orderId) {
System.out.println("微信支付:支付订单 " + orderId + " 成功!");
}
}

public class UnionPay {
/**
* 使用银联支付
* @param orderId
*/
void payWithUnionPay(String orderId) {
System.out.println("银联支付:支付订单 " + orderId + " 成功!");
}
}

//适配器!传入jar包的业务逻辑做调用,以适配支付
public class UnionPayAdapter implements PaymentService {
/**
* 持有一个银联支付的实例
*/
private UnionPay unionPay;

public UnionPayAdapter(UnionPay unionPay) {
this.unionPay = unionPay;
}

@Override
public void pay(String orderId) {
System.out.println("适配器:使用银联支付的适配器...");
// 在适配器中调用银联支付的支付方法,实现适配
unionPay.payWithUnionPay(orderId);
}
}
1
2
3
4
5
6
7
8
9
10
public void test2(){
PaymentService aliPayService = PayFactory.createPay("Ali");
PaymentService weChatPayService = PayFactory.createPay("WeChat");
PaymentService unionPay = PayFactory.createPay("Union");

aliPayService.pay("1001");
weChatPayService.pay("1002");
//适配器:使用银联支付
unionPay.pay("1003");
}

装饰模式

1
2
3
4
5
6
7
8
9
10
11
12
13
// 在这个例子中,我们有一个简单的咖啡(SimpleCoffee),然后我们可以使用装饰器(MilkCoffee 和 SugarCoffee)来动态地添加牛奶和糖。每次我们添加装饰器时,咖啡的成分和价格都会相应地改变。
public class CoffeeShop {
public static void main(String[] args) {
Coffee coffee = new SimpleCoffee();
System.out.println("Simple Coffee: " + coffee.getIngredients() + ", Cost: $" + coffee.getCost());

coffee = new MilkCoffee(coffee);
System.out.println("Milk Coffee: " + coffee.getIngredients() + ", Cost: $" + coffee.getCost());

coffee = new SugarCoffee(coffee);
System.out.println("Sugar and Milk Coffee: " + coffee.getIngredients() + ", Cost: $" + coffee.getCost());
}
}

装饰器模式是在原有类的基础上动态添加新的功能,这种添加功能的方式不同于继承,它是在对象层面实现功能扩展,而不是继承的类层面,因此说装饰器模式比继承更加灵活。大逻辑未变,本质是对方法的增强

  1. 先新建奶茶类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
//奶茶类统一接口
public interface MilkTea {
/**
* 奶茶名称
* @return
*/
String getDescription();

/**
* 售价
* @return
*/
BigDecimal cost();
}

/**
* 波波奶茶
*/
public class BoBoMilkTea implements MilkTea {
@Override
public String getDescription() {
return "波波奶茶";
}

@Override
public BigDecimal cost() {
return new BigDecimal(8.0);
}
}

/**
* 烧仙草
*/
public class HerbalJelly implements MilkTea{
@Override
public String getDescription() {
return "烧仙草";
}

@Override
public BigDecimal cost() {
return new BigDecimal(7.0);
}
}

  1. 再建调料类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
//调料类同样要有描述和价格
public interface Condiment extends MilkTea {
}

//装饰器核心代码!
//主题思想:传入对象,对对象做扩展操作!相对于直接继承波波奶茶类增加调料字段更优雅,扩展性
/**
* 珍珠
*/
public class Pearl implements Condiment{

private MilkTea milkTea;

public Pearl(MilkTea milkTea) {
this.milkTea = milkTea;
}

@Override
public String getDescription() {
return milkTea.getDescription() + ",加珍珠";
}

@Override
public BigDecimal cost() {
return milkTea.cost().add(new BigDecimal(2.0));
}
}


/**
* 椰果
*/
public class Coconut implements Condiment{

private MilkTea milkTea;

public Coconut(MilkTea milkTea) {
this.milkTea = milkTea;
}

@Override
public String getDescription() {
return milkTea.getDescription() + ",加椰果";
}

@Override
public BigDecimal cost() {
return milkTea.cost().add(new BigDecimal(1.5));
}
}

/**
* 红豆
*/
public class Ormosia implements Condiment{

private MilkTea milkTea;

public Ormosia(MilkTea milkTea) {
this.milkTea = milkTea;
}
@Override
public String getDescription() {
return milkTea.getDescription() + ",加红豆";
}

@Override
public BigDecimal cost() {
return milkTea.cost().add(new BigDecimal(2.5));
}
}
  1. 测试类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Test
public void test2(){
MilkTea boBoMilkTea = new BoBoMilkTea();
boBoMilkTea = new Pearl(boBoMilkTea);
boBoMilkTea = new Coconut(boBoMilkTea);
//milkTea 字段被移除了,但是方法指针仍能指向字段值。这就是为什么 getDescription() 能够正常返回值的原因。
System.out.println("饮料名称: "+boBoMilkTea.getDescription() + " 价格:"+boBoMilkTea.cost());


MilkTea herbalJelly = new HerbalJelly();

herbalJelly = new Pearl(herbalJelly);
herbalJelly = new Coconut(herbalJelly);
herbalJelly = new Ormosia(herbalJelly);
System.out.println("饮料名称: "+herbalJelly.getDescription() + " 价格:"+herbalJelly.cost());

}

代理模式

  1. 静态代理

就是在继承的基础上对业务更改,在调用目标对象的方法之前或之后添加一些额外的逻辑

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public class UserServiceProxy implements UserService {  
private UserService userService;

public UserServiceProxy(UserService userService) {
this.userService = userService;
}

@Override
public void addUser(String username, String password) {
// 在调用目标方法之前添加一些逻辑
System.out.println("开始记录日志...");
userService.addUser(username, password);
// 在调用目标方法之后添加一些逻辑
System.out.println("结束记录日志...");
}

@Override
public void deleteUser(String username) {
// 在调用目标方法之前添加一些逻辑
System.out.println("开始记录日志...");
userService.deleteUser(username);
// 在调用目标方法之后添加一些逻辑
System.out.println("结束记录日志...");
}
}
  1. 动态代理

动态代理利用Java的反射机制,在运行时动态地生成代理类。Java的动态代理要求被代理对象必须实现一个或多个接口。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

// 接口与实现类同静态代理示例

// 动态代理的处理器
class DynamicProxyHandler implements InvocationHandler {
private Object target;

public DynamicProxyHandler(Object target) {
this.target = target;
}

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//计算两段代码的运行时间,并打印
long start = System.currentTimeMillis();
System.out.println("代理开始...");
Object result = method.invoke(target, args);
System.out.println("代理结束...");
long end = System.currentTimeMillis();
System.out.println("代理方法运行时间:" + (end - start) + "ms");
return result;
}
}

// 测试类
public class DynamicProxyDemo {
public static void main(String[] args) {
UserService userService = new UserServiceImpl();
DynamicProxyHandler handler = new DynamicProxyHandler(userService);
UserService proxy = (UserService) Proxy.newProxyInstance(
UserServiceImpl.class.getClassLoader(),
UserServiceImpl.class.getInterfaces(),
handler
);
proxy.addUser();
}
}

在这个示例中,DynamicProxyHandler实现了InvocationHandler接口,它会在代理对象调用方法时被调用。Proxy.newProxyInstance方法用于创建代理对象,它需要三个参数:类加载器、代理对象实现的接口列表以及处理器对象。

动态代理的优点在于它可以动态地创建代理对象,而不需要手动编写代理类代码。这使得代理模式更加灵活和可重用。

模板方法模式

提供一个抽象类,子类可以重写他其中的一个方法做各种实现,但是主体作用依旧是做咖啡

模板方法模式的优点在于它提高了代码的复用性,减少了子类中的重复代码,同时保持了算法的整体结构不变。这种模式在需要定义固定操作序列,同时允许灵活定制某些步骤的场合非常有用。

模板方法模式是一种软件设计模式,它定义了一个算法的步骤,并允许子类别为一个或多个步骤提供具体的实现方式。这使得子类别在不改变算法整体架构的情况下,能够重新定义算法中的某些特定步骤。

在模板方法模式中,通常包含两种角色:抽象模板(AbstractClass)和具体实现(ConcreteClass)。抽象模板类定义了一套算法框架或流程,而具体实现类则对算法框架或流程的某些步骤进行了具体的实现。

一个常见的模板方法模式示例如下:

假设我们有一个咖啡机的类,它有一个制作咖啡的算法,包括加水、加咖啡粉、煮制和倒出咖啡等步骤。我们可以定义一个抽象的咖啡机类,其中包含一个模板方法(如makeCoffee),该方法定义了制作咖啡的整体流程,但其中的某些步骤(如加咖啡粉的具体方式)可以延迟到具体的子类中去实现。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
// 抽象咖啡机类
public abstract class CoffeeMachine {
// 模板方法,定义了制作咖啡的整体流程
public final void makeCoffee() {
addWater();
grindCoffee(); // 研磨咖啡粉,这个步骤的具体实现在子类中定义
boil();
dispense();
}

// 加水
protected void addWater() {
System.out.println("Adding water...");
}

// 煮制
protected void boil() {
System.out.println("Boiling...");
}

// 倒出咖啡
protected void dispense() {
System.out.println("Dispensing coffee...");
}

// 研磨咖啡粉,这是一个抽象方法,需要在子类中实现
protected abstract void grindCoffee();
}

// 具体咖啡机类A
public class CoffeeMachineA extends CoffeeMachine {
@Override
protected void grindCoffee() {
System.out.println("Grinding coffee using method A...");
}
}

// 具体咖啡机类B
public class CoffeeMachineB extends CoffeeMachine {
@Override
protected void grindCoffee() {
System.out.println("Grinding coffee using method B...");
}
}

在这个例子中,CoffeeMachine是一个抽象模板类,它定义了制作咖啡的算法骨架,但研磨咖啡粉的具体步骤(grindCoffee方法)是抽象的,需要在子类中实现。CoffeeMachineACoffeeMachineB是具体的咖啡机实现类,它们分别提供了研磨咖啡粉的不同实现方式。

当我们想要制作咖啡时,可以创建CoffeeMachineACoffeeMachineB的实例,并调用其makeCoffee方法。由于makeCoffeefinal的,它确保了算法的整体流程不会被子类改变,但研磨咖啡粉的具体步骤会根据子类的不同而有所不同。

模板方法模式的优点在于它提高了代码的复用性,减少了子类中的重复代码,同时保持了算法的整体结构不变。这种模式在需要定义固定操作序列,同时允许灵活定制某些步骤的场合非常有用。

观察者模式

观察者模式在Java中有广泛的应用,尤其是在事件处理中。以下是一个简单的Java示例来说明观察者模式:

首先,我们定义一个Subject接口,代表被观察的对象:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import java.util.ArrayList;
import java.util.List;

public interface Subject {
// 注册观察者
void registerObserver(Observer o);

// 移除观察者
void removeObserver(Observer o);

// 通知所有观察者
void notifyObservers();

// 被观察对象的状态变化
void setState(String state);

// 获取被观察对象的状态
String getState();
}

接下来,我们实现Subject接口的RealSubject类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
import java.util.ArrayList;
import java.util.List;

public class RealSubject implements Subject {
private List<Observer> observers;
private String state;

public RealSubject() {
observers = new ArrayList<>();
}

@Override
public void registerObserver(Observer o) {
observers.add(o);
}

@Override
public void removeObserver(Observer o) {
int i = observers.indexOf(o);
if (i >= 0) {
observers.remove(i);
}
}

@Override
public void notifyObservers() {
for (Observer observer : observers) {
observer.update(this);
}
}

@Override
public void setState(String state) {
this.state = state;
notifyObservers(); // 当状态改变时,通知所有观察者
}

@Override
public String getState() {
return state;
}
}

然后,我们定义Observer接口,代表观察者:

1
2
3
4
public interface Observer {
// 更新方法,当被观察对象状态改变时调用
void update(Subject subject);
}

接着,我们实现Observer接口的BinaryObserver类:

1
2
3
4
5
6
7
8
9
10
11
12
13
public class BinaryObserver implements Observer {
@Override
public void update(Subject subject) {
System.out.println("BinaryObserver: State is " + subject.getState());
}
}

public class HexObserver implements Observer {
@Override
public void update(Subject subject) {
System.out.println("HexObserver: State is " + subject.getState());
}
}

最后,我们编写测试代码来演示观察者模式的工作过程:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class ObserverPatternDemo {
public static void main(String[] args) {
RealSubject realSubject = new RealSubject();
Observer binaryObserver = new BinaryObserver();
Observer hexObserver = new HexObserver();

// 注册观察者
realSubject.registerObserver(binaryObserver);
realSubject.registerObserver(hexObserver);

// 改变状态并触发通知
realSubject.setState("1101");
realSubject.setState("1010");

// 移除一个观察者
realSubject.removeObserver(hexObserver);

// 再次改变状态,这次不会通知已移除的观察者
realSubject.setState("1111");
}
}

在上面的代码中,RealSubject是被观察的对象,BinaryObserverHexObserver(这里未给出实现)是观察者。当RealSubject的状态改变时,它会调用notifyObservers()方法来通知所有注册的观察者。每个观察者通过实现update()方法来响应状态的改变。

在实际应用中,观察者模式可以用于多种场景,比如GUI编程中的事件监听、数据库连接的监听、用户界面的变化监听等。它使得对象之间的依赖关系解耦,降低了系统的耦合度,提高了代码的可维护性和可扩展性。

责任链设计模式

适用于流水线任务,对每一个环节进行类的定义,顺序执行,可以保障流程扩展性

组成

  • Handler(环节类):包含执行当前环节任务的方法,以及下一个环节的指针
  • Factory(工厂类):用于创建环节链表关系,从第一个环节开始执行

image-20241007205727423

代码示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
// 定义处理者接口
interface Handler {
void setNext(Handler handler);
Object handle(Object request);
}

// 抽象处理者类,实现Handler接口,并定义next处理者
abstract class AbstractHandler implements Handler {
private Handler next;

@Override
public void setNext(Handler handler) {
this.next = handler;
}

@Override
public Object handle(Object request) {
if (this.next != null) {
return this.next.handle(request);
}
return null;
}
}

// 具体处理者类
class ConcreteHandler1 extends AbstractHandler {
@Override
public Object handle(Object request) {
if ((Integer) request < 10) {
System.out.println("Request handled in ConcreteHandler1");
} else {
super.handle(request);
}
return request;
}
}

class ConcreteHandler2 extends AbstractHandler {
@Override
public Object handle(Object request) {
if ((Integer) request >= 10 && (Integer) request < 20) {
System.out.println("Request handled in ConcreteHandler2");
} else {
super.handle(request);
}
return request;
}
}

public class ChainOfResponsibilityDemo {
public static void main(String[] args) {
// 创建处理者
Handler handler1 = new ConcreteHandler1();
Handler handler2 = new ConcreteHandler2();

// 设置责任链
handler1.setNext(handler2);

// 发送请求
for (int i = 0; i <= 25; i += 5) {
handler1.handle(i);
}
}
}

在这个例子中,ConcreteHandler1ConcreteHandler2 分别处理不同范围的请求值。当一个请求进入链后,每个处理者检查是否满足它的条件,如果满足则处理请求,否则将请求传给下一个处理者。这样就形成了一个处理请求的责任链。

注意:在实际应用中,需要确保责任链的末尾处理者能够妥善处理无法识别的请求,以避免无限递归的问题。在上面的例子中,如果没有处理者接受请求,则默认返回null