当前位置: 首页 > news >正文

深入 Spring 内核:解密 15 种设计模式的实战应用与底层实现

Spring 框架作为 Java 生态中当之无愧的霸主,其成功不仅在于功能的强大,更在于其内部蕴含的设计哲学。作为开发者,我们每天都在使用 Spring 的各种功能,却很少深入思考:这些功能背后究竟采用了哪些设计模式?为什么要这样设计?理解这些设计模式,不仅能帮助我们更好地使用 Spring,更能提升我们自身的架构设计能力。

本文将带你全面剖析 Spring 框架中 15 种核心设计模式的应用场景、实现原理和实战案例,从 Bean 的创建到 AOP 的实现,从事件机制到事务管理,让你看透 Spring 的 "设计套路"。

一、工厂模式:Spring Bean 的创建利器

工厂模式是 Java 开发中最常用的设计模式之一,它提供了一种创建对象的最佳方式。在 Spring 中,工厂模式被广泛应用于 Bean 的创建和管理,主要分为简单工厂模式和工厂方法模式。

1. 简单工厂模式:BeanFactory 的实现

简单工厂模式(Simple Factory Pattern)属于创建型模式,又叫做静态工厂方法模式,但不属于 23 种 GOF 设计模式之一。简单工厂模式是由一个工厂对象决定创建出哪一种产品类的实例。

在 Spring 中,BeanFactory就是简单工厂模式的典型应用。它负责根据配置信息创建和管理 Bean 的实例,隐藏了 Bean 创建的细节,让开发者无需关心 Bean 的具体实现和初始化过程。

/*** Spring BeanFactory接口简化版示例* 作者: ken*/
public interface BeanFactory {/*** 根据Bean名称获取Bean实例* @param beanName Bean名称* @return Bean实例* @throws BeansException 当获取Bean失败时抛出*/Object getBean(String beanName) throws BeansException;/*** 根据Bean名称和类型获取Bean实例* @param beanName Bean名称* @param requiredType 所需的Bean类型* @param <T> 泛型类型* @return 类型化的Bean实例* @throws BeansException 当获取Bean失败时抛出*/<T> T getBean(String beanName, Class<T> requiredType) throws BeansException;/*** 判断容器中是否包含指定名称的Bean* @param beanName Bean名称* @return 如果包含则返回true,否则返回false*/boolean containsBean(String beanName);/*** 判断指定名称的Bean是否为单例* @param beanName Bean名称* @return 如果是单例则返回true,否则返回false* @throws NoSuchBeanDefinitionException 当指定名称的Bean不存在时抛出*/boolean isSingleton(String beanName) throws NoSuchBeanDefinitionException;/*** 获取指定名称的Bean的类型* @param beanName Bean名称* @return Bean的类型* @throws NoSuchBeanDefinitionException 当指定名称的Bean不存在时抛出*/Class<?> getType(String beanName) throws NoSuchBeanDefinitionException;
}

BeanFactory的工作流程如下:

BeanFactory的实现类DefaultListableBeanFactory是 Spring 容器的核心,它实现了 Bean 的注册、创建和管理等核心功能。我们可以通过以下示例来理解其工作原理:

