设计模式在Springboot都用在哪些地方呢

前言

😊"spring中用了哪些设计模式?"

面试过的肯定都知道,这个问题算是非常高频的问题了。

从现在起不要再死记硬背🤯,下面我把每个设计的模式的源码贴出来分析,以后大家就不会忘了👌

单例模式

在Spring Boot中,单例模式是默认的Bean作用域,Spring容器会保证每个单例Bean仅有一个实例。以DefaultSingletonBeanRegistry类为例,它是Spring容器实现单例模式的关键类。

Spring Boot 中的单例模式没有直接采用传统单例模式(如双检锁、静态内部类、枚举)的实现方式,而是基于自身的 Bean 管理机制来实现单例

简化的源码:

java 复制代码
public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {

    // 用于存储单例Bean的缓存
    private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

    @Override
    public Object getSingleton(String beanName) {
        return getSingleton(beanName, true);
    }

    protected Object getSingleton(String beanName, boolean allowEarlyReference) {
        // 从单例缓存中获取Bean
        Object singletonObject = this.singletonObjects.get(beanName);
        if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
            //-------singletonObjects 不是已经是 concurrentHashMap线程安全的吗,为什么还要加锁
            synchronized (this.singletonObjects) {
                singletonObject = this.earlySingletonObjects.get(beanName);
                if (singletonObject == null && allowEarlyReference) {
                    ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                    if (singletonFactory != null) {
                        singletonObject = singletonFactory.getObject();
                        this.earlySingletonObjects.put(beanName, singletonObject);
                        this.singletonFactories.remove(beanName);
                    }
                }
            }
        }
        return singletonObject;
    }

    protected void addSingleton(String beanName, Object singletonObject) {
        synchronized (this.singletonObjects) {
            // 将单例Bean添加到缓存中
            this.singletonObjects.put(beanName, singletonObject);
            this.singletonFactories.remove(beanName);
            this.earlySingletonObjects.remove(beanName);
            this.registeredSingletons.add(beanName);
        }
    }
}

在上述代码里,singletonObjects这个Map用于存储单例Bean实例。getSingleton方法会先从缓存里查找Bean实例,若不存在则创建;addSingleton方法会把创建好的单例Bean添加到缓存中。

tips
  1. 可以看到singletonObjects用了ConcurrentHashMap保证线程安全

  2. synchronized (this.singletonObjects)singletonObjects 再次加锁,保证 getput 的原子操作。保证在同一时刻只有一个线程能够执行创建单例 Bean 的操作。

    假设不加sync :

    有两个线程同时调用 getSingleton 方法获取同一个单例 Bean,此时 singletonObjects 中还没有该 Bean 实例。如果没有使用 synchronized 进行同步,可能会出现以下情况:

    1. 线程 A 检查 singletonObjects 发现该 Bean 实例不存在。
    2. 同时,线程 B 也检查 singletonObjects 发现该 Bean 实例不存在。
    3. 线程 A 和线程 B 都认为需要创建该 Bean 实例,于是各自创建了一个新的 Bean 实例,并将其入 singletonObjects 中,这样就会导致创建了多个单例 Bean 实例,违反了单例模式的原则。

工厂模式

BeanFactory是Spring框架里工厂模式的核心体现,它负责创建和管理Bean实例。AbstractBeanFactoryBeanFactory的一个重要抽象实现类。

以下是部分源码分析:

java 复制代码
public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport implements ConfigurableBeanFactory {

    @Override
    public Object getBean(String name) throws BeansException {
        return doGetBean(name, null, null, false);
    }

    protected <T> T doGetBean(
            String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)
            throws BeansException {

        String beanName = transformedBeanName(name);
        Object bean;

        // 从缓存中获取单例Bean
        Object sharedInstance = getSingleton(beanName);
        if (sharedInstance != null && args == null) {
            if (logger.isTraceEnabled()) {
                if (isSingletonCurrentlyInCreation(beanName)) {
                    logger.trace("Returning eagerly cached instance of singleton bean '" + beanName +
                            "' that is not fully initialized yet - a consequence of a circular reference");
                }
                else {
                    logger.trace("Returning cached instance of singleton bean '" + beanName + "'");
                }
            }
            bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
        }
        else {
            // 创建Bean实例
            if (isPrototypeCurrentlyInCreation(beanName)) {
                throw new BeanCurrentlyInCreationException(beanName);
            }

            BeanFactory parentBeanFactory = getParentBeanFactory();
            if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
                // Not found -> check parent.
                String nameToLookup = originalBeanName(name);
                if (parentBeanFactory instanceof AbstractBeanFactory) {
                    return ((AbstractBeanFactory) parentBeanFactory).doGetBean(
                            nameToLookup, requiredType, args, typeCheckOnly);
                }
                else if (args != null) {
                    // Delegation to parent with explicit args.
                    return (T) parentBeanFactory.getBean(nameToLookup, args);
                }
                else {
                    // No args -> delegate to standard getBean method.
                    return parentBeanFactory.getBean(nameToLookup, requiredType);
                }
            }

            if (!typeCheckOnly) {
                markBeanAsCreated(beanName);
            }

            try {
                RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
                checkMergedBeanDefinition(mbd, beanName, args);

                // Guarantee initialization of beans that the current bean depends on.
                String[] dependsOn = mbd.getDependsOn();
                if (dependsOn != null) {
                    for (String dep : dependsOn) {
                        if (isDependent(beanName, dep)) {
                            throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                                    "Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
                        }
                        registerDependentBean(dep, beanName);
                        getBean(dep);
                    }
                }

                // Create bean instance.
                if (mbd.isSingleton()) {
                    sharedInstance = getSingleton(beanName, () -> {
                        try {
                            return createBean(beanName, mbd, args);
                        }
                        catch (BeansException ex) {
                            // Explicitly remove instance from singleton cache: It might have been put there
                            // eagerly by the creation process, to allow for circular reference resolution.
                            // Also remove any beans that received a temporary reference to the bean.
                            destroySingleton(beanName);
                            throw ex;
                        }
                    });
                    bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
                }
                else if (mbd.isPrototype()) {
                    // It's a prototype -> create a new instance.
                    Object prototypeInstance = null;
                    try {
                        beforePrototypeCreation(beanName);
                        prototypeInstance = createBean(beanName, mbd, args);
                    }
                    finally {
                        afterPrototypeCreation(beanName);
                    }
                    bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
                }
                else {
                    String scopeName = mbd.getScope();
                    final Scope scope = this.scopes.get(scopeName);
                    if (scope == null) {
                        throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
                    }
                    try {
                        Object scopedInstance = scope.get(beanName, () -> {
                            beforePrototypeCreation(beanName);
                            try {
                                return createBean(beanName, mbd, args);
                            }
                            finally {
                                afterPrototypeCreation(beanName);
                            }
                        });
                        bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
                    }
                    catch (IllegalStateException ex) {
                        throw new BeanCreationException(beanName,
                                "Scope '" + scopeName + "' is not active for the current thread; consider " +
                                "defining a scoped proxy for this bean if you intend to refer to it from a singleton",
                                ex);
                    }
                }
            }
            catch (BeansException ex) {
                cleanupAfterBeanCreationFailure(beanName);
                throw ex;
            }
        }

        // Check if required type matches the type of the actual bean instance.
        if (requiredType != null && !requiredType.isInstance(bean)) {
            try {
                T convertedBean = getTypeConverter().convertIfNecessary(bean, requiredType);
                if (convertedBean == null) {
                    throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
                }
                return convertedBean;
            }
            catch (TypeMismatchException ex) {
                if (logger.isTraceEnabled()) {
                    logger.trace("Failed to convert bean '" + name + "' to required type '" +
                            ClassUtils.getQualifiedName(requiredType) + "'", ex);
                }
                throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
            }
        }
        return (T) bean;
    }
}

doGetBean方法会依据Bean的名称获取对应的Bean实例。要是缓存里没有,就会调用createBean方法来创建新的实例。

代理模式

Spring AOP借助代理模式达成面向切面编程。JdkDynamicAopProxy是基于JDK动态代理实现的AOP代理类。

以下是简化的源码分析:

