美文网首页技术分享
利用自定义注解装载策略

利用自定义注解装载策略

作者: 要不再等等 | 来源:发表于2019-09-21 23:43 被阅读0次

一般使用策略模式的时候,经常使用xml、enum或者map先将策略的以key-value的形式存储起来,然后配置一个策略选择器,根据传入的参数去找到具体的策略实现类。此种方法比较简单,确实是很多情况的首选,但是如果想减少配置,每次增加一个策略只需要写一个实现类,在实现类上标记下注解就结束了,此处推荐扫描自定义注解,项目启动时依赖spring加载计划,动态加载到策略的上下文。具体实现如下:

1.自定义注解

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface HandleType {
    String value();
}

2.自定义扫描器

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
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.ClassUtils;
import org.springframework.util.CollectionUtils;

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;
public class ClassScanner {
    private final Logger logger = LoggerFactory.getLogger(getClass());
    private static final String RESOURCE_PATTERN = "**/%s/**/*.class";
    private ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();

    public ClassScanner(){
    }

    @SafeVarargs
    public final Set<Class<?>> scan(String[] confPkgs, Class<? extends Annotation>... annotationTags){
        Set<Class<?>> resClazzSet = new HashSet<>();
        List<AnnotationTypeFilter> typeFilters = new LinkedList<>();
        if (annotationTags != null){
            for (Class<? extends Annotation> annotation : annotationTags) {
                typeFilters.add(new AnnotationTypeFilter(annotation, false));
            }
        }
        if (confPkgs != null && confPkgs.length > 0) {
            for (String pkg : confPkgs) {
                String pattern = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX  + String.format(RESOURCE_PATTERN, ClassUtils.convertClassNameToResourcePath(pkg));
                try {
                    Resource[] resources = this.resourcePatternResolver.getResources(pattern);
                    MetadataReaderFactory readerFactory = new CachingMetadataReaderFactory(this.resourcePatternResolver);
                    for (Resource resource : resources) {
                        if (resource.isReadable()) {
                            MetadataReader reader = readerFactory.getMetadataReader(resource);
                            String className = reader.getClassMetadata().getClassName();
                            if (ifMatchesEntityType(reader, readerFactory,typeFilters)) {
                                Class<?> curClass = Thread.currentThread().getContextClassLoader().loadClass(className);
                                resClazzSet.add(curClass);
                            }
                        }
                    }
                } catch (Exception e) {
                    logger.error(e.getMessage(), e);
                }
            }
        }
        return resClazzSet;
    }

    /**
     * 检查当前扫描到的类是否含有任何一个指定的注解标记
     * @param reader
     * @param readerFactory
     * @return ture/false
     */
    private boolean ifMatchesEntityType(MetadataReader reader, MetadataReaderFactory readerFactory,List<AnnotationTypeFilter> typeFilters) {
        if (!CollectionUtils.isEmpty(typeFilters)) {
            for (TypeFilter filter : typeFilters) {
                try {
                    if (filter.match(reader, readerFactory)) {
                        return true;
                    }
                } catch (IOException e) {
                    logger.error("过滤匹配类型时出错 {}",e.getMessage());
                }
            }
        }
        return false;
    }
}

3.利用spring后置处理器装载

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.stereotype.Component;

import java.util.HashMap;
import java.util.Map;
import java.util.Set;

@Component
public class HandleTypeAnnotationBeanPostProcessor implements BeanFactoryPostProcessor {


    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        ClassScanner classScanner = new ClassScanner();
        Set<Class<?>> classSet = classScanner.scan(new String[]{"demo.handler"}, HandleType.class);

        Map<String, Class> handleMap = new HashMap<>();
        for (Class clazz : classSet) {
            HandleType handleType = (HandleType) clazz.getAnnotation(HandleType.class);
            handleMap.put(handleType.value(), clazz);
        }
        HandlerContext handlerContext = new HandlerContext(handleMap);
        beanFactory.registerSingleton(HandlerContext.class.getName(), handlerContext);
    }
}

4.策略处理器上下文

import java.util.Map;

public class HandlerContext {

    private Map<String, Class> handlerMap;

    public HandlerContext(Map<String, Class> handlerMap) {
        this.handlerMap = handlerMap;
    }

    public HandlerContext() {
    }

    public AbstractRiskHandler getInstance(String type) {
        Class clazz = handlerMap.get(type);

        return (AbstractRiskHandler) SpringContextUtil.getBean(clazz);
    }
}

5.抽象策略处理器类

public abstract class AbstractRiskHandler {

    public String handle(String req) {
        buildSceneData(req);

        return null;
    }

    protected abstract void buildSceneData(String req);
}

6.具体策略实现类

import org.springframework.stereotype.Component;

@Component
@HandleType("risk1")
public class Risk1Handler extends AbstractRiskHandler {

    @Override
    protected void buildSceneData(String req) {
        System.out.println("场景1");
    }
}

7.测试类

class Test{
  @Autowire
  HandlerContext handlerContext

  @Test
  public void test(){
      handlerContext.getInstance("risk1").handle("test");
  }
}

相关文章

  • 利用自定义注解装载策略

    一般使用策略模式的时候,经常使用xml、enum或者map先将策略的以key-value的形式存储起来,然后配置一...

  • 一个简单的自定义注解

    1、定义一个自定义注解 2、使用注解 3、利用反射获取注解的值 java注解是怎么实现的?

  • 注解Annotation--java26(02/19/2016)

    主要内容 JDK内置的基本注解类型(3个)自定义注解类型对注解进行注解(4个)利用反射获取注解信息(在反射部分涉及...

  • SpringBoot自定义注解

    利用AOP自定义注解能在很多时候很好的解耦代码 1. 完善pom文件 2. 自定义注解 3. 制定切点 4. 执行...

  • Spring注解-Conditional

    通过@Conditional注解可以根据代码中设置的条件装载不同的bean,在设置条件注解之前,先要把装载的bea...

  • Spring SPEL,自定义注解实现分布式锁

    1. 自定义注解实现分布式锁 利用自定义注解实现分布式锁,最麻烦的地方就是,加锁的key,怎么获取,之前项目中,对...

  • Android进阶之自定义注解

    Android进阶之自定义注解 本篇文章内容包括: 注解的概念 元注解 自定义注解 Android自定义编译时注解...

  • hibernate Validator自定义注解

    如何自定义注解,加入自己的校验逻辑 自定义注解 自定义注解校验类

  • 【JAVA】注解

    元注解 用来定义、声明注解的注解。 @Inherited注解 使用此注解声明出来的自定义注解,在使用此自定义注解时...

  • 注解学习笔记

    什么是注解注解分类注解作用分类 元注解 Java内置注解 自定义注解自定义注解实现及使用编译时注解注解处理器注解处...

网友评论

    本文标题:利用自定义注解装载策略

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