自定义Spring注解式扫描组件

借鉴Spring ComponentScan组件扫描原理,自定义基于注解的Bean扫描注册组件,实现自定义Bean注册逻辑。

@ComponentScan注解能扫描指定路径下的标识了Spring Bean注解(@Component或者是@Component参与合成的注解,如@Service,@Configuration等)的类,将其注册为一个Spring容器内的一个Bean,Bean名称默认是类名首字母小写。

当然这个是Spring内的注册逻辑,但是最近项目需求以每个扫描到的Bean为基础,统一封装,生成额外的Bean,这个Bean是和公司平台的交互,项目部署后通过平台和用户进行交互,用户的每个请求均通过平台来访问,而不是直接访问应用。

平台封装Bean的逻辑和扫描注册有些区别,仅仅通过Spring扫描难以实现,如果通过手动注册的话,那么就需要额外的很多@Bean标识方法定义或者< bean id= … >形式配置,项目就变得很臃肿,重复性代码很多。

所以想通过借鉴@ComponentScan组件扫描的形式,扫描指定包下的类,所有标识了指定注解的Bean都把他们注册成平台交互的Bean。

首先,需要有一个能够标识类的注解,他的作用类似@Component或者其合成的注解:

@Documented
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface HsfComponent {

    /**
     * 是否要将标识此注解的类注册为Spring的Bean
     *
     * @return
     */
    boolean registerBean() default false;

}

扫描包下的所有标识了这个注解的类都注册成平台交互的Bean,如果这个Bean没有在@ComponentScan的扫描包下,那么可以定义registerBean = true,可以在自定义组件中将其注册到Spring容器中。

其次需要一个类似@ComponentScan作用的扫描注解,它来指定扫描应用内哪些路径:

@Documented
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import(HsfBeanDefinitionRegistrar.class)
public @interface HsfComponentScan {

    /**
     * @return
     */
    String[] value() default {};

    /**
     * 扫描包
     *
     * @return
     */
    String[] basePackages() default {};

    /**
     * 扫描的基类
     *
     * @return
     */
    Class[] basePackageClasses() default {};

    /**
     * 包含过滤器
     *
     * @return
     */
    Filter[] includeFilters() default {};

    /**
     * 排斥过滤器
     *
     * @return
     */
    Filter[] excludeFilters() default {};

}

这个注解内的这几个属性定义我参考了@ComponentScan,@HsfComponentScan 注解的扫描包路径优先使用value(),其次basePackages(),再次是basePackageClasses(),如果这几个属性都未定义值,则使用标识了此注解的配置类的包路径作为扫描路径。includeFilters和excludeFilters是对扫描到的类作过滤,@ComponentScan定义时其属性useDefaultFilters默认值是true,也就是仅将那些标识了@Component及由@Component合成的那些注解时才算作一个Bean的类,所以我们也需要定义一个类型过滤器,将那些标识了@HsfComponent的类算作是平台交互Bean的基类:

/**
 * 自定义HSF类型过滤,过滤抽象类,接口,注解,枚举,内部类及匿名类
 *
 * @create 2017-12-25 10:23
 */
public class HsfTypeFilter extends AbstractClassTestingTypeFilter {

    private static final Logger LOGGER = LoggerFactory.getLogger(HsfTypeFilter.class);

    @Override
    protected boolean match(ClassMetadata metadata) {
        Class clazz = transformToClass(metadata.getClassName());
        if (clazz == null || !clazz.isAnnotationPresent(HsfComponent.class)) {
            return false;
        }
        HsfComponent hsfComponent = clazz.getAnnotation(HsfComponent.class);
        if (hsfComponent.registerBean() && isAnnotatedBySpring(clazz)) {
            throw new IllegalStateException("类{" + clazz.getName() + "}已经标识了Spring组件注解,不能再指定[registerBean = true]");
        }
        //过滤抽象类,接口,注解,枚举,内部类及匿名类
        return !metadata.isAbstract() && !clazz.isInterface() && !clazz.isAnnotation() && !clazz.isEnum()
            && !clazz.isMemberClass() && !clazz.getName().contains("$");
    }

