专栏电商日志财经减肥爱情
投稿投诉
爱情常识
搭配分娩
减肥两性
孕期塑形
财经教案
论文美文
日志体育
养生学堂
电商科学
头戴业界
专栏星座
用品音乐

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

  首先,先看SpringBoot的主配置类:SpringBootApplicationpublicclassStartEurekaApplication{publicstaticvoidmain(String〔〕args){SpringApplication。run(StartEurekaApplication。class,args);}}
  点进SpringBootApplication来看,发现SpringBootApplication是一个组合注解。Target(ElementType。TYPE)Retention(RetentionPolicy。RUNTIME)DocumentedInheritedSpringBootConfigurationEnableAutoConfigurationComponentScan(excludeFilters{Filter(typeFilterType。CUSTOM,classesTypeExcludeFilter。class),Filter(typeFilterType。CUSTOM,classesAutoConfigurationExcludeFilter。class)})publicinterfaceSpringBootApplication{}
  首先我们先来看SpringBootConfiguration:Target({ElementType。TYPE})Retention(RetentionPolicy。RUNTIME)DocumentedConfigurationpublicinterfaceSpringBootConfiguration{}
  可以看到这个注解除了元注解以外,就只有一个Configuration,那也就是说这个注解相当于Configuration,所以这两个注解作用是一样的,它让我们能够去注册一些额外的Bean,并且导入一些额外的配置。
  那Configuration还有一个作用就是把该类变成一个配置类,不需要额外的XML进行配置。所以SpringBootConfiguration就相当于Configuration。进入Configuration,发现Configuration核心是Component,说明Spring的配置类也是Spring的一个组件。Target({ElementType。TYPE})Retention(RetentionPolicy。RUNTIME)DocumentedComponentpublicinterfaceConfiguration{AliasFor(annotationComponent。class)Stringvalue()default;}
  继续来看下一个EnableAutoConfiguration,这个注解是开启自动配置的功能。Target({ElementType。TYPE})Retention(RetentionPolicy。RUNTIME)DocumentedInheritedAutoConfigurationPackageImport({AutoConfigurationImportSelector。class})publicinterfaceEnableAutoConfiguration{StringENABLEDOVERRIDEPROPERTYspring。boot。enableautoconfiguration;Classlt;?〔〕exclude()default{};String〔〕excludeName()default{};}
  可以看到它是由AutoConfigurationPackage,Import(EnableAutoConfigurationImportSelector。class)这两个而组成的,我们先说AutoConfigurationPackage,他是说:让包中的类以及子包中的类能够被自动扫描到spring容器中。Target({ElementType。TYPE})Retention(RetentionPolicy。RUNTIME)DocumentedInheritedImport({Registrar。class})publicinterfaceAutoConfigurationPackage{}
  使用Import来给Spring容器中导入一个组件,这里导入的是Registrar。class。来看下这个Registrar:staticclassRegistrarimplementsImportBeanDefinitionRegistrar,DeterminableImports{Registrar(){}publicvoidregisterBeanDefinitions(AnnotationMetadatametadata,BeanDefinitionRegistryregistry){AutoConfigurationPackages。register(registry,(newAutoConfigurationPackages。PackageImport(metadata))。getPackageName());}publicSetObjectdetermineImports(AnnotationMetadatametadata){returnCollections。singleton(newAutoConfigurationPackages。PackageImport(metadata));}}
  就是通过以上这个方法获取扫描的包路径,可以debug查看具体的值:
  那metadata是什么呢,可以看到是标注在SpringBootApplication注解上的DemosbApplication,也就是我们的主配置类Application:
  其实就是将主配置类(即SpringBootApplication标注的类)的所在包及子包里面所有组件扫描加载到Spring容器。因此我们要把DemoApplication放在项目的最高级中(最外层目录)。
  看看注解Import(AutoConfigurationImportSelector。class),Import注解就是给Spring容器中导入一些组件,这里传入了一个组件的选择器:AutoConfigurationImportSelector。
  可以从图中看出AutoConfigurationImportSelector继承了DeferredImportSelector继承了ImportSelector,ImportSelector有一个方法为:selectImports。将所有需要导入的组件以全类名的方式返回,这些组件就会被添加到容器中。publicString〔〕selectImports(AnnotationMetadataannotationMetadata){if(!this。isEnabled(annotationMetadata)){returnNOIMPORTS;}else{AutoConfigurationMetadataautoConfigurationMetadataAutoConfigurationMetadataLoader。loadMetadata(this。beanClassLoader);AutoConfigurationImportSelector。AutoConfigurationEntryautoConfigurationEntrythis。getAutoConfigurationEntry(autoConfigurationMetadata,annotationMetadata);returnStringUtils。toStringArray(autoConfigurationEntry。getConfigurations());}}
  会给容器中导入非常多的自动配置类(xxxAutoConfiguration);就是给容器中导入这个场景需要的所有组件,并配置好这些组件。
  有了自动配置类,免去了我们手动编写配置注入功能组件等的工作。那是如何获取到这些配置类的呢,看看下面这个方法:protectedAutoConfigurationImportSelector。AutoConfigurationEntrygetAutoConfigurationEntry(AutoConfigurationMetadataautoConfigurationMetadata,AnnotationMetadataannotationMetadata){if(!this。isEnabled(annotationMetadata)){returnEMPTYENTRY;}else{AnnotationAttributesattributesthis。getAttributes(annotationMetadata);ListStringconfigurationsthis。getCandidateConfigurations(annotationMetadata,attributes);configurationsthis。removeDuplicates(configurations);SetStringexclusionsthis。getExclusions(annotationMetadata,attributes);this。checkExcludedClasses(configurations,exclusions);configurations。removeAll(exclusions);configurationsthis。filter(configurations,autoConfigurationMetadata);this。fireAutoConfigurationImportEvents(configurations,exclusions);returnnewAutoConfigurationImportSelector。AutoConfigurationEntry(configurations,exclusions);}}
  我们可以看到getCandidateConfigurations()这个方法,他的作用就是引入系统已经加载好的一些类,到底是那些类呢:protectedListStringgetCandidateConfigurations(AnnotationMetadatametadata,AnnotationAttributesattributes){ListStringconfigurationsSpringFactoriesLoader。loadFactoryNames(this。getSpringFactoriesLoaderFactoryClass(),this。getBeanClassLoader());Assert。notEmpty(configurations,NoautoconfigurationclassesfoundinMETAINFspring。factories。Ifyouareusingacustompackaging,makesurethatfileiscorrect。);returnconfigurations;}publicstaticListStringloadFactoryNames(Classlt;?factoryClass,NullableClassLoaderclassLoader){StringfactoryClassNamefactoryClass。getName();return(List)loadSpringFactories(classLoader)。getOrDefault(factoryClassName,Collections。emptyList());}
  会从METAINFspring。factories中获取资源,然后通过Properties加载资源:privatestaticMapString,ListStringloadSpringFactories(NullableClassLoaderclassLoader){MultiValueMapString,Stringresult(MultiValueMap)cache。get(classLoader);if(result!null){returnresult;}else{try{EnumerationURLurlsclassLoader!null?classLoader。getResources(METAINFspring。factories):ClassLoader。getSystemResources(METAINFspring。factories);LinkedMultiValueMapresultnewLinkedMultiValueMap();while(urls。hasMoreElements()){URLurl(URL)urls。nextElement();UrlResourceresourcenewUrlResource(url);PropertiespropertiesPropertiesLoaderUtils。loadProperties(resource);Iteratorvar6properties。entrySet()。iterator();while(var6。hasNext()){Map。Entrylt;?,?entry(Map。Entry)var6。next();StringfactoryClassName((String)entry。getKey())。trim();String〔〕var9StringUtils。commaDelimitedListToStringArray((String)entry。getValue());intvar10var9。length;for(intvar110;var11var10;var11){StringfactoryNamevar9〔var11〕;result。add(factoryClassName,factoryName。trim());}}}cache。put(classLoader,result);returnresult;}catch(IOExceptionvar13){thrownewIllegalArgumentException(Unabletoloadfactoriesfromlocation〔METAINFspring。factories〕,var13);}}}
  可以知道SpringBoot在启动的时候从类路径下的METAINFspring。factories中获取EnableAutoConfiguration指定的值,将这些值作为自动配置类导入到容器中,自动配置类就生效,帮我们进行自动配置工作。以前我们需要自己配置的东西,自动配置类都帮我们完成了。如下图可以发现Spring常见的一些类已经自动导入。
  接下来看ComponentScan注解,ComponentScan(excludeFilters{Filter(typeFilterType。CUSTOM,classesTypeExcludeFilter。class),Filter(typeFilterType。CUSTOM,classesAutoConfigurationExcludeFilter。class)}),这个注解就是扫描包,然后放入spring容器。ComponentScan(excludeFilters{Filter(typeFilterType。CUSTOM,classes{TypeExcludeFilter。class}),Filter(typeFilterType。CUSTOM,classes{AutoConfigurationExcludeFilter。class})})publicinterfaceSpringBootApplication{}
  总结下SpringbootApplication:就是说,他已经把很多东西准备好,具体是否使用取决于我们的程序或者说配置。
  接下来继续看run方法:publicstaticvoidmain(String〔〕args){SpringApplication。run(Application。class,args);}
  来看下在执行run方法到底有没有用到哪些自动配置的东西,我们点进run:publicConfigurableApplicationContextrun(String。。。args){计时器StopWatchstopWatchnewStopWatch();stopWatch。start();ConfigurableApplicationContextcontextnull;CollectionSpringBootExceptionReporterexceptionReportersnewArrayList();this。configureHeadlessProperty();监听器SpringApplicationRunListenerslistenersthis。getRunListeners(args);listeners。starting();CollectionexceptionReporters;try{ApplicationArgumentsapplicationArgumentsnewDefaultApplicationArguments(args);ConfigurableEnvironmentenvironmentthis。prepareEnvironment(listeners,applicationArguments);this。configureIgnoreBeanInfo(environment);BannerprintedBannerthis。printBanner(environment);准备上下文contextthis。createApplicationContext();exceptionReportersthis。getSpringFactoriesInstances(SpringBootExceptionReporter。class,newClass〔〕{ConfigurableApplicationContext。class},context);预刷新contextthis。prepareContext(context,environment,listeners,applicationArguments,printedBanner);刷新contextthis。refreshContext(context);刷新之后的contextthis。afterRefresh(context,applicationArguments);stopWatch。stop();if(this。logStartupInfo){(newStartupInfoLogger(this。mainApplicationClass))。logStarted(this。getApplicationLog(),stopWatch);}listeners。started(context);this。callRunners(context,applicationArguments);}catch(Throwablevar10){this。handleRunFailure(context,var10,exceptionReporters,listeners);thrownewIllegalStateException(var10);}try{listeners。running(context);returncontext;}catch(Throwablevar9){this。handleRunFailure(context,var9,exceptionReporters,(SpringApplicationRunListeners)null);thrownewIllegalStateException(var9);}}
  那我们关注的就是refreshContext(context);刷新context,我们点进来看。privatevoidrefreshContext(ConfigurableApplicationContextcontext){refresh(context);if(this。registerShutdownHook){try{context。registerShutdownHook();}catch(AccessControlExceptionex){Notallowedinsomeenvironments。}}}
  我们继续点进refresh(context);protectedvoidrefresh(ApplicationContextapplicationContext){Assert。isInstanceOf(AbstractApplicationContext。class,applicationContext);((AbstractApplicationContext)applicationContext)。refresh();}
  他会调用((AbstractApplicationContext)applicationContext)。refresh();方法,我们点进来看:publicvoidrefresh()throwsBeansException,IllegalStateException{synchronized(this。startupShutdownMonitor){Preparethiscontextforrefreshing。prepareRefresh();Tellthesubclasstorefreshtheinternalbeanfactory。ConfigurableListableBeanFactorybeanFactoryobtainFreshBeanFactory();Preparethebeanfactoryforuseinthiscontext。prepareBeanFactory(beanFactory);try{Allowspostprocessingofthebeanfactoryincontextsubclasses。postProcessBeanFactory(beanFactory);Invokefactoryprocessorsregisteredasbeansinthecontext。invokeBeanFactoryPostProcessors(beanFactory);Registerbeanprocessorsthatinterceptbeancreation。registerBeanPostProcessors(beanFactory);Initializemessagesourceforthiscontext。initMessageSource();Initializeeventmulticasterforthiscontext。initApplicationEventMulticaster();Initializeotherspecialbeansinspecificcontextsubclasses。onRefresh();Checkforlistenerbeansandregisterthem。registerListeners();Instantiateallremaining(nonlazyinit)singletons。finishBeanFactoryInitialization(beanFactory);Laststep:publishcorrespondingevent。finishRefresh();}catch(BeansExceptionex){if(logger。isWarnEnabled()){logger。warn(Exceptionencounteredduringcontextinitializationcancellingrefreshattempt:ex);}Destroyalreadycreatedsingletonstoavoiddanglingresources。destroyBeans();Resetactiveflag。cancelRefresh(ex);Propagateexceptiontocaller。throwex;}finally{ResetcommonintrospectioncachesinSpringscore,sincewemightnoteverneedmetadataforsingletonbeansanymore。。。resetCommonCaches();}}}
  由此可知,就是一个spring的bean的加载过程。继续来看一个方法叫做onRefresh():protectedvoidonRefresh()throwsBeansException{Forsubclasses:donothingbydefault。}
  他在这里并没有直接实现,但是我们找他的具体实现:
  比如Tomcat跟web有关,我们可以看到有个ServletWebServerApplicationContext:OverrideprotectedvoidonRefresh(){super。onRefresh();try{createWebServer();}catch(Throwableex){thrownewApplicationContextException(Unabletostartwebserver,ex);}}
  可以看到有一个createWebServer();方法他是创建web容器的,而Tomcat不就是web容器,那是如何创建的呢,我们继续看:privatevoidcreateWebServer(){WebServerwebServerthis。webServer;ServletContextservletContextgetServletContext();if(webServernullservletContextnull){ServletWebServerFactoryfactorygetWebServerFactory();this。webServerfactory。getWebServer(getSelfInitializer());}elseif(servletContext!null){try{getSelfInitializer()。onStartup(servletContext);}catch(ServletExceptionex){thrownewApplicationContextException(Cannotinitializeservletcontext,ex);}}initPropertySources();}
  factory。getWebServer(getSelfInitializer());他是通过工厂的方式创建的。publicinterfaceServletWebServerFactory{WebServergetWebServer(ServletContextInitializer。。。initializers);}
  可以看到它是一个接口,为什么会是接口。因为我们不止是Tomcat一种web容器。
  我们看到还有Jetty,那我们来看TomcatServletWebServerFactory:OverridepublicWebServergetWebServer(ServletContextInitializer。。。initializers){TomcattomcatnewTomcat();FilebaseDir(this。baseDirectory!null)?this。baseDirectory:createTempDir(tomcat);tomcat。setBaseDir(baseDir。getAbsolutePath());ConnectorconnectornewConnector(this。protocol);tomcat。getService()。addConnector(connector);customizeConnector(connector);tomcat。setConnector(connector);tomcat。getHost()。setAutoDeploy(false);configureEngine(tomcat。getEngine());for(ConnectoradditionalConnector:this。additionalTomcatConnectors){tomcat。getService()。addConnector(additionalConnector);}prepareContext(tomcat。getHost(),initializers);returngetTomcatWebServer(tomcat);}
  那这块代码,就是我们要寻找的内置Tomcat,在这个过程当中,我们可以看到创建Tomcat的一个流程。
  如果不明白的话,我们在用另一种方式来理解下,大家要应该都知道stater举点例子。dependencygroupIdorg。springframework。bootgroupIdspringbootstarterdataredisartifactIddependencydependencygroupIdorg。springframework。bootgroupIdspringbootstarterfreemarkerartifactIddependency
  首先自定义一个stater。parentgroupIdorg。springframework。bootgroupIdspringbootstarterparentartifactIdversion2。1。4。RELEASEversionrelativePathparentgroupIdcom。zgwgroupIdgwspringbootstarterartifactIdversion1。0SNAPSHOTversiondependenciesdependencygroupIdorg。springframework。bootgroupIdspringbootautoconfigureartifactIddependencydependencies
  我们先来看maven配置写入版本号,如果自定义一个stater的话必须依赖springbootautoconfigure这个包,我们先看下项目目录。
  publicclassGwServiceImplimplementsGwService{AutowiredGwPropertiesproperties;OverridepublicvoidHello(){Stringnameproperties。getName();System。out。println(name说:你们好啊);}}
  我们做的就是通过配置文件来定制name这个是具体实现。ComponentConfigurationProperties(prefixspring。gwname)publicclassGwProperties{Stringnamezgw;publicStringgetName(){returnname;}publicvoidsetName(Stringname){this。namename;}}
  这个类可以通过ConfigurationProperties读取配置文件。ConfigurationConditionalOnClass(GwService。class)扫描类EnableConfigurationProperties(GwProperties。class)让配置类生效publicclassGwAutoConfiguration{功能描述托管给springauthorzgwreturnBeanConditionalOnMissingBeanpublicGwServicegwService(){returnnewGwServiceImpl();}}
  这个为配置类,为什么这么写因为,springboot的stater都是这么写的,我们可以参照他仿写stater,以达到自动配置的目的,然后我们在通过spring。factories也来进行配置。org。springframework。boot。autoconfigure。EnableAutoConfigurationcom。gw。GwAutoConfiguration
  然后这样一个简单的stater就完成了,然后可以进行maven的打包,在其他项目引入就可以使用。
  原文:cnblogs。comjing99p11504113。html

