Spring--设计模式

一、简介

设计模式是软件工程领域的一组最佳实践,它们提供了一种通用解决方案来解决常见问题。Spring框架中融入了多种设计模式,以增强其灵活性、可扩展性和可重用性。

二、单例模式

Spring的单例模式指的是:确保一个类只有一个实例,并提供一个全局访问点。Spring管理的bean默认都是单例的,通过配置文件中的singleton属性进行控制。下面是一些单例模式的样例:

1.懒汉模式(线程不安全)优点:懒加载启动快,资源占用小,使用时才实例化,无锁。缺点:非线程安全。
public class LazySingleton {// 定义私有变量存储实例private static LazySingleton singleton = null;// 私有构造方法,控制实例无法在外部通过new创建实例private LazySingleton() {}public static LazySingleton getInstance() {if (singleton == null) {singleton = new LazySingleton ();}return singleton;}
}2.懒汉模式(线程安全)
public class LazySingleton {// 定义私有变量存储实例private static LazySingleton singleton = null;// 私有构造方法,控制实例无法在外部通过new创建实例private LazySingleton() {}// 加入synchronized 为独占排他锁,并发性能差。即使在创建成功以后,获取实例仍然是串行化操作。public static *synchronized*  LazySingleton getInstance() {if (singleton == null) {singleton = new LazySingleton ();}return singleton;}
}3.懒汉模式(双重加锁检查DCL)
public class LazySingleton {// 对保存实例的变量添加volatile的修饰,保证实例变量的可见性private static volatile  LazySingleton singleton = null;// 私有构造方法,控制实例无法在外部通过new创建实例private LazySingleton() {}public static LazySingleton getInstance() {// 先检查实例是否存在,如果不存在才进入下面的同步块,否则直接返回现有实例if (singleton == null) {// 同步块,线程安全的创建实例synchronized(LazySingleton .class){//再次检查实例是否存在,如果不存在才真的创建实例if(singleton == null){singleton = new LazySingleton ();}} }return singleton;}
}4.饿汉模式优点:饿汉模式天生是线程安全的,使用时没有延迟。缺点:启动时即创建实例,启动慢,有可能造成资源浪费。
public class Singleton {//直接在这里创建类实例,只会创建一次private static Singleton instance = new Singleton();//私有构造方法,控制实例无法在外部通过new创建实例private Singleton(){}//定义一个方法来为客户端提供类实例,这个方法需要定义成类方法,也就是要加staticpublic static Singleton getInstance(){//直接使用已经创建好的实例return instance;}
}5.静态内部类
public class Singleton {/*** 类级的内部类,也就是静态的成员式内部类,该内部类的实例与外部类的实例* 没有绑定关系,而且只有被调用到才会装载,从而实现了延迟加载*/private static class SingletonHolder{/*** 静态初始化器,由JVM来保证线程安全*/private static Singleton instance = new Singleton();}// 私有构造方法,控制实例无法在外部通过new创建实例private Singleton(){}public static  Singleton getInstance(){return SingletonHolder.instance;}
}
这种方式同样利用了classloder的机制来保证初始化instance时只有一个线程,而这种方式是Singleton类被装载了,
instance不一定被初始化。因为SingletonHolder类没有被主动使用,只有显示通过调用getInstance方法时,
才会显示装载SingletonHolder类,从而实例化instance。想象一下,如果实例化instance很消耗资源,我想让他延迟加载,
另外一方面,我不希望在Singleton类加载时就实例化,因为我不能确保Singleton类还可能在其他的地方被主动使用从而被加载,
那么这个时候实例化instance显然是不合适的。

三、工厂模式

在Spring框架中,工厂模式主要体现在以下几种形式:

  • 简单工厂模式(Simple Factory Pattern):简单工厂模式是最直观的一种工厂模式,它通过一个单独的工厂类来创建其他类的实例。在Spring中,BeanFactory和ApplicationContext接口就是简单工厂模式的体现。例如,当你调用ApplicationContext.getBean("beanName")来获取Bean时,Spring容器就会返回与"beanName"关联的Bean实例。