    /**
     * @param className
     * @return
     */
    private Class transformToClass(String className) {
        Class clazz = null;
        try {
            clazz = ClassUtils.forName(className, this.getClass().getClassLoader());
        } catch (ClassNotFoundException e) {
            LOGGER.info("未找到指定HSF基础类{}", className);
        }
        return clazz;
    }

    /**
     * @param clazz
     * @return
     */
    private boolean isAnnotatedBySpring(Class clazz) {
        return clazz.isAnnotationPresent(Component.class) || clazz.isAnnotationPresent(Configuration.class)
            || clazz.isAnnotationPresent(Service.class) || clazz.isAnnotationPresent(Repository.class)
            || clazz.isAnnotationPresent(Controller.class);
    }

}

HsfTypeFilter 过滤那些不符合要求的类,比如@HsfComponent类内部类等,内部类在扫描时也是会扫描到的。

接下来的这个类时关键,它揭示了@HsfComponentScan的扫描和注册逻辑:

/**
 * Hsf注册器
 * 当HSF基类实现了多个接口时,切记将实际的业务接口放在第一个
 *
 * @create 2017-12-22 23:16
 */
public class HsfBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {

    private static final Logger LOGGER = LoggerFactory.getLogger(HsfBeanDefinitionRegistrar.class);

    private static final String RESOURCE_PATTERN = "**/*.class";
    //生成的HSF Bean名称到代理的Service Class的映射
    private static final Map> HSF_UNDERLYING_MAPPING = new HashMap>();

