工厂模式+枚举类的json序列化+redisson的使用

server/2025/2/8 20:53:42/

目录

这里分享以下工厂模式+反射+IoC容器+多态的妙用

场景引入

环境准备

代码实现 

1.设置枚举类来规定有哪些学习方式

2.设置作业的实体对象

3.获取学习方式的接口

4.进行学习的动作,有出题和搜题

spring%E7%AE%A1%E7%90%86-toc" name="tableOfContents" style="margin-left:80px">5.使用小猿搜题这种学习方式进行的两种学习动作学习,并把这两个处理器都作为bean交给spring管理

spring%E7%AE%A1%E7%90%86-toc" name="tableOfContents" style="margin-left:80px">​编辑 6.使用作业帮这种学习方式进行的两种学习动作学习,并把这两个处理器都作为bean交给spring管理

7.重点操作,工厂类对象静态方法的实现 

8.service操作

9.测试

好处

json%E5%BA%8F%E5%88%97%E5%8C%96%E5%92%8C%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96-toc" name="tableOfContents" style="margin-left:0px"> 枚举类的json序列化和反序列化

json%E5%BA%8F%E5%88%97%E5%8C%96-toc" name="tableOfContents" style="margin-left:40px">json序列化

1.默认情况下

2. 使用 @JsonValue 注解

json%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96-toc" name="tableOfContents" style="margin-left:40px">json反序列化

Redission的使用

 1.导入依赖

2.配置yml文件

3.编写配置类,维护一个RedissionClient的bean,用于操作Redisson的客户端

 4.开始操作


这里分享以下工厂模式+反射+IoC容器+多态的妙用

场景引入

现在举一个具体的例子,现在有两种学习方式,通过作业帮学习或者通过小猿搜题进行学习,进行搜题时可以选择其中一个应用进行搜题。

我们如何写这个程序呢,一般的方法肯定就是使用(if-else if)的方式进行判断使用哪一种应用进行学些,这是一种很简单的方式,但是如果我们又添加了几种学习方式呢,如大学搜题酱等,我们又得在原有代码上进行添加(else if)操作,这样就显得十分麻烦,且需要频繁的去修改原有的代码。

所以我们可以使用工厂模式,根据传入的某一种学习方式让工厂类直接帮我们进行选择。

环境准备

依赖,这里使用hutool工具包

    <dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId></dependency><!--hutool工具包--><dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>${hutool.version}</version></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional><version>${lombok.version}</version></dependency></dependencies>

代码实现 

 

1.设置枚举类来规定有哪些学习方式

java">/*** 这是一个学习方式的枚举类*/
public enum StudyTypeEnum {ZYB(1,"作业帮"),XYST(2,"小猿搜题");private Integer code;private String value;StudyTypeEnum(Integer code,String value){this.code=code;this.value=value;}public Integer getCode(){return this.code;}public String getValue() {return this.value;}
}

2.设置作业的实体对象

java">/*** 作业*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class HomeWorkDto {/*** 作业内容*/private String content;/*** 写作业的方式*/private StudyTypeEnum studyType;
}

3.获取学习方式的接口

java">public interface StudyTypeHandler {/*** 获取学习的方式,作业帮还是小猿搜题*/StudyTypeEnum getStudyType();
}

4.进行学习的动作,有出题和搜题

spring%E7%AE%A1%E7%90%86" name="5.%E4%BD%BF%E7%94%A8%E5%B0%8F%E7%8C%BF%E6%90%9C%E9%A2%98%E8%BF%99%E7%A7%8D%E5%AD%A6%E4%B9%A0%E6%96%B9%E5%BC%8F%E8%BF%9B%E8%A1%8C%E7%9A%84%E4%B8%A4%E7%A7%8D%E5%AD%A6%E4%B9%A0%E5%8A%A8%E4%BD%9C%E5%AD%A6%E4%B9%A0%EF%BC%8C%E5%B9%B6%E6%8A%8A%E8%BF%99%E4%B8%A4%E4%B8%AA%E5%A4%84%E7%90%86%E5%99%A8%E9%83%BD%E4%BD%9C%E4%B8%BAbean%E4%BA%A4%E7%BB%99spring%E7%AE%A1%E7%90%86">5.使用小猿搜题这种学习方式进行的两种学习动作学习,并把这两个处理器都作为bean交给spring管理