1. 首先,定义一个产品接口和具体的产品实现类:
public interface Product {void use();
}public class ConcreteProductA implements Product {@Overridepublic void use() {System.out.println("使用产品A");}
}public class ConcreteProductB implements Product {@Overridepublic void use() {System.out.println("使用产品B");}
}2. 然后,实现一个简单工厂类:
public class SimpleFactory {public Product createProduct(String type) {switch (type) {case "A":return new ConcreteProductA();case "B":return new ConcreteProductB();default:throw new IllegalArgumentException("未知的产品类型");}}
}3. 接下来,在Spring的配置文件中注册工厂类和产品:
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsd"><!-- 注册简单工厂 --><bean id="simpleFactory" class="com.example.SimpleFactory"/><!-- 注册产品A --><bean id="productA" class="com.example.ConcreteProductA"/><!-- 注册产品B --><bean id="productB" class="com.example.ConcreteProductB"/>
</beans>4. 最后,通过Spring的ApplicationContext来获取工厂类的实例,并使用它来创建产品:
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");// 获取工厂类
SimpleFactory factory = context.getBean("simpleFactory", SimpleFactory.class);// 创建并使用产品A
Product productA = factory.createProduct("A");
productA.use();// 创建并使用产品B
Product productB = factory.createProduct("B");
productB.use();
  • 工厂方法模式(Factory Method Pattern):工厂方法模式是通过定义一个用于创建对象的接口,让子类决定要实例化哪一个类。在Spring中,FactoryBean接口就是工厂方法模式的应用。FactoryBean接口要求实现该类的实例必须是一个单例,并且这个实例本身也是被容器管理的Bean。
1. 首先,定义一个产品接口和具体的产品实现类:
public interface Product {void show();
}public class ProductA implements Product {@Overridepublic void show() {System.out.println("这是产品A");}
}2. 接着,创建一个FactoryBean实现类:
public class ProductFactory implements FactoryBean<Product> {@Overridepublic Product getObject() throws Exception {// 返回产品实例return new ProductA();}@Overridepublic Class<?> getObjectType() {// 返回产品的类类型return Product.class;}@Overridepublic boolean isSingleton() {// 返回true表示单例return true;}
}3. 接下来,在Spring的配置文件中注册FactoryBean:
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsd"><!-- 注册FactoryBean --><bean id="productFactory" class="com.example.ProductFactory"/><!-- 当请求Product类型的Bean时,将会从productFactory中获取 --><bean id="product" class="org.springframework.beans.factory.config.ListFactoryBean"><property name="targetBeanName" value="productFactory"/></bean>
</beans>4. 最后,通过Spring的ApplicationContext获取由FactoryBean创建的产品对象:
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");// 获取由FactoryBean创建的产品对象
Product product = context.getBean("product", Product.class);
product.show();
  • 抽象工厂模式:抽象工厂模式提供一个接口来创建一系列相关或依赖对象,而不需要指定它们具体的类。在Spring中,可以使用AbstractApplicationContext来创建不同类型的ApplicationContext,每个ApplicationContext可以针对特定的应用程序需求进行配置。
1. 首先,定义一个产品接口和几种产品的具体实现类:
public interface Product {String use();
}public class ProductA implements Product {@Overridepublic String use() {return "使用产品A";}
}public class ProductB implements Product {@Overridepublic String use() {return "使用产品B";}
}2. 然后,创建一个抽象工厂接口和具体的工厂实现类:
public interface AbstractFactory {Product createProductA();Product createProductB();
}public class ConcreteFactoryA implements AbstractFactory {@Overridepublic Product createProductA() {return new ProductA();}@Overridepublic Product createProductB() {return new ProductB();}
}public class ConcreteFactoryB implements AbstractFactory {@Overridepublic Product createProductA() {return new ProductA();}@Overridepublic Product createProductB() {return new ProductB();}
}3. 接下来,在Spring的配置文件中使用组件扫描和自动装配来创建具体的工厂实例:
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:context="http://www.springframework.org/schema/context"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsd"><!-- 激活注解扫描 --><context:component-scan base-package="com.example"/><!-- 定义一个Map,用于存放不同工厂的实例 --><util:map id="factories" map-class="java.util.HashMap"><entry key="FactoryA" value-ref="concreteFactoryA"/><entry key="FactoryB" value-ref="concreteFactoryB"/></util:map>
</beans>4. 最后,在代码中使用Spring的ApplicationContext来获取具体的工厂实例,并通过工厂实例创建产品:
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");// 获取工厂实例
AbstractFactory factory = (AbstractFactory) context.getBean("FactoryA");// 创建产品
Product productA = factory.createProductA();
Product productB = factory.createProductB();// 使用产品
System.out.println(productA.use());
System.out.println(productB.use());

四、代理模式

在Spring框架中,代理模式是一种非常重要的设计模式,它被广泛应用于AOP(面向切面编程)和事务管理等方面。Spring的代理模式主要分为两大类:静态代理和动态代理。