    /**
     * @param importingClassMetadata
     * @param registry
     */
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        AnnotationAttributes annAttr = AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes(HsfComponentScan.class.getName()));
        String[] basePackages = annAttr.getStringArray("value");
        if (ObjectUtils.isEmpty(basePackages)) {
            basePackages = annAttr.getStringArray("basePackages");
        }
        if (ObjectUtils.isEmpty(basePackages)) {
            basePackages = getPackagesFromClasses(annAttr.getClassArray("basePackageClasses"));
        }
        if (ObjectUtils.isEmpty(basePackages)) {
            basePackages = new String[] {ClassUtils.getPackageName(importingClassMetadata.getClassName())};
        }
        List includeFilters = extractTypeFilters(annAttr.getAnnotationArray("includeFilters"));
        //增加一个包含的过滤器,扫描到的类只要不是抽象的,接口,枚举,注解,及匿名类那么就算是符合的
        includeFilters.add(new HsfTypeFilter());
        List excludeFilters = extractTypeFilters(annAttr.getAnnotationArray("excludeFilters"));
        List> candidates = scanPackages(basePackages, includeFilters, excludeFilters);
        if (candidates.isEmpty()) {
            LOGGER.info("扫描指定HSF基础包[{}]时未发现复合条件的基础类", basePackages.toString());
            return;
        }
        //注册HSF后处理器,为HSF对象注入环境配置信息
        registerHsfBeanPostProcessor(registry);
        //注册HSF
        registerBeanDefinitions(candidates, registry);
    }

    /**
     * @param basePackages
     * @param includeFilters
     * @param excludeFilters
     * @return
     */
    private List> scanPackages(String[] basePackages, List includeFilters, List excludeFilters) {
        List> candidates = new ArrayList>();
        for (String pkg : basePackages) {
            try {
                candidates.addAll(findCandidateClasses(pkg, includeFilters, excludeFilters));
            } catch (IOException e) {
                LOGGER.error("扫描指定HSF基础包[{}]时出现异常", pkg);
                continue;
            }
        }
        return candidates;
    }

    /**
     * 获取符合要求的Controller名称
     *
     * @param basePackage
     * @return
     * @throws IOException
     */
    private List> findCandidateClasses(String basePackage, List includeFilters, List excludeFilters) throws IOException {
        if (LOGGER.isDebugEnabled()) {
            LOGGER.debug("开始扫描指定包{}下的所有类" + basePackage);
        }
        List> candidates = new ArrayList>();
        String packageSearchPath = CLASSPATH_ALL_URL_PREFIX + replaceDotByDelimiter(basePackage) + '/' + RESOURCE_PATTERN;
        ResourceLoader resourceLoader = new DefaultResourceLoader();
        MetadataReaderFactory readerFactory = new SimpleMetadataReaderFactory(resourceLoader);
        Resource[] resources = ResourcePatternUtils.getResourcePatternResolver(resourceLoader).getResources(packageSearchPath);
        for (Resource resource : resources) {
            MetadataReader reader = readerFactory.getMetadataReader(resource);
            if (isCandidateResource(reader, readerFactory, includeFilters, excludeFilters)) {
                Class candidateClass = transform(reader.getClassMetadata().getClassName());
                if (candidateClass != null) {
                    candidates.add(candidateClass);
                    LOGGER.debug("扫描到符合要求HSF基础类:{}" + candidateClass.getName());
                }
            }
        }
        return candidates;
    }

    /**
     * 注册HSF Bean,
     * Bean的名称格式:
     * 当内部Bean的类名类似FloorServiceImpl时  "HSFSpringProviderBean#FloorService"
     * 如果内部Bean实现了多个接口,请将实际的业务接口放在第一位
     *
     * @param internalClasses
     * @param registry
     */
    private void registerBeanDefinitions(List> internalClasses, BeanDefinitionRegistry registry) {
        for (Class clazz : internalClasses) {
            if (HSF_UNDERLYING_MAPPING.values().contains(clazz)) {
                LOGGER.debug("重复扫描{}类,忽略重复注册", clazz.getName());
                continue;
            }
            String beanName = generateHsfBeanName(clazz);
            RootBeanDefinition rbd = new RootBeanDefinition(HSFSpringProviderBean.class);
            registry.registerBeanDefinition(beanName, rbd);
            if (registerSpringBean(clazz)) {
                LOGGER.debug("注册HSF基础[{}]Bean", clazz.getName());
                registry.registerBeanDefinition(ClassUtils.getShortNameAsProperty(clazz), new RootBeanDefinition(clazz));
            }
            HSF_UNDERLYING_MAPPING.put(beanName, clazz);
        }
    }

    /**
     * 注册HSF后处理器
     *
     * @param registry
     */
    private void registerHsfBeanPostProcessor(BeanDefinitionRegistry registry) {
        String beanName = ClassUtils.getShortNameAsProperty(HsfBeanPostProcessor.class);
        if (!registry.containsBeanDefinition(beanName)) {
            registry.registerBeanDefinition(beanName, new RootBeanDefinition(HsfBeanPostProcessor.class));
        }
    }

    /**
     * 当接口重名时,后注册的HSF Bean的名称后缀加上序号,从1开始,1代表第二个
     *
     * @param underlying
     * @return
     */
    private String generateHsfBeanName(Class underlying) {
        String interfaceName = underlying.getInterfaces()[0].getSimpleName();
        String beanName = HSFSpringProviderBean.class.getSimpleName() + "#" + interfaceName;
        if (HSF_UNDERLYING_MAPPING.containsKey(beanName)) {
            beanName = beanName + "#" + getNextOrderSuffix(interfaceName);
        }
        return beanName;
    }

    /**
     * 生成后注册的重名接口后缀
     *
     * @param className
     * @return
     */
    private Integer getNextOrderSuffix(String className) {
        int order = 1;
        for (String hsfBeanName : HSF_UNDERLYING_MAPPING.keySet()) {
            if (hsfBeanName.substring(hsfBeanName.indexOf("#") + 1).startsWith(className)) {
                String curOrder = hsfBeanName.substring((HSFSpringProviderBean.class.getSimpleName() + "#" + className).length());
                if (!StringUtils.isEmpty(curOrder)) {
                    order = Math.max(Integer.valueOf(curOrder), order);
                }
            }
        }
        return order;
    }

    /**
     * @param filterAttributes
     * @return
     */
    private List typeFiltersFor(AnnotationAttributes filterAttributes) {
        List typeFilters = new ArrayList();
        FilterType filterType = filterAttributes.getEnum("type");

        for (Class filterClass : filterAttributes.getClassArray("classes")) {
            switch (filterType) {
                case ANNOTATION:
                    Assert.isAssignable(Annotation.class, filterClass,
                        "@HsfComponentScan 注解类型的Filter必须指定一个注解");
                    Class annotationType = (Class)filterClass;
                    typeFilters.add(new AnnotationTypeFilter(annotationType));
                    break;
                case ASSIGNABLE_TYPE:
                    typeFilters.add(new AssignableTypeFilter(filterClass));
                    break;
                case CUSTOM:
                    Assert.isAssignable(TypeFilter.class, filterClass,
                        "@HsfComponentScan 自定义Filter必须实现TypeFilter接口");
                    TypeFilter filter = BeanUtils.instantiateClass(filterClass, TypeFilter.class);
                    typeFilters.add(filter);
                    break;
                default:
                    throw new IllegalArgumentException("当前TypeFilter不支持: " + filterType);
            }
        }
        return typeFilters;
    }

    /**
     * @param classes
     * @return
     */
    private String[] getPackagesFromClasses(Class[] classes) {
        if (ObjectUtils.isEmpty(classes)) {
            return null;
        }
        List basePackages = new ArrayList(classes.length);
        for (Class clazz : classes) {
            basePackages.add(ClassUtils.getPackageName(clazz));
        }
        return (String[])basePackages.toArray();
    }

    /**
     * 用"/"替换包路径中"."
     *
     * @param path
     * @return
     */
    private String replaceDotByDelimiter(String path) {
        return StringUtils.replace(path, ".", "/");
    }

    /**
     * @param reader
     * @param readerFactory
     * @param includeFilters
     * @param excludeFilters
     * @return
     * @throws IOException
     */
    protected boolean isCandidateResource(MetadataReader reader, MetadataReaderFactory readerFactory, List includeFilters,
                                          List excludeFilters) throws IOException {
        for (TypeFilter tf : excludeFilters) {
            if (tf.match(reader, readerFactory)) {
                return false;
            }
        }
        for (TypeFilter tf : includeFilters) {
            if (tf.match(reader, readerFactory)) {
                return true;
            }
        }
        return false;
    }

    /**
     * @param className
     * @return
     */
    private Class transform(String className) {
        Class clazz = null;
        try {
            clazz = ClassUtils.forName(className, this.getClass().getClassLoader());
        } catch (ClassNotFoundException e) {
            LOGGER.info("未找到指定HSF基础类{%s}", className);
        }
        return clazz;
    }

    /**
     * @param annAttrs
     * @return
     */
    private List extractTypeFilters(AnnotationAttributes[] annAttrs) {
        List typeFilters = new ArrayList();
        for (AnnotationAttributes filter : annAttrs) {
            typeFilters.addAll(typeFiltersFor(filter));
        }
        return typeFilters;
    }

    /**
     * @param beanClass
     * @return
     */
    private boolean registerSpringBean(Class beanClass) {
        return beanClass.getAnnotation(HsfComponent.class).registerBean();
    }

    /**
     * @param hsfBeanName
     * @return
     */
    public static Class getUnderlyingClass(String hsfBeanName) {
        return HSF_UNDERLYING_MAPPING.get(hsfBeanName);
    }

}

