Spring中经典的7种设计模式源码分析

一、工厂模式

  • Spring使用工厂模式来创建Bean对象,如BeanFactory、ApplicationContext等。
  • 工厂模式为bean的创建过程提供了一个框架,同时隔离了实例化细节,使得代码更加解耦。
  1. BeanFactory接口

BeanFactory接口仍然是Spring工厂模式的基础,它定义了获取Bean实例的基本方法。

public interface BeanFactory {
    Object getBean(String name) throws BeansException;
    // ...
}
  1. DefaultListableBeanFactory类

DefaultListableBeanFactory继承自AbstractAutowireCapableBeanFactory,实现了BeanDefinitionRegistry接口,用于存储和管理BeanDefinition。它是最常用的BeanFactory实现类。

public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory
        implements ConfigurableListableBeanFactory, BeanDefinitionRegistry {
    // ...
}
  1. AbstractBeanFactory类

AbstractBeanFactory作为抽象基类,定义了getBean()方法的模板实现。

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(final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly)
            throws BeansException {
        // 合并父BeanFactory
        final String beanName = transformedBeanName(name);
        Object sharedInstance = getSingleton(beanName);

        // ...

        // 创建Bean实例
        RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
        checkMergedBeanDefinition(mbd, beanName, args);
        Object instance = null;
        if (mbd.isSingleton()) {
            instance = resolveBeforeInstantiation(beanName, mbd);
            if (instance == null) {
                instance = createBean(beanName, mbd, args);
            }
        } else {
            instance = createBean(beanName, mbd, args);
        }

        // ...

        return instance;
    }
}
  1. 创建Bean实例

AbstractAutowireCapableBeanFactory提供了createBean()方法,用于创建Bean实例。该方法委托给了AbstractAutowireCapableBeanFactory的createBeanInstance()方法,来实现不同的实例化策略。

protected Object createBeanInstance(String beanName, RootBeanDefinition mbd, Object[] args) {
    // 决定使用哪种实例化策略
    Constructor<?> constructorToUse = null;
    // ...

    // 使用适当的实例化策略
    boolean resolved = false;
    boolean autowireNecessary = false;
    if (args == null) {
        synchronized (mbd.constructorArgumentLock) {
            // ...
            autowireNecessary = mbd.resolvedConstructorOrFactoryMethod != null;
            resolved = true;
        }
    }

    Object beanInstance = null;
    if (resolved) {
        // 使用工厂方法实例化
        beanInstance = getInstantiationStrategy().instantiate(mbd, null, this);
    }
    // ...

    return beanInstance;
}
  1. 实例化策略

Spring 5中提供了多种实例化策略,包括:

  • 简单实例化策略(SimpleInstantiationStrategy)
  • CGLIB实例化策略(CglibSubclassingInstantiationStrategy)
  • 通过工厂方法实例化(FactoryMethodInstantiationStrategy)
  • 通过静态工厂方法实例化

这些实例化策略均实现了InstantiationStrategy接口,并在AbstractAutowireCapableBeanFactory的createBeanInstance()方法中根据条件来选择合适的策略。

二、单例模式

  • Spring默认情况下将每个bean创建为单例模式,可以通过配置修改为原型模式。
  • 单例模式确保了在整个应用程序中只有一个bean实例,从而节省内存资源。
  1. 单例 Bean 缓存

DefaultSingletonBeanRegistry 使用 ConcurrentHashMap 来缓存单例 Bean 的实例:

/** Cache of singleton objects: bean name to bean instance */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
  1. getSingleton() 方法

在 AbstractBeanFactory 的 getBean() 方法中,首先会尝试从单例 Bean 缓存中获取 Bean 实例:

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

protected <T> T doGetBean(final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly) throws BeansException {
    // 获取"真实"的 beanName,考虑了 FactoryBean
    final String beanName = transformedBeanName(name);
    Object sharedInstance = getSingleton(beanName);
    if (sharedInstance != null && args == null) {
        // ...
    }
    // ...
}

