Spring中的设计模式

工厂模式

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

两者对比:

ApplicationContext容器启动的时候,一次性创建所有的Bean

BeanFactory则为延迟注入,相比于ApplicationContext来说会占用更少内存,但其仅提供最基本的依赖注入

ApplicationContext扩展了BeanFactory的功能,所以一般开发人员使用ApplicationContext更多

ApplicationContext的三个实现类

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

例如

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();
	}
}

单例模式

一般情况下我们使用单例模式是为了保证一个类有且只能有一个实例,如线程池,日志对象,打印机输出流等,那么在Spring中有哪些地方用到了单例模式呢

1.bean默认作用域

Spring中bean默认作用域就是singleton单例模式,当然bean还可以更改作用域:

  • prototype:每次请求都会创建一个新的bean实例
  • request:每次Http请求都会产生一个新的bean,该bean尽在当前HTTP request内有效
  • session: 每次Http请求都会产生一个新的bean,该bean尽在当前HTTP session内有效

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

// 通过 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));
            }
        }
}

代理模式Proxy

说到代理模式,那必须说AOP,AOP能够将那些与业务无关,却为业务模块所共同调用的逻辑或责任(事务处理,日志管理,权限控制等)封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可扩展性和可维护性。

Spring AOP就是基于动态代理的,如果要代理的对象实现了某个接口,那么Spring AOP会使用JDK Proxy,去创建代理对象,而对于没有实现接口的对象,就无法使用JDK Proxy去进行代理了,这时候Spring AOP 会使用Cglib生成一个被代理对象的子类作为代理如图:
在这里插入图片描述
当然还有一种Java生态系统中最完整的AOP框架——AspectJ,Spring AOP已经集成了AspectJ可以直接使用

使用AOP后我们可以把一些通用功能抽象出来,在需要用到的地方直接使用即可,这样大大简化了代码量,我们需要增加新功能时也方便,同样提高了系统的可扩展性。如日志功能,事务管理等等场景都用到了AOP。

Spring AOP 和AspectJ AOP有什么区别?
Spring AOP 属于运行时增强,而AspectJ是编译时增强。 Spring AOP 基于代理,而AspectJ 相比于Spring AOP功能更加强大,但是Spring AOP 相对来说更简单
如果我们的切面比较少,两者性能差异不大,但如果切面很多的话,最好选择AspectJ,它比SpringAOP快很多。

模板方法模式

模板方法模式是一种行为设计模式,它定义一个操作中的算法骨架,将一些步骤延迟到子类中,模板方法使得子类可以不改变一个算法结构即可重定义该算法某些特定步骤的实现方式。
在这里插入图片描述

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

观察者模式

观察者模式是一种对象行为型模式,他表示的是一种对象与对象之间具有依赖关系,当一个对象发生改变的时候,这个对象所依赖的对象也会作出反应。Spring事件驱动模型就是观察者模式的一个经典应用,Spring事件驱动模型在很多场景可以解耦我们的代码,比如我们每次添加商品是,都需要重新更新商品索引,这时候就可以利用观察者模式来解决这个问题

Spring事件驱动模型中三个角色

事件角色

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

Spring中默认存在以下事件,他们都是对ApplicationContextEvent的实现(继承自ApplicationContextEvent):

  • ContextStartedEvent: ApplicationContext 启动后触发的事件;
  • ContextStoppedEvent: ApplicationContext 停止后触发的事件;
  • ContextRefreshedEvent : ApplicationContext 初始化或刷新完成后触发的事件;
  • ContextClosedEvent : ApplicationContext 关闭后触发的事件
    在这里插入图片描述

事件监听者角色
ApplicationListener 充当事件监听者角色,他是一个接口,里面只定义了一个onApplicationEvent()方法来处理 ApplicationEventApplicationListener接口类源码如下, 从接口定义我们可以看出接口中事件只要实现了ApplicationEvent就可以了。 所以在Spring中我们只要实现 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()这个方法在 Abstract ApplicationContext类中被实现。

Spring事件的流程总结:

  • 定义一个事件:实现一个继承自ApplicationEvent, 并写响应的构造函数
  • 定义一个事件监听者:实现 ApplicationListener 接口,重写onApplicationEvent()方法;
  • 使用事件发布者发布消息,可以通过ApplicationEventPublisher的publishEvent()方法发布消息。
// 定义一个事件,继承自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(“你好”) *,控制台就会打印出: *接收到的信息是:你好 *。

适配器模式

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

spring AOP中的适配器模式

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

SpringMVC中的适配器模式

在SpringMVC中,DispatcherServlet 根据请求信息调用HandlerMapping,解析请求对应的Handler。解析到对应的Handler(即Controller)后,开始由HandlerAdapter适配器进行处理(这里就用到了适配者模式)。HandleAdapter作为接口,具体的适配器实现类用于对目标类进行适配,Controller就是需要适配的类。

为什么在Spring MVC中使用适配器模式? Spring MVC中的controller种类众多,不同类型的Controller通过不同的方法来对请求进行处理,那么就需要DispatcherServlet想**要获取对应类型的Controller就需要适配器来进行适配了。

装饰者模式

装饰者模式可以动态地给对象添加一些额外的属性或行为。相比于使用继承,装饰者模式更加灵活。简单点儿说就是当我们需要修改原有的功能,但我们又不愿直接去修改原有的代码时,设计一个Decorator套在原有代码外面。其实在 JDK 中就有很多地方用到了装饰者模式,比如* InputStream*家族,*InputStream *类下有 FileInputStream (读取文件)、BufferedInputStream (增加缓存,使读取文件速度大大提升)等子类都在不修改InputStream 代码的情况下扩展了它的功能。

在这里插入图片描述
Spring 中配置 DataSource 的时候,DataSource 可能是不同的数据库和数据源。我们能否根据客户的需求在少修改原有类的代码下动态切换不同的数据源?这个时候就要用到装饰者模式。

总结

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

  • 工厂模式:Spring中BeanFactory,AppliactionContext 创建对象使用了工厂模式
  • 单例模式:Spring中Bean默认为单例模式
  • 代理模式:Spring AOP 实现了动态代理
  • 模板方法模式: Spring中 jdbcTemplate ,hibernateTemplate 等以Template结尾i的对数据库操作的类,用到了模板模式
  • 观察者模式: Spring事件驱动模型 ApplicationEvent 就是用到了观察者模式
  • 适配器模式:Spring AOP 的增强或通知使用到了适配器模式,Spring MVC中HandlerAdapter使用了适配器模式
  • 装饰者模式:JDK中InputStream的输入输出中通过BufferedInputStream->FileInputStream ,BufferedReader -> FileReader这种都用到了装饰者模式
    dataSource数据源的切换同样用到了装饰者模式。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值