spring%E7%AE%A1%E7%90%86" name="%E2%80%8B%E7%BC%96%E8%BE%91%C2%A06.%E4%BD%BF%E7%94%A8%E4%BD%9C%E4%B8%9A%E5%B8%AE%E8%BF%99%E7%A7%8D%E5%AD%A6%E4%B9%A0%E6%96%B9%E5%BC%8F%E8%BF%9B%E8%A1%8C%E7%9A%84%E4%B8%A4%E7%A7%8D%E5%AD%A6%E4%B9%A0%E5%8A%A8%E4%BD%9C%E5%AD%A6%E4%B9%A0%EF%BC%8C%E5%B9%B6%E6%8A%8A%E8%BF%99%E4%B8%A4%E4%B8%AA%E5%A4%84%E7%90%86%E5%99%A8%E9%83%BD%E4%BD%9C%E4%B8%BAbean%E4%BA%A4%E7%BB%99spring%E7%AE%A1%E7%90%86"> 6.使用作业帮这种学习方式进行的两种学习动作学习,并把这两个处理器都作为bean交给spring管理

7.重点操作,工厂类对象静态方法的实现 

java">import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.extra.spring.SpringUtil;
import org.example.enums.StudyTypeEnum;import java.util.Map;public class HandlerFactory {private HandlerFactory(){}//私有化/*** 返回作者想要的处理器实例* @param studyType 学习的方式,作业帮或者小猿搜题* @param handler 处理器的类型,如有搜题处理器,出题处理器*/public static <T> T get(StudyTypeEnum studyType,Class<T>handler){//获取这个处理器类型的所有bean的实例,key为bean的名称,value为bean的实例化对象Map<String, T> beans = SpringUtil.getBeansOfType(handler);for (Map.Entry<String, T> entry : beans.entrySet()) {//使用反射机制,处理器实例对象来执行getStudyType方法Object studyType1 = ReflectUtil.invoke(entry.getValue(), "getStudyType");//找到与传入的学习方式一致的处理器并返回if(ObjectUtil.equal(studyType1,studyType)){return entry.getValue();}}return null;}public static <T>T get(String studyType,Class<T>handler){//根据字符串直接变成枚举类对象(需要保证名字一致)StudyTypeEnum studyTypeEnum = StudyTypeEnum.valueOf(studyType);return get(studyTypeEnum,handler);}
}

  Map<String, T> beans = SpringUtil.getBeansOfType(handler);

这个方法可以获取IoC容器中所有是handler类型的bean

key值为bean的名字,value值为bean的实例对象

例如,SouTiHandler类型的bean在IoC容器里面有两种xystSouTiHandler和zybSouTiHandler,我们就需要根据传入的学习方式选择其中一种并返回(多态)

Object studyType1 = ReflectUtil.invoke(entry.getValue(), "getStudyType");

这里使用hutool工具类,让bean实例对象执行获取学习方式的方法(反射的思想),然后进行比对就可以知道要使用哪一种学习方式

最后就可以返回具体的学习方式进行的学习操作的处理器(使用多态的思想,父类引用指向子类的实例对象)

8.service操作

