讲一下 Spring 中用到的设计模式

工厂模式:BeanFactory 的工厂模式实现

工厂模式是一种创建型设计模式,它提供了一种创建对象的最佳方式。在工厂模式中,我们创建对象时不会对客户端暴露创建逻辑,而是通过使用一个共同的接口来指向新创建的对象。

1. 简单工厂模式

java 复制代码
// 简化的简单工厂示例
public class BeanFactory {
    private Map<String, Object> beanMap = new HashMap<>();
    
    public Object getBean(String beanName) {
        // 根据 beanName 返回对应的 Bean
        return beanMap.get(beanName);
    }
    
    public void registerBean(String beanName, Object bean) {
        beanMap.put(beanName, bean);
    }
}

Spring 中的应用

  • BeanFactory 接口本身是简单工厂的抽象
  • 具体实现类(如 DefaultListableBeanFactory)根据 Bean 定义创建对象

2. 工厂方法模式

java 复制代码
// 工厂方法模式示例
public interface BeanFactory {
    Object getBean(String name);
}

public class XmlBeanFactory implements BeanFactory {
    private Map<String, BeanDefinition> beanDefinitions;
    
    public Object getBean(String name) {
        BeanDefinition definition = beanDefinitions.get(name);
        // 根据定义创建 Bean
        return createBean(definition);
    }
}

Spring 的具体体现

  • 不同 BeanFactory 实现类提供不同的 Bean 创建方式
  • ApplicationContextBeanFactory 的子接口,提供更多功能

3. 抽象工厂模式

java 复制代码
// 抽象工厂示例
public interface ApplicationContext extends BeanFactory, 
                                           ResourcePatternResolver,
                                           ApplicationEventPublisher {
    // 扩展了多个工厂的功能
}

// 具体实现
public class ClassPathXmlApplicationContext extends AbstractApplicationContext {
    // 实现了完整的 Bean 生命周期管理
}

单例模式:Spring Bean 的单例实现

单例模式是一种创建型设计模式,保证一个类只有一个实例,并提供一个全局访问点。

1. 容器级单例

java 复制代码
// Spring 单例注册表(简化版)
public class DefaultSingletonBeanRegistry {
    // 单例对象缓存池
    private final Map<String, Object> singletonObjects = 
        new ConcurrentHashMap<>(256);
    
    // 获取单例
    public Object getSingleton(String beanName) {
        Object singletonObject = this.singletonObjects.get(beanName);
        if (singletonObject == null) {
            // 双重检查锁定
            synchronized (this.singletonObjects) {
                singletonObject = this.singletonObjects.get(beanName);
                if (singletonObject == null) {
                    singletonObject = createBean(beanName);
                    this.singletonObjects.put(beanName, singletonObject);
                }
            }
        }
        return singletonObject;
    }
}

2. 单例作用域配置

java 复制代码
// 方式1:XML配置
<bean id="userService" class="com.example.UserService" 
      scope="singleton"/> <!-- 默认就是 singleton -->

// 方式2:注解配置
@Component
@Scope("singleton")  // 或不写,默认就是 singleton
public class UserService {
    // ...
}

// 方式3:Java配置
@Configuration
public class AppConfig {
    @Bean
    @Scope("singleton")
    public UserService userService() {
        return new UserService();
    }
}

3. 与普通单例的区别

对比项 Spring 单例 传统单例模式
创建时机 容器启动时(非懒加载)或首次请求时(懒加载) 类加载时或首次调用时
存储位置 Spring 容器缓存中 JVM 静态变量或枚举
数量限制 容器内单例,不同容器不同实例 JVM 内单例
配置方式 可配置(singleton/prototype) 代码固定
生命周期 受容器管理,可销毁 随 JVM 生命周期

代理模式:Spring AOP 的代理实现

代理模式是一种结构型设计模式,为其他对象提供一种代理以控制对这个对象的访问。代理对象在客户端和目标对象之间起到中介作用。

1. JDK 动态代理

java 复制代码
// 目标接口
public interface UserService {
    void saveUser();
}

// 目标类
public class UserServiceImpl implements UserService {
    public void saveUser() {
        System.out.println("保存用户");
    }
}

// JDK 动态代理处理器
public class JdkDynamicProxyHandler implements InvocationHandler {
    private Object target;  // 目标对象
    
    public JdkDynamicProxyHandler(Object target) {
        this.target = target;
    }
    
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 前置增强
        System.out.println("开始事务");
        
        // 调用目标方法
        Object result = method.invoke(target, args);
        
        // 后置增强
        System.out.println("提交事务");
        return result;
    }
}

// 创建代理
public class ProxyFactory {
    public static Object createProxy(Object target) {
        return Proxy.newProxyInstance(
            target.getClass().getClassLoader(),
            target.getClass().getInterfaces(),
            new JdkDynamicProxyHandler(target)
        );
    }
}

Spring 中的使用场景

  • 目标类实现了接口
  • 事务管理(@Transactional)
  • 安全控制(@Secured)

2. CGLIB 动态代理

java 复制代码
// 目标类(无接口)
public class UserService {
    public void saveUser() {
        System.out.println("保存用户");
    }
}

// CGLIB 方法拦截器
public class CglibMethodInterceptor 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;
    }
}

