深入 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;
    
    @Override
    public User getObject() throws Exception {
        // 模拟复杂的对象创建过程
        User user = new User();
        user.setId(userId);
        user.setName("Default Name");
        user.setEmail(userId + "@example.com");
        // 可以在这里添加更多复杂的初始化逻辑
        return user;
    }
    
    @Override
    public Class<?> getObjectType() {
        return User.class;
    }
    
    // setter方法用于注入依赖
    public void setUserId(String userId) {
        this.userId = userId;
    }
    
    @Data
    public static class User {
        private String id;
        private String name;
        private String email;
    }
}

在 Spring 配置中使用该FactoryBean

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

使用FactoryBean创建的 Bean:

复制代码
/**
 * FactoryBean使用示例
 * 作者: ken
 */
@SpringBootTest
public class FactoryBeanTest {
    @Autowired
    private UserFactoryBean.User user; // 注入的是FactoryBean创建的User对象
    
    @Autowired
    private UserFactoryBean userFactoryBean; // 注入的是FactoryBean本身
    
    @Test
    public 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工厂,用于延迟初始化Bean
    private 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);
    }
    
    // 用于跟踪正在创建的单例Bean
    private final Set<String> singletonsCurrentlyInCreation =
            Collections.newSetFromMap(new ConcurrentHashMap<>(16));
}

Spring 单例 Bean 的创建流程:

单例 Bean 的配置与使用

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

复制代码
/**
 * Spring Bean作用域示例
 * 作者: ken
 */