/*** BeanFactory使用示例* 作者: ken*/
public class BeanFactoryDemo {public static void main(String[] args) {// 创建BeanFactory实例DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();// 注册Bean定义RootBeanDefinition beanDefinition = new RootBeanDefinition(UserService.class);beanFactory.registerBeanDefinition("userService", beanDefinition);// 获取Bean实例UserService userService = beanFactory.getBean("userService", UserService.class);userService.sayHello();}static class UserService {public void sayHello() {System.out.println("Hello, BeanFactory!");}}
}

2. 工厂方法模式:FactoryBean 的灵活应用

工厂方法模式(Factory Method Pattern)是创建型模式的一种,它定义了一个创建对象的接口,但由子类决定要实例化的类是哪一个。工厂方法让类的实例化推迟到子类。

Spring 中的FactoryBean接口就是工厂方法模式的典型应用。它允许开发者自定义 Bean 的创建逻辑,特别是当需要创建复杂的 Bean 实例时非常有用。

/*** Spring FactoryBean接口定义* 作者: ken*/
public interface FactoryBean<T> {/*** 获取由FactoryBean创建的Bean实例* @return Bean实例* @throws Exception 当创建Bean失败时抛出*/T getObject() throws Exception;/*** 获取由FactoryBean创建的Bean的类型* @return Bean的类型*/Class<?> getObjectType();/*** 指示由FactoryBean创建的Bean是否为单例* @return 如果是单例则返回true,否则返回false*/default boolean isSingleton() {return true;}
}

FactoryBean的工作流程如下:

下面是一个自定义FactoryBean的示例,用于创建复杂的User对象:

/*** 自定义FactoryBean示例* 作者: ken*/
@Component
public class UserFactoryBean implements FactoryBean<User> {private String userId;@Overridepublic User getObject() throws Exception {// 模拟复杂的对象创建过程User user = new User();user.setId(userId);user.setName("Default Name");user.setEmail(userId + "@example.com");// 可以在这里添加更多复杂的初始化逻辑return user;}@Overridepublic Class<?> getObjectType() {return User.class;}// setter方法用于注入依赖public void setUserId(String userId) {this.userId = userId;}@Datapublic static class User {private String id;private String name;private String email;}
}

在 Spring 配置中使用该FactoryBean

/*** FactoryBean配置类* 作者: ken*/
@Configuration
public class FactoryBeanConfig {@Beanpublic UserFactoryBean userFactoryBean() {UserFactoryBean factoryBean = new UserFactoryBean();factoryBean.setUserId("123456");return factoryBean;}
}

使用FactoryBean创建的 Bean:

/*** FactoryBean使用示例* 作者: ken*/
@SpringBootTest
public class FactoryBeanTest {@Autowiredprivate UserFactoryBean.User user; // 注入的是FactoryBean创建的User对象@Autowiredprivate UserFactoryBean userFactoryBean; // 注入的是FactoryBean本身@Testpublic void testFactoryBean() {System.out.println("User: " + user);System.out.println("UserFactoryBean: " + userFactoryBean);}
}

Spring 框架自身也提供了许多FactoryBean的实现,如JndiObjectFactoryBeanLocalSessionFactoryBean等,用于处理特定场景下的 Bean 创建。

二、单例模式:Spring Bean 的默认选择

单例模式(Singleton Pattern)是一种创建型模式,它确保一个类只有一个实例,并提供一个全局访问点。在 Spring 中,Bean 默认是单例的,这有助于节省资源和提高性能。

Spring 单例模式的实现

Spring 的单例模式与传统的单例模式有所不同。传统的单例模式是通过私有构造器和静态方法来保证的,而 Spring 的单例是由容器来管理的,它确保在同一个容器中,一个 Bean 定义只会创建一个实例。

Spring 实现单例的核心是DefaultSingletonBeanRegistry类,它维护了一个缓存单例 Bean 的 Map:

/*** Spring单例Bean注册器简化版* 作者: ken*/
public class DefaultSingletonBeanRegistry {// 缓存单例Bean的Map,key是Bean名称,value是Bean实例private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);// 缓存正在创建中的Bean,用于解决循环依赖private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);// 缓存Bean工厂,用于延迟初始化Beanprivate final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);/*** 获取单例Bean* @param beanName Bean名称* @return Bean实例*/public Object getSingleton(String beanName) {// 先从一级缓存中获取Object singletonObject = singletonObjects.get(beanName);if (ObjectUtils.isEmpty(singletonObject) && isSingletonCurrentlyInCreation(beanName)) {synchronized (this.singletonObjects) {// 从二级缓存中获取singletonObject = earlySingletonObjects.get(beanName);if (ObjectUtils.isEmpty(singletonObject)) {// 从三级缓存中获取Bean工厂ObjectFactory<?> singletonFactory = singletonFactories.get(beanName);if (!ObjectUtils.isEmpty(singletonFactory)) {// 通过工厂创建Bean实例singletonObject = singletonFactory.getObject();// 将Bean实例放入二级缓存earlySingletonObjects.put(beanName, singletonObject);// 从三级缓存中移除工厂singletonFactories.remove(beanName);}}}}return singletonObject;}/*** 注册单例Bean* @param beanName Bean名称* @param singletonObject Bean实例*/public void registerSingleton(String beanName, Object singletonObject) {Assert.notNull(beanName, "Bean name must not be null");Assert.notNull(singletonObject, "Singleton object must not be null");synchronized (this.singletonObjects) {Object oldObject = this.singletonObjects.get(beanName);if (!ObjectUtils.isEmpty(oldObject)) {throw new IllegalStateException("Could not register object [" + singletonObject +"] under bean name '" + beanName + "': there is already object [" + oldObject + "]");}addSingleton(beanName, singletonObject);}}/*** 添加单例Bean到缓存* @param beanName Bean名称* @param singletonObject Bean实例*/protected void addSingleton(String beanName, Object singletonObject) {synchronized (this.singletonObjects) {this.singletonObjects.put(beanName, singletonObject);this.singletonFactories.remove(beanName);this.earlySingletonObjects.remove(beanName);}}/*** 检查Bean是否正在创建中* @param beanName Bean名称* @return 如果正在创建则返回true,否则返回false*/protected boolean isSingletonCurrentlyInCreation(String beanName) {return this.singletonsCurrentlyInCreation.contains(beanName);}// 用于跟踪正在创建的单例Beanprivate final Set<String> singletonsCurrentlyInCreation =Collections.newSetFromMap(new ConcurrentHashMap<>(16));
}

Spring 单例 Bean 的创建流程:

单例 Bean 的配置与使用

在 Spring 中,默认情况下所有的 Bean 都是单例的。我们也可以通过@Scope注解来指定 Bean 的作用域:

/*** Spring Bean作用域示例* 作者: ken*/
@Configuration
public class ScopeConfig {// 默认单例@Beanpublic SingletonService singletonService() {return new SingletonService();}// 原型模式,每次获取都会创建新实例@Bean@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)public PrototypeService prototypeService() {return new PrototypeService();}static class SingletonService {private final String instanceId = UUID.randomUUID().toString();public String getInstanceId() {return instanceId;}}static class PrototypeService {private final String instanceId = UUID.randomUUID().toString();public String getInstanceId() {return instanceId;}}
}

测试单例和原型 Bean 的区别:

/*** 单例和原型Bean测试* 作者: ken*/
@SpringBootTest
public class ScopeTest {@Autowiredprivate ScopeConfig.SingletonService singletonService1;@Autowiredprivate ScopeConfig.SingletonService singletonService2;@Autowiredprivate ApplicationContext context;@Testpublic void testSingleton() {// 单例Bean的两个引用应该指向同一个实例Assert.isTrue(singletonService1.getInstanceId().equals(singletonService2.getInstanceId()),"单例Bean应该是同一个实例");}@Testpublic void testPrototype() {// 每次获取原型Bean都会创建新实例ScopeConfig.PrototypeService prototype1 = context.getBean(ScopeConfig.PrototypeService.class);ScopeConfig.PrototypeService prototype2 = context.getBean(ScopeConfig.PrototypeService.class);Assert.isTrue(!prototype1.getInstanceId().equals(prototype2.getInstanceId()),"原型Bean应该是不同的实例");}
}

单例模式的优缺点

优点:

  1. 减少了内存开销,因为只创建一个实例
  2. 减少了对象创建的时间,提高了系统性能
  3. 可以集中管理资源,如数据库连接池

缺点:

