设计模式在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等)封装了不同的消息转换策略。

相关推荐
人工智能那些事儿2 分钟前
神经网络:从基础到应用,开启智能时代的大门
java·人工智能·python·深度学习·神经网络·算法·机器学习
小鱼人爱编程13 分钟前
AI🔥助我!三分钟实现丐版前后端注册登录需求
前端·后端·deepseek
MiniFlyZt13 分钟前
分布式数据库TiDB:架构、核心特性与生产实践(分库分表)
java·数据库·分布式·spring cloud·微服务·tidb
zzx_nihao21 分钟前
Java29:Spring MVC
java·spring·mvc
JAVA坚守者25 分钟前
Java 服务器端 jar 包内 class 文件替换与配置文件修改高级技术指南
java·pycharm·jar
化作晚霞25 分钟前
JVM有什么调优参数?
java·开发语言·jvm
callJJ28 分钟前
Floyd算法求解最短路径问题——从零开始的图论讲解(3)
java·算法·动态规划·图论·dijkstra算法·floyd算法·最短路径问题
jason成都29 分钟前
asp.net core webapi+efcore
后端·asp.net
浩哲Zhe33 分钟前
Java Web 之 Tomcat 100问
java·前端·tomcat
Seven9739 分钟前
JDK的SPI有什么缺陷?dubbo做了什么改进?
java·dubbo