24 自定义BeanDefinitionRegistryPostProcessor

BeanDefinitionRegistryPostProcessor继承自BeanFactoryPostProcessor,是一种比较特殊的BeanFactoryPostProcessor。BeanDefinitionRegistryPostProcessor中定义的postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry)方法 可以让我们实现自定义的注册bean定义的逻辑。下面的示例中就新定义了一个名为hello,类型为Hello的bean定义。

public class CustomBeanDefinitionRegistry implements BeanDefinitionRegistryPostProcessor {@Overridepublic void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {}@Overridepublic void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {RootBeanDefinition helloBean = new RootBeanDefinition(Hello.class);//新增Bean定义registry.registerBeanDefinition("hello", helloBean);}}

测试时采用的配置是基于Java类的配置,对应的配置类如下:

@Configuration
public class SpringConfiguration {@Beanpublic CustomBeanDefinitionRegistry customBeanDefinitionRegistry() {return new CustomBeanDefinitionRegistry();}}

测试如下:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes={SpringConfiguration.class})
public class CustomBeanDefinitionRegistryTest {@Autowiredprivate Hello hello;@Testpublic void test() {//能运行就说明hello是不为空的Assert.assertNotNull(hello);}}

24.1 ClassPathScanningCandidateComponentProvider

在使用自定义的BeanDefinitionRegistryPostProcessor来添加自定义的bean定义时可以配合ClassPathScanningCandidateComponentProvider一起使用,ClassPathScanningCandidateComponentProvider可以根据一定的规则扫描类路径下满足特定条件的Class来作为候选的bean定义。 ClassPathScanningCandidateComponentProvider在扫描时可以通过TypeFilter来指定需要匹配的类和需要排除的类,使用ClassPathScanningCandidateComponentProvider时可以通过构造参数useDefaultFilter指定是否需要使用默认的TypeFilter,默认的TypeFilter将包含类上拥有 @Component、@Service、@Repository、@Controller、@javax.annotation.ManagedBean和@javax.inject.Named注解的类。在扫描时需要指定扫描的根包路径。以下是一些使用ClassPathScanningCandidateComponentProvider扫描并注册bean定义的示例。

24.1.1 扫描指定包及其子包下面的所有非接口和非抽象类。

@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {boolean useDefaultFilters = false;//是否使用默认的filter,使用默认的filter意味着只扫描那些类上拥有Component、Service、Repository或Controller注解的类。String basePackage = "com.elim.learn.spring.bean";ClassPathScanningCandidateComponentProvider beanScanner = new ClassPathScanningCandidateComponentProvider(useDefaultFilters);TypeFilter includeFilter = new TypeFilter() {@Overridepublic boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory)throws IOException {return metadataReader.getClassMetadata().isConcrete();}};beanScanner.addIncludeFilter(includeFilter);Set<BeanDefinition> beanDefinitions = beanScanner.findCandidateComponents(basePackage);for (BeanDefinition beanDefinition : beanDefinitions) {//beanName通常由对应的BeanNameGenerator来生成,比如Spring自带的AnnotationBeanNameGenerator、DefaultBeanNameGenerator等,也可以自己实现。String beanName = beanDefinition.getBeanClassName();registry.registerBeanDefinition(beanName, beanDefinition);}
}

24.1.2 扫描指定包及其子包下面拥有指定注解的类。

@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {boolean useDefaultFilters = false;//是否使用默认的filter,使用默认的filter意味着只扫描那些类上拥有Component、Service、Repository或Controller注解的类。String basePackage = "com.elim.learn.spring.bean";ClassPathScanningCandidateComponentProvider beanScanner = new ClassPathScanningCandidateComponentProvider(useDefaultFilters);TypeFilter includeFilter = new AnnotationTypeFilter(HelloAnnotation.class);beanScanner.addIncludeFilter(includeFilter);Set<BeanDefinition> beanDefinitions = beanScanner.findCandidateComponents(basePackage);for (BeanDefinition beanDefinition : beanDefinitions) {//beanName通常由对应的BeanNameGenerator来生成,比如Spring自带的AnnotationBeanNameGenerator、DefaultBeanNameGenerator等,也可以自己实现。String beanName = beanDefinition.getBeanClassName();registry.registerBeanDefinition(beanName, beanDefinition);}
}

AnnotationTypeFilter是Spring自带的一个TypeFilter,可以扫描指定的注解。AnnotationTypeFilter一共有三个构造方法,分别如下:

public AnnotationTypeFilter(Class<? extends Annotation> annotationType) {this(annotationType, true, false);
}public AnnotationTypeFilter(Class<? extends Annotation> annotationType, boolean considerMetaAnnotations) {this(annotationType, considerMetaAnnotations, false);
}public AnnotationTypeFilter(Class<? extends Annotation> annotationType, boolean considerMetaAnnotations, boolean considerInterfaces) {super(annotationType.isAnnotationPresent(Inherited.class), considerInterfaces);this.annotationType = annotationType;this.considerMetaAnnotations = considerMetaAnnotations;
}

主要差别在于considerMetaAnnotations和considerInterfaces。

24.1.2.1 considerMetaAnnotations

指定considerMetaAnnotations="true"时则如果目标类上没有指定的注解,但是目标类上的某个注解上加上了指定的注解则该类也将匹配。比如:

@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface HelloAnnotation {}
@HelloAnnotation
@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface HasHelloAnnotation {}
@HasHelloAnnotation
public class Hello {}

24.1.2.2 considerInterfaces

在上面的代码中定义了两个注解HelloAnnotation和HasHelloAnnotation,其中HasHelloAnnotation上加上了@HelloAnnotation注解,类Hello上面加上了@HasHelloAnnotation注解,则在通过AnnotationTypeFilter扫描标注有HelloAnnotation注解的类时,如果指定了considerMetaAnnotations="true"则类Hello也会被扫描到。
指定considerInterfaces="true"时,则如果对应的类实现的接口上拥有指定的注解时也将匹配。比如下面这种情况扫描加了HelloAnnotation注解的类时就会扫描到Hello类。

@HelloAnnotation
public interface HelloInterface {}
public class Hello implements HelloInterface {}

24.1.2.3 父类上拥有指定的注解

如果我们需要扫描的目标注解上是加了@Inherited注解的,则如果一个类上没有指定的目标注解,但是其父类拥有对应的注解,则也会被扫描到。比如我们将HelloAnnotation加上@Inherited注解。

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface HelloAnnotation {}

Hello类上加上@HelloAnnotation注解。

@HelloAnnotation
public class Hello {}
然后HelloChild类继承自Hello类。
```java
public class HelloChild extends Hello {}

这时候进行扫描时HelloChild也会被扫描到,但如果拿掉HelloAnnotation上的@Inherited,则HelloChild扫描不到。

24.1.3 扫描指定包及其子包下面能赋值给指定Class的Class

@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {boolean useDefaultFilters = false;//是否使用默认的filter,使用默认的filter意味着只扫描那些类上拥有Component、Service、Repository或Controller注解的类。String basePackage = "com.elim.learn.spring.bean";ClassPathScanningCandidateComponentProvider beanScanner = new ClassPathScanningCandidateComponentProvider(useDefaultFilters);//指定considerMetaAnnotations="true"时则如果目标类上没有指定的注解,但是目标类上的某个注解上加上了指定的注解则该类也将匹配。比如:TypeFilter includeFilter = new AssignableTypeFilter(Hello.class);beanScanner.addIncludeFilter(includeFilter);Set<BeanDefinition> beanDefinitions = beanScanner.findCandidateComponents(basePackage);for (BeanDefinition beanDefinition : beanDefinitions) {//beanName通常由对应的BeanNameGenerator来生成,比如Spring自带的AnnotationBeanNameGenerator、DefaultBeanNameGenerator等,也可以自己实现。String beanName = beanDefinition.getBeanClassName();registry.registerBeanDefinition(beanName, beanDefinition);}
}

AssignableTypeFilter也是Spring内置的一个TypeFilter,用于扫描指定类型的类。只要目标类型能够赋值给指定的类型,则表示匹配。即如果指定的是一个接口,则所有直接或间接实现该接口的类都将被扫描到。

基于ClassPathScanningCandidateComponentProvider的特性,我们常常可以利用它构建一个工具类用以扫描指定包路径下指定类型的Class,获取满足条件的Class,然后加以利用,这常用于需要扫描的类不是Spring bean的场景。

(注:本文是基于Spring4.1.0所写)

Spring(24)——自定义BeanDefinitionRegistryPostProcessor相关推荐

  1. java 自定义xml_6.1 如何在spring中自定义xml标签

    dubbo自定义了很多xml标签,例如,那么这些自定义标签是怎么与spring结合起来的呢?我们先看一个简单的例子. 一 编写模型类 1 packagecom.hulk.testdubbo.model ...

  2. spring AOP自定义注解方式实现日志管理

    转:spring AOP自定义注解方式实现日志管理 今天继续实现AOP,到这里我个人认为是最灵活,可扩展的方式了,就拿日志管理来说,用Spring AOP 自定义注解形式实现日志管理.废话不多说,直接 ...

  3. 3.Spring Security 自定义用户认证

    Spring Security自定义用户认证 自定义认证过程 自定义认证的过程需要实现Spring Security提供的UserDetailService接口,该接口只有一个抽象方法loadUser ...

  4. spring security 自定义认证登录

    spring security 自定义认证登录 1.概要 1.1.简介 spring security是一种基于 Spring AOP 和 Servlet 过滤器的安全框架,以此来管理权限认证等. 1 ...

  5. spring security自定义指南

    序 本文主要研究一下几种自定义spring security的方式 主要方式 自定义UserDetailsService 自定义passwordEncoder 自定义filter 自定义Authent ...

  6. java 外部覆盖内部配置,Spring 与自定义注解、外部配置化的结合使用

    Spring 与自定义注解.外部配置化的结合使用 一.Java注解的简单介绍 注解,也叫Annotation.标注,是 Java 5 带来的新特性. 可使用范围 类.字段.方法.参数.构造函数.包等, ...

  7. Spring Security——自定义认证错误提示信息及自适应返回格式解决方案

    解决方案 package com.hailiu.web.handler;import com.hailiu.model.Log; import com.hailiu.web.bean.Response ...

  8. spring使用自定义注解_用Spring组成自定义注释

    spring使用自定义注解 Java批注在2004年随Java 5一起引入,是一种将元数据添加到Java源代码中的方法. 如今,许多主要框架(如Spring或Hibernate)都严重依赖注释. 在本 ...

  9. Spring Boot自定义横幅生成

    每当我们启动Spring Boot应用程序时,都会显示如下所示的文本消息. 这称为横幅. 现在,如果我们可以创建一个特定于我们的Spring Boot应用程序的自定义横幅并使用它代替默认的Spring ...

最新文章

  1. php中添加访问器,php – 结合访问器和mutator逻辑,为模型添加自定义属性
  2. linux 某个文件打不开了,linux操作系统下,exe文件为什么打不开?
  3. 小程序-demo:小程序示例-page/component
  4. 基于VHDL的可变速彩灯控制器的设计
  5. kali Linux 基础
  6. 等压线上怎么画风向_战场上骑兵应该怎么拔刀?从清人佩刀为何总是刀柄向后说起...
  7. SharePoint 2010 - 如何导入\导出WebPart
  8. 我们相信加密! 教程
  9. 第二十五期:搞定Linux Shell文本处理工具,看完这篇集锦就够了
  10. 赛锐信息:SAP img 文档
  11. c调用python函数_python - Linux C调用Python 函数
  12. Python 虚拟环境迁移路径后pip报错解决记录
  13. 继电保护原理4-自动重合闸
  14. 如何查看哪些期刊被SCI收录
  15. cocos2d-x 3.2 之 三消类游戏——万圣大作战
  16. 图层重命名快捷键_Principle快捷键大全
  17. iOS7 tabbar遮盖tableview的cell解决方法
  18. QT环境搭建及常见错误
  19. 小白从零开始:STM32双闭环(速度环、位置环)电机控制(硬件篇)
  20. 最短路径:Dijikstra算法/Floyd算法

热门文章

  1. 配置linux下node节点的ip
  2. android:fitsSystemWindows=“true”
  3. 在Homebrew 发布程序
  4. Flutter开发之《闲鱼的探索与收获》笔记(51)
  5. 树莓派私有云(OwnCloud)搭建(三) OwnCloud安装
  6. docker常用命令行集锦
  7. RabbitMQ在windows系统安装部署文档
  8. 如何解决普通用户使用sudo找不到命令
  9. 通过crontab命令创建任务
  10. [小明学算法]3.启发式搜索算法----A*算法之我见