帝國建設網(wǎng)站成功營銷十大經(jīng)典案例
文章目錄
- 前言
- SpringBoot核心源碼
- 拓展Initializer
- 拓展監(jiān)聽器ApplicationListener
- BeanFactory的后置處理器 & Bean的后置處理器
- AOP
- 其他的拓展點
前言
- 當我們引入注冊中心的依賴,比如nacos的時候,當我們啟動springboot,這個服務就會根據(jù)配置文件自動注冊到注冊中心中,這個動作是如何完成的?
- 注冊中心使用了SpringBoot中的事件監(jiān)聽機制,在springboot初始化的時候完成服務注冊
SpringBoot核心源碼
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) { ...this.primarySources = new LinkedHashSet(Arrays.asList(primarySources));// Servletthis.webApplicationType = WebApplicationType.deduceFromClasspath(); this.bootstrapRegistryInitializers = new ArrayList(this.getSpringFactoriesInstances(BootstrapRegistryInitializer.class)); // 注意這里,Initializersthis.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class)); // 注意這里 Listenersthis.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class)); this.mainApplicationClass = this.deduceMainApplicationClass();
}
我們可以看到空的SpringBoot項目有一些initializers以及一些listeners
注意這兩行,換言之我們只要實現(xiàn)這兩個類就可以自定義拓展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("自定義初始化器執(zhí)行..."); 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,就會發(fā)現(xiàn):
最后經(jīng)過一次回調(diào):
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); }
}
流程:
拓展監(jiān)聽器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的后置處理器:
@Component
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {@Overridepublic void postProcessBeanFactory(ConfigurableListableBeanFactorybeanFactory) throws BeansException {Arrays.asList(beanFactory.getBeanDefinitionNames()).forEach(beanDefinitionName ->System.out.println(beanDefinitionName));System.out.println("BeanFactoryPostProcessor...");}
}
自定義Bean的后置處理器:
@Component
public class MyBeanPostProcessor implements BeanPostProcessor {@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName)throws BeansException {if(beanName.equals("userController")){System.out.println("找到了userController: "+bean);}return null;}
}
AOP
這個相信大家用的比較多,可以自定義切面:
@Aspect
@Component
public 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("[環(huán)繞通知] 日志記錄前的操作...");try {joinPoint.proceed();System.out.println("[環(huán)繞通知] 日志記錄后的操作...");System.out.println("[環(huán)繞通知] "+joinPoint.getTarget());System.out.println("[環(huán)繞通知] "+joinPoint.getSignature().getName());} catch (Throwable throwable) {System.out.println("[環(huán)繞通知] 發(fā)生異常的操作...");throwable.printStackTrace();}finally {...}
}
其他的拓展點
- Banner
方法地址:
printBanner(env)->bannerPrinter.print->SpringBootBanner#printBanner
可以在resource目錄下建立banner.txt文件夾實現(xiàn)自定義Banner
- Runners
流程:
自定義:
@Component
public class JackApplicationRunner implements ApplicationRunner {@Overridepublic void run(ApplicationArguments args) throws Exception {System.out.println("JackApplicationRunner...");}
}