写点什么

SpringBoot 设计了哪些可拓展的机制?

  • 2023-04-17
    湖南
  • 本文字数:3281 字

    阅读完需:约 11 分钟

当我们引入注册中心的依赖,比如 nacos 的时候,当我们启动 springboot,这个服务就会根据配置文件自动注册到注册中心中,这个动作是如何完成的?


(注册中心使用了 SpringBoot 中的事件监听机制,在 springboot 初始化的时候完成服务注册)

SpringBoot 核心源码

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {      ...    this.primarySources = new LinkedHashSet(Arrays.asList(primarySources));    // Servlet    this.webApplicationType = WebApplicationType.deduceFromClasspath();      this.bootstrapRegistryInitializers = new ArrayList(this.getSpringFactoriesInstances(BootstrapRegistryInitializer.class));         // 注意这里,Initializers    this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));      // 注意这里 Listeners    this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));      this.mainApplicationClass = this.deduceMainApplicationClass();  }
复制代码

我们可以看到空的 SpringBoot 项目有一些 initializers 以及一些 listeners


注意这两行,换言之我们只要实现这两个类就可以自定义拓展 SpringBoot 了!

这里和手写 Starter 都是对 SpringBoot 的拓展

拓展 Initializer

再看这张图

我们需要研究一下ApplicationContextInitializer这个类:

@FunctionalInterface  public interface ApplicationContextInitializer<C extends ConfigurableApplicationContext> {      /**      * Initialize the given application context.      * @param applicationContext the application to configure      */      void initialize(C applicationContext);  }
复制代码

这样就很清晰了,我们尝试手写一个继承类:

public class DemoInitializer implements ApplicationContextInitializer {      @Override      public void initialize(ConfigurableApplicationContext applicationContext) {          System.out.println("自定义初始化器执行...");          ConfigurableEnvironment environment =          applicationContext.getEnvironment();          Map<String, Object> map = new HashMap<>(1);          map.put("name", "sccccc");          environment.getPropertySources().addLast(new          MapPropertySource("DemoInitializer", map));          System.out.println("DemoInitializer execute, and add some property");      }  }
复制代码

通过 SPI 机制将自定义初始化器交给 list 集合initializers

然后再 debug,就会发现:

最后经过一次回调:

private void prepareContext(DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context,      ...      applyInitializers(context);      ...    // Add boot specific singleton beans     下面是beanFactory的操作
复制代码

遍历所有的初始化器,然后

/**  * Apply any {@link ApplicationContextInitializer}s to the context before it is  * refreshed.  * @param context the configured ApplicationContext (not refreshed yet)  * @see ConfigurableApplicationContext#refresh()  */  @SuppressWarnings({ "rawtypes", "unchecked" })  protected void applyInitializers(ConfigurableApplicationContext context) {      for (ApplicationContextInitializer initializer : getInitializers()) {          Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(initializer.getClass(),          ApplicationContextInitializer.class);          Assert.isInstanceOf(requiredType, context, "Unable to call initializer.");          initializer.initialize(context);      }  }
复制代码


流程:

拓展监听器 ApplicationListener


@FunctionalInterface  public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {      /**      * Handle an application event.      */      void onApplicationEvent(E event);  
/** * Create a new {@code ApplicationListener} for the given payload consumer. */ static <T> ApplicationListener<PayloadApplicationEvent<T>> forPayload(Consumer<T> consumer) { return event -> consumer.accept(event.getPayload()); } }
复制代码

这里和上面 initializer 一样,就不演示了

BeanFactory 的后置处理器 & Bean 的后置处理器

Spring Boot解析配置成BeanDefinition的操作在invokeBeanFactoryPostProcessors方法中自定义 BeanFactory 的后置处理器:

@Componentpublic class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {    @Override    public void postProcessBeanFactory(ConfigurableListableBeanFactory    beanFactory) throws BeansException {        Arrays.asList(beanFactory.getBeanDefinitionNames())        .forEach(beanDefinitionName ->        System.out.println(beanDefinitionName));        System.out.println("BeanFactoryPostProcessor...");    }}
复制代码

自定义 Bean 的后置处理器:

@Componentpublic class MyBeanPostProcessor implements BeanPostProcessor {    @Override    public Object postProcessBeforeInitialization(Object bean, String beanName)    throws BeansException {        if(beanName.equals("userController")){            System.out.println("找到了userController: "+bean);        }        return null;    }}
复制代码

AOP

这个相信大家用的比较多,可以自定义切面:

@Aspect@Componentpublic class LogAspect {
// 切入点 Pointcut 可以对Service服务做切面@Pointcut("execution(* com.example.service.*.*(..))")public void mypointcut(){}
// 前置通知@Before(value = "mypointcut()")public void before(JoinPoint joinPoint){ System.out.println("[前置通知] 准备开始记录日志..."); System.out.println("[前置通知] 目标类是: "+joinPoint.getTarget()); System.out.println("[前置通知] 目标方法是: "+joinPoint.getSignature().getName());}
// 后置通知@AfterReturning(value = "mypointcut()")public void afterReturning(JoinPoint joinPoint){ System.out.println("[后置通知] 记录日志完成..."); System.out.println("[后置通知] 目标类是: "+joinPoint.getTarget()); System.out.println("[后置通知] 目标方法是: "+joinPoint.getSignature().getName());}
/*@Around(value = "mypointcut()")public void around(ProceedingJoinPoint joinPoint){ System.out.println("[环绕通知] 日志记录前的操作..."); try { joinPoint.proceed(); System.out.println("[环绕通知] 日志记录后的操作..."); System.out.println("[环绕通知] "+joinPoint.getTarget()); System.out.println("[环绕通知] "+joinPoint.getSignature().getName()); } catch (Throwable throwable) { System.out.println("[环绕通知] 发生异常的操作..."); throwable.printStackTrace(); }finally { ... }}
复制代码

其他的拓展点

  1. Banner

方法地址:printBanner(env)->bannerPrinter.print->SpringBootBanner#printBanner

可以在 resource 目录下建立 banner.txt 文件夹实现自定义 Banner


  1. Runners

流程:

自定义:

@Componentpublic class JackApplicationRunner implements ApplicationRunner {    @Override    public void run(ApplicationArguments args) throws Exception {        System.out.println("JackApplicationRunner...");    }}
复制代码

作者:ovO

链接:https://juejin.cn/post/7222602874631389241

来源:稀土掘金

用户头像

还未添加个人签名 2021-07-28 加入

公众号:该用户快成仙了

评论

发布
暂无评论
SpringBoot设计了哪些可拓展的机制?_做梦都在改BUG_InfoQ写作社区