  1. 单例 Bean 是线程不安全的,需要开发者自行处理线程安全问题
  2. 单例 Bean 的生命周期与容器一致,不适用于需要频繁创建和销毁的对象
  3. 可能会导致资源占用过多,特别是对于大对象

Spring 的单例模式实现非常巧妙,通过三级缓存机制不仅保证了单例,还解决了 Bean 之间的循环依赖问题,这是 Spring 容器的一大亮点。

三、代理模式:AOP 的底层实现

代理模式(Proxy Pattern)是一种结构型模式,它为其他对象提供一种代理以控制对这个对象的访问。在 Spring 中,代理模式是 AOP(面向切面编程)的底层实现,主要有两种代理方式:JDK 动态代理和 CGLIB 代理。

1. JDK 动态代理

JDK 动态代理是 Java 自带的代理机制,它基于接口实现代理。Spring 在目标对象实现了接口的情况下,会优先使用 JDK 动态代理。

/*** JDK动态代理示例* 作者: ken*/
public class JdkDynamicProxyDemo {// 定义接口public interface UserService {void addUser(String username);void deleteUser(String username);}// 实现接口public static class UserServiceImpl implements UserService {@Overridepublic void addUser(String username) {System.out.println("添加用户: " + username);}@Overridepublic void deleteUser(String username) {System.out.println("删除用户: " + username);}}// 实现InvocationHandler接口public static class LoggingHandler implements InvocationHandler {private final Object target;public LoggingHandler(Object target) {this.target = target;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {// 前置通知System.out.println("调用方法前: " + method.getName());// 调用目标方法Object result = method.invoke(target, args);// 后置通知System.out.println("调用方法后: " + method.getName());return result;}}// 测试JDK动态代理public static void main(String[] args) {// 创建目标对象UserService target = new UserServiceImpl();// 创建代理对象UserService proxy = (UserService) Proxy.newProxyInstance(UserService.class.getClassLoader(),new Class[]{UserService.class},new LoggingHandler(target));// 调用代理方法proxy.addUser("张三");proxy.deleteUser("李四");}
}

JDK 动态代理的工作原理:

2. CGLIB 代理

当目标对象没有实现接口时,Spring 会使用 CGLIB(Code Generation Library)来创建代理对象。CGLIB 通过继承目标类来实现代理,因此需要目标类不能是 final 的。

/*** CGLIB代理示例* 作者: ken*/
public class CglibProxyDemo {// 目标类(没有实现接口)public static class OrderService {public void createOrder(String orderId) {System.out.println("创建订单: " + orderId);}public void cancelOrder(String orderId) {System.out.println("取消订单: " + orderId);}}// 实现MethodInterceptor接口public static class TransactionInterceptor implements MethodInterceptor {@Overridepublic Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {// 前置通知:开启事务System.out.println("开启事务");// 调用目标方法Object result = proxy.invokeSuper(obj, args);// 后置通知:提交事务System.out.println("提交事务");return result;}}// 测试CGLIB代理public static void main(String[] args) {// 创建Enhancer对象Enhancer enhancer = new Enhancer();// 设置目标类enhancer.setSuperclass(OrderService.class);// 设置回调对象enhancer.setCallback(new TransactionInterceptor());// 创建代理对象OrderService proxy = (OrderService) enhancer.create();// 调用代理方法proxy.createOrder("ORDER_001");proxy.cancelOrder("ORDER_002");}
}

CGLIB 代理的工作原理:

3. Spring AOP 中的代理选择

Spring 会根据目标对象是否实现接口来自动选择合适的代理方式:

我们也可以通过配置强制使用 CGLIB 代理:

/*** AOP配置示例* 作者: ken*/
@Configuration
@EnableAspectJAutoProxy(proxyTargetClass = true) // proxyTargetClass = true 强制使用CGLIB代理
public class AopConfig {@Beanpublic LoggingAspect loggingAspect() {return new LoggingAspect();}
}/*** 日志切面* 作者: ken*/
@Aspect
@Component
public class LoggingAspect {// 定义切入点:匹配com.example.service包下的所有方法@Pointcut("execution(* com.example.service..*(..))")public void serviceMethods() {}// 前置通知@Before("serviceMethods()")public void beforeMethod(JoinPoint joinPoint) {String methodName = joinPoint.getSignature().getName();System.out.println("调用方法前: " + methodName);}// 后置通知@After("serviceMethods()")public void afterMethod(JoinPoint joinPoint) {String methodName = joinPoint.getSignature().getName();System.out.println("调用方法后: " + methodName);}
}

使用 AOP 的服务类:

/*** 用户服务类* 作者: ken*/
@Service
public class UserService {/*** 添加用户* @param username 用户名*/public void addUser(String username) {System.out.println("添加用户: " + username);}/*** 删除用户* @param username 用户名*/public void deleteUser(String username) {System.out.println("删除用户: " + username);}
}

测试 AOP 效果:

/*** AOP测试类* 作者: ken*/
@SpringBootTest
public class AopTest {@Autowiredprivate UserService userService;@Testpublic void testAop() {userService.addUser("张三");userService.deleteUser("李四");}
}

运行结果:

调用方法前: addUser
添加用户: 张三
调用方法后: addUser
调用方法前: deleteUser
删除用户: 李四
调用方法后: deleteUser

代理模式是 Spring AOP 的基础,理解代理模式的工作原理,有助于我们更好地理解 AOP 的实现机制,以及在实际开发中排查与 AOP 相关的问题。

四、适配器模式:Spring MVC 的请求处理

适配器模式(Adapter Pattern)是一种结构型模式,它将一个类的接口转换成客户希望的另一个接口。适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。

在 Spring MVC 中,适配器模式被广泛应用于处理器(Handler)的适配,使得不同类型的处理器可以以统一的方式被调用。

Spring MVC 中的 HandlerAdapter

Spring MVC 定义了HandlerAdapter接口作为适配器的抽象,不同类型的处理器有对应的适配器实现:

/*** Spring MVC HandlerAdapter接口* 作者: ken*/
public interface HandlerAdapter {/*** 判断当前适配器是否支持指定的处理器* @param handler 处理器对象* @return 如果支持则返回true,否则返回false*/boolean supports(Object handler);/*** 使用适配器调用处理器处理请求* @param request 请求对象* @param response 响应对象* @param handler 处理器对象* @return 模型视图对象* @throws Exception 处理过程中发生异常时抛出*/ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;/*** 获取最后修改时间* @param request 请求对象* @param handler 处理器对象* @return 最后修改时间*/long getLastModified(HttpServletRequest request, Object handler);
}

Spring MVC 提供了多种HandlerAdapter的实现:

  1. SimpleControllerHandlerAdapter:适配实现了Controller接口的处理器
  2. HttpRequestHandlerAdapter:适配实现了HttpRequestHandler接口的处理器
  3. HandlerFunctionAdapter:适配 Spring WebFlux 中的HandlerFunction
  4. RequestMappingHandlerAdapter:适配被@RequestMapping注解标记的方法

适配器模式在 Spring MVC 中的工作流程:

自定义 HandlerAdapter 示例

下面我们来实现一个自定义的处理器和对应的适配器:

/*** 自定义处理器接口* 作者: ken*/
public interface MyHandler {/*** 处理请求* @param request 请求对象* @return 模型视图名称*/String handle(HttpServletRequest request);
}/*** 自定义处理器实现* 作者: ken*/
public class UserMyHandler implements MyHandler {@Overridepublic String handle(HttpServletRequest request) {String username = request.getParameter("username");request.setAttribute("username", username);return "user"; // 返回视图名称}
}/*** 自定义HandlerAdapter* 作者: ken*/
public class MyHandlerAdapter implements HandlerAdapter {@Overridepublic boolean supports(Object handler) {// 只支持MyHandler类型的处理器return handler instanceof MyHandler;}@Overridepublic ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {// 将handler转换为MyHandler类型MyHandler myHandler = (MyHandler) handler;// 调用自定义处理器的handle方法String viewName = myHandler.handle(request);// 返回ModelAndView对象return new ModelAndView(viewName);}@Overridepublic long getLastModified(HttpServletRequest request, Object handler) {return -1; // 表示不支持最后修改时间}
}/*** 配置自定义Handler和HandlerAdapter* 作者: ken*/
@Configuration
public class MyHandlerConfig implements WebMvcConfigurer {@Beanpublic MyHandler userMyHandler() {return new UserMyHandler();}@Beanpublic MyHandlerAdapter myHandlerAdapter() {return new MyHandlerAdapter();}@Overridepublic void addViewControllers(ViewControllerRegistry registry) {// 注册处理器映射,将/user请求映射到userMyHandlerregistry.addViewController("/user").setHandlerMethod(new HandlerMethod(userMyHandler()));}
}

在这个示例中,我们创建了一个MyHandler接口和它的实现UserMyHandler,然后实现了对应的MyHandlerAdapter。通过配置,将/user请求映射到我们的自定义处理器。

DispatcherServlet收到/user请求时,会找到UserMyHandler作为处理器,然后遍历所有的HandlerAdapter,找到支持MyHandler类型的MyHandlerAdapter,最后通过该适配器调用处理器的handle方法处理请求。

适配器模式的优势

适配器模式在 Spring MVC 中的应用带来了以下优势:

