基于netty框架 自定注解+strategy策略设计模式 解决

作者: 白沙forever | 来源:发表于2020-04-03 11:27 被阅读0次

    netty项目中添加strategy策略模式,来实现接收websocket指令码,处理对应的聊天软件app业务流程 例如单聊、群聊
    采用自定义注解的方式将指令码对应到策略实现 。

    指令枚举类

    
    /**
     * @Description ws请求类型
     */
    public enum WsRequestPathEnum {
    
        /**
         * 单聊
         */
        SINGLE(1),
    
        /**
         * 加密群聊
         */
        GROUP_ENCTYPT(2),
    
        /**
         * 普通群聊
         */
        GROUP(3),
    
        /**
         * 获取所有离线消息
         */
        All_OFFLINE_MSG(4);
    
    
        private final int uriCode;
    
        WsRequestPathEnum(int uriCode) {
            this.uriCode = uriCode;
        }
    
        public int getUriCode() {
            return uriCode;
        }
    
        /**
         * 根据uriCode获取
         *
         * @param uriCode
         * @return
         */
        public static WsRequestPathEnum getByCode(int uriCode) {
            for (WsRequestPathEnum wsRequestUriPathEnum : values()) {
                if (wsRequestUriPathEnum.getUriCode() == uriCode) {
                    return wsRequestUriPathEnum;
                }
            }
            return null;
        }
    }
    
    

    策略抽象类

    /**
     * @Description 接收netty不同类型请求
     * 抽象类 策略设计模式
     */
    public abstract class ReceiveAbstractStrategy {
    
        /**
         * 处理业务流程
         *
         * @param requestModel
         * @param user
         * @param language
         * @throws Exception
         */
        abstract public void process(ReceiveModel requestModel, User user, String language) throws Exception;
    }
    
    

    策略模式 上下文
    维护指令码与策略实现的对应

    public class ReceiveStrategyContext {
    
        private Map<WsRequestPathEnum, Class> strategyMap;
    
        public ReceiveStrategyContext(Map<WsRequestPathEnum, Class> strategyMap) {
            this.strategyMap = strategyMap;
        }
    
        public ReceiveAbstractStrategy getStrategy(WsRequestPathEnum wsRequestPathEnum) {
    
            if (wsRequestPathEnum == null) {
                throw new IllegalArgumentException("not fond enum");
            }
    
            if (CollectionUtils.isEmpty(strategyMap)) {
                throw new IllegalArgumentException("strategy map is empty,please check you strategy package path");
            }
    
            Class aClass = strategyMap.get(wsRequestPathEnum);
    
            if (aClass == null) {
                throw new IllegalArgumentException("not fond strategy for type:" + wsRequestPathEnum.getUriCode());
            }
    
            return (ReceiveAbstractStrategy) SpringBeanUtils.getBean(aClass);
        }
    }
    
    
    

    策略 bean注解扫描注册类

    /**
     * @Description
     * 扫描自定义注解,将指令码与实现类绑定,将对应关系添加到上下文对象中
     * <p>
     * BeanStrategyProcessor是spring在容器初始化后对外暴露的扩展点,
     * spring ioc容器允许beanFactoryPostProcessor在容器加载注册BeanDefinition后读取BeanDefinition,并能修改它。
     */
    @Component
    public class ReceiveStrategyProcessor implements BeanFactoryPostProcessor {
    
        // 扫码注解的包路径
        private static final String STRATEGY_PACK = "com.xx.strategy";
    
        @Override
        public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
            Map<WsRequestPathEnum, Class> handlerMap = Maps.newHashMapWithExpectedSize(5);
    
            // 扫码ReceiveTypeAnnotation注解的类
            Set<Class<?>> classSet = ClassScanner.scan(STRATEGY_PACK, ReceiveTypeAnnotation.class);
    
            classSet.forEach(clazz -> {
                // 获取注解中的类型值,与枚举类一一对应
                WsRequestPathEnum type = clazz.getAnnotation(ReceiveTypeAnnotation.class).type();
                handlerMap.put(type, clazz);
            });
    
    
            // 初始化Contenxt, 将其注册到spring容器当中
            ReceiveStrategyContext context = new ReceiveStrategyContext(handlerMap);
    
            try {
                configurableListableBeanFactory.registerResolvableDependency(Class.forName(ReceiveStrategyContext.class.getName()), context);
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
        }
    }
    
    
    

    注解扫描工具类

    
    import org.apache.commons.lang3.ArrayUtils;
    import org.springframework.beans.factory.BeanDefinitionStoreException;
    import org.springframework.context.ResourceLoaderAware;
    import org.springframework.core.io.Resource;
    import org.springframework.core.io.ResourceLoader;
    import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
    import org.springframework.core.io.support.ResourcePatternResolver;
    import org.springframework.core.io.support.ResourcePatternUtils;
    import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
    import org.springframework.core.type.classreading.MetadataReader;
    import org.springframework.core.type.classreading.MetadataReaderFactory;
    import org.springframework.core.type.filter.AnnotationTypeFilter;
    import org.springframework.core.type.filter.TypeFilter;
    import org.springframework.util.StringUtils;
    import org.springframework.util.SystemPropertyUtils;
    
    import java.io.IOException;
    import java.lang.annotation.Annotation;
    import java.util.HashSet;
    import java.util.LinkedList;
    import java.util.List;
    import java.util.Set;
    
    
    /**
     * @Description 扫描注解 工具类
     * @Author hewei hwei1233@163.com
     * @Date 2020-01-03
     */
    public class ClassScanner implements ResourceLoaderAware {
    
        private final List<TypeFilter> includeFilters = new LinkedList<TypeFilter>();
        private final List<TypeFilter> excludeFilters = new LinkedList<TypeFilter>();
    
        private ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
        private MetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory(this.resourcePatternResolver);
    
    
        @SafeVarargs
        public static Set<Class<?>> scan(String[] basePackages, Class<? extends Annotation>... annotations) {
            ClassScanner cs = new ClassScanner();
    
            if (ArrayUtils.isNotEmpty(annotations)) {
                for (Class anno : annotations) {
                    cs.addIncludeFilter(new AnnotationTypeFilter(anno));
                }
            }
    
            Set<Class<?>> classes = new HashSet<>();
            for (String s : basePackages) {
                classes.addAll(cs.doScan(s));
            }
    
            return classes;
        }
    
        @SafeVarargs
        public static Set<Class<?>> scan(String basePackages, Class<? extends Annotation>... annotations) {
            return ClassScanner.scan(StringUtils.tokenizeToStringArray(basePackages, ",; \t\n"), annotations);
        }
    
        public final ResourceLoader getResourceLoader() {
            return this.resourcePatternResolver;
        }
    
        @Override
        public void setResourceLoader(ResourceLoader resourceLoader) {
            this.resourcePatternResolver = ResourcePatternUtils
                    .getResourcePatternResolver(resourceLoader);
            this.metadataReaderFactory = new CachingMetadataReaderFactory(
                    resourceLoader);
        }
    
        public void addIncludeFilter(TypeFilter includeFilter) {
            this.includeFilters.add(includeFilter);
        }
    
        public void addExcludeFilter(TypeFilter excludeFilter) {
            this.excludeFilters.add(0, excludeFilter);
        }
    
        public void resetFilters(boolean useDefaultFilters) {
            this.includeFilters.clear();
            this.excludeFilters.clear();
        }
    
        public Set<Class<?>> doScan(String basePackage) {
            Set<Class<?>> classes = new HashSet<>();
            try {
                String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX
                        + org.springframework.util.ClassUtils
                        .convertClassNameToResourcePath(SystemPropertyUtils
                                .resolvePlaceholders(basePackage))
                        + "/**/*.class";
                Resource[] resources = this.resourcePatternResolver
                        .getResources(packageSearchPath);
    
                for (int i = 0; i < resources.length; i++) {
                    Resource resource = resources[i];
                    if (resource.isReadable()) {
                        MetadataReader metadataReader = this.metadataReaderFactory.getMetadataReader(resource);
                        if ((includeFilters.size() == 0 && excludeFilters.size() == 0) || matches(metadataReader)) {
                            try {
                                classes.add(Class.forName(metadataReader
                                        .getClassMetadata().getClassName()));
                            } catch (ClassNotFoundException e) {
                                e.printStackTrace();
                            }
                        }
                    }
                }
            } catch (IOException ex) {
                throw new BeanDefinitionStoreException(
                        "I/O failure during classpath scanning", ex);
            }
            return classes;
        }
    
        protected boolean matches(MetadataReader metadataReader) throws IOException {
            for (TypeFilter tf : this.excludeFilters) {
                if (tf.match(metadataReader, this.metadataReaderFactory)) {
                    return false;
                }
            }
            for (TypeFilter tf : this.includeFilters) {
                if (tf.match(metadataReader, this.metadataReaderFactory)) {
                    return true;
                }
            }
            return false;
        }
    
    }
    
    
    

    处理app单聊消息具体策略实现

    
    @ReceiveTypeAnnotation(type = WsRequestPathEnum.SINGLE)
    @Service
    public class SingleConcreteStrategy extends ReceiveAbstractStrategy {
      
        @Override
        public void process(ReceiveModel requestModel, User user, String language) throws Exception {
      
      //具体业务代码
        }
    
    }
    
    

    工具类
    在非spring管理的类中获取spring注册的bean

    @Component
    public class SpringBeanUtils implements ApplicationContextAware {
    
        private static ApplicationContext applicationContext;
    
        @Override
        public void setApplicationContext(ApplicationContext context) throws BeansException {
    
            if (context != null) {
                applicationContext = context;
            }
    
        }
    
        public static Object getBean(String name) {
            return applicationContext.getBean(name);
        }
    
        public static <T> T getBean(Class<T> clazz) {
            return applicationContext.getBean(clazz);
        }
    }
    
    
    

    调用示例

        // idea此处报红 属于正常
        @SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection")
        @Autowired
        private ReceiveStrategyContext receiveStrategyContext;
    
        private void core(String language, ReceiveModel requestModel, User user) throws Exception {
    
            WsRequestPathEnum wsRequestUriPathEnum = WsRequestPathEnum.getByCode(requestModel.getPath());
            // 使用策略模式, 根据不同类型请求调用不同实现类
            ReceiveAbstractStrategy receiveStrategy = receiveStrategyContext.getStrategy(wsRequestUriPathEnum);
            receiveStrategy.process(requestModel, user, language);
    
        }
    
    
    

    相关文章

      网友评论

        本文标题:基于netty框架 自定注解+strategy策略设计模式 解决

        本文链接:https://www.haomeiwen.com/subject/cgfryhtx.html