Spring 设计模式

参考链接:

https://snailclimb.gitee.io/javaguide/#/docs/system-design/framework/spring/Spring-Design-Patterns

工厂模式

Spring 使用工厂模式通过 BeanFactoryApplicationContext 创建 bean 对象。

两者对比

  • BeanFactory延迟注入(使用到某个 bean 的时候才会注入),相比于ApplicationContext 来说会占用更少的内存,程序启动速度更快。
  • ApplicationContext :容器启动的时候,不管你用没用到,一次性创建所有 beanBeanFactory 仅提供了最基本的依赖注入支持,ApplicationContext 扩展了 BeanFactory ,除了有BeanFactory的功能还有更多功能,所以一般开发人员使用ApplicationContext会更多。

ApplicationContext 的三个实现类:

  1. ClassPathXmlApplication:把上下文文件当成类路径资源。
  2. FileSystemXmlApplication:从文件系统中的 XML 文件载入上下文定义信息。
  3. XmlWebApplicationContext:从Web系统中的 XML 文件载入上下文定义信息。

Example:

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;

public class App {
    public static void main(String[] args) {
        ApplicationContext context = new FileSystemXmlApplicationContext(
                "C:/work/IOC Containers/springframework.applicationcontext/src/main/resources/bean-factory-config.xml");
        HelloApplicationContext obj = (HelloApplicationContext) context.getBean("helloApplicationContext");
        obj.getMsg();
    }
}

单例模式

对象创建型模式。

单例模式确保一个类只有一个实例,并提供一个全局访问点来访问这个唯一实例。
在这里插入图片描述

在系统中,有一些对象其实只需要一个,比如:线程池、缓存、对话框、注册表、日志对象、充当打印机、显卡等设备驱动程序的对象。事实上,这一类对象只能有一个实例,如果制造出多个实例就可能会导致一些问题,比如:程序的行为异常、资源使用过量、或者不一致性的结果。

使用单例模式的好处:

  • 对于频繁使用的对象,可以省略创建对象所花费的时间,这对于那些重量级对象而言,是非常可观的一笔系统开销;
  • 由于 new 操作的次数减少,因而对系统内存的使用频率也会降低,这将减轻 GC 压力,缩短 GC 停顿时间

Spring 中 bean 的默认作用域就是 singleton(单例)的。

Spring 实现单例的方式:

  • XML: <bean id="userService" class="top.snailclimb.UserService" scope="singleton"/>
  • 注解:@Scope(value = "singleton")

Spring 通过 ConcurrentHashMap 实现单例注册表的特殊方式实现单例模式。

Spring 实现单例的核心代码如下:

// 通过 ConcurrentHashMap(线程安全) 实现单例注册表
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>(64);

public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
        Assert.notNull(beanName, "'beanName' must not be null");
        synchronized (this.singletonObjects) {
            // 检查缓存中是否存在实例  
            Object singletonObject = this.singletonObjects.get(beanName);
            if (singletonObject == null) {
                //...省略了很多代码
                try {
                    singletonObject = singletonFactory.getObject();
                }
                //...省略了很多代码
                // 如果实例对象在不存在,我们注册到单例注册表中。
                addSingleton(beanName, singletonObject);
            }
            return (singletonObject != NULL_OBJECT ? singletonObject : null);
        }
    }
    //将对象添加到单例注册表
    protected void addSingleton(String beanName, Object singletonObject) {
            synchronized (this.singletonObjects) {
                this.singletonObjects.put(beanName, (singletonObject != null ? singletonObject : NULL_OBJECT));
            }
        }
}

代理模式

对象结构型模式。

给某一个对象提供一个代理或占位符,并由代理对象来控制对原对象的访问。
在这里插入图片描述

代理模式在 AOP 中的应用

AOP(Aspect-Oriented Programming:面向切面编程)能够将那些与业务无关,却为业务模块所共同调用的逻辑或责任(例如事务处理、日志管理、权限控制等)封装起来,便于减少系统的重复代码降低模块间的耦合度,并有利于未来的可拓展性和可维护性

