• SpringSecurity6从入门到上天系列第七篇:讲明白SpringBoot的自动装配完善上篇文章中的结论


    文章目录

    一:SpringBoot的自动装配

    1:从run方法到入口类内容被注册到注解解读器中。

    2:解析入口类注解到加载Bean实例


    大神链接:作者有幸结识技术大神孙哥为好友,获益匪浅。现在把孙哥视频分享给大家。

    孙哥链接:孙哥个人主页
    作者简介:一个颜值99分,只比孙哥差一点的程序员
    本专栏简介:话不多说,让我们一起干翻SpringSecurity6

    本文章简介:话不多说,让我们讲清楚SpringSecurity6中为什么在引入SpringSecurity之后所有的请求都需要先做登录认证才可以进行访问呢

    一:SpringBoot的自动装配

    1:从run方法到入口类内容被注册到注解解读器中。

    1. public static void main(String[] args) {
    2. SpringApplication.run(AlibabaApplication.class, args);
    3. }

           然后走到了一个run方法:

    1. public static ConfigurableApplicationContext run(Class[] primarySources, String[] args) {
    2. return new SpringApplication(primarySources).run(args);
    3. }

            查看这里边的构造器方法:

    1. public SpringApplication(Class... primarySources) {
    2. this(null, primarySources);
    3. }
    4. public SpringApplication(ResourceLoader resourceLoader, Class... primarySources) {
    5. this.resourceLoader = resourceLoader;
    6. Assert.notNull(primarySources, "PrimarySources must not be null");
    7. this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
    8. this.webApplicationType = WebApplicationType.deduceFromClasspath();
    9. this.bootstrapRegistryInitializers = new ArrayList<>(
    10. getSpringFactoriesInstances(BootstrapRegistryInitializer.class));
    11. setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
    12. setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
    13. this.mainApplicationClass = deduceMainApplicationClass();
    14. }

            随后,我们查看具体的run方法:

    1. public ConfigurableApplicationContext run(String... args) {
    2. long startTime = System.nanoTime();
    3. DefaultBootstrapContext bootstrapContext = createBootstrapContext();
    4. ConfigurableApplicationContext context = null;
    5. configureHeadlessProperty();
    6. SpringApplicationRunListeners listeners = getRunListeners(args);
    7. listeners.starting(bootstrapContext, this.mainApplicationClass);
    8. try {
    9. ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
    10. ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
    11. Banner printedBanner = printBanner(environment);
    12. context = createApplicationContext();
    13. context.setApplicationStartup(this.applicationStartup);
    14. //准备解析工作
    15. prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
    16. //真正的解析工作
    17. refreshContext(context);
    18. afterRefresh(context, applicationArguments);
    19. Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime);
    20. if (this.logStartupInfo) {
    21. new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), timeTakenToStartup);
    22. }
    23. listeners.started(context, timeTakenToStartup);
    24. callRunners(context, applicationArguments);
    25. }
    26. catch (Throwable ex) {
    27. if (ex instanceof AbandonedRunException) {
    28. throw ex;
    29. }
    30. handleRunFailure(context, ex, listeners);
    31. throw new IllegalStateException(ex);
    32. }
    33. try {
    34. if (context.isRunning()) {
    35. Duration timeTakenToReady = Duration.ofNanos(System.nanoTime() - startTime);
    36. listeners.ready(context, timeTakenToReady);
    37. }
    38. }
    39. catch (Throwable ex) {
    40. if (ex instanceof AbandonedRunException) {
    41. throw ex;
    42. }
    43. handleRunFailure(context, ex, null);
    44. throw new IllegalStateException(ex);
    45. }
    46. return context;
    47. }

    我们查看准备工作:

    1. private void prepareContext(DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context,
    2. ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
    3. ApplicationArguments applicationArguments, Banner printedBanner) {
    4. context.setEnvironment(environment);
    5. postProcessApplicationContext(context);
    6. addAotGeneratedInitializerIfNecessary(this.initializers);
    7. applyInitializers(context);
    8. listeners.contextPrepared(context);
    9. bootstrapContext.close(context);
    10. if (this.logStartupInfo) {
    11. logStartupInfo(context.getParent() == null);
    12. logStartupProfileInfo(context);
    13. }
    14. // Add boot specific singleton beans
    15. ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
    16. beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
    17. if (printedBanner != null) {
    18. beanFactory.registerSingleton("springBootBanner", printedBanner);
    19. }
    20. if (beanFactory instanceof AbstractAutowireCapableBeanFactory autowireCapableBeanFactory) {
    21. autowireCapableBeanFactory.setAllowCircularReferences(this.allowCircularReferences);
    22. if (beanFactory instanceof DefaultListableBeanFactory listableBeanFactory) {
    23. listableBeanFactory.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
    24. }
    25. }
    26. if (this.lazyInitialization) {
    27. context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
    28. }
    29. context.addBeanFactoryPostProcessor(new PropertySourceOrderingBeanFactoryPostProcessor(context));
    30. if (!AotDetector.useGeneratedArtifacts()) {
    31. // Load the sources
    32. Set sources = getAllSources();
    33. Assert.notEmpty(sources, "Sources must not be empty");
    34. load(context, sources.toArray(new Object[0]));
    35. }
    36. listeners.contextLoaded(context);
    37. }
    38.         getAllSources获取入口类的信息。放到sources这个Set集合里边,然后去做load,我们查看load方法。
      1. protected void load(ApplicationContext context, Object[] sources) {
      2. if (logger.isDebugEnabled()) {
      3. logger.debug("Loading source " + StringUtils.arrayToCommaDelimitedString(sources));
      4. }
      5. BeanDefinitionLoader loader = createBeanDefinitionLoader(getBeanDefinitionRegistry(context), sources);
      6. if (this.beanNameGenerator != null) {
      7. loader.setBeanNameGenerator(this.beanNameGenerator);
      8. }
      9. if (this.resourceLoader != null) {
      10. loader.setResourceLoader(this.resourceLoader);
      11. }
      12. if (this.environment != null) {
      13. loader.setEnvironment(this.environment);
      14. }
      15. loader.load();
      16. }
      17. void load() {
      18. for (Object source : this.sources) {
      19. load(source);
      20. }
      21. }
      22. private void load(Object source) {
      23. Assert.notNull(source, "Source must not be null");
      24. if (source instanceof Class clazz) {
      25. load(clazz);
      26. return;
      27. }
      28. if (source instanceof Resource resource) {
      29. load(resource);
      30. return;
      31. }
      32. if (source instanceof Package pack) {
      33. load(pack);
      34. return;
      35. }
      36. if (source instanceof CharSequence sequence) {
      37. load(sequence);
      38. return;
      39. }
      40. throw new IllegalArgumentException("Invalid source type " + source.getClass());
      41. }
      42. private void load(Class source) {
      43. if (isGroovyPresent() && GroovyBeanDefinitionSource.class.isAssignableFrom(source)) {
      44. // Any GroovyLoaders added in beans{} DSL can contribute beans here
      45. GroovyBeanDefinitionSource loader = BeanUtils.instantiateClass(source, GroovyBeanDefinitionSource.class);
      46. ((GroovyBeanDefinitionReader) this.groovyReader).beans(loader.getBeans());
      47. }
      48. if (isEligible(source)) {
      49. this.annotatedReader.register(source);
      50. }
      51. }

              到这里完成了一个重要的工作:读取入口类中重要的信息,包括注解包括入口类本身。将入口类中的注解注册到注解解读器annotationreader当中。

      2:解析入口类注解到加载Bean

              真正解析Bean的工作是从refreshContext当中进行的。

      1. private void refreshContext(ConfigurableApplicationContext context) {
      2. if (this.registerShutdownHook) {
      3. shutdownHook.registerApplicationContext(context);
      4. }
      5. refresh(context);
      6. }
      7. protected void refresh(ConfigurableApplicationContext applicationContext) {
      8. applicationContext.refresh();
      9. }

                 最后跑到了一个applicationContext的refresh方法当中。

      1. @Override
      2. public void refresh() throws BeansException, IllegalStateException {
      3. synchronized (this.startupShutdownMonitor) {
      4. StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");
      5. // Prepare this context for refreshing.
      6. prepareRefresh();
      7. // Tell the subclass to refresh the internal bean factory.
      8. ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
      9. // Prepare the bean factory for use in this context.
      10. prepareBeanFactory(beanFactory);
      11. try {
      12. // Allows post-processing of the bean factory in context subclasses.
      13. postProcessBeanFactory(beanFactory);
      14. StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");
      15. // Invoke factory processors registered as beans in the context.
      16. invokeBeanFactoryPostProcessors(beanFactory);
      17. // Register bean processors that intercept bean creation.
      18. registerBeanPostProcessors(beanFactory);
      19. beanPostProcess.end();
      20. // Initialize message source for this context.
      21. initMessageSource();
      22. // Initialize event multicaster for this context.
      23. initApplicationEventMulticaster();
      24. // Initialize other special beans in specific context subclasses.
      25. onRefresh();
      26. // Check for listener beans and register them.
      27. registerListeners();
      28. // Instantiate all remaining (non-lazy-init) singletons.
      29. finishBeanFactoryInitialization(beanFactory);
      30. // Last step: publish corresponding event.
      31. finishRefresh();
      32. }
      33. catch (BeansException ex) {
      34. if (logger.isWarnEnabled()) {
      35. logger.warn("Exception encountered during context initialization - " +
      36. "cancelling refresh attempt: " + ex);
      37. }
      38. // Destroy already created singletons to avoid dangling resources.
      39. destroyBeans();
      40. // Reset 'active' flag.
      41. cancelRefresh(ex);
      42. // Propagate exception to caller.
      43. throw ex;
      44. }
      45. finally {
      46. // Reset common introspection caches in Spring's core, since we
      47. // might not ever need metadata for singleton beans anymore...
      48. resetCommonCaches();
      49. contextRefresh.end();
      50. }
      51. }
      52. }

      接下来会进行Bean处理的13方法,其中一个比较关键的方法:invokeBeanFactoryPostProcessors

      1. protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
      2. PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());
      3. // Detect a LoadTimeWeaver and prepare for weaving, if found in the meantime
      4. // (e.g. through an @Bean method registered by ConfigurationClassPostProcessor)
      5. if (!NativeDetector.inNativeImage() && beanFactory.getTempClassLoader() == null &&
      6. beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
      7. beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
      8. beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
      9. }
      10. }

              这里边会对我们的入口类中的注解进行详细的解析和复杂调用。其中对这些注解进行解析的时候,要用到了这么一个类:AutoConfigurationImportSelector

              我们可以从调用链路上去证明这件事情:

      1. "main@1" prio=5 tid=0x1 nid=NA runnable
      2. java.lang.Thread.State: RUNNABLE
      3. at org.springframework.boot.context.annotation.ImportCandidates.load(ImportCandidates.java:90)
      4. at org.springframework.boot.autoconfigure.AutoConfigurationImportSelector.getCandidateConfigurations(AutoConfigurationImportSelector.java:180)
      5. at org.springframework.boot.autoconfigure.AutoConfigurationImportSelector.getAutoConfigurationEntry(AutoConfigurationImportSelector.java:126)
      6. at org.springframework.boot.autoconfigure.AutoConfigurationImportSelector$AutoConfigurationGroup.process(AutoConfigurationImportSelector.java:430)
      7. at org.springframework.context.annotation.ConfigurationClassParser$DeferredImportSelectorGrouping.getImports(ConfigurationClassParser.java:796)
      8. at org.springframework.context.annotation.ConfigurationClassParser$DeferredImportSelectorGroupingHandler.processGroupImports(ConfigurationClassParser.java:726)
      9. at org.springframework.context.annotation.ConfigurationClassParser$DeferredImportSelectorHandler.process(ConfigurationClassParser.java:697)
      10. at org.springframework.context.annotation.ConfigurationClassParser.parse(ConfigurationClassParser.java:182)
      11. at org.springframework.context.annotation.ConfigurationClassPostProcessor.processConfigBeanDefinitions(ConfigurationClassPostProcessor.java:415)
      12. at org.springframework.context.annotation.ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry(ConfigurationClassPostProcessor.java:287)
      13. at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanDefinitionRegistryPostProcessors(PostProcessorRegistrationDelegate.java:344)
      14. at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:115)
      15. at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:779)
      16. at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:597)
      17. - locked <0x12a7> (a java.lang.Object)
      18. at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:146)
      19. at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:733)
      20. at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:435)
      21. at org.springframework.boot.SpringApplication.run(SpringApplication.java:311)
      22. at org.springframework.boot.SpringApplication.run(SpringApplication.java:1301)
      23. at org.springframework.boot.SpringApplication.run(SpringApplication.java:1290)
      24. at com.dashu.AlibabaApplication.main(AlibabaApplication.java:10)

              所以,这个在自动装配的过程当中,确实完成了SpringSecurity的自动加载和配置。

    39. 相关阅读:
      java设计模式之装饰者模式
      广州大学2023-2024学年第一学期《计算机网络》A卷
      AI读懂中国,文心方可雕龙
      2022“金九银十”精选20道JVM面试重点问题及十大模块知识点笔记,看看你会多少?
      Mac 多版本jdk安装与切换
      接收表单数据
      分布式锁3:基于redis的插件redission实现分布式锁
      网络安全(黑客)自学
      单点登录原理
      redis高可用之持久化
    40. 原文地址:https://blog.csdn.net/Facial_Mask/article/details/134432079