《Spring Boot 3实战案例合集》现已囊括超过90篇精选实战文章,并且此合集承诺将永久持续更新,为您带来最前沿的技术资讯与实践经验。欢迎积极订阅,享受不断升级的知识盛宴!订阅用户将特别获赠合集内所有文章的最终版MD文档(详尽学习笔记),以及完整的项目源码,助您在学习道路上畅通无阻。
【重磅发布】《Spring Boot 3实战案例锦集》PDF电子书现已出炉!
🎉🎉我们精心打造的《Spring Boot 3实战案例锦集》PDF电子书现已正式完成,目前已经有90个案例,后续还将继续更新。文末有电子书目录。
💪💪永久更新承诺:
我们郑重承诺,所有订阅合集的粉丝都将享受永久免费的后续更新服务。这意味着,随着技术的不断发展和Spring Boot 3的深入应用,我们的电子书也将持续更新,确保您始终掌握最前沿、最实用的技术知识。
💌💌如何获取:
请立即订阅我们的合集《点我订阅》,并通过私信联系我们,我们将第一时间将电子书发送给您。
环境:Java21
1. 简介
设计模式它提供了一套经过验证的、可复用的设计思路,用于解决在特定上下文中反复出现的问题。这些模式涵盖了创建型、结构型和行为型三大类别,旨在提高代码的可重用性、可读性和可扩展性。通过应用设计模式,开发人员可以更加高效地设计出灵活、易于维护和扩展的软件系统。
在我们的日常开发中,设计模式无处不在。本篇文章,我将结合实际工作场景和源代码示例,详细的讲述工作中最常用的7种设计模式。
2. 七种设计模式
2.1 单例模式
单例模式是一种创建型设计模式,确保一个类仅有一个实例,并且它通常用于管理共享资源,如配置、缓存、线程池等。
下面介绍2种单例模式的实现
双重检查DCL
public class Singleton {
private static volatile Singleton instance;
private Singleton() {
}
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
解释:
private static volatile Singleton instance
volatile关键字确保了实例变量的可见性。当一个线程修改instance的值时,其他线程可以立即看到最新的值。同时,volatile还防止了指令重排序。在创建单例实例时,new Singleton()操作不是原子操作,它包括三个步骤:分配内存空间、初始化对象以及将对象引用赋值给instance。如果没有volatile,可能会发生指令重排序,导致其他线程获取到尚未完全初始化的实例对象。
private Singleton()
私有构造函数确保了Singleton类不能从外部直接实例化,从而保证了单例模式的唯一性。
public static Singleton getInstance()方法体
首先,它检查instance是否为null。如果不为null,则直接返回实例,避免进入同步代码块,从而提高性能。如果instance为null,则进入同步代码块。
synchronized (Singleton.class)语句锁定了Singleton类,确保同时只有一个线程可以进入创建实例的代码块。它再次检查instance是否为null。这是为了防止多个线程同时通过第一次检查,进入同步代码块,并重复创建实例。当instance为null时,执行instance = new Singleton()来创建单例实例。
枚举实现
在《Effective Java》中更推荐这种实现单例模式的方式。
public enum SingletonEnum {
INSTANCE;
public void operator() {
System.out.println("TODO PACK");
}
}
解释:
在Java中,枚举类型(enum)是一种特殊的类,它保证了实例的唯一性,并且天生就是线程安全的。
INSTANCE是SingletonEnum枚举的唯一实例。你可以通过SingletonEnum.INSTANCE来获取这个单例。
你可以在枚举中添加自定义方法,比如operator(),来实现单例需要完成的业务逻辑。
枚举单例可以防止反射和序列化破坏单例模式,因为Java规范保证了枚举类型的实例化是唯一的,在序列化和反序列化过程中不会创建新的实例。
JDK中的应用:
java.lang.Runtime.getRuntime()
java.util.logging.Logger
Spring中的应用:
默认情况下,Spring中定义的Bean都是单例的;可以通过@Scope("prototype")修改为多例。
2.2 工厂模式
工厂模式是一种创建型设计模式,它提供了一种无需指定具体类的情况下创建对象的机制。在工厂模式中,创建对象的代码被封装在一个工厂类中,客户端通过调用工厂类的方法来获取所需的对象,而无需直接实例化对象。这种方式使得代码更加灵活和可扩展,因为如果需要创建不同类型的对象,只需修改工厂类的实现即可,而无需修改客户端代码。
简单工厂实现
public class FactoryPatternExample {
static abstract class Vehicle {
public abstract void drive();
}
static class Car extends Vehicle {
public void drive() {
System.out.println("Driving a car.");
}
}
static class Bicycle extends Vehicle {
public void drive() {
System.out.println("Riding a bicycle.");
}
}
static class Truck extends Vehicle {
public void drive() {
System.out.println("Driving a truck.");
}
}
class VehicleFactory {
public static Vehicle createVehicle(String vehicleType) {
return switch (vehicleType) {
case "car" -> new Car();
case "bicycle" -> new Bicycle();
case "truck" -> new Truck();
default -> throw new IllegalArgumentException("无效参数类型: " + vehicleType);
} ;
}
}
public static void main(String[] args) {
Vehicle car = VehicleFactory.createVehicle("car");
car.drive();
Vehicle bicycle = VehicleFactory.createVehicle("bicycle");
bicycle.drive();
Vehicle truck = VehicleFactory.createVehicle("truck");
truck.drive();
}
}
解释:
当你需要制造一辆车时,不是直接调用Car、Bicycle或Truck的构造函数,而是调用VehicleFactory.createVehicle()方法,并传入你想要制造的车辆类型。工厂会根据你提供的类型创建并返回相应的车辆实例。你可以通过调用drive()方法来使用制造出的车辆。
优化,如果你不想使用如上switch方式来创建具体的实例,那么你可以通过反射机制:
class VehicleFactory2 {
public static Vehicle createVehicle(Class<? extends Vehicle> vehicleClass) {
try {
Constructor extends Vehicle> constructor = vehicleClass.getDeclaredConstructor();
return constructor.newInstance();
} catch (NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException e) {
throw new RuntimeException("Failed to create vehicle: " + e.getMessage());
}
}
}
// 使用
Vehicle car = VehicleFactory2.createVehicle(Car.class) ;
car.drive() ;
JDK中的应用:
javax.xml.parsers.DocumentBuilderFactory.newInstance()
java.text.NumberFormat.getInstance()
java.util.Calendar.getInstance()
Spring中的应用:
BeanFactory 和 ApplicationContext 都是工厂模式的体现。
2.3 建造者模式(Builder)
Builder Pattern(建造者模式)是一种创建型设计模式,它允许通过分步骤的方式构建复杂对象。该模式将对象的构建过程与其表示分离,使得相同的构建过程可以创建不同的表示,提高了代码的灵活性和可复用性。
下面组装计算机实现
class Computer {
private String cpu;
private String memory;
private String storage;
private String graphicsCard;
private String operatingSystem;
private Computer(Builder builder) {
this.cpu = builder.cpu;
this.memory = builder.memory;
this.storage = builder.storage;
this.graphicsCard = builder.graphicsCard;
this.operatingSystem = builder.operatingSystem;
}
public static class Builder {
private String cpu;
private String memory;
private String storage;
private String graphicsCard;
private String operatingSystem;
public Builder(String cpu, String memory) {
this.cpu = cpu;
this.memory = memory;
}
public Builder storage(String storage) {
this.storage = storage;
return this;
}
public Builder graphicsCard(String graphicsCard) {
this.graphicsCard = graphicsCard;
return this;
}
public Builder operatingSystem(String operatingSystem) {
this.operatingSystem = operatingSystem;
return this;
}
public Computer build() {
return new Computer(this);
}
}
public void showConfiguration() {
System.out.println("CPU: " + cpu);
System.out.println("Memory: " + memory);
System.out.println("Storage: " + storage);
System.out.println("Graphics Card: " + graphicsCard);
System.out.println("Operating System: " + operatingSystem);
}
}
public class BuilderPatternExample {
public static void main(String[] args) {
Computer computer = new Computer.Builder("Intel i7", "16GB")
.storage("1TB SSD")
.graphicsCard("NVIDIA RTX 4060")
.operatingSystem("Windows 10")
.build() ;
computer.showConfiguration() ;
}
}
JDK中的应用:
StringBuilder
Stream.Builder
Spring中的应用:
BeanDefinitionBuilder创建BeanDefinition对象
UriComponentsBuilder构建URI
2.4 策略模式
策略模式是一种行为型设计模式,它定义了一系列算法,并将每个算法封装起来,使它们可以相互替换。该模式让算法独立于使用它的客户而变化,提高了代码的灵活性和可维护性,适用于算法经常变化或需要频繁切换算法的场景。
如下通过策略模式实现出行方式
interface TravelStrategy {
void travel();
double calculateCost(double distance);
double calculateTime(double distance);
}
class WalkStrategy implements TravelStrategy {
public void travel() {
System.out.println("步行.");
}
public double calculateCost(double distance) {
return 0.0;
}
public double calculateTime(double distance) {
return distance / 5.0;
}
}
class BikeStrategy implements TravelStrategy {
public void travel() {
System.out.println("自行车🚲.");
}
public double calculateCost(double distance) {
return 0.0;
}
public double calculateTime(double distance) {
return distance / 15.0;
}
}
class CarStrategy implements TravelStrategy {
public void travel() {
System.out.println("汽车🚕.");
}
public double calculateCost(double distance) {
return distance * 0.5;
}
public double calculateTime(double distance) {
return distance / 60.0;
}
}
class AirplaneStrategy implements TravelStrategy {
public void travel() {
System.out.println("飞机✈.");
}
public double calculateCost(double distance) {
return distance * 0.8;
}
public double calculateTime(double distance) {
return distance / 800.0;
}
}
class TravelPlanner {
private TravelStrategy travelStrategy;
public TravelPlanner(TravelStrategy travelStrategy) {
this.travelStrategy = travelStrategy;
}
public void setTravelStrategy(TravelStrategy travelStrategy) {
this.travelStrategy = travelStrategy;
}
public void planTravel() {
travelStrategy.travel();
}
public double calculateCost(double distance) {
return travelStrategy.calculateCost(distance);
}
public double calculateTime(double distance) {
return travelStrategy.calculateTime(distance);
}
}
public class StrategyPatternExample {
public static void main(String[] args) {
double distance = 100.0;
// 步行出行
TravelPlanner travelPlanner1 = new TravelPlanner(new WalkStrategy());
travelPlanner1.planTravel();
System.out.println("Cost: " + travelPlanner1.calculateCost(distance));
System.out.println("Time: " + travelPlanner1.calculateTime(distance));
// 自行车出行🚲
TravelPlanner travelPlanner2 = new TravelPlanner(new BikeStrategy());
travelPlanner2.planTravel();
System.out.println("Cost: " + travelPlanner2.calculateCost(distance));
System.out.println("Time: " + travelPlanner2.calculateTime(distance));
// 汽车出行🚕
TravelPlanner travelPlanner3 = new TravelPlanner(new CarStrategy());
travelPlanner3.planTravel();
System.out.println("Cost: " + travelPlanner3.calculateCost(distance));
System.out.println("Time: " + travelPlanner3.calculateTime(distance));
// 飞机出行✈
TravelPlanner travelPlanner4 = new TravelPlanner(new AirplaneStrategy());
travelPlanner4.planTravel();
System.out.println("Cost: " + travelPlanner4.calculateCost(distance));
System.out.println("Time: " + travelPlanner4.calculateTime(distance));
}
}
说明:
首先,通过实现TravelStrategy接口来定义不同的出行策略。
然后,创建一个TravelPlanner对象(上下文),并根据需要选择不同的出行策略。
最后,调用planTravel()方法来执行出行操作,并使用calculateCost()和calculateTime()方法来计算成本和时间。
优点:
易于扩展:如果想添加一种新的出行方式,只需创建TravelStrategy的一个新实现类,而无需修改TravelPlanner类的代码。
灵活切换:可以轻松地在不同的出行策略之间进行切换,以满足不同的出行需求。
代码清晰:不同出行方式的逻辑被封装在各自的策略类中,使代码更加可读和易于维护。
JDK中的应用:
java.util.Comparator
Spring中的应用:
事务管理对象TransactionManager。它定义了开始、提交和回滚事务的方法。Spring 提供了多种 PlatformTransactionManager 的实现,例如 JtaTransactionManager, DataSourceTransactionManager 等,以支持不同类型的数据源和技术。
2.5 观察者模式
观察者模式是一种行为型设计模式,它定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。当主题对象的状态发生变化时,会通知所有观察者对象,使它们能够自动更新,从而提高了应用程序的可维护性和重用性。
如下示例通过观察者模式实现股票价格变化后进行通知
interface StockObserver {
void update(double price);
}
class Investor implements StockObserver {
private String name;
public Investor(String name) {
this.name = name;
}
public void update(double price) {
System.out.println(name + " 价格发生变化,当前价格: " + price);
}
}
interface StockSubject {
void registerObserver(StockObserver observer);
void removeObserver(StockObserver observer);
void notifyObservers();
}
class Stock implements StockSubject {
private List<StockObserver> observers = new ArrayList<>();
private double price;
public void registerObserver(StockObserver observer) {
observers.add(observer);
}
public void removeObserver(StockObserver observer) {
observers.remove(observer);
}
public void notifyObservers() {
for (StockObserver observer : observers) {
observer.update(price);
}
}
public void setPrice(double price) {
this.price = price;
notifyObservers() ;
}
}
public class ObserverPatternExample {
public static void main(String[] args) {
Stock stock = new Stock();
Investor investor1 = new Investor("Pack");
Investor investor2 = new Investor("XO");
Investor investor3 = new Investor("Pxg");
// 将投资者注册为观察员
stock.registerObserver(investor1);
stock.registerObserver(investor2);
stock.registerObserver(investor3);
//更新价格,并通知投资者
stock.setPrice(100.0);
stock.setPrice(105.0);
// 删除观察者
stock.removeObserver(investor2);
// 再次更新
stock.setPrice(110.0);
}
}
输出结果
说明:
首先,创建一个Stock对象作为主题。
创建多个Investor对象作为观察者。
使用registerObserver方法将这些投资者注册到Stock对象中。
当调用setPrice方法来更新股票价格时,所有已注册的投资者都会自动收到通知。
可以使用removeObserver方法来移除不需要接收通知的投资者。
JDK中的应用:
java.util.Observer and java.util.Observable(从JDK9开始已经声明为过时的类)。
javax.swing.event.ChangeListener
Spring中的应用:
Spring中的事件监听机制,ApplicationEvent , ApplicationListener。
2.6 代理模式
代理模式是程序设计中的一种结构型设计模式,它为一个对象提供一种代理以控制对该对象的访问。代理对象在客户端和目标对象之间起到中介作用,可以在不改变目标对象的情况下为其添加额外功能或控制访问,从而保护目标对象并降低系统耦合度。
代理模式,有静态代理,动态代理(基于接口,基于类CGLIB)实现,这里我们只介绍基于JDK的动态代理。
interface UserService {
void save() ;
}
class UserServiceImpl implements UserService {
public void save() {
System.out.println("UserService save method invoke...");
}
}
class UserServiceInvocationHandler implements InvocationHandler {
private Object target;
public UserServiceInvocationHandler(Object target) {
this.target = target;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.err.println("execute before...") ;
Object result = method.invoke(target, args);
System.err.println("execute after...") ;
return result ;
}
}
public class DynamicProxyExample {
public static void main(String[] args) {
UserService realService = new UserServiceImpl();
UserServiceInvocationHandler handler = new UserServiceInvocationHandler(realService);
UserService proxyService = (UserService) Proxy.newProxyInstance(UserService.class.getClassLoader(),
new Class<?>[] { UserService.class }, handler);
proxyService.save() ;
}
}
说明:
JDK动态代理核心就2点:
实现InvocationHandler接口,该接口就是用来扩展功能及目标方法的调用。
使用Proxy创建目标接口的代理对象。
2.7 模板模式
模板模式是一种行为型设计模式,它定义了一个算法的骨架,并允许子类为骨架中的一个或多个步骤提供具体实现。通过这种模式,子类可以在不改变算法结构的前提下,重新定义算法中的某些步骤,从而实现代码复用和算法扩展。
如下示例模拟饮品的制作
abstract class BeverageMaker {
public final void makeBeverage() {
prepareIngredients();
brew();
pourInCup();
addCondiments();
}
protected abstract void prepareIngredients();
protected abstract void brew();
protected void pourInCup() {
System.out.println("饮品倒入杯中");
}
protected abstract void addCondiments();
}
class CoffeeMaker extends BeverageMaker {
protected void prepareIngredients() {
System.out.println("准备咖啡豆及水");
}
protected void brew() {
System.out.println("煮咖啡");
}
protected void addCondiments() {
System.out.println("添加糖和牛奶");
}
}
class TeaMaker extends BeverageMaker {
protected void prepareIngredients() {
System.out.println("准备茶叶和水");
}
protected void brew() {
System.out.println("使用沸水泡茶");
}
protected void addCondiments() {
System.out.println("添加柠檬🍋");
}
}
public class TemplateMethodPatternDemo {
public static void main(String[] args) {
BeverageMaker coffeeMaker = new CoffeeMaker();
System.out.println("制作咖啡: ");
coffeeMaker.makeBeverage();
System.out.println("-----------------");
BeverageMaker teaMaker = new TeaMaker();
System.out.println("制作茶: ");
teaMaker.makeBeverage();
}
}
说明:
BeverageMaker 是一个抽象类,其中 makeBeverage 是一个模板方法。它定义了制作饮品的算法骨架,即先准备原料,然后制作饮品,将饮品倒入杯中,最后添加调料。这个方法是最终的(final),不允许子类修改这个过程的顺序。
prepareIngredients、brew 和 addCondiments 是抽象方法。因为不同的饮品需要不同的原料准备、冲泡和添加调料的方法,这些方法由具体的子类来实现。
pourInCup 方法是一个具有默认实现的方法。对于大多数饮品来说,将饮品倒入杯中的操作是相似的,但如果子类有特殊要求,也可以覆盖这个方法。
CoffeeMaker 和 TeaMaker 是 BeverageMaker 的具体子类。它们分别覆盖了 prepareIngredients、brew 和 addCondiments 方法,以实现制作咖啡和茶的具体步骤。
JDK中的应用:
java.util.AbstractList 与 java.util.AbstractMap
Spring中的应用:
JdbcTemplate 与 RestTemplate
以上是本篇文章的全部内容,如对你有帮助帮忙点赞+转发+收藏
推荐文章
太强了!SpringBoot结合Camel实现各种协议路由规则定义
接口优化!Spring Boot 整合 Kryo 提升接口性能,传输数据更小