Spring AOP 就是基于动态代理的,如果要代理的对象,实现了某个接口,那么Spring AOP会使用 JDK Proxy去创建代理对象,而对于没有实现接口的对象,就无法使用 JDK Proxy 去进行代理了,这时候 Spring AOP 会使用Cglib ,这时候 Spring AOP 会使用 Cglib 生成一个被代理对象的子类来作为代理,如下图所示:

SpringAOPProcess

Spring AOP 与 AspectJ AOP 的区别

Spring AOP 属于运行时增强,而 AspectJ 是编译时增强。 Spring AOP 基于代理(Proxying),而 AspectJ 基于字节码操作(Bytecode Manipulation)。

Spring AOP 已经集成了 AspectJ ,AspectJ 应该算的上是 Java 生态系统中最完整的 AOP 框架了。AspectJ 相比于 Spring AOP 功能更加强大,但是 Spring AOP 相对来说更简单。

如果我们的切面比较少,那么两者性能差异不大。但是,当切面太多的话,最好选择 AspectJ ,AspectJ 比 Spring AOP 快很多

模板方法模式

类行为型模式。

定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。 模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤的实现方式。

在这里插入图片描述

public abstract class Template {
    //这是模板方法
    public final void TemplateMethod(){
        PrimitiveOperation1();  
        PrimitiveOperation2();
        PrimitiveOperation3();
    }

    protected void PrimitiveOperation1(){
        //当前类实现
    }

    //被子类实现的方法
    protected abstract void PrimitiveOperation2();
    protected abstract void PrimitiveOperation3();

}
public class TemplateImpl extends Template {

    @Override
    public void PrimitiveOperation2() {
        //当前类实现
    }

    @Override
    public void PrimitiveOperation3() {
        //当前类实现
    }
}

Spring 中 jdbcTemplatehibernateTemplate 等以 Template 结尾的对数据库操作的类,它们就使用到了模板方法模式。一般情况下,我们都是使用继承的方式来实现模板方法模式,但是 Spring 并没有使用这种方式,而是使用 Callback 模式与模板方法模式配合,既达到了代码复用的效果,同时增加了灵活性。

Callback 模式与模板方法模式

参考资料:

https://iask.sina.com.cn/b/iQQUiOGpREPJ.html

Spring中的 Callback 模式与 Template 模式合用,随处可见。模板方法模式被广泛的使用,像 Servlet 就是使用这个模式。

模板方法模式虽然能简化很多重复的代码,但这种模式的也有不少限制。模板方法模式将一个功能的实现分成许多小的步骤,在父类中定义了这些步骤的顺序,让子类来具体实现每一个小的步骤。这些小的步骤是 protected,以防止用户不正确的使用这些小的步骤而产生异常。这样就产生了一个限制,那就是需要继承 Template 然后重新实现具体的小步骤。如果这个 Template 有许多方法,就像 JdbcTemplate,如果每次继承这个庞大的类,然后只是重写某个小步骤中来订制你自己的功能,就会显得非常笨重,更何况数据库操作使用的如此频繁,难道每进行一个操作就通过继承订制一个?显然不可能这么做。

Spring 使用 Callback 模式与之配合,达到了去掉重复代码的效果,同时增加了很大的灵活性,只需要实现某些CallBack 就可轻松订制出 Template。

那么什么时候才是 Callback 模式与 Template 模式结合的最佳时机呢?显然如果每个具体的步骤都需要真正去具体实现而不是简单的改变参数或设置某个对象就可以的话,使用 Callback 很难去订制,因为可能需要传递多个 Callback 作为参数,并让用户去实现,使用 Java 的内部类本来就是一个比较丑陋的语法,更何况参数是多个。
这相当于把每个小步骤封装成为接口,然后分别继承然后实现。显然没有达到方便灵活的效果。

这时候直接使用模板方法模式就比结合 Callback 模式要好。而如果用户只需要定制一个方法能就达到用户的要求,或者更简单,只是设置不同的参数,那么使用 Callback 模式就具有很好的灵活性。

