前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【Spring源码】- 04 扩展点之BeanFactoryPostProcessor

【Spring源码】- 04 扩展点之BeanFactoryPostProcessor

作者头像
Reactor2020
发布2023-03-22 18:56:47
2480
发布2023-03-22 18:56:47
举报

之前分析IoC容器的启动流程时,夹杂在启动流程中我们发现Spring给我们提供了大量的扩展点,基于这些扩展点我们就可以实现很多灵活的功能定制需求。这篇我们首先来看下BeanFactoryPostProcessor这个扩展点,它是非常重要的一个扩展点,面向IoC容器进行扩展。

类结构

BeanFactoryPostProcessorBeanFactory的后置处理器,针对BeanFactory实现各种功能扩展。BeanFactoryPostProcessor又分为两类:BeanFactoryPostProcessorBeanDefinitionRegistryPostProcessorBeanDefinitionRegistryPostProcessor继承了BeanFactoryPostProcessor接口,对其进行了扩展。

执行时机

前面分析IoC容器启动流程中,refresh()方法中invokeBeanFactoryPostProcessors(beanFactory)处触发BeanFactoryPostProcessor扩展类的执行。

代码语言:javascript
复制
执行逻辑总结:
	1、先执行BeanDefinitionRegistryPostProcessor#postProcessBeanDefinitionRegistry方法,其中BeanDefinitionRegistryPostProcessor执行优先级如下:
		a、addBeanFactoryPostProcessor()传入到优先级最高,因为不需要实例化,直接可以获取到对象进行执行;
	 	b、然后从IoC容器中获取PriorityOrdered接口的BeanDefinitionRegistryPostProcessor,实例化并排序后执行postProcessBeanDefinitionRegistry方法
	 	c、然后从IoC容器中获取Ordered接口的BeanDefinitionRegistryPostProcessor,实例化并排序后执行postProcessBeanDefinitionRegistry方法
	 	d、然后从IoC容器中获取剩余的BeanDefinitionRegistryPostProcessor,实例化后执行postProcessBeanDefinitionRegistry方法;注意这个处理步骤存在一个循环,主要是存在执行前面的BeanDefinitionRegistryPostProcessor#postProcessBeanDefinitionRegistry方法时,存在可能会向IoC容器中注册新的BeanDefinitionRegistryPostProcessor,通过循环保证都会被执行;
	2、然后执行BeanDefinitionRegistryPostProcessor#postProcessBeanFactory方法,执行顺序参照步骤1中执行顺序;
	3、最后才会执行BeanFactoryPostProcessor#postProcessBeanFactory,执行优先级和BeanDefinitionRegistryPostProcessor一致:
	 	a、addBeanFactoryPostProcessor()传入到优先级最高,因为不需要实例化,直接可以获取到对象进行执行;
	 	b、然后从IoC容器中获取PriorityOrdered接口的BeanFactoryPostProcessor,实例化并排序后执行postProcessBeanFactory方法
	 	c、然后从IoC容器中获取Ordered接口的BeanFactoryPostProcessor,实例化并排序后执行postProcessBeanFactory方法
	 	d、然后从IoC容器中获取剩余的BeanFactoryPostProcessor,实例化后执行postProcessBeanFactory方法

总结两句话:

代码语言:javascript
复制
1、不同方法执行优先级:`BeanDefinitionRegistryPostProcessor#postProcessBeanDefinitionRegistry` > `BeanDefinitionRegistryPostProcessor#postProcessBeanFactory` > `BeanFactoryPostProcessor#postProcessBeanFactory`;
代码语言:javascript
复制
2、同方法执行优先级:`addBeanFactoryPostProcessor` > `PriorityOrdered` > `Ordered` > 非排序

使用场景

这两个接口都比较简单,都只定义一个方法。首先看下BeanDefinitionRegistryPostProcessor#postProcessBeanDefinitionRegistry这个方法,主要使用场景是:向IoC容器中注册BeanDefinition。这里面有个非常重要的实现类:ConfigurationClassPostProcessor,完成各种将Bean解析成BeanDefinition注入到IoC容器功能。

