Spring 中使用到的设计模式,以及个模式的特点?

在Spring技术中涉及到的设计模式有:

1. 单例模式(Singleton Pattern):Spring容器中管理的Bean默认都是单例的,通过保证每个Bean只有一个实例,提高了系统的性能和资源利用。

特点:  单例模式是一种创建型设计模式,用于确保一个类只有一个实例,并提供一个全局访问点来访问该实例。 在单例模式中,类的构造函数被私有化,以防止其他类直接实例化该类。然后,类提供一个静态方法来创建或获取该类的唯一实例。这个静态方法会检查是否已经有一个实例存在,如果有,则返回该实例;如果没有,则创建一个新实例并返回。

 单例模式的应用场景包括:

  1. 需要确保系统中只有一个实例存在的情况,例如线程池、数据库连接池等。

  2. 需要共享资源的情况,例如全局配置信息、日志记录器等。

  3. 需要频繁创建和销毁对象的情况,例如对象池、缓存等。

创建方式:

1. 饿汉式:在类加载时就创建实例,保证了线程安全,但可能会造成资源浪费。

public class Singleton {
    private static final Singleton instance = new Singleton();
    
    private Singleton() {}
    
    public static Singleton getInstance() {
        return instance;
    }
}

2. 懒汉式(线程不安全):在第一次调用getInstance()方法时才创建实例。这种方式在多线程环境下可能会创建多个实例。

public class Singleton {
    private static Singleton instance;
    
    private Singleton() {}
    
    public static Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}

3. 懒汉式(线程安全):在第一次调用getInstance()方法时才创建实例,使用synchronized关键字保证了线程安全,但会影响性能。

public class Singleton {
    private static Singleton instance;
    
    private Singleton() {}
    
    public static synchronized Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}

    4. 静态内部类:在第一次调用getInstance()方法时才创建实例,利用类加载机制和静态内部类的特性保证了线程安全,同时延迟了实例的创建。

public class Singleton {
    private Singleton() {}
    
    private static class SingletonHolder {
        private static final Singleton instance = new Singleton();
    }
    
    public static Singleton getInstance() {
        return SingletonHolder.instance;
    }
}

     5. 双重检查锁定(Double-Checked Locking):在第一次调用getInstance()方法时才创建实例,使用双重检查锁定保证了线程安全,同时避免了每次都需要获取锁的性能损耗。

public class Singleton {
    private static volatile Singleton instance;
    
    private Singleton() {}
    