观察者模式

对象行为型模式。

定义对象之间的一种一对多依赖关系,使得每当一个对象状态发生改变时,其相关依赖对象都得到通知并被自动更新。
在这里插入图片描述

Spring 事件驱动模型就是观察者模式很经典的一个应用。Spring 事件驱动模型非常有用,在很多场景都可以解耦代码。比如我们每次添加商品的时候都需要重新更新商品索引,这个时候就可以利用观察者模式来解决这个问题。

Spring 事件驱动模型中的三种角色

事件角色

ApplicationEvent (org.springframework.context包下)充当事件的角色,这是一个抽象类,它继承了java.util.EventObject并实现了 java.io.Serializable接口。

Spring 中默认存在以下事件,他们都是继承自 ApplicationEvent

  • ContextStartedEventApplicationContext 启动后触发的事件;
  • ContextStoppedEventApplicationContext 停止后触发的事件;
  • ContextRefreshedEventApplicationContext 初始化或刷新完成后触发的事件;
  • ContextClosedEventApplicationContext 关闭后触发的事件。
事件监听者角色

ApplicationListener 充当了事件监听者角色,它是一个接口,里面只定义了一个 onApplicationEvent()方法来处理ApplicationEventApplicationListener接口类源码如下,只要实现 ApplicationListener 接口中的 onApplicationEvent() 方法即可完成监听事件

package org.springframework.context;
import java.util.EventListener;
@FunctionalInterface
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
    void onApplicationEvent(E var1);
}
事件发布者角色

ApplicationEventPublisher 充当了事件的发布者,它也是一个接口。

@FunctionalInterface
public interface ApplicationEventPublisher {
    default void publishEvent(ApplicationEvent event) {
        this.publishEvent((Object)event);
    }

    void publishEvent(Object var1);
}

ApplicationEventPublisher 接口的publishEvent()这个方法在 AbstractApplicationContext 类中实现,阅读这个方法的实现,会发现实际上事件真正是通过 ApplicationEventMulticaster 来广播出去的。

Spring 的事件流程总结

  1. 定义一个事件: 继承自 ApplicationEvent,编写构造函数;
  2. 定义一个事件监听者:实现 ApplicationListener 接口,重写 onApplicationEvent() 方法;
  3. 使用事件发布者发布消息: 可以通过 ApplicationEventPublisherpublishEvent() 方法发布消息。

Example:

// 定义一个事件,继承自 ApplicationEvent,编写构造函数
public class DemoEvent extends ApplicationEvent {
    private static final long serialVersionUID = 1L;

    private String message;

    public DemoEvent(Object source,String message){
        super(source);
        this.message = message;
    }

    public String getMessage() {
         return message;
    }
}
// 定义一个事件监听者,实现 ApplicationListener 接口,重写 onApplicationEvent() 方法
@Component
public class DemoListener implements ApplicationListener<DemoEvent>{

    //使用 onApplicationEvent 接收消息
    @Override
    public void onApplicationEvent(DemoEvent event) {
        String msg = event.getMessage();
        System.out.println("接收到的信息是:"+msg);
    }
}
// 发布事件,可以通过 ApplicationEventPublisher 的 publishEvent() 方法发布消息
@Component
public class DemoPublisher {

    @Autowired
    ApplicationContext applicationContext;

    public void publish(String message){
        //发布事件
        applicationContext.publishEvent(new DemoEvent(this, message));
    }
}

当调用 DemoPublisherpublish() 方法的时候,比如 demoPublisher.publish("你好") ,控制台就会打印出:接收到的信息是:你好

适配器模式

结构型模式。

将一个类的接口转换成客户希望的另一个接口。适配器模式让那些接口不兼容的类可以一起工作。

适配器模式:

在这里插入图片描述

对象适配器模式:

在这里插入图片描述

适配器模式(Adapter Pattern) 将一个接口转换成客户希望的另一个接口,适配器模式使接口不兼容的那些类可以一起工作,其别名为包装器(Wrapper)。