java 复制代码
final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializable {

    private final AdvisedSupport advised;

    public JdkDynamicAopProxy(AdvisedSupport config) throws AopConfigException {
        Assert.notNull(config, "AdvisedSupport must not be null");
        if (config.getAdvisors().length == 0 && config.getTargetSource() == AdvisedSupport.EMPTY_TARGET_SOURCE) {
            throw new AopConfigException("No advisors and no TargetSource specified");
        }
        this.advised = config;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        MethodInvocation invocation;
        Object oldProxy = null;
        boolean setProxyContext = false;

        TargetSource targetSource = this.advised.getTargetSource();
        Object target = null;

        try {
            if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
                // The target does not implement the equals(Object) method itself.
                return equals(args[0]);
            }
            else if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
                // The target does not implement the hashCode() method itself.
                return hashCode();
            }
            else if (method.getDeclaringClass() == DecoratingProxy.class) {
                // There is only getDecoratedClass() declared -> dispatch to proxy config.
                return AopProxyUtils.ultimateTargetClass(this.advised);
            }
            else if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&
                    method.getDeclaringClass().isAssignableFrom(Advised.class)) {
                // Service invocations on ProxyConfig with the proxy config...
                return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
            }

            Object retVal;

            if (this.advised.exposeProxy) {
                // Make invocation available if necessary.
                oldProxy = AopContext.setCurrentProxy(proxy);
                setProxyContext = true;
            }

            // Get as late as possible to minimize the time we "own" the target,
            // in case it comes from a pool.
            target = targetSource.getTarget();
            Class<?> targetClass = (target != null ? target.getClass() : null);

            // Get the interception chain for this method.
            List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);

            // Check whether we have any advice. If we don't, we can fallback on direct
            // reflective invocation of the target, and avoid creating a MethodInvocation.
            if (chain.isEmpty()) {
                // We can skip creating a MethodInvocation: just invoke the target directly
                // Note that the final invoker must be an InvokerInterceptor so we know it does
                // nothing but a reflective operation on the target, and no hot swapping or fancy proxying.
                Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
                retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
            }
            else {
                // We need to create a method invocation...
                invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
                // Proceed to the joinpoint through the interceptor chain.
                retVal = invocation.proceed();
            }

            // Massage return value if necessary.
            Class<?> returnType = method.getReturnType();
            if (retVal != null && retVal == target &&
                    returnType != Object.class && returnType.isInstance(proxy) &&
                    !RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {
                // Special case: it returned "this" and the return type of the method
                // is type-compatible. Note that we can't help if the target sets
                // a reference to itself in another returned object.
                retVal = proxy;
            }
            else if (retVal == null && returnType != Void.TYPE && returnType.isPrimitive()) {
                throw new AopInvocationException(
                        "Null return value from advice does not match primitive return type for: " + method);
            }
            return retVal;
        }
        finally {
            if (target != null && !targetSource.isStatic()) {
                // Must have come from TargetSource.
                targetSource.releaseTarget(target);
            }
            if (setProxyContext) {
                // Restore old proxy.
                AopContext.setCurrentProxy(oldProxy);
            }
        }
    }
}

invoke方法是代理对象的核心方法,在调用目标方法时,会先获取拦截器链,接着按顺序执行拦截器逻辑,最后调用目标方法。

观察者模式

Spring的事件驱动机制运用了观察者模式。SimpleApplicationEventMulticaster是事件广播器的实现类。

以下是简化的源码分析:

java 复制代码
public class CustomEvent extends ApplicationEvent {
    public CustomEvent(Object source) {
        super(source);
    }
}

import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;

@Component
public class CustomEventListener implements ApplicationListener<CustomEvent> {
    @Override
    public void onApplicationEvent(CustomEvent event) {
        System.out.println("Custom event received: " + event.getSource());
    }
}

SimpleApplicationEventMulticaster是事件广播器的实现类,multicastEvent方法将事件广播给所有注册的监听器。

java 复制代码
@Override
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
    ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
    Executor executor = getTaskExecutor();
    for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
       if (executor != null) {
          executor.execute(() -> invokeListener(listener, event));
       }
       else {
          invokeListener(listener, event);
       }
    }
}

模板方法模式

JdbcTemplate是Spring JDBC模块里模板方法模式的典型应用。

以下是简化的源码分析:

java 复制代码
public class JdbcTemplate extends JdbcAccessor implements JdbcOperations {

    @Override
    public <T> T query(final String sql, final ResultSetExtractor<T> rse) throws DataAccessException {
        Assert.notNull(sql, "SQL must not be null");
        Assert.notNull(rse, "ResultSetExtractor must not be null");
        if (logger.isDebugEnabled()) {
            logger.debug("Executing SQL query [" + sql + "]");
        }

        class QueryStatementCallback implements StatementCallback<T>, SqlProvider {
            @Override
            public T doInStatement(Statement stmt) throws SQLException {
                ResultSet rs = null;
                try {
                    rs = stmt.executeQuery(sql);
                    return rse.extractData(rs);
                }
                finally {
                    JdbcUtils.closeResultSet(rs);
                }
            }

            @Override
            public String getSql() {
                return sql;
            }
        }

        return execute(new QueryStatementCallback());
    }

    @Override
    public <T> T execute(StatementCallback<T> action) throws DataAccessException {
        Assert.notNull(action, "Callback object must not be null");

        Connection con = DataSourceUtils.getConnection(obtainDataSource());
        Statement stmt = null;
        try {
            stmt = con.createStatement();
            applyStatementSettings(stmt);
            T result = action.doInStatement(stmt);
            handleWarnings(stmt);
            return result;
        }
        catch (SQLException ex) {
            // Release Connection early, to avoid potential connection pool deadlock
            // in the case when the exception translator hasn't been initialized yet.
            String sql = getSql(action);
            JdbcUtils.closeStatement(stmt);
            stmt = null;
            DataSourceUtils.releaseConnection(con, getDataSource());
            con = null;
            throw getExceptionTranslator().translate("StatementCallback", sql, ex);
        }
        finally {
            JdbcUtils.closeStatement(stmt);
            DataSourceUtils.releaseConnection(con, getDataSource());
        }
    }
}

query方法定义了查询操作的模板,把具体的结果集处理逻辑委托给ResultSetExtractorexecute方法则封装了通用的数据库连接和语句执行逻辑。

装饰器模式

  • 原理 :装饰器模式允许向一个现有的对象添加新的功能,同时又不改变其结构。在 Spring Boot 中,BufferedReaderBufferedWriter的使用类似装饰器模式,用于增强ReaderWriter的功能。
  • 应用场景:当需要在不修改现有对象结构的前提下,动态地给对象添加额外的功能时使用。
  • 代码示例
java 复制代码
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;