这个类扫描@HsfComponentScan内定义的包路径,将那些标识了@HsfComponent的复合要求的类注册为平台BeanDefinition,HSFSpringProviderBean类型。但是因为HSFSpringProviderBean的一些配置信息比如版本号等是在maven管理的Properties配置文件中,Spring容器只能在配置配置文件中才能得到,也就是在< bean id = … >这种类型下才能在项目编译时由maven替换获取,直接在java代码内不能直接获取这些配置信息的,所以HSFSpringProviderBean这个类型的Bean的一些属性由PostProcessor后处理器来注入。

因为平台Bean HSFSpringProviderBean的配置文件由maven管理,所以需要一个桥梁将maven的配置信息引入到Spring容器内,因此我们定义了一个Bean,配置在xml配置文件中,这样maven在编译时用配置值替换那些#{…}占位符号,这样在项目运行时我们就能通过代码获取到这些配置信息,将其注入到HSFSpringProviderBean中。

/**
 * HSF通用属性配置
 * 定义此类的目的是为了获取HSF的基本配置,原先配置是定义在antx.properties文件中,
 * 这个文件由maven管理,在编译后不同环境下的value赋给相应的${...};
 * 在项目运行时获取不到antx中的属性信息,因此不能通过代码从antx.properties中提取指定key的value;
 * 至少需要定义一个Bean,由maven将当前环境的配置信息传入Spring容器中。
 *
 * @create 2017-12-25 13:42
 */