BeanDefinitionRegistryPostProcessor

案例一

首先,我们来看个例子,自定义BeanDefinition方式注册到IoC容器中。

1、定义一个Bean

代码语言:javascript
复制
public class TestService02 {

 private TestService01 testService01;

 @Autowired
 private TestService03 testService03;

 private String name;
 
    //省略setter、toString方法
}

2、由于该Bean没有@Component等注解,所以我们可以定义一个BeanDefinitionRegistryPostProcessor扩展注册到IoC容器中:

代码语言:javascript
复制
@Component
public class MyBeanFactoryPostProcessor implements BeanDefinitionRegistryPostProcessor {
 @Override
 public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
  System.out.println("===MyBeanFactoryPostProcessor#postProcessBeanDefinitionRegistry===");
  //创建一个BeanDefinition
  RootBeanDefinition beanDefinition = new RootBeanDefinition(TestService02.class);
  //获取MutablePropertyValues,用于添加依赖注入
  MutablePropertyValues pvs = beanDefinition.getPropertyValues();
  //为依赖注入指定固定值方式
  pvs.addPropertyValue("name", "张三");
  //为依赖注入指定beanName,自动将beanName指向IoC中的Bean注入进去
  pvs.addPropertyValue("testService01", new RuntimeBeanReference("testService01"));
  //将生成的BeanDefinition注册到IoC容器中
  registry.registerBeanDefinition("testService02", beanDefinition);
 }

 @Override
 public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
  System.out.println("===MyBeanFactoryPostProcessor#postProcessBeanFactory===");
 }

}

3、输出结果:

代码语言:javascript
复制
myConfig
testService01
testService03
myBeanFactoryPostProcessor
testService02
===TestService02属性信息===
TestService02{testService01=xxx.bean.TestService01@56ef9176, testService03=xxx.bean.TestService03@4566e5bd, name='张三'}

从输出结果可以看到,TestService02已被注册到IoC容器中,且其属性字段都已成功进行依赖注入。通过这种方式,我们就可以更加灵活的向IoC容器注册Bean,实现各种功能的扩展。

案例二

我们可以模拟@ComponentScan@Component注解方式实现:将指定路径下的带有指定注解的Bean注册到IoC容器中。

1、首先,模仿@ComponentScan@Component定义两个注解:

代码语言:javascript
复制
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Indexed
public @interface MyComponent {
 String value() default "";
}
代码语言:javascript
复制
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
public @interface MyComponentScan {

 @AliasFor("basePackages")
 String[] value() default {};

 @AliasFor("value")
 String[] basePackages() default {};

 Class<?>[] basePackageClasses() default {};

}

2、定义一个BeanDefinitionRegistryPostProcessor实现类:

代码语言:javascript
复制
@Component
public class MyBeanFactoryPostProcessor implements BeanDefinitionRegistryPostProcessor {
 private List<String> basePackageList = new ArrayList<>();