getSingleton() 方法定义在 DefaultSingletonBeanRegistry 中:

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

protected Object getSingleton(String beanName, boolean allowEarlyReference) {
    // 检查缓存中是否存在实例
    Object singletonObject = this.singletonObjects.get(beanName);
    if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
        // 如果当前 Bean 正在创建中,则需要处理循环依赖
        singletonObject = this.earlySingletonObjects.get(beanName);
        if (singletonObject == null && allowEarlyReference) {
            // ...
        }
    }
    return singletonObject;
}
  1. addSingleton() 方法

如果缓存中不存在该 Bean 的实例,Spring 会调用相应的实例化策略创建 Bean 实例,并在创建完成后,将其缓存到单例缓存中:

protected void addSingleton(String beanName, Object singletonObject) {
    synchronized (this.singletonObjects) {
        this.singletonObjects.put(beanName, singletonObject);
        // 从 singletonFactories 中移除
        this.singletonFactories.remove(beanName);
        this.earlySingletonObjects.remove(beanName);
        this.registeredSingletons.add(beanName);
    }
}
  1. 单例作用域

在解析 Bean 定义时,Spring 会根据 scope 属性确定 Bean 的作用域,默认为 singleton,即单例模式。如果是单例作用域,Spring 会在获取 Bean 实例时从单例缓存中获取或创建一个实例并加入缓存。

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

protected <T> T doGetBean(final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly) {
    // ...

    // 创建或获取已经创建的实例
    if (mbd.isSingleton()) {
        sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {
            @Override
            public Object getObject() throws BeansException {
                try {
                    return createBean(beanName, mbd, args);
                } catch (BeansException ex) {
                    // ...
                }
            }
        });
        // ...
    }

    // ...
}

在上面的代码中,如果 Bean 的作用域是单例,Spring 会调用 getSingleton() 方法尝试从单例缓存中获取 Bean 实例。如果缓存中不存在,则使用传入的 ObjectFactory 回调函数创建 Bean 实例,并将其加入单例缓存中。

  1. 循环依赖处理

Spring 还提供了处理单例 Bean 循环依赖的机制。在 getSingleton() 方法中,如果发现当前 Bean 正在创建中,会尝试从 earlySingletonObjects 缓存中获取提前曝光的 Bean 实例(ObjectFactory),从而解决循环依赖问题。

总结起来,Spring 5 中通过维护一个单例 Bean 缓存 (ConcurrentHashMap)、getSingleton() 方法从缓存中获取或创建 Bean 实例、以及 addSingleton() 方法将 Bean 实例加入缓存,实现了单例模式的管理。同时,Spring 还根据 Bean 定义的 scope 属性确定 Bean 的作用域,默认为单例模式。这种实现方式保证了在同一个 Spring IoC 容器中,单例 Bean 只会被实例化一次,提高了性能并节省了内存资源,

三、代理模式

  • Spring的AOP (Aspect Oriented Programming) 功能就是利用代理模式实现的。
  • 代理模式在不修改目标对象的情况下,通过代理对象来增强目标对象的功能。Spring AOP的实现主要依赖两个模块:Spring AOP和AspectJ
  1. AOP代理创建

在Spring AOP中,如果目标对象实现了接口,则默认使用JDK动态代理创建AOP代理。如果目标对象没有实现接口,则使用CGLIB创建代理对象。这种选择是由AopProxyFactory决定的:

public class DefaultAopProxyFactory implements AopProxyFactory, Serializable {

    @Override
    public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
        if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
            Class<?> targetClass = config.getTargetClass();
            if (targetClass == null) {
                throw new AopConfigException("TargetSource cannot determine target class: " +
                        "Either an interface or a target is required for proxy creation.");
            }
            if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
                return new JdkDynamicAopProxy(config);
            }
            return new ObjenesisCglibAopProxy(config);
        }
        else {
            return new JdkDynamicAopProxy(config);
        }
    }
}
  1. JDK动态代理