@Configuration
public class ScopeConfig {
    // 默认单例
    @Bean
    public 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 {
    @Autowired
    private ScopeConfig.SingletonService singletonService1;
    
    @Autowired
    private ScopeConfig.SingletonService singletonService2;
    
    @Autowired
    private ApplicationContext context;
    
    @Test
    public void testSingleton() {
        // 单例Bean的两个引用应该指向同一个实例
        Assert.isTrue(singletonService1.getInstanceId().equals(singletonService2.getInstanceId()),
                "单例Bean应该是同一个实例");
    }
    
    @Test
    public 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 {
        @Override
        public void addUser(String username) {
            System.out.println("添加用户: " + username);
        }
        
        @Override
        public 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;
        }
        
        @Override
        public 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 {
        @Override
        public 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 {
    @Bean
    public 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 {
    @Autowired
    private UserService userService;
    
    @Test
    public 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 {
    @Override
    public String handle(HttpServletRequest request) {
        String username = request.getParameter("username");
        request.setAttribute("username", username);
        return "user"; // 返回视图名称
    }
}

/**
 * 自定义HandlerAdapter
 * 作者: ken
 */
public class MyHandlerAdapter implements HandlerAdapter {
    @Override
    public boolean supports(Object handler) {
        // 只支持MyHandler类型的处理器
        return handler instanceof MyHandler;
    }
    
    @Override
    public 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);
    }
    
    @Override
    public long getLastModified(HttpServletRequest request, Object handler) {
        return -1; // 表示不支持最后修改时间
    }
}

/**
 * 配置自定义Handler和HandlerAdapter
 * 作者: ken
 */
@Configuration
public class MyHandlerConfig implements WebMvcConfigurer {
    @Bean
    public MyHandler userMyHandler() {
        return new UserMyHandler();
    }
    
    @Bean
    public MyHandlerAdapter myHandlerAdapter() {
        return new MyHandlerAdapter();
    }
    
    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        // 注册处理器映射,将/user请求映射到userMyHandler
        registry.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 {
    @Data
    public 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"会被转换为Integer
        wrapper.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 {
    @Override
    public void addUser(String username) {
        System.out.println("添加用户: " + username);
    }
    
    @Override
    public 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;
    }
    
    @Override
    public void addUser(String username) {
        userService.addUser(username);
    }
    
    @Override
    public void deleteUser(String username) {
        userService.deleteUser(username);
    }
}

/**
 * 日志装饰器
 * 作者: ken
 */
public class LoggingUserServiceDecorator extends UserServiceDecorator {
    public LoggingUserServiceDecorator(UserService userService) {
        super(userService);
    }
    
    @Override
    public void addUser(String username) {
        System.out.println("开始添加用户: " + username);
        super.addUser(username);
        System.out.println("完成添加用户: " + username);
    }
    
    @Override
    public 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);
    }
    
    @Override
    public void addUser(String username) {
        System.out.println("开启事务");
        try {
            super.addUser(username);
            System.out.println("提交事务");
        } catch (Exception e) {
            System.out.println("回滚事务");
            throw e;
        }
    }
    
    @Override
    public 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> {
    @Override
    public void onApplicationEvent(UserRegisteredEvent event) {
        System.out.println("发送欢迎邮件到: " + event.getEmail());
        // 实际应用中这里会调用邮件发送服务
    }
}

/**
 * 优惠券发放监听器
 * 作者: ken
 */
@Component
public class CouponListener implements ApplicationListener<UserRegisteredEvent> {
    @Override
    public void onApplicationEvent(UserRegisteredEvent event) {
        System.out.println("向用户 " + event.getUsername() + " 发放新人优惠券");
        // 实际应用中这里会调用优惠券服务
    }
}

/**
 * 用户服务
 * 作者: ken
 */
@Service
@Slf4j
public class UserService implements ApplicationEventPublisherAware {
    private ApplicationEventPublisher eventPublisher;
    
    @Override
    public 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 {
    @Autowired
    private UserService userService;
    
    @Test
    public void testUserRegistration() {
        userService.register("张三", "zhangsan@example.com", "123456");
    }
}

运行结果:

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

异步事件处理

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

复制代码
/**
 * 异步事件配置
 * 作者: ken
 */
@Configuration
@EnableAsync
public class AsyncEventConfig {
    @Bean
    public 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 // 异步处理
    @Override
    public 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 {
    @Override
    public String pay(double amount) {
        return "使用支付宝支付了 " + amount + " 元";
    }
    
    @Override
    public String getPaymentName() {
        return "alipay";
    }
}

/**
 * 微信支付策略
 * 作者: ken
 */
@Component
public class WechatPayStrategy implements PaymentStrategy {
    @Override
    public String pay(double amount) {
        return "使用微信支付了 " + amount + " 元";
    }
    
    @Override
    public String getPaymentName() {
        return "wechat";
    }
}

/**
 * 银联支付策略
 * 作者: ken
 */
@Component
public class UnionPayStrategy implements PaymentStrategy {
    @Override
    public String pay(double amount) {
        return "使用银联支付了 " + amount + " 元";
    }
    
    @Override
    public 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 {
    @Autowired
    private PaymentService paymentService;
    
    @Test
    public 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;
    
    @Autowired
    public 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());
    }
    
    @Data
    public 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 {
    @Override
    protected 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 {
    @Override
    protected 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文件
    @Override
    protected 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);
    
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        log.info("请求开始: {} {}", request.getMethod(), request.getRequestURI());
        return true;
    }
    
    @Override
    public 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);
    
    @Override
    public 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<>();
    
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        long startTime = System.currentTimeMillis();
        START_TIME.set(startTime);
        return true;
    }
    
    @Override
    public 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 {
    @Autowired
    private LoggingInterceptor loggingInterceptor;
    
    @Autowired
    private AuthInterceptor authInterceptor;
    
    @Autowired
    private PerformanceInterceptor performanceInterceptor;
    
    @Override
    public 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();
    }
    
    @Data
    public static class Order {
        private String id = UUID.randomUUID().toString();
        private LocalDateTime createTime = LocalDateTime.now();
    }
}

/**
 * 原型模式测试
 * 作者: ken
 */
@SpringBootTest
public class PrototypeTest {
    @Autowired
    private ApplicationContext context;
    
    @Test
    public 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", "北京");
        
        // 创建PropertySources
        MutablePropertySources 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();
        
        // 子容器可以访问父容器中的Bean
        ParentService parentService = childContext.getBean(ParentService.class);
        parentService.doSomething();
        
        // 父容器不能访问子容器中的Bean
        try {
            parentContext.getBean(ChildService.class);
        } catch (NoSuchBeanDefinitionException e) {
            System.out.println("父容器中不存在ChildService");
        }
        
        // 关闭容器
        childContext.close();
        parentContext.close();
    }
    
    @Configuration
    static class ParentConfig {
        @Bean
        public ParentService parentService() {
            return new ParentService();
        }
    }
    
    @Configuration
    static class ChildConfig {
        @Bean
        public 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<>();
        
        @Override
        public 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 开发的道路上走得更远。

相关推荐
一抓掉一大把11 小时前
RuoYI框架.net版本实现Redis数据隔离
java·开发语言
.格子衫.11 小时前
Maven高级
java·maven
lkbhua莱克瓦2411 小时前
Java基础——常用算法4
java·数据结构·笔记·算法·github·排序算法·快速排序
.格子衫.11 小时前
Maven前奏
java·pycharm·maven
在未来等你11 小时前
AI Agent设计模式 Day 1:ReAct模式:推理与行动的完美结合
设计模式·llm·react·ai agent·plan-and-execute
Mos_x11 小时前
springboot系列--自动配置原理
java·后端
神奇侠202412 小时前
基于spring-boot-admin实现对应用、数据库、nginx等监控
java·数据库·nginx
一叶飘零_sweeeet12 小时前
手写 RPC 框架
java·网络·网络协议·rpc
脸大是真的好~13 小时前
黑马JAVAWeb-01 Maven依赖管理-生命周期-单元测试
java·maven
zhangkaixuan45614 小时前
Apache Paimon 查询全流程深度分析
java·apache·paimon