public class DecoratorExample {
    public static void main(String[] args) {
        try (BufferedReader br = new BufferedReader(new FileReader("test.txt"))) {
            String line;
            while ((line = br.readLine()) != null) {
                System.out.println(line);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
  • 源码体现:虽然 Spring Boot 本身没有典型的装饰器模式代码,但 Java I/O 流的设计是装饰器模式的经典应用,Spring Boot 在使用 I/O 流时间接运用了该模式。

策略模式

  • 原理 :策略模式定义了一系列的算法,并将每个算法封装起来,使它们可以相互替换。在 Spring Boot 中,不同的MessageConverter实现就是策略模式的应用,用于处理不同类型的消息转换。
  • 应用场景:当需要根据不同的条件选择不同的算法或行为时使用。
  • 代码示例
java 复制代码
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

@RestController
public class ConverterController {
    private final List<HttpMessageConverter<?>> messageConverters;

    public ConverterController(List<HttpMessageConverter<?>> messageConverters) {
        this.messageConverters = messageConverters;
    }

    @GetMapping("/converters")
    public String getConverters() {
        StringBuilder sb = new StringBuilder();
        for (HttpMessageConverter<?> converter : messageConverters) {
            sb.append(converter.getClass().getName()).append("\n");
        }
        return sb.toString();
    }
}

适配器模式

  • 原理:将一个类的接口转换成客户希望的另外一个接口,使得原本由于接口不兼容而不能一起工作的那些类能一起工作。

  • 应用场景 :在 Spring MVC 中,HandlerAdapter 就是适配器模式的典型应用。Spring MVC 为不同类型的处理器(如 HttpRequestHandlerController 等)提供了不同的适配器,将不同处理器的调用适配到统一的 HandlerAdapter 接口上,这样 DispatcherServlet 就可以通过统一的方式调用不同类型的处理器。

  • 代码示例

java

kotlin 复制代码
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
public class MyController {
    @GetMapping("/example")
    @ResponseBody
    public String handleRequest() {
        return "This is an example response.";
    }
}

在上述代码中,MyController 是自定义的处理器。Spring MVC 内部通过 HandlerAdapter 来适配对 MyController 中方法的调用。

  • 源码体现RequestMappingHandlerAdapter 是处理 @RequestMapping 注解的处理器适配器,它实现了 HandlerAdapter 接口,将带有 @RequestMapping 注解的控制器方法适配到统一的调用流程中。

责任链模式

  • 原理:为请求创建了一个接收者对象的链,每个接收者都包含对另一个接收者的引用,如果一个对象不能处理该请求,那么它会把相同的请求传给下一个接收者,依此类推。

  • 应用场景 :Spring Security 中使用了责任链模式来处理安全验证。一系列的 Filter 组成了一个过滤器链,每个 Filter 负责特定的安全验证任务,如身份验证、权限检查等。请求会依次经过这些过滤器,直到找到能够处理该请求的过滤器或者整个链处理完毕。

  • 代码示例

java

java 复制代码
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;

@WebFilter(urlPatterns = "/*")
public class MyFilter implements Filter {
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        // 执行过滤逻辑
        System.out.println("Before filtering...");
        chain.doFilter(request, response);
        System.out.println("After filtering...");
    }
}

在这个示例中,MyFilter 是一个自定义的过滤器,它可以在请求处理前后执行特定的逻辑。多个这样的过滤器可以组成一个责任链。

  • 源码体现FilterChainProxy 是 Spring Security 中过滤器链的代理类,它管理着一系列的 Filter,并按照顺序依次调用这些过滤器。

建造者模式

  • 原理:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。

  • 应用场景 :在 Spring Boot 的配置类中,有时会使用建造者模式来创建复杂的配置对象。例如,RestTemplateBuilder 用于构建 RestTemplate 对象,它提供了一系列的方法来设置 RestTemplate 的各种属性,如消息转换器、拦截器等,最后通过 build() 方法创建出 RestTemplate 实例。

  • 代码示例

java

typescript 复制代码
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.web.client.RestTemplate;

public class RestTemplateExample {
    public static void main(String[] args) {
        RestTemplate restTemplate = new RestTemplateBuilder()
               .setConnectTimeout(java.time.Duration.ofSeconds(5))
               .setReadTimeout(java.time.Duration.ofSeconds(5))
               .build();
    }
}

在上述代码中,通过 RestTemplateBuilder 逐步设置 RestTemplate 的连接超时时间和读取超时时间,最后调用 build() 方法创建出 RestTemplate 实例。

  • 源码体现RestTemplateBuilder 类提供了一系列的方法用于设置 RestTemplate 的属性,最终通过 build() 方法构建出 RestTemplate 对象。

  • 源码体现 :Spring 框架中定义了HttpMessageConverter接口,不同的实现类(如MappingJackson2HttpMessageConverterStringHttpMessageConverter等)封装了不同的消息转换策略。

相关推荐
没明白白5 分钟前
结构型模式之桥接模式:解耦抽象和实现
java·网络·桥接模式
Liii40322 分钟前
Java学习——数据库查询操作
java·数据库·学习
PXM的算法星球1 小时前
Java爬虫抓取B站视频信息
java·windows·爬虫
uhakadotcom1 小时前
FPGA编程语言入门:从基础到实践
后端·面试·github
皮卡兔子屋1 小时前
redis过期删除、内存淘汰、双写一致性---java
java·redis·mybatis
whysqwhw1 小时前
手机如何抓包
java
kevindanglu1 小时前
Spring相关面试题
java·spring boot·spring
Hommi1 小时前
dify 向量数据库为es kibana无法连接解决方案
后端·ai 编程
奔跑的废柴2 小时前
LeetCode 452. 用最少数量的箭引爆气球 java题解
java·算法·leetcode·贪心算法·贪心
neoooo2 小时前
Spring Boot 整合 Redis 实现附近位置查找 (LBS功能)
java·redis·后端