  1. 灵活性:可以轻松添加新类型的处理器,只需提供对应的适配器即可,无需修改现有代码
  2. 一致性:所有类型的处理器都通过相同的接口被调用,简化了DispatcherServlet的实现
  3. 扩展性:开发者可以自定义处理器和适配器,扩展 Spring MVC 的功能

适配器模式使得 Spring MVC 能够支持多种类型的处理器,从早期的Controller接口实现,到现在主流的注解式控制器,都得益于适配器模式的灵活性。

五、装饰器模式:增强 Bean 的功能

装饰器模式(Decorator Pattern)是一种结构型模式,它允许向一个现有的对象添加新的功能,同时又不改变其结构。装饰器模式是继承的一个替代方案,它通过组合的方式动态地扩展对象的功能。

在 Spring 中,装饰器模式被广泛应用,如BeanWrapperHttpServletRequestWrapper等,用于在不修改原始对象的情况下增强其功能。

Spring 中的 BeanWrapper

BeanWrapper是 Spring 提供的一个用于操作 JavaBean 的工具类,它基于装饰器模式实现,可以为 Bean 添加额外的功能,如属性编辑、类型转换等。

/*** BeanWrapper使用示例* 作者: ken*/
public class BeanWrapperDemo {@Datapublic static class User {private String name;private Integer age;private Date birthday;}public static void main(String[] args) {// 创建User对象User user = new User();// 创建BeanWrapper,包装User对象BeanWrapper wrapper = new BeanWrapperImpl(user);// 设置属性值,BeanWrapper会自动进行类型转换wrapper.setPropertyValue("name", "张三");wrapper.setPropertyValue("age", "25"); // 字符串"25"会被转换为Integerwrapper.setPropertyValue("birthday", "1998-01-01"); // 字符串会被转换为Date// 获取包装的对象User decoratedUser = (User) wrapper.getWrappedInstance();System.out.println("姓名: " + decoratedUser.getName());System.out.println("年龄: " + decoratedUser.getAge());System.out.println("生日: " + decoratedUser.getBirthday());}
}

BeanWrapper的工作原理:

自定义装饰器示例

下面我们来实现一个自定义的装饰器,为UserService添加日志记录功能:

/*** 用户服务接口* 作者: ken*/
public interface UserService {/*** 添加用户* @param username 用户名*/void addUser(String username);/*** 删除用户* @param username 用户名*/void deleteUser(String username);
}/*** 用户服务实现* 作者: ken*/
public class UserServiceImpl implements UserService {@Overridepublic void addUser(String username) {System.out.println("添加用户: " + username);}@Overridepublic void deleteUser(String username) {System.out.println("删除用户: " + username);}
}/*** 用户服务装饰器抽象类* 作者: ken*/
public abstract class UserServiceDecorator implements UserService {protected final UserService userService;public UserServiceDecorator(UserService userService) {this.userService = userService;}@Overridepublic void addUser(String username) {userService.addUser(username);}@Overridepublic void deleteUser(String username) {userService.deleteUser(username);}
}/*** 日志装饰器* 作者: ken*/
public class LoggingUserServiceDecorator extends UserServiceDecorator {public LoggingUserServiceDecorator(UserService userService) {super(userService);}@Overridepublic void addUser(String username) {System.out.println("开始添加用户: " + username);super.addUser(username);System.out.println("完成添加用户: " + username);}@Overridepublic void deleteUser(String username) {System.out.println("开始删除用户: " + username);super.deleteUser(username);System.out.println("完成删除用户: " + username);}
}/*** 事务装饰器* 作者: ken*/
public class TransactionUserServiceDecorator extends UserServiceDecorator {public TransactionUserServiceDecorator(UserService userService) {super(userService);}@Overridepublic void addUser(String username) {System.out.println("开启事务");try {super.addUser(username);System.out.println("提交事务");} catch (Exception e) {System.out.println("回滚事务");throw e;}}@Overridepublic void deleteUser(String username) {System.out.println("开启事务");try {super.deleteUser(username);System.out.println("提交事务");} catch (Exception e) {System.out.println("回滚事务");throw e;}}
}/*** 装饰器模式测试* 作者: ken*/
public class DecoratorTest {public static void main(String[] args) {// 创建原始服务对象UserService userService = new UserServiceImpl();// 使用日志装饰器包装UserService loggingUserService = new LoggingUserServiceDecorator(userService);// 使用事务装饰器包装日志装饰器UserService transactionalUserService = new TransactionUserServiceDecorator(loggingUserService);// 调用装饰后的服务transactionalUserService.addUser("张三");System.out.println("-----");transactionalUserService.deleteUser("李四");}
}

运行结果:

开启事务
开始添加用户: 张三
添加用户: 张三
完成添加用户: 张三
提交事务
-----
开启事务
开始删除用户: 李四
删除用户: 李四
完成删除用户: 李四
提交事务

装饰器模式的组合关系:

装饰器模式与代理模式的区别

装饰器模式和代理模式都可以增强对象的功能,但它们有以下区别:

  1. 目的不同

    • 装饰器模式的目的是动态地为对象添加功能
    • 代理模式的目的是控制对对象的访问
  2. 关系不同

    • 装饰器模式中,装饰器和被装饰者通常是同一类型,都实现相同的接口
    • 代理模式中,代理类和被代理类可能没有继承关系,代理类通常持有被代理类的引用
  3. 使用场景不同

    • 装饰器模式适用于需要动态添加或组合功能的场景
    • 代理模式适用于需要控制访问、添加横切关注点(如日志、事务)的场景

在 Spring 中,装饰器模式和代理模式都有广泛应用,理解它们的区别有助于我们更好地理解 Spring 的实现机制。

六、观察者模式:Spring 的事件驱动模型

观察者模式(Observer Pattern)是一种行为型模式,它定义了对象之间的一对多依赖关系,当一个对象的状态发生变化时,所有依赖它的对象都会得到通知并自动更新。

在 Spring 中,观察者模式被应用于事件驱动模型,通过ApplicationEventApplicationListener实现组件之间的解耦通信。

Spring 的事件机制

Spring 的事件机制主要由三个部分组成:

  1. ApplicationEvent:事件对象,所有自定义事件都需要继承它
  2. ApplicationListener:事件监听器接口,所有监听器都需要实现它
  3. ApplicationEventPublisher:事件发布器,用于发布事件
/*** Spring事件机制核心接口* 作者: ken*/
// 事件基类
public abstract class ApplicationEvent extends EventObject {private static final long serialVersionUID = 7099057708183571937L;private final long timestamp;public ApplicationEvent(Object source) {super(source);this.timestamp = System.currentTimeMillis();}public final long getTimestamp() {return this.timestamp;}
}// 事件监听器接口
@FunctionalInterface
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {void onApplicationEvent(E event);
}// 事件发布器接口
public interface ApplicationEventPublisher {default void publishEvent(ApplicationEvent event) {publishEvent((Object) event);}void publishEvent(Object event);
}

Spring 事件机制的工作流程:

自定义事件示例

下面我们来实现一个用户注册的事件通知机制:

/*** 用户注册事件* 作者: ken*/
@Data
public class UserRegisteredEvent extends ApplicationEvent {private final String username;private final String email;/*** 构造函数* @param source 事件源* @param username 用户名* @param email 邮箱*/public UserRegisteredEvent(Object source, String username, String email) {super(source);this.username = username;this.email = email;}
}/*** 邮件通知监听器* 作者: ken*/
@Component
public class EmailNotificationListener implements ApplicationListener<UserRegisteredEvent> {@Overridepublic void onApplicationEvent(UserRegisteredEvent event) {System.out.println("发送欢迎邮件到: " + event.getEmail());// 实际应用中这里会调用邮件发送服务}
}/*** 优惠券发放监听器* 作者: ken*/
@Component
public class CouponListener implements ApplicationListener<UserRegisteredEvent> {@Overridepublic void onApplicationEvent(UserRegisteredEvent event) {System.out.println("向用户 " + event.getUsername() + " 发放新人优惠券");// 实际应用中这里会调用优惠券服务}
}/*** 用户服务* 作者: ken*/
@Service
@Slf4j
public class UserService implements ApplicationEventPublisherAware {private ApplicationEventPublisher eventPublisher;@Overridepublic void setApplicationEventPublisher(ApplicationEventPublisher publisher) {this.eventPublisher = publisher;}/*** 用户注册* @param username 用户名* @param email 邮箱* @param password 密码*/public void register(String username, String email, String password) {// 1. 保存用户信息log.info("用户 {} 注册成功", username);// 2. 发布用户注册事件eventPublisher.publishEvent(new UserRegisteredEvent(this, username, email));}
}/*** 事件机制测试* 作者: ken*/
@SpringBootTest
public class EventTest {@Autowiredprivate UserService userService;@Testpublic void testUserRegistration() {userService.register("张三", "zhangsan@example.com", "123456");}
}

运行结果:

用户 张三 注册成功
发送欢迎邮件到: zhangsan@example.com
向用户 张三 发放新人优惠券

异步事件处理

默认情况下,Spring 的事件处理是同步的,即事件发布者会等待所有监听器处理完毕后才继续执行。我们可以通过@Async注解实现异步事件处理:

/*** 异步事件配置* 作者: ken*/
@Configuration
@EnableAsync
public class AsyncEventConfig {@Beanpublic Executor taskExecutor() {ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();executor.setCorePoolSize(5);executor.setMaxPoolSize(10);executor.setQueueCapacity(25);executor.setThreadNamePrefix("EventExecutor-");executor.initialize();return executor;}
}/*** 异步日志监听器* 作者: ken*/
@Component
public class AsyncLoggingListener implements ApplicationListener<UserRegisteredEvent> {private static final Logger log = LoggerFactory.getLogger(AsyncLoggingListener.class);@Async // 异步处理@Overridepublic void onApplicationEvent(UserRegisteredEvent event) {try {// 模拟耗时操作Thread.sleep(1000);log.info("异步记录用户 {} 注册日志", event.getUsername());} catch (InterruptedException e) {Thread.currentThread().interrupt();}}
}

添加异步监听器后,再次运行测试:

用户 张三 注册成功
发送欢迎邮件到: zhangsan@example.com
向用户 张三 发放新人优惠券
(1秒后)
异步记录用户 张三 注册日志

可以看到,异步监听器的处理不会阻塞主线程。

观察者模式在 Spring 中的应用,使得组件之间的通信更加灵活和松耦合。通过事件机制,我们可以轻松实现模块间的解耦,提高系统的可扩展性和可维护性。

七、策略模式:Spring 的灵活配置

策略模式(Strategy Pattern)是一种行为型模式,它定义了一系列算法,并将每个算法封装起来,使它们可以相互替换。策略模式让算法的变化独立于使用算法的客户。

在 Spring 中,策略模式被广泛应用于各种配置和行为的选择,如资源加载策略、事务管理策略、包扫描过滤策略等。

Spring 中的策略模式应用

Spring 的Resource接口及其实现是策略模式的典型应用,它定义了资源访问的统一接口,而不同的实现类则对应不同的资源访问策略:

/*** Spring Resource接口* 作者: ken*/
public interface Resource extends InputStreamSource {boolean exists();boolean isReadable();boolean isOpen();URL getURL() throws IOException;URI getURI() throws IOException;File getFile() throws IOException;long contentLength() throws IOException;long lastModified() throws IOException;Resource createRelative(String relativePath) throws IOException;String getFilename();String getDescription();
}

Resource的主要实现类:

  • ClassPathResource:访问类路径下的资源
  • FileSystemResource:访问文件系统中的资源
  • UrlResource:访问 URL 指向的资源
  • ServletContextResource:访问 Web 应用中的资源

资源加载的策略选择:

自定义策略模式示例

下面我们来实现一个支付策略的示例,展示策略模式的应用:

/*** 支付策略接口* 作者: ken*/
public interface PaymentStrategy {/*** 支付方法* @param amount 支付金额* @return 支付结果*/String pay(double amount);/*** 获取支付方式名称* @return 支付方式名称*/String getPaymentName();
}/*** 支付宝支付策略* 作者: ken*/
@Component
public class AlipayStrategy implements PaymentStrategy {@Overridepublic String pay(double amount) {return "使用支付宝支付了 " + amount + " 元";}@Overridepublic String getPaymentName() {return "alipay";}
}/*** 微信支付策略* 作者: ken*/
@Component
public class WechatPayStrategy implements PaymentStrategy {@Overridepublic String pay(double amount) {return "使用微信支付了 " + amount + " 元";}@Overridepublic String getPaymentName() {return "wechat";}
}/*** 银联支付策略* 作者: ken*/
@Component
public class UnionPayStrategy implements PaymentStrategy {@Overridepublic String pay(double amount) {return "使用银联支付了 " + amount + " 元";}@Overridepublic String getPaymentName() {return "unionpay";}
}/*** 支付服务* 作者: ken*/
@Service
public class PaymentService {private final Map<String, PaymentStrategy> paymentStrategies;/*** 构造函数,自动注入所有PaymentStrategy实现* @param strategies 所有支付策略*/public PaymentService(List<PaymentStrategy> strategies) {this.paymentStrategies = Maps.newHashMap();for (PaymentStrategy strategy : strategies) {this.paymentStrategies.put(strategy.getPaymentName(), strategy);}}/*** 根据支付方式进行支付* @param paymentMethod 支付方式* @param amount 支付金额* @return 支付结果*/public String pay(String paymentMethod, double amount) {PaymentStrategy strategy = paymentStrategies.get(paymentMethod);if (ObjectUtils.isEmpty(strategy)) {throw new IllegalArgumentException("不支持的支付方式: " + paymentMethod);}return strategy.pay(amount);}
}/*** 策略模式测试* 作者: ken*/
@SpringBootTest
public class StrategyTest {@Autowiredprivate PaymentService paymentService;@Testpublic void testPayment() {System.out.println(paymentService.pay("alipay", 100.0));System.out.println(paymentService.pay("wechat", 200.0));System.out.println(paymentService.pay("unionpay", 300.0));}
}

运行结果:

使用支付宝支付了 100.0 元
使用微信支付了 200.0 元
使用银联支付了 300.0 元

策略模式的结构:

Spring 中的 @ComponentScan 过滤策略

在 Spring 中,@ComponentScan注解的includeFiltersexcludeFilters属性也是策略模式的应用,它们允许我们定义组件扫描时的过滤策略:

/*** 组件扫描配置* 作者: ken*/
@Configuration
@ComponentScan(basePackages = "com.example",includeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION, classes = Service.class),@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = Repository.class)},excludeFilters = {@ComponentScan.Filter(type = FilterType.REGEX, pattern = "com.example.test.*")}
)
public class ComponentScanConfig {
}

FilterType定义了不同的过滤策略:

  • ANNOTATION:根据注解过滤
  • ASSIGNABLE_TYPE:根据类或接口过滤
  • REGEX:根据正则表达式过滤
  • ASPECTJ:根据 AspectJ 表达式过滤
  • CUSTOM:自定义过滤策略

策略模式在 Spring 中的应用,使得框架具有很强的灵活性和可扩展性。通过定义不同的策略,我们可以根据实际需求选择合适的行为,而无需修改使用策略的核心代码。

八、模板方法模式:Spring 的代码复用

模板方法模式(Template Method Pattern)是一种行为型模式,它定义了一个操作中的算法骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。

在 Spring 中,模板方法模式被广泛应用于各种模板类中,如JdbcTemplateHibernateTemplateRestTemplate等,用于封装固定的流程,而将可变的部分留给用户实现。

JdbcTemplate 中的模板方法

JdbcTemplate是 Spring 提供的一个 JDBC 模板类,它封装了 JDBC 操作的固定流程(如获取连接、创建语句、处理异常、释放资源等),而将 SQL 执行和结果处理留给用户实现。

/*** JdbcTemplate使用示例* 作者: ken*/
@Service
public class UserDao {private final JdbcTemplate jdbcTemplate;@Autowiredpublic UserDao(JdbcTemplate jdbcTemplate) {this.jdbcTemplate = jdbcTemplate;}/*** 根据ID查询用户* @param id 用户ID* @return 用户对象*/public User getUserById(Long id) {String sql = "SELECT id, username, email FROM user WHERE id = ?";// 使用JdbcTemplate的query方法,只需要提供SQL和结果处理器return jdbcTemplate.queryForObject(sql, new Object[]{id}, (rs, rowNum) -> {User user = new User();user.setId(rs.getLong("id"));user.setUsername(rs.getString("username"));user.setEmail(rs.getString("email"));return user;});}/*** 插入用户* @param user 用户对象* @return 影响的行数*/public int insertUser(User user) {String sql = "INSERT INTO user (username, email) VALUES (?, ?)";// 使用JdbcTemplate的update方法执行插入return jdbcTemplate.update(sql, user.getUsername(), user.getEmail());}@Datapublic static class User {private Long id;private String username;private String email;}
}

JdbcTemplate的模板方法流程:

自定义模板方法示例

下面我们来实现一个文件处理的模板类,展示模板方法模式的应用:

/*** 文件处理模板类* 作者: ken*/
public abstract class FileProcessingTemplate {/*** 模板方法,定义文件处理的流程* @param filePath 文件路径*/public final void processFile(String filePath) {File file = null;try {// 1. 打开文件file = openFile(filePath);// 2. 验证文件if (!validateFile(file)) {throw new IllegalArgumentException("文件验证失败: " + filePath);}// 3. 处理文件内容(留给子类实现)processFileContent(file);// 4. 完成处理onSuccess(file);} catch (Exception e) {// 5. 处理异常onError(e, filePath);} finally {// 6. 关闭文件if (file != null) {closeFile(file);}}}/*** 打开文件* @param filePath 文件路径* @return 文件对象*/protected File openFile(String filePath) {return new File(filePath);}/*** 验证文件* @param file 文件对象* @return 如果验证通过则返回true,否则返回false*/protected boolean validateFile(File file) {return file.exists() && file.isFile() && file.canRead();}/*** 处理文件内容(抽象方法,由子类实现)* @param file 文件对象* @throws IOException IO异常*/protected abstract void processFileContent(File file) throws IOException;/*** 处理成功* @param file 文件对象*/protected void onSuccess(File file) {System.out.println("文件处理成功: " + file.getName());}/*** 处理异常* @param e 异常对象* @param filePath 文件路径*/protected void onError(Exception e, String filePath) {System.err.println("文件处理失败 (" + filePath + "): " + e.getMessage());}/*** 关闭文件* @param file 文件对象*/protected void closeFile(File file) {// 对于File对象,不需要显式关闭,这里只是模拟System.out.println("关闭文件: " + file.getName());}
}/*** 文本文件处理器* 作者: ken*/
public class TextFileProcessor extends FileProcessingTemplate {@Overrideprotected void processFileContent(File file) throws IOException {System.out.println("开始处理文本文件: " + file.getName());// 读取文件内容并统计行数try (BufferedReader reader = new BufferedReader(new FileReader(file))) {int lineCount = 0;while (reader.readLine() != null) {lineCount++;}System.out.println("文本文件 " + file.getName() + " 包含 " + lineCount + " 行");}}
}/*** CSV文件处理器* 作者: ken*/
public class CsvFileProcessor extends FileProcessingTemplate {@Overrideprotected void processFileContent(File file) throws IOException {System.out.println("开始处理CSV文件: " + file.getName());// 读取CSV文件并统计记录数try (BufferedReader reader = new BufferedReader(new FileReader(file))) {int recordCount = 0;while (reader.readLine() != null) {recordCount++;}System.out.println("CSV文件 " + file.getName() + " 包含 " + (recordCount - 1) + " 条记录(排除表头)");}}// 重写验证方法,只处理.csv文件@Overrideprotected boolean validateFile(File file) {return super.validateFile(file) && file.getName().endsWith(".csv");}
}/*** 模板方法模式测试* 作者: ken*/
public class TemplateMethodTest {public static void main(String[] args) {// 创建文本文件处理器并处理文件FileProcessingTemplate textProcessor = new TextFileProcessor();textProcessor.processFile("test.txt");System.out.println("-----");// 创建CSV文件处理器并处理文件FileProcessingTemplate csvProcessor = new CsvFileProcessor();csvProcessor.processFile("data.csv");System.out.println("-----");// 尝试用CSV处理器处理非CSV文件(会验证失败)csvProcessor.processFile("image.jpg");}
}

模板方法模式的结构:

模板方法模式在 Spring 中的应用,极大地简化了重复代码的编写,将不变的流程封装在模板类中,而将可变的部分留给子类或用户实现。这种模式不仅提高了代码的复用性,也保证了流程的一致性。

九、责任链模式:Spring 的过滤器与拦截器

责任链模式(Chain of Responsibility Pattern)是一种行为型模式,它为请求创建了一个接收者对象的链。这种模式给予请求的类型,对请求的发送者和接收者进行解耦。

在 Spring 中,责任链模式被广泛应用于过滤器(Filter)、拦截器(Interceptor)和 AOP 通知的执行等场景。

Spring MVC 中的拦截器链

Spring MVC 的拦截器(Interceptor)机制就是基于责任链模式实现的,它允许我们在请求处理的不同阶段进行干预。

/*** Spring MVC HandlerInterceptor接口* 作者: ken*/
public interface HandlerInterceptor {/*** 在请求处理之前调用* @param request 请求对象* @param response 响应对象* @param handler 处理器* @return 如果返回true,则继续执行后续拦截器和处理器;如果返回false,则中断执行* @throws Exception 异常*/default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)throws Exception {return true;}/*** 在请求处理之后、视图渲染之前调用* @param request 请求对象* @param response 响应对象* @param handler 处理器* @param modelAndView 模型视图* @throws Exception 异常*/default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,@Nullable ModelAndView modelAndView) throws Exception {}/*** 在整个请求完成之后调用,包括视图渲染完成* @param request 请求对象* @param response 响应对象* @param handler 处理器* @param ex 异常对象,如果没有异常则为null* @throws Exception 异常*/default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,@Nullable Exception ex) throws Exception {}
}

拦截器链的工作流程:

自定义拦截器示例

下面我们来实现一个自定义的拦截器链,包括日志记录、权限验证和性能监控:

/*** 日志拦截器* 作者: ken*/
@Component
public class LoggingInterceptor implements HandlerInterceptor {private static final Logger log = LoggerFactory.getLogger(LoggingInterceptor.class);@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {log.info("请求开始: {} {}", request.getMethod(), request.getRequestURI());return true;}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,@Nullable Exception ex) throws Exception {log.info("请求结束: {} {},状态码: {}", request.getMethod(), request.getRequestURI(), response.getStatus());}
}/*** 权限验证拦截器* 作者: ken*/
@Component
public class AuthInterceptor implements HandlerInterceptor {private static final Logger log = LoggerFactory.getLogger(AuthInterceptor.class);@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {log.info("验证权限: {}", request.getRequestURI());// 简单的权限验证示例String token = request.getHeader("Authorization");if (StringUtils.hasText(token)) {// 实际应用中这里会验证token的有效性return true;}log.warn("权限验证失败: 未提供token");response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);return false;}
}/*** 性能监控拦截器* 作者: ken*/
@Component
public class PerformanceInterceptor implements HandlerInterceptor {private static final Logger log = LoggerFactory.getLogger(PerformanceInterceptor.class);private static final ThreadLocal<Long> START_TIME = new ThreadLocal<>();@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {long startTime = System.currentTimeMillis();START_TIME.set(startTime);return true;}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,@Nullable Exception ex) throws Exception {long endTime = System.currentTimeMillis();long duration = endTime - START_TIME.get();log.info("请求耗时: {} ms", duration);START_TIME.remove();}
}/*** 配置拦截器* 作者: ken*/
@Configuration
public class InterceptorConfig implements WebMvcConfigurer {@Autowiredprivate LoggingInterceptor loggingInterceptor;@Autowiredprivate AuthInterceptor authInterceptor;@Autowiredprivate PerformanceInterceptor performanceInterceptor;@Overridepublic void addInterceptors(InterceptorRegistry registry) {// 注册拦截器,并指定拦截的路径registry.addInterceptor(loggingInterceptor).addPathPatterns("/**");registry.addInterceptor(authInterceptor).addPathPatterns("/api/**").excludePathPatterns("/api/public/**");registry.addInterceptor(performanceInterceptor).addPathPatterns("/**");}
}/*** 测试控制器* 作者: ken*/
@RestController
@RequestMapping("/api")
public class ApiController {@GetMapping("/public/hello")public String publicHello() {return "Public Hello";}@GetMapping("/private/hello")public String privateHello() {return "Private Hello";}
}

当我们访问/api/private/hello时,拦截器的执行顺序如下:

  1. LoggingInterceptor.preHandle:记录请求开始
  2. AuthInterceptor.preHandle:验证权限
  3. PerformanceInterceptor.preHandle:记录开始时间
  4. 执行ApiController.privateHello方法
  5. PerformanceInterceptor.afterCompletion:计算并记录请求耗时
  6. AuthInterceptor.afterCompletion:(无操作)
  7. LoggingInterceptor.afterCompletion:记录请求结束

责任链模式与装饰器模式的区别

责任链模式和装饰器模式都涉及到对象的链式调用,但它们有以下区别:

  1. 目的不同

    • 责任链模式的目的是将请求的发送者和接收者解耦,让多个接收者都有机会处理请求
    • 装饰器模式的目的是动态地为对象添加功能
  2. 处理方式不同

    • 责任链模式中,每个处理者可以选择处理请求或将其传递给下一个处理者
    • 装饰器模式中,每个装饰器都会处理请求,并将请求传递给被装饰的对象
  3. 链的结构不同

    • 责任链模式的链通常是线性的,每个处理者只知道下一个处理者
    • 装饰器模式的链可以是嵌套的,每个装饰器都包装一个对象

在 Spring 中,责任链模式的应用使得请求处理更加灵活,我们可以根据需要添加或移除拦截器,而无需修改核心代码。这种模式非常适合处理具有多个处理步骤的场景。

十、其他重要设计模式

除了前面介绍的几种主要设计模式,Spring 中还应用了许多其他设计模式,下面我们简要介绍几种重要的模式。

1. 原型模式:多实例 Bean 的创建

原型模式(Prototype Pattern)是一种创建型模式,它用原型实例指定创建对象的种类,并且通过拷贝这个原型来创建新的对象。

在 Spring 中,当 Bean 的作用域被设置为prototype时,容器会为每次请求创建一个新的 Bean 实例,这就是原型模式的应用。

/*** 原型模式示例* 作者: ken*/
@Configuration
public class PrototypeConfig {@Bean@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)public Order order() {return new Order();}@Datapublic static class Order {private String id = UUID.randomUUID().toString();private LocalDateTime createTime = LocalDateTime.now();}
}/*** 原型模式测试* 作者: ken*/
@SpringBootTest
public class PrototypeTest {@Autowiredprivate ApplicationContext context;@Testpublic void testPrototype() {PrototypeConfig.Order order1 = context.getBean(PrototypeConfig.Order.class);PrototypeConfig.Order order2 = context.getBean(PrototypeConfig.Order.class);System.out.println("订单1 ID: " + order1.getId());System.out.println("订单2 ID: " + order2.getId());Assert.isTrue(!order1.getId().equals(order2.getId()), "原型Bean应该是不同的实例");}
}

2. 迭代器模式:集合数据的遍历

迭代器模式(Iterator Pattern)是一种行为型模式,它提供了一种方法来访问聚合对象中的各个元素,而又不暴露该对象的内部表示。

Spring 的PropertySources接口及其实现就使用了迭代器模式,允许我们遍历多个属性源:

/*** 迭代器模式示例* 作者: ken*/
public class IteratorDemo {public static void main(String[] args) {// 创建属性源Map<String, Object> map1 = Maps.newHashMap();map1.put("name", "张三");map1.put("age", 25);Map<String, Object> map2 = Maps.newHashMap();map2.put("name", "李四");map2.put("address", "北京");// 创建PropertySourcesMutablePropertySources propertySources = new MutablePropertySources();propertySources.addFirst(new MapPropertySource("source1", map1));propertySources.addFirst(new MapPropertySource("source2", map2));// 使用迭代器遍历属性源Iterator<PropertySource<?>> iterator = propertySources.iterator();while (iterator.hasNext()) {PropertySource<?> source = iterator.next();System.out.println("属性源名称: " + source.getName());System.out.println("属性值: " + source.getSource());}// 获取属性值(会遍历所有属性源)System.out.println("name属性值: " + propertySources.getProperty("name"));}
}

3. 组合模式:树形结构的处理

组合模式(Composite Pattern)是一种结构型模式,它允许你将对象组合成树形结构来表现 "整体 - 部分" 的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。

Spring 的ApplicationContext层级结构就是组合模式的应用,它允许我们创建父子容器:

/*** 组合模式示例* 作者: ken*/
public class CompositeDemo {public static void main(String[] args) {// 创建父容器AnnotationConfigApplicationContext parentContext = new AnnotationConfigApplicationContext();parentContext.register(ParentConfig.class);parentContext.refresh();// 创建子容器,并设置父容器AnnotationConfigApplicationContext childContext = new AnnotationConfigApplicationContext();childContext.register(ChildConfig.class);childContext.setParent(parentContext);childContext.refresh();// 子容器可以访问父容器中的BeanParentService parentService = childContext.getBean(ParentService.class);parentService.doSomething();// 父容器不能访问子容器中的Beantry {parentContext.getBean(ChildService.class);} catch (NoSuchBeanDefinitionException e) {System.out.println("父容器中不存在ChildService");}// 关闭容器childContext.close();parentContext.close();}@Configurationstatic class ParentConfig {@Beanpublic ParentService parentService() {return new ParentService();}}@Configurationstatic class ChildConfig {@Beanpublic ChildService childService() {return new ChildService();}}static class ParentService {public void doSomething() {System.out.println("ParentService doing something");}}static class ChildService {public void doSomething() {System.out.println("ChildService doing something");}}
}

4. 享元模式:资源的复用

享元模式(Flyweight Pattern)是一种结构型模式,它尝试重用现有的同类对象,如果未找到匹配的对象,则创建新对象。

Spring 中的StringValueResolver就使用了享元模式,缓存已经解析过的字符串,避免重复解析:

/*** 享元模式示例* 作者: ken*/
public class FlyweightDemo {public static void main(String[] args) {// 创建字符串解析器StringValueResolver resolver = new PlaceholderResolvingStringValueResolver();// 多次解析相同的字符串,第二次会使用缓存String result1 = resolver.resolveStringValue("Hello ${name}");String result2 = resolver.resolveStringValue("Hello ${name}");System.out.println(result1);System.out.println(result2);}static class PlaceholderResolvingStringValueResolver implements StringValueResolver {// 缓存已解析的字符串private final Map<String, String> resolvedStrings = new ConcurrentHashMap<>();@Overridepublic String resolveStringValue(String strVal) {// 先从缓存中获取String resolved = resolvedStrings.get(strVal);if (resolved != null) {System.out.println("使用缓存解析: " + strVal);return resolved;}// 解析字符串(实际应用中会更复杂)System.out.println("首次解析: " + strVal);resolved = strVal.replace("${name}", "Spring");// 存入缓存resolvedStrings.put(strVal, resolved);return resolved;}}
}

总结

Spring 框架是设计模式的集大成者,它巧妙地运用了多种设计模式来解决实际开发中的问题。通过本文的介绍,我们了解了 Spring 中 15 种主要设计模式的应用场景、实现原理和实战案例。

理解这些设计模式不仅有助于我们更好地理解 Spring 的内部工作机制,更能提升我们自身的设计能力。在实际开发中,我们可以借鉴这些设计模式的思想,设计出更加灵活、可扩展和可维护的系统。

希望本文能帮助你更深入地理解 Spring 和设计模式,在 Java 开发的道路上走得更远。

http://www.dtcms.com/a/499106.html

相关文章:

