范文健康探索娱乐情感热点
投稿投诉
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文

一次性搞懂SpringBoot注解原理与自动装配原理,万字长文

  首先,先看SpringBoot的主配置类:  @SpringBootApplication public class StartEurekaApplication {     public static void main(String[] args)     {         SpringApplication.run(StartEurekaApplication.class, args);     } }
  点进@SpringBootApplication来看,发现@SpringBootApplication是一个组合注解。  @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @SpringBootConfiguration @EnableAutoConfiguration @ComponentScan(excludeFilters = {       @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),       @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) }) public @interface SpringBootApplication {  }
  首先我们先来看 @SpringBootConfiguration:  @Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Configuration public @interface SpringBootConfiguration { }
  可以看到这个注解除了元注解以外,就只有一个@Configuration,那也就是说这个注解相当于@Configuration,所以这两个注解作用是一样的,它让我们能够去注册一些额外的Bean,并且导入一些额外的配置。
  那@Configuration还有一个作用就是把该类变成一个配置类,不需要额外的XML进行配置。所以@SpringBootConfiguration就相当于@Configuration。进入@Configuration,发现@Configuration核心是@Component,说明Spring的配置类也是Spring的一个组件。  @Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Component public @interface Configuration {     @AliasFor(         annotation = Component.class     )     String value() default ""; }
  继续来看下一个@EnableAutoConfiguration,这个注解是开启自动配置的功能。  @Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @AutoConfigurationPackage @Import({AutoConfigurationImportSelector.class}) public @interface EnableAutoConfiguration {     String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";      Class<?>[] exclude() default {};      String[] excludeName() default {}; }
  可以看到它是由 @AutoConfigurationPackage,@Import(EnableAutoConfigurationImportSelector.class)这两个而组成的,我们先说@AutoConfigurationPackage,他是说:让包中的类以及子包中的类能够被自动扫描到spring容器中。  @Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @Import({Registrar.class}) public @interface AutoConfigurationPackage { }
  使用@Import来给Spring容器中导入一个组件 ,这里导入的是Registrar.class。来看下这个Registrar:  static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {         Registrar() {         }          public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {             AutoConfigurationPackages.register(registry, (new AutoConfigurationPackages.PackageImport(metadata)).getPackageName());         }          public Set determineImports(AnnotationMetadata metadata) {             return Collections.singleton(new AutoConfigurationPackages.PackageImport(metadata));         }     }
  就是通过以上这个方法获取扫描的包路径,可以debug查看具体的值:
  那metadata是什么呢,可以看到是标注在@SpringBootApplication注解上的DemosbApplication,也就是我们的主配置类Application:
  其实就是将主配置类(即@SpringBootApplication标注的类)的所在包及子包里面所有组件扫描加载到Spring容器。因此我们要把DemoApplication放在项目的最高级中(最外层目录)。
  看看注解@Import(AutoConfigurationImportSelector.class),@Import注解就是给Spring容器中导入一些组件,这里传入了一个组件的选择器:AutoConfigurationImportSelector。
  可以从图中看出AutoConfigurationImportSelector 继承了 DeferredImportSelector 继承了 ImportSelector,ImportSelector有一个方法为:selectImports。将所有需要导入的组件以全类名的方式返回,这些组件就会被添加到容器中。  public String[] selectImports(AnnotationMetadata annotationMetadata) {     if (!this.isEnabled(annotationMetadata)) {         return NO_IMPORTS;     } else {         AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);         AutoConfigurationImportSelector.AutoConfigurationEntry autoConfigurationEntry =          this.getAutoConfigurationEntry(autoConfigurationMetadata, annotationMetadata);         return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());     } }
  会给容器中导入非常多的自动配置类(xxxAutoConfiguration);就是给容器中导入这个场景需要的所有组件,并配置好这些组件。
  有了自动配置类,免去了我们手动编写配置注入功能组件等的工作。那是如何获取到这些配置类的呢,看看下面这个方法:  protected AutoConfigurationImportSelector.AutoConfigurationEntry    getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata, AnnotationMetadata annotationMetadata) {     if (!this.isEnabled(annotationMetadata)) {         return EMPTY_ENTRY;     } else {         AnnotationAttributes attributes = this.getAttributes(annotationMetadata);         List configurations = this.getCandidateConfigurations(annotationMetadata, attributes);         configurations = this.removeDuplicates(configurations);         Set exclusions = this.getExclusions(annotationMetadata, attributes);         this.checkExcludedClasses(configurations, exclusions);         configurations.removeAll(exclusions);         configurations = this.filter(configurations, autoConfigurationMetadata);         this.fireAutoConfigurationImportEvents(configurations, exclusions);         return new AutoConfigurationImportSelector.AutoConfigurationEntry(configurations, exclusions);     } }
  我们可以看到getCandidateConfigurations()这个方法,他的作用就是引入系统已经加载好的一些类,到底是那些类呢:  protected List getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {     List configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader());     Assert.notEmpty(configurations,      "No auto configuration classes found in META-INF/spring.factories. If you are using a custom packaging, make sure that file is correct.");     return configurations; } public static List loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {     String factoryClassName = factoryClass.getName();     return (List)loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList()); }
  会从META-INF/spring.factories中获取资源,然后通过Properties加载资源:  private static Map> loadSpringFactories(@Nullable ClassLoader classLoader) {     MultiValueMap result = (MultiValueMap)cache.get(classLoader);     if (result != null) {         return result;     } else {         try {             Enumeration urls = classLoader !=            null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories");             LinkedMultiValueMap result = new LinkedMultiValueMap();              while(urls.hasMoreElements()) {                 URL url = (URL)urls.nextElement();                 UrlResource resource = new UrlResource(url);                 Properties properties = PropertiesLoaderUtils.loadProperties(resource);                 Iterator var6 = properties.entrySet().iterator();                  while(var6.hasNext()) {                     Map.Entry<?, ?> entry = (Map.Entry)var6.next();                     String factoryClassName = ((String)entry.getKey()).trim();                     String[] var9 = StringUtils.commaDelimitedListToStringArray((String)entry.getValue());                     int var10 = var9.length;                      for(int var11 = 0; var11 < var10; ++var11) {                         String factoryName = var9[var11];                         result.add(factoryClassName, factoryName.trim());                     }                 }             }              cache.put(classLoader, result);             return result;         } catch (IOException var13) {             throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var13);         }     } }
  可以知道SpringBoot在启动的时候从类路径下的META-INF/spring.factories中获取EnableAutoConfiguration指定的值,将这些值作为自动配置类导入到容器中,自动配置类就生效,帮我们进行自动配置工作。以前我们需要自己配置的东西,自动配置类都帮我们完成了。如下图可以发现Spring常见的一些类已经自动导入。
  接下来看@ComponentScan注解,@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class), @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) }),这个注解就是扫描包,然后放入spring容器。  @ComponentScan(excludeFilters = {   @Filter(type = FilterType.CUSTOM,classes = {TypeExcludeFilter.class}),    @Filter(type = FilterType.CUSTOM,classes = {AutoConfigurationExcludeFilter.class})}) public @interface SpringBootApplication {}
  总结下@SpringbootApplication:就是说,他已经把很多东西准备好,具体是否使用取决于我们的程序或者说配置。
  接下来继续看run方法:  public static void main(String[] args) {         SpringApplication.run(Application.class, args);     }
  来看下在执行run方法到底有没有用到哪些自动配置的东西,我们点进run:  public ConfigurableApplicationContext run(String... args) {     //计时器     StopWatch stopWatch = new StopWatch();     stopWatch.start();     ConfigurableApplicationContext context = null;     Collection exceptionReporters = new ArrayList();     this.configureHeadlessProperty();     //监听器     SpringApplicationRunListeners listeners = this.getRunListeners(args);     listeners.starting();      Collection exceptionReporters;     try {         ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);         ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);         this.configureIgnoreBeanInfo(environment);         Banner printedBanner = this.printBanner(environment);         //准备上下文         context = this.createApplicationContext();         exceptionReporters = this.getSpringFactoriesInstances(SpringBootExceptionReporter.class,                       new Class[]{ConfigurableApplicationContext.class}, context);         //预刷新context         this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);         //刷新context         this.refreshContext(context);         //刷新之后的context         this.afterRefresh(context, applicationArguments);         stopWatch.stop();         if (this.logStartupInfo) {             (new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);         }          listeners.started(context);         this.callRunners(context, applicationArguments);     } catch (Throwable var10) {         this.handleRunFailure(context, var10, exceptionReporters, listeners);         throw new IllegalStateException(var10);     }      try {         listeners.running(context);         return context;     } catch (Throwable var9) {         this.handleRunFailure(context, var9, exceptionReporters, (SpringApplicationRunListeners)null);         throw new IllegalStateException(var9);     } }
  那我们关注的就是 refreshContext(context); 刷新context,我们点进来看。  private void refreshContext(ConfigurableApplicationContext context) {    refresh(context);    if (this.registerShutdownHook) {       try {          context.registerShutdownHook();       }       catch (AccessControlException ex) {          // Not allowed in some environments.       }    } }
  我们继续点进refresh(context);  protected void refresh(ApplicationContext applicationContext) {    Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);    ((AbstractApplicationContext) applicationContext).refresh(); }
  他会调用 ((AbstractApplicationContext) applicationContext).refresh();方法,我们点进来看:  public void refresh() throws BeansException, IllegalStateException {    synchronized (this.startupShutdownMonitor) {       // Prepare this context for refreshing.       prepareRefresh();       // Tell the subclass to refresh the internal bean factory.       ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();       // Prepare the bean factory for use in this context.       prepareBeanFactory(beanFactory);        try {          // Allows post-processing of the bean factory in context subclasses.          postProcessBeanFactory(beanFactory);          // Invoke factory processors registered as beans in the context.          invokeBeanFactoryPostProcessors(beanFactory);          // Register bean processors that intercept bean creation.          registerBeanPostProcessors(beanFactory);          // Initialize message source for this context.          initMessageSource();          // Initialize event multicaster for this context.          initApplicationEventMulticaster();          // Initialize other special beans in specific context subclasses.          onRefresh();          // Check for listener beans and register them.          registerListeners();          // Instantiate all remaining (non-lazy-init) singletons.          finishBeanFactoryInitialization(beanFactory);          // Last step: publish corresponding event.          finishRefresh();       }catch (BeansException ex) {          if (logger.isWarnEnabled()) {             logger.warn("Exception encountered during context initialization - " +                   "cancelling refresh attempt: " + ex);          }          // Destroy already created singletons to avoid dangling resources.          destroyBeans();          // Reset "active" flag.          cancelRefresh(ex);          // Propagate exception to caller.          throw ex;       }finally {          // Reset common introspection caches in Spring"s core, since we          // might not ever need metadata for singleton beans anymore...          resetCommonCaches();       }    } }
  由此可知,就是一个spring的bean的加载过程。继续来看一个方法叫做 onRefresh():  protected void onRefresh() throws BeansException {    // For subclasses: do nothing by default. }
  他在这里并没有直接实现,但是我们找他的具体实现:
  比如Tomcat跟web有关,我们可以看到有个ServletWebServerApplicationContext:  @Override protected void onRefresh() {    super.onRefresh();    try {       createWebServer();    }    catch (Throwable ex) {       throw new ApplicationContextException("Unable to start web server", ex);    } }
  可以看到有一个createWebServer();方法他是创建web容器的,而Tomcat不就是web容器,那是如何创建的呢,我们继续看:  private void createWebServer() {    WebServer webServer = this.webServer;    ServletContext servletContext = getServletContext();    if (webServer == null && servletContext == null) {       ServletWebServerFactory factory = getWebServerFactory();       this.webServer = factory.getWebServer(getSelfInitializer());    }    else if (servletContext != null) {       try {          getSelfInitializer().onStartup(servletContext);       }       catch (ServletException ex) {          throw new ApplicationContextException("Cannot initialize servlet context",                ex);       }    }    initPropertySources(); }
  factory.getWebServer(getSelfInitializer());他是通过工厂的方式创建的。  public interface ServletWebServerFactory {    WebServer getWebServer(ServletContextInitializer... initializers); }
  可以看到 它是一个接口,为什么会是接口。因为我们不止是Tomcat一种web容器。
  我们看到还有Jetty,那我们来看TomcatServletWebServerFactory:  @Override public WebServer getWebServer(ServletContextInitializer... initializers) {    Tomcat tomcat = new Tomcat();    File baseDir = (this.baseDirectory != null) ? this.baseDirectory          : createTempDir("tomcat");    tomcat.setBaseDir(baseDir.getAbsolutePath());    Connector connector = new Connector(this.protocol);    tomcat.getService().addConnector(connector);    customizeConnector(connector);    tomcat.setConnector(connector);    tomcat.getHost().setAutoDeploy(false);    configureEngine(tomcat.getEngine());    for (Connector additionalConnector : this.additionalTomcatConnectors) {       tomcat.getService().addConnector(additionalConnector);    }    prepareContext(tomcat.getHost(), initializers);    return getTomcatWebServer(tomcat); }
  那这块代码,就是我们要寻找的内置Tomcat,在这个过程当中,我们可以看到创建Tomcat的一个流程。
  如果不明白的话, 我们在用另一种方式来理解下,大家要应该都知道stater举点例子。       org.springframework.boot     spring-boot-starter-data-redis       org.springframework.boot     spring-boot-starter-freemarker 
  首先自定义一个stater。       org.springframework.boot     spring-boot-starter-parent     2.1.4.RELEASE       com.zgw gw-spring-boot-starter 1.0-SNAPSHOT                org.springframework.boot         spring-boot-autoconfigure      
  我们先来看maven配置写入版本号,如果自定义一个stater的话必须依赖spring-boot-autoconfigure这个包,我们先看下项目目录。
  public class GwServiceImpl  implements GwService{     @Autowired     GwProperties properties;      @Override     public void Hello()     {         String name=properties.getName();         System.out.println(name+"说:你们好啊");     } }
  我们做的就是通过配置文件来定制name这个是具体实现。  @Component @ConfigurationProperties(prefix = "spring.gwname") public class GwProperties {      String name="zgw";      public String getName() {         return name;     }      public void setName(String name) {         this.name = name;     } }
  这个类可以通过@ConfigurationProperties读取配置文件。  @Configuration @ConditionalOnClass(GwService.class)  //扫描类 @EnableConfigurationProperties(GwProperties.class) //让配置类生效 public class GwAutoConfiguration {      /**     * 功能描述 托管给spring     * @author zgw     * @return     */     @Bean     @ConditionalOnMissingBean     public GwService gwService()     {         return new GwServiceImpl();     } }
  这个为配置类,为什么这么写因为,spring-boot的stater都是这么写的,我们可以参照他仿写stater,以达到自动配置的目的,然后我们在通过spring.factories也来进行配置。  org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.gw.GwAutoConfiguration
  然后这样一个简单的stater就完成了,然后可以进行maven的打包,在其他项目引入就可以使用。
  原文:cnblogs.com/jing99/p/11504113.html
原神申鹤云堇加强,增伤能力进一步提升,往好的方向发展派蒙都说了别急,这不,加强来了冻梨这才V2,看来后面还有机会继续加强,搞快点!这次有关申鹤云堇这两位2。4版本的新角色调整终于来了,注意,这仍然不是最终结果,还有加强的机会。申鹤进最自由的游戏家园,玩家徒手建高达,甚至还造了个能动的太阳系对各位热爱网络游戏的玩家而言,相信对家园系统不会陌生,目前只要是MMO网游,基本上都会标配家园,而且随着行业内卷,各大网游的家园系统也是越来越自由。但要说整个国产网游圈,哪款游戏的质感爆表的太阳骑士盔甲!黑魂3盔甲纹理增强mod在黑暗之魂中不仅拥有独特的世界观,刺激的Boss战以及各种宫崎英高的恶意,当然还少不了各种造型帅气或风格迥异的盔甲。近日mod制作者PinkBish为黑暗之魂3(DarkSoulsReno7Pro英雄联盟手游限定版开箱来和金克斯共赴枪炮盛宴金克斯的含义?就是金克斯,笨!最近英雄联盟双城之战收视率火爆,动画中的金克斯讨人怜爱,与其关联的剧情令人唏嘘,但相信不少人早在接触英雄联盟游戏时就已经对金克斯这位暴走萝莉充满好感了玩转战舰世界竞技端游,潜艇作战干货大放送据前瞻产业研究院发布的中国网络游戏行业商业模式创新与投资机会分析报告称,根据网游行业市场规模与发展趋势分析得出,现阶段我国网络游戏用户规模呈现波动上升态势,且使用率保持在50以上,咒语力量3增强版今日上线Steam官方发布PC版预告THQNordic工作室旗下的奇幻即时战略游戏咒语力量3增强版今日上线Steam,同时原版下架,目前只可购买游戏增强版,增强版原价132元,现在至12月10日期间购买只需33元。拥传奇打米不是梦!盟重冰雪真正可以打米的传奇你还没有玩过吗大家好,小编这次继续跟大家介绍现下最火爆的传奇打金游戏盟重冰雪,实现真正打米,传奇可以打金不是梦。感兴趣的就继续往下看吧。盟重冰雪采用复古画风,原滋原味回味经典,打金天堂,长久稳定12月新游推荐太空无限魅力,光环终于降临2021年的末班车悄然而至,相比于11月大作云集战火连天的火热场景,12月发行的新游稍显平淡,重量级游戏虽然不多,但是还是有像光环无限真三国无双8帝国这样的高知名度作品。所以12月微软确认光环无限战役任务不能重复游玩近日,微软官方的一位发言人在一次采访中向外媒表示,旗下新作光环无限的战役任务一旦通关之后就不能重复游玩,除非玩家开启一个全新的存档。这对很多喜欢完美通关或者是多次游玩任务的玩家来说动森新DLC上线一个月后,小动物的衣服依然会无故消失不适宜在工作期间浏览,但非要看也行。一个月前,人气游戏集合啦!动物森友会发布了它最新的付费DLC快乐家乐园。众所周知,动森是个全年龄向的游戏,这次更新的内容自然也很正常,除了新的岛在Dnf这款游戏中,哪些职业的设计堪称经典,哪些职业又是敷衍?dnf职业这么多,想要保持百花齐放各有千秋确实十分困难。后面新出的职业,职业模型的建立既要有自己的特色,也要避免与前面的老职业重复,这样的工作量显然远超当时设计老职业时的工作量!大
无差别忍者池移除地怨虞角都,有个忍者上架一个月,就放进去了2021年还有不到一个月的时间就要结束了,这一年一年的时间过得真快,曾经很多人猜测过的仙人兜和须佐能乎佐助也都如约上架,并且在明年1月份还有一个人气极高的秽土鼬。虽然看到了秽土鼬的三国志英杰传,救援公孙瓒时,你是选择困难还是简单的关卡呢?DOS版的三国志英杰传在当年玩家还不熟悉或者初次接触这个游戏的情况下,难度还是非常大的,前面几关就恶意满满,许多玩家甚至被卡在界桥之战过不去。当把这个游戏搞懂后,又发现这个游戏非常LOL手游官方全英雄难度评级,仅13位被评困难,皎月只是简单?在LOL手游中,当玩家自己的视野中仅有一名敌方英雄时,此时释放技能会默认对英雄这一目标释放,而且在目标繁多的团战中也可以通过手动锁敌,来防止自己技能给错人。这样的设定有一个好处,那简单的车辆入门,带你玩转坦克世界活动坦克世界游戏的全新活动将要来临啦!相信本次的活动也会跟往常一样,引来众多新玩家们的疯狂涌入。可是,坦克世界毕竟是一款以硬核的装甲战斗为主的游戏,想要玩的通透,总要需要一些指引和建议皇室战争冰法增加登场效果,折磨套将成往事,平衡调整大快人心大家好,在看到皇室战争今年最后一次卡牌平衡调整消息后,小享觉得太解气了,这一期文章可能有强烈个人观点。大家继续往下看,首先三言两语给大家说一下这一次更新的卡牌(消息灵通的小伙伴早知朋友打游戏太菜了怎么办?不得不说,我有个朋友打游戏是真的菜,自己的谜之操作能把自己逗笑的那种这就是所谓的又菜又爱玩吧,而且总喜欢拉着我玩我们宿舍我只跟她最好,别的室友经常不在,所以我两去哪都是结伴一起最近2021年下半年switch发布的游戏,你最喜欢哪个呢?我们的2021年下半年switch游戏推荐终于来了!近期,有很多端游和手游都玩得有点腻了的同学在问小编,有没有其他平台的好游戏推荐,好准备在为数不多的寒假时光中能够尽情地畅游游戏世球王会真的是太令人失望了,成都AG超玩会不敌RW侠,糟糕的比赛球王会RW侠战队终于突破了自己的极限,他们在12月2号的比赛中成功击败了AG战队,这是一支非常强大的队伍,他们能够获得这次比赛的胜利得益于他们整体发挥更为出色,这场比赛让他们的士气黑暗风动作游戏IllusionLands试玩版即将上线ManyDevStudio正在打造一款黑暗幻想类动作冒险RPGIllusionLands,该作已上架Steam平台,支持中文。官方发布游戏新预告,并表示将于近期推出Demo试玩版,战舰世界全球同步活动上线,心心念念的国服研发局要来了今天战舰世界最热门的话题莫过于心心念念的研发局了,打开浏览器,有关国服研发局消息在百度贴吧里讨论不断,几十万的热度表达了玩家对国服研发局的期待。临近年末,战舰世界作为模拟海上战争游部落冲突全球锦标赛圆满结束,国人部落霸气夺冠经过三天的精彩战斗,2021部落冲突全球锦标赛决赛终于落下了帷幕。国人部落J。X。Tiger不负众望,一路过关斩将,以4战4胜,决赛15星的好成绩,最终荣登2021年部落冲突全球锦