public class HsfGenericConfig {

    private String serviceVersion;
    private String serviceGroup;
    private String clientTimeout;

    public String getServiceVersion() {
        return serviceVersion;
    }

    public void setServiceVersion(String serviceVersion) {
        this.serviceVersion = serviceVersion;
    }

    public String getServiceGroup() {
        return serviceGroup;
    }

    public void setServiceGroup(String serviceGroup) {
        this.serviceGroup = serviceGroup;
    }

    public String getClientTimeout() {
        return clientTimeout;
    }

    public void setClientTimeout(String clientTimeout) {
        this.clientTimeout = clientTimeout;
    }
}

HsfGenericConfig 的属性就是HSFSpringProviderBean的相应配置信息,Spring容器初始化后,HsfGenericConfig 就提取到了HSFSpringProviderBean的配置信息。

最后一个类就是将HsfGenericConfig 的配置信息拷贝到HSFSpringProviderBean类型的Bean中,完善平台Bean的初始化过程:

/**
 * HSF后处理器
 *
 * @create 2017-12-25 10:51
 */
public class HsfBeanPostProcessor extends InstantiationAwareBeanPostProcessorAdapter implements InitializingBean, BeanFactoryAware {

    private BeanFactory beanFactory;
    private HsfGenericConfig hsfGenericConfig;

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        this.beanFactory = beanFactory;
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        hsfGenericConfig = beanFactory.getBean(HsfGenericConfig.class);
    }

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        if (bean instanceof HSFSpringProviderBean && isScanedGeneratedHsfBean(beanName)) {
            //拷贝配置信息
            BeanUtils.copyProperties(hsfGenericConfig, bean);
            Class underlyingClass = HsfBeanDefinitionRegistrar.getUnderlyingClass(beanName);
            Object underlyingBean = beanFactory.getBean(underlyingClass);
            ((HSFSpringProviderBean)bean).setTarget(underlyingBean);
            ((HSFSpringProviderBean)bean).setServiceInterface(underlyingClass.getInterfaces()[0].getName());
        }
        return bean;
    }

    /**
     * 当前Bean是否是通过{@linkplain HsfComponentScan}注解式生成的Bean
     *
     * @param beanName
     * @return
     */
    private boolean isScanedGeneratedHsfBean(String beanName) {
        return beanName.startsWith(HSFSpringProviderBean.class.getSimpleName()) && beanName.contains("#");
    }
}

这个组件的类都介绍完了,总共6个,每个有各自的作用。

下面介绍使用方法:

  1. 对于被注册类来说,标识@HsfComponent就可以了,很简单。如果这个类没有被Spring注册到容器中,则加上registerBean = true,那么组件会自动将其注册到容器中,替代了Spring的@ComponentScan的作用。
  2. 将@HsfComponentScan(“包路径”)标识到一个配置类上,这个类最好标识了@Configuration,然后将这个类定义为一个Bean,方式可以采用XML定义或者@Import()的形式被其他总配置类引用。

总结:
HsfComponentScan类上用@Import()引入了HsfBeanDefinitionRegistrar类,而这个类是一个ImportBeanDefinitionRegistrar的实现类,Spring容器在解析ImportBeanDefinitionRegistrar类型的Bean时会调用其registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry)方法,将@HsfComponentScan注解上的信息提取成AnnotationMetadata以及容器注册器对象作为此方法的参数,这个就是自定义注解式组件扫描的关键逻辑。

你可能感兴趣的:(spring,造轮子)