java">@Service
public class StudentService {/*** 进行搜题操作*/public String souTiWork(HomeWorkDto homeWork){//获取对应搜题类型的处理器,作业帮或者小猿搜题SouTiHandler souTiHandler = HandlerFactory.get(homeWork.getStudyType(), SouTiHandler.class);String s = souTiHandler.souTi(homeWork);return s;}/*** 进行出题操作*/public String chuTiWork(HomeWorkDto homeWork){//获取对应出题类型的处理器ChuTiHandler chuTiHandler = HandlerFactory.get(homeWork.getStudyType(), ChuTiHandler.class);return chuTiHandler.chuTi(homeWork);}
}

9.测试

java">@SpringBootTest(classes = App.class)
public class Test1 {@Resourceprivate StudentService service;@Testpublic void test1(){//模拟前端传数据String content="1+1等于";String studyType="ZYB";StudyTypeEnum studyTypeEnum = StudyTypeEnum.valueOf(studyType);HomeWorkDto homeWork = HomeWorkDto.builder().content(content).studyType(studyTypeEnum).build();//进行搜题String s = service.souTiWork(homeWork);System.out.println(s);//进行出题s=service.chuTiWork(homeWork);System.out.println(s);}
}

好处

可以发现使用工厂模式虽然在前期准备可能有一点麻烦,但是如果需要持续的增加学习方式,我们并不需要在原有代码上进行修改,只需要在创建这种学习方式的处理器bean即可 

json%E5%BA%8F%E5%88%97%E5%8C%96%E5%92%8C%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96" name="%C2%A0%E6%9E%9A%E4%B8%BE%E7%B1%BB%E7%9A%84json%E5%BA%8F%E5%88%97%E5%8C%96%E5%92%8C%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96"> 枚举类的json序列化和反序列化

json%E5%BA%8F%E5%88%97%E5%8C%96" name="json%E5%BA%8F%E5%88%97%E5%8C%96">json序列化

1.默认情况下

无法使用hutool的JSONUtil,所以使用jackson依赖

java"><dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-databind</artifactId></dependency><dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-core</artifactId></dependency><dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-annotations</artifactId></dependency>
java">/*** 这是一个学习方式的枚举类*/
public enum StudyTypeEnum {ZYB(1,"作业帮"),XYST(2,"小猿搜题");private Integer code;private String value;StudyTypeEnum(Integer code,String value){this.code=code;this.value=value;}public Integer getCode(){return this.code;}public String getValue() {return this.value;}
}
java">  @Testpublic void test2(){ObjectMapper objectMapper= new ObjectMapper();try {String s = objectMapper.writeValueAsString(StudyTypeEnum.ZYB);System.out.println(s);//"ZYB"} catch (JsonProcessingException e) {throw new RuntimeException(e);}}

直接序列化成枚举实例的名称

2. 使用 @JsonValue 注解

希望枚举类序列化为某个特定字段的值,可以在枚举类中使用 @JsonValue 注解。

java">/*** 这是一个学习方式的枚举类*/
public enum StudyTypeEnum {ZYB(1,"作业帮"),XYST(2,"小猿搜题");private Integer code;private String value;StudyTypeEnum(Integer code,String value){this.code=code;this.value=value;}public Integer getCode(){return this.code;}@JsonValue//规定value值作为json序列化的内容public String getValue() {return this.value;}
}
java">    @Testpublic void test2(){ObjectMapper objectMapper= new ObjectMapper();try {String s = objectMapper.writeValueAsString(StudyTypeEnum.ZYB);System.out.println(s);//"作业帮"} catch (JsonProcessingException e) {throw new RuntimeException(e);}}

序列化成枚举实例的 value 字段值

json%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96" name="json%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96">json反序列化

 1.默认情况下

如果json字符串是"ZYB",Jackson 会将其反序列化为 StudyTypeEnum.ZYB实例

java">    @Testpublic void test3() throws IOException {ObjectMapper objectMapper = new ObjectMapper();String jsonString = "\"ZYB\"";StudyTypeEnum studyTypeEnum = objectMapper.readValue(jsonString, StudyTypeEnum.class);System.out.println(studyTypeEnum);}

 

2. 使用@JsonValue注解

java">@JsonValue//规定value值作为json序列化的内容public String getValue() {return this.value;}

json字符串"作业帮",Jackson 会将其反序列化为 StudyTypeEnum.ZYB实例

java">    @Testpublic void test3() throws IOException {ObjectMapper objectMapper = new ObjectMapper();String jsonString = "\"作业帮\"";StudyTypeEnum studyTypeEnum = objectMapper.readValue(jsonString, StudyTypeEnum.class);System.out.println(studyTypeEnum);}

Redission的使用

 1.导入依赖

<dependency><groupId>org.redisson</groupId><artifactId>redisson</artifactId><version>3.17.1</version></dependency>

2.配置yml文件

spring:redis: #redis的配置port: 192.168.150.10host: 6379password: 1234

3.编写配置类,维护一个RedissionClient的bean,用于操作Redisson的客户端

java">@Data
@Configuration
@EnableConfigurationProperties(RedisProperties.class)
public class RedissonConfiguration {@Resourceprivate RedisProperties redisProperties;@Beanpublic RedissonClient redissonSingle() {Config config = new Config();SingleServerConfig serverConfig = config.useSingleServer().setAddress("redis://" + redisProperties.getHost() + ":" + redisProperties.getPort());if (null != (redisProperties.getTimeout())) {//设置持有时间serverConfig.setTimeout(1000 * Convert.toInt(redisProperties.getTimeout().getSeconds()));}if (StrUtil.isNotEmpty(redisProperties.getPassword())) {//设置密码serverConfig.setPassword(redisProperties.getPassword());}//创建RedissonClientreturn Redisson.create(config);}}

这里开启的@EnableConfigurationProperties(RedisProperties.class)

RedisProperties类是spring提供的