// 创建代理
public class CglibProxyFactory {
    public static Object createProxy(Class<?> targetClass) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(targetClass);
        enhancer.setCallback(new CglibMethodInterceptor());
        return enhancer.create();
    }
}

3. Spring AOP 的选择策略

java 复制代码
// 简化的 Spring AOP 代理创建逻辑
public class DefaultAopProxyFactory implements AopProxyFactory {
    @Override
    public AopProxy createAopProxy(AdvisedSupport config) {
        // 根据配置决定使用 JDK 代理还是 CGLIB 代理
        if (config.isOptimize() || 
            config.isProxyTargetClass() || 
            hasNoUserSuppliedProxyInterfaces(config)) {
            
            Class<?> targetClass = config.getTargetClass();
            if (targetClass.isInterface() || 
                Proxy.isProxyClass(targetClass)) {
                return new JdkDynamicAopProxy(config);
            }
            return new CglibAopProxy(config);
        } else {
            return new JdkDynamicAopProxy(config);
        }
    }
}

4. Spring AOP 的配置方式

java 复制代码
// 方式1:注解配置
@Configuration
@EnableAspectJAutoProxy(proxyTargetClass = true) // 强制使用 CGLIB
public class AopConfig {
}

// 切面定义
@Aspect
@Component
public class LoggingAspect {
    @Before("execution(* com.example.service.*.*(..))")
    public void logBefore(JoinPoint joinPoint) {
        System.out.println("方法执行前:" + joinPoint.getSignature().getName());
    }
}

// 方式2:XML配置
<aop:config proxy-target-class="true">
    <aop:aspect ref="loggingAspect">
        <aop:before pointcut="execution(* com.example.service.*.*(..))" 
                    method="logBefore"/>
    </aop:aspect>
</aop:config>

5. 代理模式在 Spring 的其他应用

  • 声明式事务管理@Transactional 注解通过代理实现
  • Spring Security:安全拦截通过代理实现
  • 缓存抽象@Cacheable 注解通过代理实现缓存逻辑
  • 远程调用:RMI、HTTP Invoker 等通过代理封装

总结对比

特性 JDK 动态代理 CGLIB 代理
目标要求 必须实现接口 类即可(不能是 final)
性能 调用时反射,较慢 生成子类,调用较快
生成方式 运行时生成接口实现类 运行时生成目标类的子类
Spring 默认 有接口时使用 无接口或配置 proxyTargetClass=true 时使用

Spring 通过这些设计模式的巧妙结合,实现了 IoC 容器、AOP、声明式事务等核心功能,提供了灵活且强大的企业级开发框架。

责任链模式

责任链模式是一种行为型设计模式,它允许多个对象都有机会处理请求,将这些对象连成一条链,并沿着这条链传递请求,直到有一个对象处理它为止。

解耦发送者和接收者:发送者不需要知道哪个对象会处理请求

动态组合处理链:可以动态地添加、删除或重新排序处理者

避免请求者和处理者紧耦合。

在Spring中,例如拦截器链(HandlerInterceptor)就是纯责任链,因为如果某个拦截器的preHandle返回false,则链终止,且只有部分拦截器(在返回false之前的那些)的preHandle被调用,而postHandle和afterCompletion则只有符合条件的拦截器才会执行(且每个拦截器只执行一次)。而过滤器链(Filter)则是不纯责任链,因为每个过滤器都会执行doFilter,并且每个过滤器都会执行。

拦截器(Interceptor)的使用

  1. 创建自定义拦截器,实现 HandlerInterceptor 接口
  2. 注册拦截器WebConfig implements WebMvcConfigurer

过滤器(Filter)的使用

  1. 创建自定义过滤器,实现 Filter 接口
  2. 使用 FilterRegistrationBean 注册过滤器

总结

工厂模式:BeanFactory就是简单工厂模式的体现,用来创建对象的实例;

单例模式:Bean默认为单例模式。

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

责任链模式:在Spring中,例如拦截器链(HandlerInterceptor)就是纯责任链,因为如果某个拦截器的preHandle返回false,则链终止,且只有部分拦截器(在返回false之前的那些)的preHandle被调用,而postHandle和afterCompletion则只有符合条件的拦截器才会执行(且每个拦截器只执行一次)。而过滤器链(Filter)则是不纯责任链,因为每个过滤器都会执行doFilter,并且每个过滤器都会执行。

相关推荐
bbq粉刷匠1 小时前
Java-顺序表
java
Tan_Ying_Y2 小时前
Mybatis的mapper文件中#和$的区别
java·tomcat·mybatis
难以触及的高度2 小时前
Java for循环完全指南:从基础到高性能实践
java·开发语言
sheji34162 小时前
【开题答辩全过程】以 农产品销售系统为例,包含答辩的问题和答案
java·eclipse
budingxiaomoli2 小时前
多线程(三)
java·开发语言
klzdwydz2 小时前
注解与反射
java·开发语言
talenteddriver3 小时前
java: 分页查询(自用笔记)
java·开发语言
enjoy编程3 小时前
Spring-AI 利用KeywordMetadataEnricher & SummaryMetadataEnricher 构建文本智能元数据
java·人工智能·spring
繁华似锦respect3 小时前
lambda表达式中的循环引用问题详解
java·开发语言·c++·单例模式·设计模式·哈希算法·散列表