 @Override
 public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
  System.out.println("===MyBeanFactoryPostProcessor#postProcessBeanFactory===");
 }

 @Override
 public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {

  Arrays.stream(registry.getBeanDefinitionNames()).forEach(x -> parseMyComponentScan(registry.getBeanDefinition(x)));
  if(basePackageList.isEmpty()){//没有扫描路径情况
   return;
  }
  //定义一个BeanDefinitionScanner,扫描指定路径下Bean
  ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(registry, false);
  scanner.addIncludeFilter(new AnnotationTypeFilter(MyComponent.class));
  scanner.setIncludeAnnotationConfig(false);
  String[] basePackages = basePackageList.stream().toArray(String[]::new);
  int beanCount = scanner.scan(basePackages);
  System.out.println("注册成功:"+beanCount);
 }

 /**
  * 遍历所有的BeanDefinition,然后解析出有@MyComponentScan注解信息,并提取basePackageClasses属性值,
  * 解析成扫描路径存储到basePackageList集合中
  * @param beanDefinition
  */
 private void parseMyComponentScan(BeanDefinition beanDefinition){
  if(AnnotatedBeanDefinition.class.isAssignableFrom(beanDefinition.getClass())){
   AnnotatedBeanDefinition bdf = (AnnotatedBeanDefinition) beanDefinition;
   Class<?> beanClass;
   try {
    beanClass = Class.forName(bdf.getBeanClassName());
    AnnotationMetadata metadata = AnnotationMetadata.introspect(beanClass);
    Map<String, Object> config = metadata.getAnnotationAttributes(MyComponentScan.class.getName());
    if(config != null && config.containsKey("basePackageClasses")){
     Object obj = config.get("basePackageClasses");
     Class clz = ((Class[]) obj)[0];
     basePackageList.add(clz.getPackage().getName());
    }
   } catch (Throwable e) {
    e.printStackTrace();
   }
  }
 }
}

3、测试:

代码语言:javascript
复制
@Configuration
@ComponentScan(basePackageClasses = MyConfig.class)
@MyComponentScan(basePackageClasses = MyConfig.class)
public class MyConfig {
}

@ComponentScan可以把常规的@Component注册到IoC容器中,@MyComponentScan可以把指定包路径下带有@MyComponent注解的Bean注册到IoC容器中。

BeanFactoryPostProcessor

上面我们利用BeanDefinitionRegistryPostProcessor#postProcessBeanDefinitionRegistry扩展方法实现了@MyComponentScan@MyComponent注解功能,实现将指定包路径下带有@MyComponent注解的Bean注册到IoC容器中。BeanDefinitionRegistryPostProcessor接口中还有一个继承父接口BeanFactoryPostProcessor中的方法:postProcessBeanFactory

上一个案例实现了@MyComponent注解的Bean注册到IoC功能,现在我们来实现对所有@MyComponent注解的类进行AOP增强。

1、定义一个增强类:

代码语言:javascript
复制
public class MyMethodInterceptor implements MethodInterceptor {

    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy)
            throws Throwable {
        try {
            before(method);//前置通知
            Object ret = methodProxy.invokeSuper(obj, args);//目标方法执行
            after(method, ret);//后置通知
            return ret;
        } catch (Exception e) {
            exception();//异常通知
        } finally {
            afterReturning();//方法返回通知
        }
        return null;
    }
 //前置增强
    private void before(Method method) {
        System.out.printf("before execute:%s\r\n", method.getName());
    }
 //后置增强
    private void after(Method method, Object ret) {
        System.out.printf("after execute:%s, ret:%s\r\n", method.getName(), ret);
    }
 //异常增强
    private void exception() {
        System.out.println("execute failure");
    }
 //after返回增强
    private void afterReturning() {
        System.out.println("execute finish");
    }
}

2、基于Spring Enhancer工具类,实现一个传入Class,创建一个带有增强逻辑的Class返回出来工具类:

代码语言:javascript
复制
public class EnhancerUtil  {
 //定义增强实例
 private static final Callback[] CALLBACKS = new Callback[] {
   new MyMethodInterceptor(),
   NoOp.INSTANCE
 };

 private static final Class[] CALLBACKTYPES = new Class[] {
   new MyMethodInterceptor().getClass(),
   NoOp.INSTANCE.getClass()
 };


 public static Class createProxyClass(Class targetClz){
  Enhancer enhancer =  new Enhancer();
  //基于继承方式代理:设置代理类的父类,就是目标对象,创建出来的对象就是目标对象的子类
  enhancer.setSuperclass(targetClz);
  //设置回调过滤器,返回值是callbacks数组的下标
  enhancer.setCallbackFilter(new CallbackFilter() {
   @Override
   public int accept(Method method) {
    /**
     * 屏蔽掉Object类中定义方法的拦截
     */
    if (method.getDeclaringClass() == targetClz) {
     return 0;
    }
    return 1;
   }
  });
  /**
   * NoOp回调把对方法调用直接委派给这个方法在父类中的实现,即可理解为真实对象直接调用方法
   */
  enhancer.setCallbackTypes(CALLBACKTYPES);
  //设置类加载器
  enhancer.setClassLoader(targetClz.getClassLoader());
  //创建代理对象
  Class clz = enhancer.createClass();
  Enhancer.registerStaticCallbacks(clz, CALLBACKS);
  return clz;
 }

}

