Java 设计模式
Java后端常用设计模式总览表
模式 | 核心思想 | Spring / Spring Boot应用 | 手写实现核心 |
---|---|---|---|
单例模式 (Singleton) | 一个类只有一个实例,提供全局访问点 | Spring容器中的默认Bean都是单例管理 | volatile + synchronized 双重检查锁定,懒加载单例 |
工厂模式 (Factory) | 统一管理和创建对象,不暴露具体创建逻辑 | ApplicationContext/BeanFactory 统一管理Bean创建 | 定义工厂类,使用if-else 或注册表管理产品实例化 |
建造者模式 (Builder) | 分步骤、链式构建复杂对象,分离构建和表示 | Lombok的@Builder注解,客户端配置如OkHttpClient | 写Builder内部静态类,链式调用设置属性,最后build |
原型模式 (Prototype) | 复制已有对象,避免重新new实例 | @Scope("prototype") 让Bean每次getBean都生成新实例 | 实现Cloneable接口,重写clone() 方法深/浅拷贝 |
代理模式 (Proxy) | 通过代理对象控制访问、扩展目标对象功能 | Spring AOP(@Transactional、@Aspect等) | JDK动态代理(接口)/ CGLIB子类代理(继承+增强) |
装饰器模式 (Decorator) | 动态添加功能,不修改原对象结构 | Servlet Filter链、Spring MVC拦截器链(增强请求) | 抽象组件接口+抽象装饰器类+具体装饰器层层包裹 |
适配器模式 (Adapter) | 将一个接口转换成另一个兼容的新接口 | Spring MVC HandlerAdapter适配不同类型Controller | 定义适配器类,包装老接口对象,实现新接口 |
外观模式 (Facade) | 提供统一高层接口,简化复杂子系统调用 | Service层聚合调用多个子系统服务(FacadeService) | 定义一个Facade类,组合子系统并统一暴露简洁接口 |
策略模式 (Strategy) | 封装一系列可互换的算法或行为,动态切换 | Spring Security中多种认证策略,支付策略选择 | 策略接口+多策略实现+上下文Context动态持有策略 |
模板方法模式 (Template Method) | 固定流程结构,子类实现细节步骤 | JdbcTemplate/RestTemplate/RedisTemplate等 | 抽象父类定义流程+抽象方法留给子类实现 |
责任链模式 (Chain of Responsibility) | 请求沿链传递,每个节点决定处理或传递 | Servlet FilterChain、HandlerInterceptor链 | Handler接口+设置next+handle递归链式传递 |
观察者模式 (Observer) | 一对多通知机制,主题变化自动通知观察者 | Spring事件机制(ApplicationEventPublisher+@EventListener) | Subject接口+Observer接口+列表管理观察者,通知更新 |
✅ 1. 单例模式(Singleton Pattern)
✏️ 基本概念
单例模式的定义是:
保证一个类在整个系统中只有一个实例,并且提供一个全局访问点。
Spring容器默认就是单例模式,通过IoC管理Bean,确保一个Bean一个实例,方便全局统一调度。
简单说,就是一个类只new一次,后面都复用同一个对象。
✏️ 为什么要用单例模式?
-
节省内存(避免反复创建同一对象)
-
保持全局统一性(比如日志类、缓存管理器、数据库连接池)
-
控制共享资源(保证线程安全)
✏️ 在Spring中的应用
在Spring中:
-
默认情况下,所有@Bean都是单例模式管理的。
-
只要一个类被注册到Spring容器里(比如加了
@Component
、@Service
、@Controller
), -
Spring容器在启动的时候就创建一个实例,整个容器里共享这一份实例。
✏️ 示例讲解
@Service
public class UserService {public void print() {System.out.println("UserService实例: " + this);}
}
调用代码:
@Autowired
private UserService userService1;@Autowired
private UserService userService2;public void test() {userService1.print();userService2.print();
}
输出结果:
UserService实例: com.example.UserService@1b6d3586
UserService实例: com.example.UserService@1b6d3586
- 说明
userService1
和userService2
是同一个实例。
✏️手写示例
volatile
+ synchronized
(双重检查锁定)
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;}
}
✅ 2. 工厂模式(Factory Pattern)
✏️ 基本概念
工厂模式的定义是:
定义一个用于创建对象的接口,让子类决定实例化哪一个类。
简单说,就是把对象的创建过程交给工厂统一管理,而不是自己new对象。
✏️ 为什么要用工厂模式?
-
解耦对象的创建过程和使用过程
-
统一集中管理对象创建,便于维护
-
可以根据不同参数返回不同子类实例(多态)
✏️ 在Spring中的应用
在Spring中:
-
BeanFactory是Spring容器的最基本工厂接口。
-
ApplicationContext是BeanFactory的子接口,提供了更强大的功能。
Spring容器就是一个超大的工厂,你只需要调用getBean()
,容器就帮你创建对象并注入依赖了。
✏️ 示例讲解
Spring工厂使用例子:
ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);UserService userService = context.getBean(UserService.class);
userService.print();
-
AnnotationConfigApplicationContext
是工厂(Factory)。 -
getBean()
就是工厂方法,通过Bean的名字或者类型拿到对象。 -
创建、初始化、依赖注入,这些细节都隐藏了。
内容 | 对应 |
---|---|
接口:定义了创建对象的方法 | ApplicationContext 定义了 getBean() 等方法 |
子类:决定具体如何创建对象 | AnnotationConfigApplicationContext 决定通过注解扫描来实例化哪些Bean |
使用时,只关心接口,不关心细节 | 调用 context.getBean(UserService.class) ,不用关心UserService是怎么被创建出来的 |
✏️手写示例
public class CarFactory {public static Car getCar(String type) {if ("bmw".equals(type)) {return new BmwCar();} else if ("audi".equals(type)) {return new AudiCar();}return null;}
}
-
外部只管调用
CarFactory.getCar("bmw")
-
不关心Car是怎么new出来的
✅ 3. 建造者模式(Builder Pattern)
✏️ 基本概念
建造者模式的定义是:
将一个复杂对象的构建过程分步进行,并且允许构建过程和对象本身解耦。
简单说,就是链式调用,一步步设置参数,最后一次性build出对象。
✏️ 为什么要用建造者模式?
-
对象属性太多,构造函数太复杂时,避免构造方法参数过长问题
-
使对象创建过程清晰有条理
-
支持不同顺序、不同属性构建对象(灵活)
✏️ 在Spring Boot/Lombok中的应用
Spring Boot项目里常常结合Lombok的@Builder注解来使用建造者模式。
比如定义一个实体类:
@Data
@Builder
public class User {private String name;private Integer age;private String email;
}
调用时:
User user = User.builder().name("Tom").age(18).email("tom@example.com").build();
优势:
-
不需要自己写冗长的构造器
-
不需要记得属性顺序
-
可读性极强
✏️手写示例
public class User {private String name;private Integer age;private User(Builder builder) {this.name = builder.name;this.age = builder.age;}public static class Builder {private String name;private Integer age;public Builder name(String name) { this.name = name; return this; }public Builder age(Integer age) { this.age = age; return this; }public User build() { return new User(this); }}
}
✅ 4. 原型模式(Prototype Pattern)
✏️ 基本概念
原型模式的定义是:
通过复制已有的对象来创建新的对象,而不是通过new新的实例。
Spring通过@Scope("prototype")实现了原型模式,让Bean可以按需每次创建一个新实例,而不是单例复用。
简单说,就是复制一份已有对象的副本。
✏️ 为什么要用原型模式?
-
new一个新对象成本高时,用复制可以节省资源。
-
对象初始化复杂时,可以直接clone已有对象。
-
Spring容器中,有时候需要每次getBean()都返回新的实例。
✏️ 在Spring中的应用
在Spring中,如果一个Bean标注了@Scope("prototype")
:
-
每次从容器拿Bean,Spring都会新建一个实例。
-
而不是像单例那样复用。
示例:
@Component
@Scope("prototype")
public class OrderService {
}
调用:
@Autowired
private OrderService orderService1;@Autowired
private OrderService orderService2;
输出对比:
orderService1 = com.example.OrderService@1b6d3586
orderService2 = com.example.OrderService@2a1f58b6
可以看到,orderService1
和orderService2
是不同实例!
✏️手写示例
public class User implements Cloneable {private String name;@Overrideprotected Object clone() throws CloneNotSupportedException {return super.clone();}
}
通过clone()
方法复制对象,而不是new。
✅ 5. 代理模式(Proxy Pattern)
代理模式的本质是:
不直接访问目标对象,而是通过代理对象间接访问目标对象,并且在访问前后可以增加一些额外操作。
代理模式通过代理对象统一管理对目标对象的访问,实现功能增强和解耦。Spring AOP就是最典型的应用。
-
代理对象和目标对象实现相同接口或继承相同父类。
-
客户端不感知目标对象变化,所有增强逻辑都由代理完成。
-
本质是控制访问 + 功能增强。
✏️ 为什么要用代理模式?
-
在目标对象执行前后加入通用逻辑(日志、权限控制、事务管理)
-
隐藏目标对象的细节,隔离调用者和被调用者
-
延迟加载、远程代理、保护访问等
✏️ Spring 中的应用
Spring AOP(面向切面编程)核心就是基于代理模式!
当你在Spring中使用如@Transactional、@Async、@Cacheable、@Aspect等功能时, 实际上Spring会:
-
创建一个代理对象来代替你的目标对象。
-
所有方法调用都会先进入代理,再决定是否增强(比如加事务、异步执行等)。
AOP代理的具体实现
类型 | 说明 |
---|---|
JDK动态代理 | 基于接口代理,只能代理接口方法 |
CGLIB代理 | 继承目标类,重写方法代理,没有接口也能代理 |
例子(SpringBoot中):
调用过程实际上是:
代理对象帮我们织入了事务开启/提交的逻辑。
✏️手写示例
方法一:JDK动态代理(基于接口)
定义接口:
public interface UserService {void addUser();
}
实现接口:
public class UserServiceImpl implements UserService {@Overridepublic void addUser() {System.out.println("新增用户");}
}
定义代理逻辑:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;public class UserServiceProxy {public static UserService createProxy(UserService target) {return (UserService) Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("方法执行前增强逻辑...");Object result = method.invoke(target, args);System.out.println("方法执行后增强逻辑...");return result;}});}
}
使用代理:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;public class UserServiceProxy {public static UserService createProxy(UserService target) {return (UserService) Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("方法执行前增强逻辑...");Object result = method.invoke(target, args);System.out.println("方法执行后增强逻辑...");return result;}});}
}
public class Main {public static void main(String[] args) {UserService target = new UserServiceImpl();UserService proxy = UserServiceProxy.createProxy(target);proxy.addUser();}
}
输出结果:
方法执行前增强逻辑...
新增用户
方法执行后增强逻辑...
方法二:自己手写静态代理(和CGLIB动态代理相似)
静态代理 | CGLIB 动态代理 | |
---|---|---|
生成代理类的时机 | 编译时期(代码阶段就有) | 运行时期(根据目标类即时生成字节码) |
是否需要手写代理类 | 是,开发者需要显式写一个代理类 | 否,由框架(如 CGLIB)自动生成 |
代理类的数量 | 代理一个类就要写一个代理类 | 可以动态为任意类生成代理,不需要事先知道 |
为什么你觉得 CGLIB 像静态代理?
因为 CGLIB 的底层原理是:
-
给目标类创建一个子类,
-
然后重写父类的方法,
-
再在方法前后加逻辑(比如AOP切面),
-
最后调用
super.method()
来执行原来的逻辑。
这种重写+调用 super
的模式,看起来和我们手动写静态代理类很像,比如手写:
public class UserServiceProxy extends UserService {@Overridepublic void save() {System.out.println("before...");super.save();System.out.println("after...");}
}
CGLIB底层生成的字节码跟这种继承加增强的思路非常像。
但是生成动作是运行时动态完成的,而手写静态代理是编译期就固定好的源代码,这是根本区别!
✅ 6. 装饰器模式(Decorator Pattern)
装饰器模式的本质是:
在不修改原有类的基础上,动态地为对象添加新的功能。
-
装饰器和原对象实现相同接口。
-
装饰器持有原对象引用,并在调用原方法前后增加逻辑。
-
可以灵活叠加多个装饰器,功能组合不受限制。
✏️ 为什么要用装饰器模式?
-
需要动态添加、扩展类的功能。
-
不改变原有对象结构,不影响已有代码。
-
职责单一,每个装饰器只专注于一类功能增强。
✏️ Spring / Spring Boot中的应用示例
(1)Servlet Filter链(FilterChain)
Servlet 容器(如 Tomcat)处理一个 HTTP 请求时,会:
-
创建一个
FilterChain
对象(内部保存所有配置好的Filter
列表)。 -
按照顺序调用每个 Filter 的
doFilter()
方法。 -
每个
Filter
自己选择:-
是否继续调用
chain.doFilter(request, response)
; -
在调用前后加逻辑。
-
典型Filter结构:
public class MyFilter implements Filter {public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {System.out.println("Filter 前置处理");chain.doFilter(request, response); // 继续调用下一个Filter或目标ServletSystem.out.println("Filter 后置处理");}
}
底层原理(Tomcat中)
Tomcat 中 ApplicationFilterChain
类的源码大概逻辑是:
public class ApplicationFilterChain implements FilterChain {private List<Filter> filters;private int pos = 0; // 当前执行到第几个Filterpublic void doFilter(ServletRequest request, ServletResponse response) {if (pos < filters.size()) {Filter currentFilter = filters.get(pos++);currentFilter.doFilter(request, response, this);} else {// 所有Filter执行完了,真正调用目标Servletservlet.service(request, response);}}
}
每个Filter调用完自己的逻辑后,再调用chain.doFilter
,递归推进,形成前后增强。
-
每个Filter不修改原Servlet;
-
可以动态添加新的Filter增强功能;
-
Filter和Servlet都基于ServletRequest/ServletResponse接口约定,保证了统一性。
(2)Spring MVC的HandlerInterceptor链
Spring MVC 收到请求后,会执行:
-
DispatcherServlet -> HandlerMapping -> 获取到一个 HandlerExecutionChain
-
HandlerExecutionChain 内部保存了很多
HandlerInterceptor
-
调用每个拦截器的
preHandle()
方法。 -
如果全部返回 true,继续执行真正的 Controller。
-
Controller执行完成后,依次倒序调用拦截器的
postHandle()
和afterCompletion()
。
拦截器基本结构:
public class MyInterceptor implements HandlerInterceptor {public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {System.out.println("前置拦截逻辑");return true; // 继续流程}public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {System.out.println("后置拦截逻辑");}public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {System.out.println("请求完成后的清理逻辑");}
}
✏️手写示例
// 抽象组件(统一接口)
public interface Component {void operation();
}// 具体组件(核心功能)
public class ConcreteComponent implements Component {public void operation() {System.out.println("核心功能");}
}// 抽象装饰器(持有Component引用)
public abstract class Decorator implements Component {protected Component component;public Decorator(Component component) {this.component = component;}
}// 具体装饰器A
public class DecoratorA extends Decorator {public DecoratorA(Component component) {super(component);}public void operation() {System.out.println("功能A增强之前");component.operation(); // 调用原对象方法System.out.println("功能A增强之后");}
}// 具体装饰器B
public class DecoratorB extends Decorator {public DecoratorB(Component component) {super(component);}public void operation() {System.out.println("功能B增强之前");component.operation();System.out.println("功能B增强之后");}
}
-
核心组件(ConcreteComponent)负责最基础的逻辑;
-
装饰器(ConcreteDecorator)持有原组件引用,在调用前后加功能;
-
可以嵌套多个装饰器动态叠加功能。
✅ 7. 适配器模式(Adapter Pattern)
适配器模式的本质是:
将一个类的接口转换成客户希望的另一个接口,解决接口不兼容问题。
-
适配器是一个中间层。
-
外部系统调用的是统一接口,不需要关心内部老接口、旧实现。
-
包装原对象,把它“适配”成新的标准。
✏️ 为什么要用适配器模式?
-
兼容老系统和新系统(让不同接口风格能互通)
-
重用已有类(不改动老类源码)
-
降低代码耦合度(通过中间适配)
✏️ 在Spring / Spring Boot中的应用示例
Spring MVC 中的 HandlerAdapter
-
Spring MVC允许开发者用不同风格的Controller编写业务逻辑(比如传统的Controller类、Rest风格、HttpRequestHandler等)。每种Controller风格调用方式不同,但DispatcherServlet要统一调用。
-
DispatcherServlet调用HandlerMapping(处理器映射器)找到Handler。通常,Handler是一个Controller方法,即:一个带有
@RequestMapping
、@PostMapping
等注解的方法。 -
拿到Handler之后,DispatcherServlet调用HandlerAdapter(处理器适配器)。HandlerAdapter负责真正执行Handler。因为Handler可能是不同风格的(比如普通Controller,还是Rest风格Controller),需要一个适配器统一调用。
核心接口:
public interface HandlerAdapter {boolean supports(Object handler);ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;
}
✏️手写示例
老系统的播放器只能播放MP3 (OldPlayer
)
新系统标准接口要求播放MP4 (NewPlayer
)
定义老接口(不改动它):
public interface OldPlayer {void playMp3(String fileName);
}
老播放器实现:
public class OldMp3Player implements OldPlayer {@Overridepublic void playMp3(String fileName) {System.out.println("播放老式MP3:" + fileName);}
}
定义新接口:
public interface NewPlayer {void playMp4(String fileName);
}
写一个适配器(Adapter),包装老接口,适配新接口:
public class PlayerAdapter implements NewPlayer {private OldPlayer oldPlayer;public PlayerAdapter(OldPlayer oldPlayer) {this.oldPlayer = oldPlayer;}@Overridepublic void playMp4(String fileName) {System.out.println("适配器转换中...");oldPlayer.playMp3(fileName);}
}
使用适配器:
public class Main {public static void main(String[] args) {OldPlayer oldPlayer = new OldMp3Player();NewPlayer newPlayer = new PlayerAdapter(oldPlayer);newPlayer.playMp4("test.mp3");}
}
✅8. 外观模式(Facade Pattern)
外观模式的本质是:
为一组复杂子系统提供一个统一的、高层次的接口,简化客户端对子系统的使用。
简单理解:
-
内部可能很复杂(好多类、好多服务互相调用)。
-
但是对外,只暴露一个简单易用的入口。
-
客户端不需要知道内部细节,只管用外观类。
✏️ 为什么用外观模式?
-
简化调用流程,屏蔽内部复杂性
-
降低模块之间的耦合
-
更易于维护和扩展
✏️ Spring / Spring Boot中的应用示例
在实际项目中,Service层经常使用外观模式:
-
将多个Service组合在一个统一的Facade服务里。
-
Controller只调用Facade,不关心内部细节。
例子:电商系统下单逻辑
涉及多个子系统:
子系统 | 功能 |
---|---|
OrderService | 创建订单 |
PaymentService | 支付订单 |
InventoryService | 减库存 |
LogisticsService | 安排发货 |
如果Controller自己依次调用这些子系统,代码又长又乱!
于是写一个外观服务统一封装:
@Service
public class OrderFacadeService {@Autowiredprivate OrderService orderService;@Autowiredprivate PaymentService paymentService;@Autowiredprivate InventoryService inventoryService;@Autowiredprivate LogisticsService logisticsService;public void placeOrder(OrderRequest request) {orderService.createOrder(request);paymentService.payOrder(request.getOrderId());inventoryService.deductStock(request.getProductId());logisticsService.arrangeShipment(request.getOrderId());}
}
Controller调用变得超简单:
@RestController
public class OrderController {@Autowiredprivate OrderFacadeService orderFacadeService;@PostMapping("/order")public void createOrder(@RequestBody OrderRequest request) {orderFacadeService.placeOrder(request);}
}
✅ 9. 策略模式(Strategy Pattern)
策略模式的本质是:
定义一系列算法(行为),将它们封装起来,使它们可以互相替换使用。
核心思想:
-
不同策略可以动态切换。
-
行为由策略对象决定,不由调用者写死。
-
避免
if...else...
或switch...case...
代码膨胀。
✏️ 为什么要用策略模式?
-
将算法/行为从使用者中分离,更灵活更可扩展。
-
减少代码耦合,新策略只需新增类,不改动原有代码(符合开闭原则)。
-
运行时可以动态切换策略,满足不同业务场景。
✏️ Spring / Spring Boot中的应用示例
(1)Spring Security中的认证机制
Spring Security允许通过不同认证策略(用户名密码登录、OAuth2登录、JWT认证等),
每种认证方式就是一种策略,由AuthenticationProvider接口统一约定。
核心接口:
public interface AuthenticationProvider {Authentication authenticate(Authentication authentication) throws AuthenticationException;
}
不同实现:
-
DaoAuthenticationProvider
:基于数据库用户名密码认证 -
JwtAuthenticationProvider
:基于JWT Token认证 -
OAuth2AuthenticationProvider
:基于OAuth2认证
Spring Security会根据具体情况选择不同的AuthenticationProvider策略执行认证。
(2)实际业务开发中常见使用(举例)
比如你开发一个支付系统:
-
有微信支付(WeChatPayStrategy)
-
有支付宝支付(AliPayStrategy)
-
有银联支付(UnionPayStrategy)
不同支付方式就是不同的策略!
✏️手写示例
场景模拟:支付策略系统
定义策略接口:
public interface PayStrategy {void pay(double amount);
}
定义不同策略实现:
public class WeChatPayStrategy implements PayStrategy {@Overridepublic void pay(double amount) {System.out.println("使用微信支付:" + amount + "元");}
}
微信支付:
public class AliPayStrategy implements PayStrategy {@Overridepublic void pay(double amount) {System.out.println("使用支付宝支付:" + amount + "元");}
}
支付宝支付:
public class PayContext {private PayStrategy payStrategy;public PayContext(PayStrategy payStrategy) {this.payStrategy = payStrategy;}public void executePay(double amount) {payStrategy.pay(amount);}
}
定义上下文类(Context),持有一个策略对象:
public class PayContext {private PayStrategy payStrategy;public PayContext(PayStrategy payStrategy) {this.payStrategy = payStrategy;}public void executePay(double amount) {payStrategy.pay(amount);}
}
使用策略:
public class Main {public static void main(String[] args) {PayContext context = new PayContext(new WeChatPayStrategy());context.executePay(100.0);context = new PayContext(new AliPayStrategy());context.executePay(200.0);}
}
输出结果:
使用微信支付:100.0元
使用支付宝支付:200.0元
✅ 10. 模板方法模式(Template Method Pattern)
模板方法模式的本质是:
在父类定义一个操作的算法骨架(模板),而将一些可变的步骤延迟到子类去实现。
简单说:
-
父类固定流程框架。
-
子类定义具体细节。
-
保证整体流程稳定,但细节可以自由变化。
✏️ 为什么要用模板方法模式?
-
固定流程标准,防止子类破坏流程结构。
-
让子类只关注差异化实现,复用公共逻辑。
-
满足开闭原则,新增业务只需继承,不改动父类。
✏️ Spring / Spring Boot中的应用示例
JdbcTemplate / RedisTemplate / RestTemplate
Spring提供了各种Template
类,使用了模板方法模式。
以JdbcTemplate
为例:
-
固定了获取连接 -> 执行SQL -> 释放资源的流程。
-
留出扩展点,比如定义ResultSet如何映射成对象(RowMapper接口)。
典型流程示例(JdbcTemplate简化版)
public class JdbcTemplate {public <T> List<T> query(String sql, RowMapper<T> rowMapper) {// 1. 获取数据库连接// 2. 执行SQL查询// 3. 遍历ResultSet,用RowMapper映射每行数据// 4. 释放连接资源}
}
-
固定的查询流程由JdbcTemplate控制。
-
每一行如何映射成对象由
RowMapper
子类控制。
✏️手写示例
场景模拟:制作咖啡 or 茶饮品
定义抽象父类,写好模板方法:
public abstract class BeverageTemplate {// 模板方法,定义制作饮品的流程public final void prepareBeverage() {boilWater();brew();pourInCup();addCondiments();}protected void boilWater() {System.out.println("烧水");}protected void pourInCup() {System.out.println("倒入杯中");}// 变化点,留给子类实现protected abstract void brew();protected abstract void addCondiments();
}
定义具体子类(茶):
public class Tea extends BeverageTemplate {@Overrideprotected void brew() {System.out.println("泡茶叶");}@Overrideprotected void addCondiments() {System.out.println("加柠檬");}
}
定义具体子类(咖啡):
public class Coffee extends BeverageTemplate {@Overrideprotected void brew() {System.out.println("冲泡咖啡粉");}@Overrideprotected void addCondiments() {System.out.println("加牛奶和糖");}
}
使用模板方法:
public class Main {public static void main(String[] args) {BeverageTemplate tea = new Tea();tea.prepareBeverage();System.out.println("-----------------");BeverageTemplate coffee = new Coffee();coffee.prepareBeverage();}
}
输出结果:
烧水
泡茶叶
倒入杯中
加柠檬
-----------------
烧水
冲泡咖啡粉
倒入杯中
加牛奶和糖
✅11. 责任链模式(Chain of Responsibility Pattern)
责任链模式的本质是:
将请求沿着一条链进行传递,每个节点可以决定处理还是传递给下一个节点。
简单理解:
-
把多个处理器串成一条链。
-
一个请求来了,沿着链一个一个传递。
-
每个处理器只关心自己负责的部分,
处理完了可以继续传递,也可以终止传递。
✏️ 为什么要用责任链模式?
-
发送者和处理者解耦,不需要指定由哪个具体对象处理。
-
灵活组合责任链,可以动态增删节点。
-
责任细分,每个处理器只做自己的事,符合单一职责原则。
-
增强扩展性,新增处理器不需要改动已有代码(符合开闭原则)。
✏️ Spring / Spring Boot中的应用示例
(1)Servlet Filter链(FilterChain)
在Java Web开发中:
-
一个请求经过多个Filter(日志、权限、限流、防护等)处理。
-
每个Filter处理完后调用
chain.doFilter(request, response)
,继续往下传。
每个Filter:
-
处理自己的逻辑。
-
决定是否继续调用下一个。
这是标准的责任链模式。
(2)Spring MVC拦截器链(HandlerInterceptor)
配置多个拦截器组成链
只要在你的WebMvcConfigurer
里按顺序添加多个拦截器,就自动形成一条链了:
@Configuration
public class WebConfig implements WebMvcConfigurer {@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(new Interceptor1()).addPathPatterns("/**"); // 拦截所有路径registry.addInterceptor(new Interceptor2()).addPathPatterns("/api/**"); // 拦截/api路径registry.addInterceptor(new Interceptor3()).addPathPatterns("/admin/**"); // 拦截/admin路径}
}
注意:
-
注册顺序决定了链的执行顺序。
addPathPatterns()
可以控制每个拦截器生效的路径。 -
如果某个拦截器的
preHandle()
返回了false
,那么后续的拦截器链不会继续执行,请求也不会进入Controller了。 -
preHandle按注册顺序执行。postHandle和afterCompletion按注册顺序的逆序执行。
处理+可中断+链式传递,完全符合责任链模式!
✏️手写示例
场景模拟:请求处理责任链(日志、鉴权)
定义抽象处理器(Handler):
public abstract class Handler {protected Handler next;// 设置下一个处理器public void setNext(Handler next) {this.next = next;}// 处理请求public abstract void handle(String request);
}
定义具体处理器
日志处理器:
public class LogHandler extends Handler {@Overridepublic void handle(String request) {System.out.println("日志记录:" + request);if (next != null) {next.handle(request);}}
}
鉴权处理器:
public class AuthHandler extends Handler {@Overridepublic void handle(String request) {System.out.println("鉴权检查:" + request);if (next != null) {next.handle(request);}}
}
组装责任链并使用:
public class Main {public static void main(String[] args) {Handler logHandler = new LogHandler();Handler authHandler = new AuthHandler();logHandler.setNext(authHandler); // 先日志,再鉴权logHandler.handle("用户请求访问系统资源");}
}
✅ 12. 观察者模式(Observer Pattern)
观察者模式的本质是:
定义对象之间一对多的依赖关系,当主题对象状态变化时,所有依赖它的观察者都会收到通知并自动更新。
简单理解:
-
一个主题对象(Subject)。
-
多个观察者对象(Observer)。
-
主题变化 → 通知所有观察者。
✏️ 为什么要用观察者模式?
-
实现发布者与订阅者之间的松耦合。
-
灵活扩展:任意增加/减少观察者,无需改动主题对象。
-
实时响应:事件驱动,变化自动通知。
✏️ Spring / Spring Boot中的应用示例
Spring事件机制(ApplicationEventPublisher)
在Spring中:
-
通过
ApplicationEventPublisher
发布事件(比如用户注册、订单完成等)。 -
有多个
ApplicationListener
/@EventListener
注册监听器。 -
发布者不需要知道谁在监听,监听器只管监听自己感兴趣的事件。
发布事件
调用applicationContext.publishEvent(event)
→
AbstractApplicationContext.publishEvent()
→
getApplicationEventMulticaster().multicastEvent(event)
。分派给监听器
SimpleApplicationEventMulticaster
(默认实现)维护一个监听器列表,对每个注册的ApplicationListener
:
调用
supportsEventType()
判断是否感兴趣;若是,则同步或异步执行
listener.onApplicationEvent(event)
。监听器注册
上下文启动时,容器会扫描所有实现ApplicationListener
接口的 Bean,或带@EventListener
注解的方法,
并将它们注册到ApplicationEventMulticaster
。
定义事件:
public class OrderCreatedEvent extends ApplicationEvent {private final String orderId;public OrderCreatedEvent(Object source, String orderId) {super(source);this.orderId = orderId;}public String getOrderId() {return orderId;}
}
发布事件:
@Autowired
private ApplicationEventPublisher publisher;public void createOrder(String orderId) {publisher.publishEvent(new OrderCreatedEvent(this, orderId));
}
监听事件:
@Component
public class OrderCreatedListener {@EventListenerpublic void onOrderCreated(OrderCreatedEvent event) {System.out.println("收到订单创建事件:" + event.getOrderId());}
}
一发布,所有监听器同步或异步收到通知。
✏️手写示例
场景模拟:消息发布订阅
定义主题(Subject)接口:
public interface Subject {void addObserver(Observer observer);void removeObserver(Observer observer);void notifyObservers(String message);
}
定义观察者(Observer)接口:
public interface Observer {void update(String message);
}
主题实现类:
import java.util.ArrayList;
import java.util.List;public class ConcreteSubject implements Subject {private List<Observer> observers = new ArrayList<>();@Overridepublic void addObserver(Observer observer) {observers.add(observer);}@Overridepublic void removeObserver(Observer observer) {observers.remove(observer);}@Overridepublic void notifyObservers(String message) {for (Observer observer : observers) {observer.update(message);}}
}
观察者实现类:
public class ConcreteObserver implements Observer {private String name;public ConcreteObserver(String name) {this.name = name;}@Overridepublic void update(String message) {System.out.println(name + " 收到消息:" + message);}
}
测试使用:
public class Main {public static void main(String[] args) {ConcreteSubject subject = new ConcreteSubject();Observer observerA = new ConcreteObserver("观察者A");Observer observerB = new ConcreteObserver("观察者B");subject.addObserver(observerA);subject.addObserver(observerB);subject.notifyObservers("系统通知:有新活动上线!");}
}