Spring AOP 中的适配器模式

Spring AOP 的实现是基于代理模式,但是 Spring AOP 的增强或通知(Advice)使用到了适配器模式,与之相关的接口是AdvisorAdapter 。Advice 常用的类型有:BeforeAdvice(目标方法调用前,前置通知)、AfterAdvice(目标方法调用后,后置通知)、AfterReturningAdvice(目标方法执行结束后,return之前)等等。每个类型Advice(通知)都有对应的拦截器:MethodBeforeAdviceInterceptorAfterReturningAdviceAdapterAfterReturningAdviceInterceptor。Spring预定义的通知要通过对应的适配器,适配成 MethodInterceptor接口(方法拦截器)类型的对象(如:MethodBeforeAdviceInterceptor 负责适配 MethodBeforeAdvice)。

Spring MVC 中的适配器模式

在 Spring MVC 中,DispatcherServlet 根据请求信息调用 HandlerMapping,解析请求对应的 Handler。解析到对应的 Handler(即 Controller 控制器)后,开始由 HandlerAdapter 适配器处理。HandlerAdapter 作为期望接口,具体的适配器实现类用于对目标类进行适配,Controller 作为需要适配的类。

为什么要在 Spring MVC 中使用适配器模式? Spring MVC 中的 Controller 种类众多,不同类型的 Controller 通过不同的方法来对请求进行处理。如果不利用适配器模式的话,DispatcherServlet 直接获取对应类型的 Controller,需要的自行来判断,像下面这段代码一样:

if (mappedHandler.getHandler() instanceof MultiActionController) {  
   ((MultiActionController)mappedHandler.getHandler()).xxx  
} else if (mappedHandler.getHandler() instanceof XXX) {  
    ...  
} else if (...) {  
   ...  
}

假如我们再增加一个 Controller类型就要在上面代码中再加入一行判断语句,这种形式就使得程序难以维护,也违反了设计模式中的开闭原则: 对扩展开放,对修改关闭。

装饰者模式

对象结构型模式。

装饰者模式可以动态地给对象添加一些额外的属性或行为。就拓展功能而言,相比于使用继承,装饰者模式更加灵活。
在这里插入图片描述

简单点儿说就是当我们需要修改原有的功能,但我们又不愿直接去修改原有的代码时,设计一个 Decorator 套在原有代码外面。其实在 JDK 中就有很多地方用到了装饰者模式,比如 InputStream 家族,InputStream 类下有 FileInputStream (读取文件)、BufferedInputStream (增加缓存,使读取文件速度大大提升)等子类都在不修改InputStream 代码的情况下扩展了它的功能。

Spring 中配置 DataSource 的时候,DataSource 可能是不同的数据库和数据源。我们能否根据客户的需求在少量修改原有类的代码下动态切换不同的数据源?这个时候就要用到装饰者模式。Spring 中用到的装饰者模式在类名上含有 Wrapper或者 Decorator。这些类基本上都是动态地给一个对象添加一些额外的职责。

总结

Spring 框架中用到了哪些设计模式?

  • 工厂模式 : Spring 使用工厂模式通过 BeanFactoryApplicationContext 创建 bean 对象。
  • 代理模式 : Spring AOP 功能的实现。
  • 单例模式 : Spring 中的 Bean 默认都是单例的。
  • 模板方法模式 : Spring 中 jdbcTemplatehibernateTemplate 等以 Template 结尾的对数据库操作的类,它们就使用到了模板模式。
  • 装饰模式 : 项目需要连接多个数据库,而且不同的客户在每次访问中根据需要会去访问不同的数据库。这种模式可以根据客户的需求能够动态切换不同的数据源。
  • 观察者模式: Spring 事件驱动模型就是观察者模式很经典的一个应用。
  • 适配器模式 : Spring AOP 的增强或通知(Advice)使用到了适配器模式、spring MVC 中也是用到了适配器模式适配 Controller
  • 等等。
  • 5
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值