JDK动态代理通过实现InvocationHandler接口和使用Proxy类来创建代理对象,代码如下:

public class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializable {

    private final Class<?> proxiedInterface;

    public Object getProxy(ClassLoader classLoader) {
        // 使用Proxy创建代理对象
        return Proxy.newProxyInstance(classLoader, new Class<?>[] { this.proxiedInterface }, this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 调用拦截器链执行方法
        MethodInvocation invocation = new ReflectiveMethodInvocation(...);
        return invocation.proceed();
    }
}
  1. CGLIB代理

CGLIB代理通过继承目标对象的方式创建代理对象,代码如下:

public class ObjenesisCglibAopProxy extends CglibAopProxy {

    @Override
    public Object getProxy(ClassLoader classLoader) {
        // 使用Enhancer创建代理对象
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(this.advised.getTargetClass());
        enhancer.setInterfaces(AopProxyUtils.completeProxiedInterfaces(this.advised));
        enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
        enhancer.setStrategy(new ClassLoaderAwareUndeclaredThrowableStrategy(classLoader));
        
        Callback[] callbacks = getCallbacks(this.advised);
        Class<?>[] types = new Class<?>[callbacks.length];
        for (int x = 0; x < types.length; x++) {
            types[x] = callbacks[x].getClass();
        }
        // 设置过滤器
        enhancer.setCallbackFilter(new ProxyCallbackFilter(this.advised.getConfigurationOnlyCopy(), types));
        enhancer.setCallbackTypes(types);

        // 创建代理对象
        return createProtectedProxy(enhancer, callbacks, this.advised);
    }
}
  1. Advice链执行

无论是JDK动态代理还是CGLIB代理,在目标方法调用时都会执行相应的Advice链。Spring使用MethodInvocation类来执行Advice链:

public class ReflectiveMethodInvocation implements ProxyMethodInvocation {

    protected final Object[] arguments;

    public Object proceed() throws Throwable {
        // 获取适当的拦截器链
        List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
        
        if (chain.isEmpty()) {
            // 直接执行目标方法
            retVal = invokeSingalTarget();
        }
        else {
            // 执行拦截器链
            retVal = new ReflectiveMethodInvocation(proxy, target, method, arguments, target.getClass(), chain).proceed();
        }
        return retVal;
    }
}

在执行Advice链的过程中,Spring会根据Advice的类型(前置、环绕、异常、最终返回)在适当的位置执行对应的Advice逻辑。

总的来说,Spring 5利用JDK动态代理和CGLIB两种方式创建AOP代理对象,并通过执行相应的Advice链来增强目标对象的功能。代理模式的使用使得Spring AOP能够在不修改目标对象的前提下,动态地为目标对象增加新的行为和职责。可以自定义Advice,从而灵活地扩展目标对象的功能。

四、模板方法模式

  • Spring中的 JDBCTemplate、HibernateTemplate等以模板方法模式为基础。
  • 模板方法模式定义了一个算法的骨架,并允许子类重写特定的步骤。
  1. JdbcTemplate

JdbcTemplate是Spring JDBC中的核心类,用于简化JDBC操作。它提供了一系列执行SQL语句的模板方法,如query()、update()等。这些模板方法都遵循相同的模式:

public <T> T query(String sql, ResultSetExtractor<T> rse) throws DataAccessException {
    // 创建语句
    PreparedStatement stmt = null;
    try {
        stmt = createPreparedStatement(sql);
        // 设置参数
        applyStatementSettings(stmt);
        // 执行查询
        ResultSet rs = null;
        try {
            rs = executeQuery(stmt);
            // 执行ResultSetExtractor
            return rse.extractData(rs);
        } finally {
            // ...
        }
    } finally {
        // ...
    }
}
  1. 模板方法骨架

在JdbcTemplate的模板方法中,有一些固定的步骤是不可变的,如创建语句、设置参数、执行查询等,这些步骤构成了模板方法的骨架。