3、下面我们来实现BeanFactoryPostProcessor#postProcessBeanFactory方法:

代码语言:javascript
复制
@Component
public class MyBeanFactoryPostProcessor implements BeanDefinitionRegistryPostProcessor {
 private List<String> basePackageList = new ArrayList<>();

 /**
  * 基于该扩展实现AOP增强
  * @param beanFactory
  * @throws BeansException
  */
 @Override
 public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        //遍历IoC中BeanDefinition
  Arrays.stream(beanFactory.getBeanDefinitionNames())
    .forEach(x -> enhancer(beanFactory.getBeanDefinition(x)));
 }

    /**
  * 判断beanDefinition是否有@MyComponent注解,如果有则调用EnhancerUtil.createProxyClass创建一个增强后的类,
  * 并替换到BeanDefinition中的beanClass
  * @param beanDefinition
  */
 private void enhancer(BeanDefinition beanDefinition){
  if(ScannedGenericBeanDefinition.class.isAssignableFrom(beanDefinition.getClass())){
   ScannedGenericBeanDefinition bdf = (ScannedGenericBeanDefinition) beanDefinition;
   try {
    String beanClassName = bdf.getBeanClassName();
    Class beanClass = Class.forName(beanClassName);
    if (beanClass != null){
     AnnotationMetadata metadata = AnnotationMetadata.introspect(beanClass);
     if(metadata.hasAnnotation(MyComponent.class.getName())){
      Class enhancedClass = EnhancerUtil.createProxyClass(beanClass);
      if(enhancedClass != beanClass){
       bdf.setBeanClass(enhancedClass);
      }
     }
    }
   } catch (Throwable e) {
    e.printStackTrace();
   }
  }
 }

}

4、定义Bean

代码语言:javascript
复制
@MyComponent
public class TestService02 {

 public String hi(String name){
  System.out.println("===TestService02#hi===");
  return "hello,"+name;
 }

}

5、context.getBean(TestService02.class).testService02.hi("zhangsan")调用方法时,会发现方法前后都已被加入增强逻辑:

代码语言:javascript
复制
before execute:hi
===TestService02#hi===
after execute:hi, ret:hello,zhangsan
execute finish
返回结果:hello,zhangsan

总结

通过使用场景案例分析,我们对BeanFactoryPostProcessor扩展点的使用大致情况应该有了一些了解:

  • BeanDefinitionRegistryPostProcessor#postProcessBeanDefinitionRegistry这个扩展点主要用于实现向IoC容器中注册Bean相关的功能扩展。比如上面案例中我们九利用该扩展点实现了自定义扫描包路径下自定义注解,并向IoC容器中注册Bean
  • BeanFactoryPostProcessor#postProcessBeanFactory这个扩展点主要用于实现将注册到IoC容器中BeanDefinition进行相关操作。以为这个方法执行时机是在postProcessBeanDefinitionRegistry之后,这时Bean都已经以BeanDefinition样式被注册到IoC容器中,这时就可以对这些BeanDefinition进行操作。比如案例中我们获取到BeanDefinition中的beanClass属性,并使用Enhancer工具类创建AOP代理类并设置进去,就实现了Spring中的AOP功能。
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-04-16,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Reactor2020 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 类结构
  • 执行时机
  • 使用场景
    • BeanDefinitionRegistryPostProcessor
      • 案例一
      • 案例二
    • BeanFactoryPostProcessor
    • 总结
    相关产品与服务
    容器服务
    腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档