dnf为什么要有精神这个属性?咋一看这个问题还真是问倒我了,DNF中精神到底有什么用,还有存在的必要吗?力智体精四大属性撑起角色面板在DNF中力智体精被称为四维,对于任何一个职业来说四维都是非常重要的。在对角色为什么英雄联盟设计师,不设计一个所有技能都是被动的英雄?英雄联盟一身都是被动技能的英雄有啊,乌迪尔不就是么。乌迪尔虽然说需要主动切换形态,但本质仍然是依靠平A去触发形态的效果,如虎形态,切换后乌迪尔需要平A才能打出高频攻击,龟形态摸人有你玩过的最良心的游戏可以良心到什么地步?良心的游戏和良心的游戏公司最具代表性的我认为是波兰的游戏公司CDProjektRed和其发布的巫师3游戏,被玩家亲切的称为波兰蠢驴,尤其是该作游戏的资料片,血与酒内容充实,游戏流程英雄联盟中,有人说皇子是乐芙兰假扮的,这是真的吗?作者提出这个问题,应该是看了一个叫做油炸童年亚索老大的漫画,该漫画是作者脑洞大开想出的故事剧情,但是不得不承认,该漫画还是很有意思的。不仅内容搞笑,而且剧情也很丰富,虽然有时候让人魔兽世界8。0中的玛格汉兽人怎么样?魔兽世界玛格汉兽人魔兽世界8。0版本同步开放的玛格汉兽人上线至今已经有半年的时间了,这期间很多玩家都早早解锁的这一新种族,胖哥也在荣耀战团声望达到崇拜后第一时间把自己的母牛战士转成当IG基地被摧毁的时候,TheShy笑得比RNG还开心,姿态被打出阴影英雄联盟上单选手TheShy是目前当之无愧的第一上单,他的打法异常凶悍,每次精彩反打都让人意想不到,莽夫之王的打法非常吸粉!虽然TheShy本人总是强调自己喜欢发育劝架,队伍太喜欢方舟世上竟有如此丑陋的仙女龙?真的长见识了大家好,我是大手游戏,想要获得更多有意思的资讯?快快关注小编吧!感谢方舟生存进化这款游戏的普罗米修斯mod,令小编每天都能发现许多新奇的东西。今天又发现了一只特别有意思的恐龙,它的孤存带人夜闯4AM基地,龙神绝当面回应直接报警,走法律!大家好,我是可爱又迷人的反派角色,电竞魔王。孤存事件已经处于平淡期了,但在今日凌晨,孤存又搞事情了!孤存在3月24日晚上突然发微博,在微博中他表示自己即将回归4AM,这个声明直接让韩服第一男刀竟是中国人?扛着演员狙击硬是两天冲到王者第18名!想必只要是玩过LOL的同学,对于刀锋之影这名英雄肯定有所了解。帅气的外表,飘逸的技能,中二的台词以及恐怖的瞬间伤害使得男刀成为了登场率最高的几名英雄之一。不过喜欢玩男刀的玩家很多,SMLZ打出最强一战!招牌烬1打5女粉丝当场哭了!弹幕狂给马哥道歉备受LOL玩家关注的LPLS9春季赛正在如火如荼的进行,在昨日的比赛中SS21击败OMG,第一局SDG前期取得不小的优势拿到三火龙,顺利赢下大龙团战获得首局胜利第二局SDG掌握初期免费游戏套路深玩家直呼不充值玩不下去氪金,原为课金,最初来源于日语,指支付费用,现特指在网络游戏中的充值行为。免费游戏的盈利模式是在游戏内加入内购项目,让付费的玩家可以获得更多的游戏资源,获得更好的游戏体验。而未付费
Apex英雄疯狗流又添一员猛将,辛烷打法阵容及武器选择攻略随着3月12日的游戏内容更新,Apex英雄正式宣告进入了第一赛季阶段并推出了相应的赛季通行证奖励系统,不过要小天说在这次更新中最引人注目的莫过于全新的传奇角色动力小子辛烷正式加入到打游戏年入850万的小伙带人私闯民宅?网友带路人队友攻楼?本以为上次孤存的瓜已经是吃完了,没想到我们的小存存就是要搞个大新闻出来,那不放在头条怎么对的起他,所以今天的瓜就来说说孤存瓜的后续。上回我们说到,孤存因为和队内发生了合约的纠纷所以RNG败给SDG后,姿态微博被爆破想C没实力,你还是退役吧!3月25日晚上结束的LPL春季赛RNG和SDG的比赛,在比赛之前不少人认为这是一场没有看点的比赛,因为RNG虽然在上一局输给了IG,但是整体打得还不错,而SDG现在还不能确定拿到季看电竞圈内人如何评价孤存带队硬闯4am基地事件,海涛一语点破现在孤存事件已经发展的不光是绝地圈的事了,已经惊动了整个电竞圈。孤存在其微博针对昨晚硬闯4am基地做出回应后,下方的评论超过了12000多条。而这些评论中就包括了很多电竞行业的圈内云台战报不屈的超强反击!惊险决赛连战5场,最后的赢家是天命者泥萌好欢迎收看本期云台战报昨夜是一个不平静的夜晚,在新一届云台争霸战中,16支队伍激烈争斗各方强者齐聚一堂。其中不乏有新面孔参赛,小仙女带大家来回顾一下精彩瞬间吧16进8游园BT手游公益服幻想西游H5一款全新西游回合大作,好玩的手游BT手游公益服幻想西游H5一款全新西游回合大作,好玩的手游幻想西游H5终于广大手游玩家见面啦!幻想西游H5这款游戏在开测前就吸引了大批手游玩家的关注,大家都在问小编这款游戏怎么样,只狼影逝二度主要人物合集和背景介绍在只狼影逝二度的任务中,你会遇到很多人和事。其中一些角色会让你在他们的商店里买东西(或卖东西),而另一些则会通过提示学习和任务来指导你。下面我们就一起来看看主要人物和背景吧。狼(主第五人格疯人院中有猫腻!玩家误入密室后,意外发现监控装置!对此你怎么看?大家好,我是电竞周公瑾。欢迎各位小伙伴来到本期的第五人格不知道多少课监控室今天呢公瑾就和大家来聊一聊第五人格中所谓的监控室,在游戏中,不管是监管者还是求生者,自身都拥有着不一样的技崩坏3宵禁,腾讯儿童锁防沉迷能解决游戏与家庭的矛盾吗?前段时间米哈游搞的防沉迷系统没少被吐槽。也就半个月前,崩坏3突然推出了宵禁制度未成年人每天2200至次日600不能登陆游戏。防沉迷不新鲜,但宵禁这玩法是真的新。而最受这制度影响的,元气骑士假如游戏中只剩4把红武,拿10包辣条打赌,你会选图4哈喽,小伙伴们!我是欧气满满的老宅。现在很多玩家都在抱怨元气骑士中的武器系统太过复杂,武器种类实在太多了,简直让选择障碍的玩家备受煎熬。那么老宅给大家做个假设,假如游戏只剩4种红武CF英雄级武器那些鸡肋的功能,你用够了吗?无影喷射该移除了!大家好,我是殿堂君,今天跟大家聊一聊CF英雄级武器的鸡肋功能。穿越火线已经推出了十几二十把英雄级武器,有的成为了玩家群体的主流武器,比如雷神火麒麟毁灭而有些武器,却沦为鸡肋,不被人
友情链接:快好知快生活快百科快传网中准网文好找聚热点快软件