  1. 钩子方法

在模板方法中,也会调用一些抽象方法或钩子方法,这些方法留给子类去实现。比如executeQuery()、applyStatementSettings()等。子类可以通过扩展JdbcTemplate并重写这些钩子方法来自定义相应的行为。

protected PreparedStatement createPreparedStatement(String sql, @Nullable List<Object> paramList) throws SQLException {
    PreparedStatement stmt = getPreparedStatementCreator().createPreparedStatement(sql, this.getRetrieveKeys(paramList));
    applyStatementSettings(stmt);
    return stmt;
}

protected abstract PreparedStatementCreator getPreparedStatementCreator();

protected void applyStatementSettings(PreparedStatement stmt) {
    // ...
}
  1. 回调接口

除了通过子类继承的方式来自定义行为,Spring还提供了一些回调接口,允许开发者在执行模板方法时注入自定义的逻辑。比如ResultSetExtractor、PreparedStatementSetter等。

public <T> T query(String sql, ResultSetExtractor<T> rse) throws DataAccessException {
    // ...
    return rse.extractData(rs);
}

总的来说,Spring 5中通过模板方法模式提供了一些可复用的模板类,如JdbcTemplate。这些模板类定义了一系列算法的骨架,并通过钩子方法和回调接口,允许开发者在不改变模板结构的情况下,自定义相应的行为。这种模式提高了代码的可重用性和可扩展性,同时也增强了代码的可维护性。Spring框架中还有许多其他模板类的实现,如RedisTemplate、RestTemplate等,都遵循了模板方法模式的设计思想。

五、观察者模式

  • Spring的事件机制就是基于观察者模式实现的。
  • 观察者模式定义了对象之间的一对多依赖关系,当一个对象改变状态时,它的所有依赖者都会得到通知。
    Spring 5中使用观察者模式来实现事件驱动机制,主要通过ApplicationEvent和ApplicationListener来实现。
  1. ApplicationEvent

ApplicationEvent是Spring中事件的基类,它继承自jdk的EventObject。所有的事件都需要继承ApplicationEvent。

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();
    }
    
    // ...
}
  1. ApplicationListener

ApplicationListener是观察者接口,用于监听并处理ApplicationEvent事件。

@FunctionalInterface
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
    void onApplicationEvent(E event);
}
  1. ApplicationEventMulticaster

ApplicationEventMulticaster是观察者模式中的主题或发布者角色,它维护了一个ApplicationListener集合,用于发布事件并通知所有监听器。

public abstract class AbstractApplicationEventMulticaster implements ApplicationEventMulticaster {
    private final ListenerRetriever defaultRetriever = new DefListenerRetriever(false);
    private final ListenerRetriever retrieverWithSet = new DefListenerRetriever(true);
    
    // 添加监听器
    public void addApplicationListener(ApplicationListener<?> listener) {
        synchronized (this.retrieveListeners) {
            this.retrieveListeners.add(listener);
            this.singletonRetrievers.clear();
        }
    }
    
    // 发布事件并通知监听器
    public void multicastEvent(final ApplicationEvent event, ResolvableType eventType) {
        resolveListeners(event, retrieverCache.getListeners(eventType, this.defaultRetriever, this.applicationContext.getId()));
    }
    
    // 内部通知监听器处理事件
    protected void invokeListener(ApplicationListener listener, ApplicationEvent event) {
        listener.onApplicationEvent(event);
    }
    
    // ...
}
  1. 注册和发布事件

在Spring中,可以通过实现ApplicationListener接口并将其注册为Bean来监听事件。发布事件时,ApplicationEventMulticaster会遍历所有的监听器并调用其onApplicationEvent()方法。

// 定义事件
public class MyEvent extends ApplicationEvent {
    // ...
}