    public static Singleton getInstance() {
        if (instance == null) {
            synchronized (Singleton.class) {
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

在上面的例子中,Singleton类的构造函数被私有化,确保其他类不能直接实例化该类。getInstance()方法提供了获取Singleton实例的接口,通过双重检查锁定来确保只有一个实例被创建。

使用单例模式的好处包括:

     1. 提供了对唯一实例的控制,确保系统中只有一个实例存在。

      2. 提供了全局访问点,方便其他类访问该实例。

      3. 避免了重复创建实例的开销,提高了系统的性能。

然而,单例模式也有一些缺点,例如:

       1. 单例模式的实例一旦创建,就不能动态地改变其行为。

        2. 单例模式的实例在整个系统的生命周期中都存在,可能会导致资源的浪费。

        3. 单例模式的实现可能会增加代码的复杂性。

因此,在使用单例模式时需要权衡其优缺点,确保选择合适的设计模式来满足系统的需求。


2. 工厂模式(Factory Pattern):Spring使用工厂模式创建和管理Bean,通过配置文件或注解来指定要创建的Bean,从而实现了对象的解耦和灵活性。

         工厂模式是一种创建型设计模式,它提供了一种创建对象的方式,将对象的创建和使用分离开来。

        工厂模式包括三个主要角色:抽象工厂(Abstract Factory)、具体工厂(Concrete Factory)和产品(Product)。

        抽象工厂定义了一个创建产品的接口,具体工厂实现了抽象工厂的接口,负责创建具体的产品对象。产品是工厂创建的对象,它们共同实现了一个共同的接口或者继承了一个共同的父类。

 工厂模式的使用场景和优势:

           1. 当一个类不知道它所需要的对象的具体类时,可以使用工厂模式。通过工厂方法创建对象,客户端只需要知道产品的接口或父类,不需要关心具体的实现类。

           2. 工厂模式可以隐藏对象的创建细节,客户端只需要调用工厂的方法即可获取所需的对象,无需了解对象的具体创建过程。

            3. 工厂模式可以通过工厂方法对对象进行灵活的配置和管理,可以根据需求返回不同的实例。

            4. 工厂模式可以解耦客户端和具体的产品类,降低了客户端和具体产品之间的依赖关系。

    下面以一个简单的示例来说明工厂模式的使用: 假设有一个汽车制造工厂,它可以制造不同类型的汽车,包括轿车和越野车。我们可以使用工厂模式来创建这些汽车对象。 首先,定义一个汽车接口,包含一个制造汽车的方法:

public interface Car {
    void manufacture();
}

然后,创建两个具体的汽车类,实现汽车接口:

public class SedanCar implements Car {
    @Override
    public void manufacture() {
        System.out.println("Manufacturing Sedan Car");
    }
}

public class SUVCar implements Car {
    @Override
    public void manufacture() {
        System.out.println("Manufacturing SUV Car");
    }
}

接下来,定义一个抽象工厂,包含一个创建汽车的方法:

public abstract class CarFactory {
    public abstract Car createCar();
}

然后,创建两个具体的汽车工厂类,实现抽象工厂:

public class SedanCarFactory extends CarFactory {
    @Override
    public Car createCar() {
        return new SedanCar();
    }
}

public class SUVCarFactory extends CarFactory {
    @Override
    public Car createCar() {
        return new SUVCar();
    }
}

最后,客户端使用工厂模式来创建汽车对象:

public class Client {
    public static void main(String[] args) {
        CarFactory sedanCarFactory = new SedanCarFactory();
        Car sedanCar = sedanCarFactory.createCar();
        sedanCar.manufacture();
        
        CarFactory suvCarFactory = new SUVCarFactory();
        Car suvCar = suvCarFactory.createCar();
        suvCar.manufacture();
    }
}

运行以上代码,输出结果为:

Manufacturing Sedan Car
Manufacturing SUV Car

          通过工厂模式,我们可以通过工厂方法来创建不同类型的汽车对象,客户端只需要知道汽车的接口和具体的工厂类即可,无需关心具体的汽车实现类。这样可以提高代码的可维护性和扩展性。


3. 代理模式(Proxy Pattern):Spring AOP(面向切面编程)基于代理模式,通过动态代理的方式实现了对方法的增强和横切逻辑的集中管理。

         代理模式是一种结构型设计模式,它允许通过创建一个代理对象来控制对其他对象的访问。代理对象充当了被代理对象的接口,客户端通过代理对象间接地访问被代理对象。

        代理模式的主要目的是在不改变原始对象的情况下,提供额外的功能或控制对原始对象的访问。它可以用于实现访问控制、延迟加载、缓存、日志记录等功能。

   代理模式的核心角色包括

   -抽象主题(Subject):定义了被代理对象和代理对象之间的共同接口。

   - 真实主题(Real Subject):实现了抽象主题接口,是被代理的对象。

   - 代理(Proxy):实现了抽象主题接口,包含对真实主题对象的引用,并可以在调用真实主题对象之前或之后添加额外的逻辑。

下面是一个代理模式的应用实例和场景

         实例:假设有一个图片加载器,它可以从网络上下载图片并显示在屏幕上。由于图片下载可能会消耗较长的时间,为了提高用户体验,我们可以使用代理模式来实现一个图片加载器的代理,用于在图片下载完成之前显示一张占位图片。

# 抽象主题(图片加载器接口)
class ImageLoader:
    def display(self):
        pass

# 真实主题(图片加载器)
class RealImageLoader(ImageLoader):
    def __init__(self, image_url):
        self.image_url = image_url
        self.load_image()

    def load_image(self):
        print("Loading image from:", self.image_url)
        # 模拟图片下载过程

    def display(self):
        print("Displaying image:", self.image_url)

# 代理(图片加载器代理)
class ImageLoaderProxy(ImageLoader):
    def __init__(self, image_url):
        self.image_url = image_url
        self.image_loader = None

    def display(self):
        if self.image_loader is None:
            self.image_loader = RealImageLoader(self.image_url)
        self.image_loader.display()

# 客户端代码
image_url = "https://example.com/image.jpg"
image_loader = ImageLoaderProxy(image_url)
# 图片加载完成前,显示占位图片
image_loader.display()
# 图片加载完成后,显示实际图片
image_loader.display()

在上述示例中,

RealImageLoader 

是真实主题,负责下载和显示图片。

ImageLoaderProxy 

是代理,它在第一次调用

display 

方法时创建并初始化真实主题对象,并在后续调用中直接调用真实主题对象的

display 

方法。

代理模式的应用场景包括

     - 远程代理:用于在不同的地址空间中访问对象,例如远程方法调用(RPC)。

     - 虚拟代理:用于延迟创建昂贵的对象,例如延迟加载大型图片或视频。

      - 安全代理:用于控制对对象的访问权限,例如访问控制列表(ACL)。

      - 缓存代理:用于缓存对象的结果,以提高性能。 - 日志记录代理:用于记录对象的方法调用日志。

总结:代理模式通过引入一个代理对象,可以在不改变原始对象的情况下,提供额外的功能或控制对原始对象的访问。它在许多场景中都有应用,例如远程调用、延迟加载、访问控制、缓存和日志记录等。


4. 观察者模式(Observer Pattern):Spring的事件驱动模型基于观察者模式,通过定义事件和监听器,实现了对象之间的解耦和事件的处理。

        观察者模式(Observer Pattern)是一种行为型设计模式,它定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象,当主题对象发生变化时,它的所有依赖者(观察者)都会收到通知并自动更新。

        观察者模式的核心思想是将观察者和被观察者解耦,使得它们可以独立地进行扩展和修改,同时也降低了它们之间的依赖关系。 观察者模式的主要角色包括:

       1. Subject(主题):被观察者,它维护了一个观察者列表,并提供了添加、删除和通知观察者的方法。

       2. Observer(观察者):观察者,它定义了接收通知和更新的方法。

       3. ConcreteSubject(具体主题):具体的被观察者,它维护了一个状态,并在状态发生变化时通知观察者。

       4. ConcreteObserver(具体观察者):具体的观察者,它实现了观察者接口,接收到通知后进行相应的更新。

   观察者模式的应用场景包括:

         1. 系统中存在一对多的依赖关系,一个对象的状态发生变化需要通知其他多个对象。

         2. 当一个对象的改变需要同时改变其他对象的时候,可以使用观察者模式来解耦。

         3. 当一个对象需要通知其他对象,但是又不希望与被通知的对象产生耦合关系时,可以使用观察者模式。

          4. 当一个抽象模型有两个方面,其中一个方面依赖于另一个方面,而另一个方面不依赖于第一个方面时,可以使用观察者模式。

 总之,观察者模式可以帮助我们实现对象之间的松耦合,提高系统的灵活性和可扩展性


5. 模板方法模式(Template Method Pattern):Spring的JdbcTemplate和HibernateTemplate等模板类使用了模板方法模式,将通用的操作封装在父类中,子类只需要实现具体的业务逻辑。

           模板模式是一种行为设计模式,它定义了一个算法的框架,将一些步骤的实现延迟到子类中。模板模式可以使子类在不改变算法结构的情况下,重新定义算法中的某些步骤。 模板模式由以下几个角色组成:

        1. 抽象类(Abstract Class):定义了一个模板方法,该方法中包含了算法的框架,以及一些基本操作的实现。

         2. 具体类(Concrete Class):继承抽象类,实现模板方法中的具体操作。

 模板模式的应用场景包括:

        1. 算法的框架已经确定,但是一些具体的步骤需要由子类来实现。模板模式可以将这些可变的步骤延迟到子类中,提供了一种灵活的扩展方式。

         2. 多个类中存在相似的行为,可以将这些行为抽象到一个父类中,避免代码的重复。

         3. 需要在不改变算法结构的情况下,对算法的某些步骤进行定制化的扩展。 一个常见的应用场景是在软件开发中的代码生成器。

  在代码生成器中,可以定义一个抽象类作为代码生成的模板,其中包含了一些固定的代码结构和基本操作的实现。然后通过继承这个抽象类来实现不同类型的代码生成器,子类可以根据不同的需求来定制化生成代码的某些步骤,从而实现不同类型的代码生成。这样可以提高代码的复用性和可维护性。


6. 适配器模式(Adapter Pattern):Spring的适配器模式用于将不同的接口转换成统一的接口,使得不同的组件可以无缝集成。

适配器模式是一种结构型设计模式,用于将一个类的接口转换成客户端所期望的另一个接口。适配器模式允许不兼容的接口之间进行协同工作。 适配器模式通常包括三个主要角色:

        1. 目标接口(Target):定义客户端所期望的接口,适配器将目标接口转换成适配者接口。            2. 适配者(Adaptee):已经存在的接口,需要被适配的类或对象。

        3. 适配器(Adapter):实现目标接口,并将适配者的接口转换成目标接口,使得适配者能够被客户端使用。

适配器模式可以帮助解决以下问题:

     1. 将已经存在的类或对象与其他类协同工作,而这些类的接口不兼容。

      2. 在不改变现有代码的情况下,复用已有的类或对象。

适配器模式的应用场景包括:

     1. 在使用第三方库或框架时,将其接口转换成自己所期望的接口。

      2. 在系统演进过程中,逐步替换旧的组件或接口。

总之,适配器模式可以帮助解决接口不兼容的问题,使得不同接口之间能够协同工作。


7. 依赖注入模式(Dependency Injection Pattern):Spring的核心特性之一就是依赖注入,通过配置文件或注解的方式将依赖关系注入到对象中,实现了对象之间的解耦和灵活性。

   依赖注入模式(Dependency Injection,简称DI)是一种软件设计模式,用于解耦组件之间的依赖关系。它通过将对象的创建和依赖关系的管理交给外部容器来处理,从而减少了组件之间的耦合,提高了代码的可测试性和可维护性。 在依赖注入模式中,组件不再负责创建它所依赖的对象,而是通过构造函数、方法参数、或者属性注入的方式将依赖的对象传递进来。这样,组件只需要关注自身的业务逻辑,而不需要关心如何创建和管理依赖的对象。 依赖注入模式有以下几个主要的优点:

       1. 解耦性:组件之间的依赖关系由外部容器来管理,组件之间的耦合度降低,易于替换和重用。

        2. 可测试性:依赖注入使得组件的依赖关系明确,可以通过传递模拟对象来进行单元测试,提高了代码的可测试性。

         3. 可维护性:依赖注入将对象的创建和依赖关系的管理集中到了外部容器中,使得代码更加清晰和易于维护。

          4. 可扩展性:通过依赖注入,可以方便地添加新的组件和功能,而不需要修改已有的代码。

依赖注入模式有三种常见的实现方式: 

          1. 构造函数注入(Constructor Injection):通过构造函数将依赖的对象传递给组件。

           2. 方法参数注入(Method Parameter Injection):通过方法的参数将依赖的对象传递给组件。

            3. 属性注入(Property Injection):通过属性的方式将依赖的对象注入到组件中。

         总之,依赖注入模式是一种解耦组件之间依赖关系的设计模式,可以提高代码的可测试性、可维护性和可扩展性。它通过将对象的创建和依赖关系的管理交给外部容器来处理,使得组件只需要关注自身的业务逻辑,而不需要关心如何创建和管理依赖的对象。


以上是Spring技术中涉及到的一些常见设计模式,通过应用这些设计模式,Spring实现了高度可扩展、灵活和可维护的应用开发。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值