 4.开始操作

redisson加的锁的value是一个hash结构,大key是我们自己定义的,用来区别不同的锁,而小key是生成的客户端uuid+线程id,value值就是加锁的次数

java">      //对交易订单加锁Long productOrderNo = tradingEntity.getProductOrderNo();String key = TradingCacheConstant.CREATE_PAY + productOrderNo;RLock lock = redissonClient.getFairLock(key);//设置大key,即锁的名字try {//获取锁if (lock.tryLock(10, TimeUnit.SECONDS)) {//非阻塞加锁,如果到指定时间还没获得锁,就抛异常(10s)}throw new MyException(TradingEnum.NATIVE_PAY_FAIL);//没加到锁就直接报错报错} catch (Exception e) {} finally {lock.unlock();//在finally语句块解锁,防止因为异常没有及时释放锁}

redisson在加锁的时候会给锁默认设置一个30秒过期的时间,那如果当前线程线程还没有执行结束怎么办,所以还有一个看门狗机制,这个机制会每隔10判断当前进程是否活着,如果或者就把过期时间重新变成30秒,以此反复操作,如果线程死掉,那么这个看门狗机制也会失效,所以锁到过期时间就会自动释放


http://www.ppmy.cn/server/166026.html

相关文章

VSCode使用总结

1、VSCode左边资源窗口字体大小设置 方法一&#xff08;使用&#xff0c;已成功&#xff09; 进入安装目录Microsoft VS Code\resources\app\out\vs\workbench(如果是下载的压缩包&#xff0c;解压后resources\app\out\vs\workbench) 打开文件 workbench.desktop.main.css 搜…

Android 系统的启动流程

第一步&#xff0c;当用户按下手机电源键时&#xff0c;首先会从 ROM 中预定义的地方加载引导程序 Boot Loader 到 ARM 中&#xff1b; ROM&#xff08;Ready-Only-Memory&#xff09;&#xff1a;只读存储器&#xff0c;只能读出无法写入&#xff0c;即便是切断电源数据也不会…

JDBC笔记

简介 JDBC简单执行过程&#xff1a; 总结Java提供接口&#xff1b;数据库厂商提供实现&#xff1b;程序员调用接口&#xff1b;接口调用实现类&#xff0c;连接操作数据库 JDBC的概念 JDBC是Java提供的一组独立于任何数据库管理系统的API。 java操作数据库 步骤&#xff1a…

一键掌握多平台短视频矩阵营销/源码部署

短视频矩阵系统的介绍与应用 随着数字化营销策略的不断演进&#xff0c;传统的短视频矩阵操作方法可能已显陈旧。为此&#xff0c;一款全新的短视频矩阵系统应运而生&#xff0c;它通过整合多个社交媒体账户、创建多样化的任务、运用先进的智能视频编辑工具、实现多平台内容的…

如何使用 Excel 进行多元回归分析?

多元回归分析&#xff0c;一种统计方法&#xff0c;用来探索一个因变量&#xff08;即结果变量&#xff09;与多个自变量&#xff08;即预测变量&#xff09;之间的关系。广泛用于预测、趋势分析以及因果关系的分析。 听起来这个方法很复杂&#xff0c;但其实在 Excel 中可以很…

Docker镜像管理:掌握save/load与export/import的精髓

0x01 Docker Save & Load docker save: 用于将Docker镜像保存为.tar文件。这个过程会保存镜像的所有层、元数据和历史记录&#xff0c;因此生成的文件较大。当你需要备份或者迁移某个镜像时非常有用&#xff0c;尤其是当你需要保留镜像的全部构建历史以便将来在其他地方恢复…

在 Ubuntu 22.04 上运行 Filebeat 7.10.2

环境 操作系统&#xff1a;阿里云 Ubuntu 22.04.3 LTS (GNU/Linux 5.15.0-83-generic x86_64) 软件版本&#xff1a;Filebeat 7.10.2 用户&#xff1a;root 运行 下载 从这里下载 filebeat 7.10.2。 配置 简单配置一下 filebeat.yml&#xff0c;从标准输入采集&#xf…

Vue(4)

一.组件的三大组成部分-注意点说明 &#xff08;1&#xff09;scoped样式冲突 默认情况&#xff1a;写在组件中的样式会全局生效 → 因此很容易造成多个组件之间的样式冲突 ①全局样式&#xff1a;默认组件中的样式会作用到全局 ②局部样式&#xff1a;可以给组件加上scoped属…