基于Spring实现策略模式

背景:

  看多很多策略模式,总结下来实现原理大体都差不多,在这里主要是讲解下自己基于Spring更优雅的实现方案;这个方案主要是看了一些开源rpc和Spring相关源码后的一些思路,所以在此进行总结

首先看下比较常见的策略模式写法

  • 一个接口或者抽象类,里面两个方法(一个方法匹配类型,一个可替换的逻辑实现方法)
  • 不同策略的差异化实现(就是说,不同策略的实现类)
  • 使用策略模式

1.3.1 一个接口,两个方法

public interface IFileStrategy {  //属于哪种文件解析类型 FileTypeResolveEnum gainFileType();  //封装的公用算法(具体的解析方法) void resolve(Object objectparam);}

1.3.2 不同策略的差异化实现

A 类型策略具体实现

@Componentpublic class AFileResolve implements IFileStrategy {  @Override public FileTypeResolveEnum gainFileType() {  return FileTypeResolveEnum.File_A_RESOLVE; } @Override public void resolve(Object objectparam) {  logger.info("A 类型解析文件,参数:{}",objectparam);  //A类型解析具体逻辑 }}

B 类型策略具体实现

@Componentpublic class BFileResolve implements IFileStrategy {  @Override public FileTypeResolveEnum gainFileType() {  return FileTypeResolveEnum.File_B_RESOLVE; } @Override public void resolve(Object objectparam) {  logger.info("B 类型解析文件,参数:{}",objectparam);  //B类型解析具体逻辑 }}

默认类型策略具体实现

@Componentpublic class DefaultFileResolve implements IFileStrategy { @Override public FileTypeResolveEnum gainFileType() {  return FileTypeResolveEnum.File_DEFAULT_RESOLVE; } @Override public void resolve(Object objectparam) {  logger.info("默认类型解析文件,参数:{}",objectparam);  //默认类型解析具体逻辑 }}

1.3.3 使用策略模式

扫描二维码关注公众号,回复: 14318315 查看本文章

如何使用呢?我们借助spring的生命周期,使用ApplicationContextAware接口,把对用的策略,初始化到map里面。然后对外提供resolveFile方法即可。

/** * */@Componentpublic class StrategyUseService implements ApplicationContextAware{  private Map<FileTypeResolveEnum, IFileStrategy> iFileStrategyMap = new ConcurrentHashMap<>(); public void resolveFile(FileTypeResolveEnum fileTypeResolveEnum, Object objectParam) {  IFileStrategy iFileStrategy = iFileStrategyMap.get(fileTypeResolveEnum);  if (iFileStrategy != null) {   iFileStrategy.resolve(objectParam);  } } //把不同策略放到map @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {  Map<String, IFileStrategy> tmepMap = applicationContext.getBeansOfType(IFileStrategy.class);  tmepMap.values().forEach(strategyService -> iFileStrategyMap.put(strategyService.gainFileType(), strategyService)); }}


基于Spring服务策略实现

      稍微了解过Spring源码都知道,在Spring里面我们定义好的bean被@Autowired修饰后,实际这个bean是被Spring进行了统一管理,当需要调用的时候实际是从Spring工厂里拿到这个bean;所以大致思路就是在如何拿到bean之前注入一个代理类,让代理类根据元数据的一些自定义规则后去组装成一个能从Spring里拿到实际的bean元素;基于以上的思路进行编码如下

注解定义

  • 自定义一个@RouteBizService注解(作用可以理解为@Autowired)
@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})@Retention(RetentionPolicy.RUNTIME)public @interface RouteBizService { String serviceName();}

  • 自定义一个@RouteBizParam参数注解,用于给代理类组装实际beanName
@Target(ElementType.PARAMETER)@Retention(RetentionPolicy.RUNTIME)public @interface RouteBizParam {}

  • 定义一个代理类:RouteServiceProxy
/** * */package com.gitee.adapter.proxy;import org.springframework.context.ApplicationContext;import java.lang.annotation.Annotation;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;public class RouteServiceProxy<T> implements InvocationHandler{ private String serviceName; private ApplicationContext context; public RouteServiceProxy(String serviceName, ApplicationContext context) {  this.serviceName = serviceName;  this.context = context; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {  String routeCode = null;  Annotation[ /* 参数个数索引 */][ /* 注解个数索引 */ ] paramsAnno = method.getParameterAnnotations();  if (paramsAnno != null) {   for (int i = 0; i < paramsAnno.length; i++) {    if (paramsAnno[i].length > 0) {     routeCode = (String) args[i]; // 获取到路由的参数值     break;    }   }  }  return method.invoke(context.getBean(genBeanName(routeCode, serviceName)), args); } /**  *  * @param sellerCode 用于区分是哪个Service 编码  * @param interfaceSimpleName 服务接口  * @return  */ private String genBeanName(String sellerCode, String interfaceSimpleName) {  return new StringBuilder(sellerCode.toLowerCase()).append(interfaceSimpleName).toString(); }}


View Code

  •  基于BeanFactoryPostProcessor 定义一个用于扫描 @RouteBizService修饰的实现类,该类的作用是为了注入代理类
package com.gitee.adapter.spring;import com.gitee.adapter.annation.RouteBizService;import com.gitee.adapter.proxy.RouteServiceProxy;import org.springframework.beans.BeansException;import org.springframework.beans.factory.config.BeanDefinition;import org.springframework.beans.factory.config.BeanFactoryPostProcessor;import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;import org.springframework.context.ApplicationContext;import org.springframework.context.ApplicationContextAware;import org.springframework.core.annotation.AnnotationUtils;import org.springframework.util.ClassUtils;import org.springframework.util.ReflectionUtils;import java.lang.reflect.Proxy;/** * @Classname BizRouteServiceProcessor * @Description bean 后置处理器 获取所有bean * 判断bean字段是否被 {@link com.gitee.adapter.annation.RouteBizService } 注解修饰 */public class BizRouteServiceProcessor implements BeanFactoryPostProcessor, ApplicationContextAware { private ApplicationContext applicationContext; @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {  for (String beanDefinitionName : beanFactory.getBeanDefinitionNames()) {   BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanDefinitionName);   String beanClassName = beanDefinition.getBeanClassName();   if (beanClassName != null) {    Class<?> clazz = ClassUtils.resolveClassName(beanClassName, this.getClass().getClassLoader());    ReflectionUtils.doWithFields(clazz, field -> {     RouteBizService routeBizService = AnnotationUtils.getAnnotation(field, RouteBizService.class);     if (routeBizService != null) {      Object bean = applicationContext.getBean(clazz);      field.setAccessible(true);      // 修改为代理对象      ReflectionUtils.setField(field, bean,        Proxy.newProxyInstance(field.getType().getClassLoader(), new Class[] { field.getType() }, new RouteServiceProxy(routeBizService.serviceName(),this.applicationContext)));     }    });   }  } } @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {  this.applicationContext = applicationContext; }}


View Code

测试

  

环境搭建

  • 操作系统:Windows
  • 集成开发工具:IntelliJ IDEA 2021
  • 项目技术栈:SpringBoot 2.2.11 + JDK 1.8 
  • 项目依赖管理工具:Maven 4.0.0

小伙伴们有兴趣想了解内容和更多相关学习资料的请点赞收藏+评论转发+关注我,后面会有很多干货。
我有一些面试题、架构、设计类资料可以说是程序员面试必备!所有资料都整理到网盘了,需要的话欢迎下载!私信我回复【07】即可免费获取

 

原文出处:www.shaoqun.com/a/1437729.html

猜你喜欢

转载自blog.csdn.net/wadfdhsajd/article/details/125339171