  • 静态代理:是在编译时就已经确定好的代理对象,它需要手动编写一个代理类来实现目标类的接口或者继承目标类。这种方式比较简单,但是不够灵活,因为每次增加新的业务逻辑都需要修改代理类的代码。
  • 动态代理:是在运行时动态生成的代理对象,它通过反射机制在运行时动态创建一个实现了目标接口的代理类。Spring主要使用JDK动态代理和CGLIB代理两种方式来实现动态代理。
    • JDK动态代理:JDK动态代理只能代理实现了接口的类,它通过字节码技术在运行时动态地生成一个子类,这个子类实现了所有的接口,并在内部持有目标对象的引用。当调用接口方法时,代理对象会截取方法调用并执行相关的逻辑。
    • CGLIB代理:CGLIB是一个第三方的字节码处理库,它可以在运行时扩展Java类和实现Java接口。与JDK动态代理相比,CGLIB可以代理没有实现接口的类,它通过继承目标类并覆盖其方法来实现代理。

以下是一个基于Spring AOP的代理模式实现样例:

1. 首先,我们定义一个目标接口:
public interface Service {void execute();
}2. 然后,创建实现该接口的目标类:
public class ServiceImpl implements Service {@Overridepublic void execute() {System.out.println("执行业务操作");}
}3. 接下来,定义一个切面类,用于拦截目标类的方法调用:
@Aspect
@Component
public class LoggingAspect {@Before("execution(* com.example.Service.*(..))")public void logBeforeService() {System.out.println("方法执行前的日志记录");}
}4. 然后,在Spring配置文件或使用注解配置中,开启AOP支持并定义切面组件:
@Configuration
@EnableAspectJAutoProxy // 启用基于注解的自动代理
public class AppConfig {
}5. 最后,在主应用程序中使用目标对象:
public class MainApp {public static void main(String[] args) {ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);// 获取代理对象Service service = context.getBean(Service.class);// 调用方法,此时会触发切面的逻辑service.execute();}
}

五、观察者模式

在Spring框架中,观察者模式通常是通过事件处理机制来实现的,这是观察者模式的一种应用。在Spring中,可以通过定义事件和监听器来实现观察者模式。通过Spring的事件发布/订阅机制,可以实现解耦的观察者模式,使得事件的发布者和监听器之间不需要直接的引用关系。以下是Spring观察者模式的一个简单实现示例:

1. 首先定义一个事件类:
public class MyEvent extends ApplicationEvent {private final String message;public MyEvent(Object source, String message) {super(source);this.message = message;}public String getMessage() {return message;}
}2. 接着,创建一个监听器接口:
public interface MyEventListener extends ApplicationListener<MyEvent> {void onApplicationEvent(MyEvent event);
}3. 然后实现监听器接口:
@Component
public class MyEventListenerImpl implements MyEventListener {@Overridepublic void onApplicationEvent(MyEvent event) {System.out.println("接收到事件: " + event.getMessage());}
}4. 在Spring配置中,需要注册事件监听器:
@Configuration
public class EventConfig {public EventConfig(MyEventListener myEventListener) {SimpleApplicationEventMulticaster eventMulticaster = new SimpleApplicationEventMulticaster();eventMulticaster.addApplicationListener(myEventListener);// 设置为Spring应用上下文中的事件发布者((AbstractApplicationContext) myEventListener).setEventMulticaster(eventMulticaster);}
}5. 最后,在需要触发事件的地方发布事件:
@Component
public class MyEventPublisher {private final ApplicationEventPublisher publisher;@Autowiredpublic MyEventPublisher(ApplicationEventPublisher publisher) {this.publisher = publisher;}public void publishEvent() {MyEvent event = new MyEvent(this, "Hello, Spring!");publisher.publishEvent(event);}
}

六、模板方法模式

在Spring框架中,模板方法模式是一种行为设计模式,它在抽象类中定义了算法的骨架,将一些步骤延迟到子类中实现。通过这种方式,Spring框架的模板方法模式允许子类在不改变算法结构的前提下,重写算法中的某些步骤,从而实现代码复用和灵活性。Spring框架中的很多组件都采用了模板方法模式,例如JdbcTemplateHibernateTemplate等。以下是一个简单的模板方法模式的实现示例:

1. 首先,定义一个抽象类,其中包含模板方法和一些钩子方法(hook methods):
public abstract class AbstractTemplate {// 模板方法,定义算法的骨架public final void templateMethod() {beforeHook();doStep();afterHook();}// 钩子方法1,子类可以重写该方法以提供具体实现protected void beforeHook() {// 默认实现,可以被覆盖}// 钩子方法2,子类可以重写该方法以提供具体实现protected abstract void doStep();// 钩子方法3,子类可以重写该方法以提供具体实现protected void afterHook() {// 默认实现,可以被覆盖}
}2. 然后,创建子类继承抽象类,并重写钩子方法:
public class ConcreteClass extends AbstractTemplate {@Overrideprotected void doStep() {// 实现具体步骤System.out.println("执行具体步骤");}
}3. 最后,使用抽象类的模板方法调用子类的具体实现:
public class Client {public static void main(String[] args) {AbstractTemplate template = new ConcreteClass();template.templateMethod();}
}

七、适配器模式

八、装饰器模式

九、策略模式

十、建造者模式

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/755400.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

zynq7000 neon 裸机编译

最近想用zynq7000跑下neon&#xff0c;所以就开始折腾。 因为ne10不能用在裸机上&#xff0c;所以盯上了cmsis_dsp。因为这个库可以在cortex-a和cortex-m上面跑&#xff0c;而zynq7000用的是cortex-a9。应该是可以跑的。在这里特别感谢大佬的文章&#xff0c;少走不少弯路。 …

android api 34 编译ffmpeg with libfdk-aac

利用ffmpeg-android-maker编译&#xff0c;地址Javernaut/ffmpeg-android-maker: Contains a script that assembles FFmpeg library for Android (github.com)libfdk-aac编译脚本 #!/bin/bash NDK$ANDROID_NDK_HOME # 这里需要替换成你本地的 NDK 路径 HOST_TAGlinux-x86_64…

Cinema 4D 2024 for mac/Win:开启三维动画与建模新纪元

在数字化时代&#xff0c;三维动画与建模已成为影视、游戏、广告等多个领域不可或缺的创作工具。而Cinema 4D&#xff0c;作为这一领域的佼佼者&#xff0c;始终以其卓越的性能和创新的功能引领着行业的发展。如今&#xff0c;Cinema 4D 2024的发布&#xff0c;更是为我们带来了…

键盘映射工具KeyTweak的使用,把F9和F10改为 Home、End

如果你的笔记本没有Home、End键 对于写文字和写代码影响还是比较大的 下面使用键盘映射工具KeyTweak 把F9和F10分别改为 Home、End 然后点击ok 电脑重启后 就生效了 很好用 完美解决 小尺寸笔记本 的按键少的烦恼 可以自己再琢磨琢磨 去映射 符合自己需求的按键 软件下载链接&…

全屏时框架的message alert 下拉框失效问题

在工作中将#app div设为全屏后&#xff0c;elementUI的message和alert和下拉框展开都不见了&#xff0c;解决方法是将设为全屏 // 全屏 function fullScreen() { let el document.documentElement; //之前是#app // console.log(el.requestFullscreen); let refs el.request…

【早鸟优惠|高录用|EI稳定检索】2024年虚拟现实、图像和信号处理国际学术会议(ICVISP 2024)诚邀投稿/参会!

【早鸟优惠|高录用|EI稳定检索】 2024年虚拟现实、图像和信号处理国际学术会议&#xff08;ICVISP 2024&#xff09;诚邀投稿/参会&#xff01; # 早鸟优惠 # 先投稿先送审 # #投稿免费参会、口头汇报及海报展示# 2024年虚拟现实、图像和信号处理国际学术会议&#xff08;I…

Golang案例开发之gopacket抓包入门(1)

文章目录 前言一、gopacket是什么&#xff1f;二、gopacket前提二、实践 枚举主机上网络设备的接口1、代码2、结果文档 总结 前言 说起网络抓包&#xff0c;我们一定听过大名鼎鼎的Tcpdump和WireShark。他们分别是Linux平台和Windows平台的抓包工具。 Wireshark抓包工具&…

杉德支付配合调查 - 数字藏品服务

最近&#xff0c;数字收藏品平台淘派发布了一则公告&#xff0c;宣布支付通道杉德已暂停接口服务&#xff0c;以配合调查。 近期发现多个异常账户&#xff0c;涉嫌盗取他人信息和银行卡&#xff0c;利用平台从事非法交易。淘派已第一时间报警&#xff0c;协助警方追回资金(回执…

idea maven 项目融合

背景 &#xff1a;项目A 和项目B 是两个独立的多模块项目&#xff0c;项目A 和项目B &#xff0c;均为独立的数据源 。其中项目B 有两个数据原。 需要将项目B 以多模块的方式融合进项目A。 解决版本。建立项目C&#xff0c;只含有pom的&#xff0c;空项目&#xff0c;项目A和项…

RPM与DNF的操作实践

这几课有三个目标&#xff1a; 第一步&#xff1a;先配置软件源 跳转到yum.repos.d目录&#xff0c;用vim创建一个openeuler_x84_64.repo文件。这个文件就是我们将会用到的软件源。 我们在里面添加这些东西&#xff0c;保存并退出即可。 然后&#xff0c;我们用yum list all就…

深入理解nginx的请求限速模块[下]

目录 3. 源码分析3.1 配置指令3.1.1 limit_req_zone指令3.1.2 limit_req指令3.1.3 limit_req_dry_run指令3.1.4 limit_req_log_level指令3.1.5 limit_req_status指令3.2 模块初始化3.3 请求处理3.3.1 ngx_http_limit_req_handler3.3.1 ngx_http_limit_req_lookup3.3.2 ngx_http…

阿里云发布 AI 编程助手 “通义灵码”——VSCode更强了 !!

文章目录 什么是 通义灵码&#xff08;TONGYI Lingma&#xff09; 快速体验“通义灵码” 什么是“通义灵码”&#xff08;TONGYI Lingma&#xff09; 通义灵码&#xff08;TONGYI Lingma&#xff09;&#xff0c;是阿里云出品的一款基于通义大模型的智能编码辅助工具&#xff…

C语言字符函数和字符串函数以及内存函数(全是代码版):一篇文章让你秒懂基础!

JAMES别扣了-CSDN博客&#xff08;个人主页&#xff09; &#x1f495;在校大学生一枚。对IT有着极其浓厚的兴趣 ✨系列专栏目前为C语言初阶、后续会更新c语言的学习方法以及c题目分享. &#x1f60d;希望我的文章对大家有着不一样的帮助&#xff0c;欢迎大家关注我&#xff0c…

【Java常用API】正则表达式练习

&#x1f36c; 博主介绍&#x1f468;‍&#x1f393; 博主介绍&#xff1a;大家好&#xff0c;我是 hacker-routing &#xff0c;很高兴认识大家~ ✨主攻领域&#xff1a;【渗透领域】【应急响应】 【Java】 【VulnHub靶场复现】【面试分析】 &#x1f389;点赞➕评论➕收藏 …

【TypeScript系列】混入

混入 Table of contents 介绍 混入示例 理解示例 介绍 ↥ 回到顶端 除了传统的面向对象继承方式&#xff0c;还流行一种通过可重用组件创建类的方式&#xff0c;就是联合另一个简单类的代码。 你可能在Scala等语言里对mixins及其特性已经很熟悉了&#xff0c;但它在Java…

Excel新函数TEXTJOIN太强大了,这些高级用法太实用了

今天跟大家分享WPS中新函数TEXTJOIN的使用方法和技巧&#xff0c;它不仅仅是一个强大的文本连接函数&#xff0c;还有一些高级用法可以帮助我们快速解决日常难题。 TEXTJOIN函数介绍 作用&#xff1a;TEXTJOIN函数是文本连接函数&#xff0c;使用分隔符连接列表或文本字符串区…

【C++】手撕AVL树

> 作者简介&#xff1a;დ旧言~&#xff0c;目前大二&#xff0c;现在学习Java&#xff0c;c&#xff0c;c&#xff0c;Python等 > 座右铭&#xff1a;松树千年终是朽&#xff0c;槿花一日自为荣。 > 目标&#xff1a;能直接手撕AVL树。 > 毒鸡汤&#xff1a;放弃自…

数媒大厦会议中心 成都数字产业园示范基地

数媒大厦会议中心&#xff0c;位于成都市金牛区国际数字影像产业园3楼区域&#xff0c;这里也是成都数字产业园示范基地的核心区域。该成都文创产业园及辐射周边配套公园用地约500亩&#xff0c;涵盖产业实训空间、产业实验室、数字资产交易平台、产业集群发展空间、双创孵化空…

工业智能网关的功能特点、应用及其对企业产生的价值-天拓四方

一、工业智能网关的功能特点 工业智能网关是一种具备数据采集、传输、处理能力的智能设备&#xff0c;它能够将工业现场的各种传感器、执行器、控制器等设备连接起来&#xff0c;实现设备间的信息互通与协同工作。同时&#xff0c;工业智能网关还具备强大的数据处理能力&#…

PR是啥?一篇文章学会Pull Request到底是干嘛的

PR&#xff1f;Pull Request 概念 PR,全称Pull Request&#xff08;拉取请求&#xff09;&#xff0c;是一种非常重要的协作机制&#xff0c;它是 Git 和 GitHub 等代码托管平台中常见的功能。在开源项目中&#xff0c;Pull Request 被广泛用于参与社区贡献&#xff0c;从而促…