  • 【S32K3XX系列MCAL配置-第一节开发环境搭建】
  • 中矿资源的财报估值分析
  • 网站关键词优化方案分为几个步骤嘉兴微信网站建设
  • stm32驱动LTC2494详解
  • AI写作赋能SEO:用提示词打造从标题到发布的全流程优化策略
  • PVT(Pyramid Vision Transformer):金字塔结构,适合检测/分割
  • SSRF 漏洞深度剖析:从原理到实战
  • Python第十八节 命名空间作用域详细介绍及注意事项
  • 网站怎么做跳转链接域名备案要多少钱
  • 哪个网站查公司信息比较准网站设计像素
  • mq和rocketmq
  • AI搜索自由:Perplexica+cpolar构建你的私人知识引擎
  • C++基础:(十五)queue的深度解析和模拟实现
  • VSR 项目解析
  • 软件工程新纪元:AI协同编程架构师的修养与使命
  • 一、RPA基础认知与环境搭建
  • 网站域名过期了怎么办怎样做网站导航界面
  • armbian 滚动更新锁定
  • Rust 设计模式 Marker Trait + Blanket Implementation
  • 在 MacOS Tahoe 上使用VMware虚拟机安装 ARM版 Windows11
  • 苏州手机网站建设报价下载好的字体怎么安装到wordpress
  • 品牌出海进入精细化阶段,全球业财一体化成为系统选型关键
  • K8S(十二)—— Kubernetes安全机制深度解析与实践:从认证到RBAC授权
  • 【Zephyr存储专题】16_内存泄露检测可视化脚本自动化
  • 云服务器怎么建网站wordpress 仿 主题下载
  • 怎么搭建手机网站m国内最近重大新闻2024
  • 企业微信ipad协议接口优势
  • AI协同编程架构师如何解决技术债问题
  • 官方网站下载打印机驱动网站服务商
  • 解决部分程序中文乱码的问题