// 定义监听器
@Component
public class MyListener implements ApplicationListener<MyEvent> {
    @Override
    public void onApplicationEvent(MyEvent event) {
        // 处理事件
    }
}

// 发布事件
@Component
public class EventPublisher {
    @Autowired
    private ApplicationContext context;
    
    public void publish() {
        context.publishEvent(new MyEvent("Hello Event"));
    }
}

Spring 5中通过ApplicationEvent、ApplicationListener和ApplicationEventMulticaster实现了观察者模式。ApplicationEvent是事件对象,ApplicationListener是观察者接口,ApplicationEventMulticaster扮演发布者的角色,负责管理监听器并发布事件。开发者可以自定义事件和监听器,并通过ApplicationContext发布事件。这种实现方式使得Spring框架具有了良好的可扩展性和灵活性,能够很好地支持事件驱动编程。

六、策略模式

  • Spring的资源访问模块(Resource)中使用了策略模式。
  • 策略模式定义了一系列算法,并将每个算法封装成一个对象,从而使它们可以互相替换。
    Spring 5中使用了策略模式来实现一些功能,允许在运行时根据不同的条件选择不同的算法或策略。以Spring中的Resource接口为例,我们来分析一下策略模式在Spring 5中的实现。
  1. Resource接口

Resource接口定义了一组用于获取低级资源的操作方法。它是Spring资源访问策略的入口接口。

public interface Resource extends InputStreamSource {
    boolean exists();
    boolean isOpen();
    URL getURL() throws IOException;
    File getFile() throws IOException;
    // ...
}
  1. Resource实现类

Spring提供了多个Resource接口的具体实现类,每个实现类都对应一种资源访问策略。

  • ClassPathResource: 用于访问类路径下的资源
  • FileSystemResource: 用于访问文件系统中的资源
  • UrlResource: 用于访问URL资源
  • ByteArrayResource: 用于访问字节数组资源
  • InputStreamResource: 用于访问输入流资源
public class ClassPathResource extends AbstractResource {
    // ...
}

public class FileSystemResource extends AbstractResource implements ResourceLoaderAware {
    // ...  
}

public class UrlResource extends AbstractFileResolvingResource {
    // ...
}
  1. Resource加载器

Spring提供了ResourceLoader接口及其实现类DefaultResourceLoader,用于根据不同的前缀来选择合适的Resource实现类。

public interface ResourceLoader {
    Resource getResource(String location);
}

public class DefaultResourceLoader implements ResourceLoader {
    @Override
    public Resource getResource(String location) {
        if (location.startsWith("/")) {
            return getResourceByPath(location);
        } else if (location.startsWith("classpath:")) {
            return new ClassPathResource(location.substring("classpath:".length()), getClassLoader());
        } else {
            try {
                // Try URL
                URL url = new URL(location);
                return new UrlResource(url);
            } catch (MalformedURLException ex) {
                // No URL -> resolve as resource path.
                return getResourceByPath(location);
            }
        }
    }
    // ...
}
  1. 使用示例

在应用程序中,可以通过ResourceLoader或ApplicationContext来获取不同类型的Resource实例。

@Component
public class MyService {
    @Autowired
    private ResourceLoader resourceLoader;

    public void loadResource(String location) {
        Resource resource = resourceLoader.getResource(location);
        // 使用Resource执行相关操作
    }
}

Spring 5通过Resource接口及其多个具体实现类,以及ResourceLoader加载器,实现了策略模式。不同的Resource实现类对应不同的资源访问策略,而ResourceLoader则根据资源位置的前缀来选择合适的Resource实现类。

七、装饰器模式

  • Spring中的 HttpServletRequestDecorator和 HttpServletResponseDecorator 使用了装饰器模式。
  • 装饰器模式可以在不修改原有对象结构的情况下,动态地给该对象增加新的行为和职责。
  • 25
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Java语录精选

你的鼓励是我坚持下去的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值