Ifyouwanttoconfigureabeandefinitionforastaticnestedclass,youhavetousethebinarynameofthenestedclass.
TheSpringteamgenerallyadvocatesconstructorinjectionasitenablesonetoimplementapplicationcomponentsasimmutableobjectsandtoensurethatrequireddependenciesarenotnull.Furthermoreconstructor-injectedcomponentsarealwaysreturnedtoclient(calling)codeinafullyinitializedstate.Asasidenote,alargenumberofconstructorargumentsisabadcodesmell,implyingthattheclasslikelyhastoomanyresponsibilitiesandshouldberefactoredtobetteraddressproperseparationofconcerns.
UsetheDIstylethatmakesthemostsenseforaparticularclass.Sometimes,whendealingwiththird-partyclassesforwhichyoudonothavethesource,thechoiceismadeforyou.Forexample,ifathird-partyclassdoesnotexposeanysettermethods,thenconstructorinjectionmaybetheonlyavailableformofDI.
Circulardependencies
Ifyouusepredominantlyconstructorinjection,itispossibletocreateanunresolvablecirculardependencyscenario.
Forexample:ClassArequiresaninstanceofclassBthroughconstructorinjection,andclassBrequiresaninstanceofclassAthroughconstructorinjection.IfyouconfigurebeansforclassesAandBtobeinjectedintoeachother,theSpringIoCcontainerdetectsthiscircularreferenceatruntime,andthrowsaBeanCurrentlyInCreationException.
Onepossiblesolutionistoeditthesourcecodeofsomeclassestobeconfiguredbysettersratherthanconstructors.Alternatively,avoidconstructorinjectionandusesetterinjectiononly.Inotherwords,althoughitisnotrecommended,youcanconfigurecirculardependencieswithsetterinjection.
Unlikethetypicalcase(withnocirculardependencies),acirculardependencybetweenbeanAandbeanBforcesoneofthebeanstobeinjectedintotheotherpriortobeingfullyinitializeditself(aclassicchicken/eggscenario).
Youcancontrolnotonlythevariousdependenciesandconfigurationvaluesthataretobepluggedintoanobjectthatiscreatedfromaparticularbeandefinition,butalsothescopeoftheobjectscreatedfromaparticularbeandefinition.ThisapproachispowerfulandflexibleinthatyoucanchoosethescopeoftheobjectsyoucreatethroughconfigurationinsteadofhavingtobakeinthescopeofanobjectattheJavaclasslevel.Beanscanbedefinedtobedeployedinoneofanumberofscopes:outofthebox,theSpringFrameworksupportssixscopes,fourofwhichareavailableonlyifyouuseaweb-awareApplicationContext.
Onlyonesharedinstanceofasingletonbeanismanaged,andallrequestsforbeanswithanidoridsmatchingthatbeandefinitionresultinthatonespecificbeaninstancebeingreturnedbytheSpringcontainer.(只管理单例bean的一个共享实例,并且所有对id或id与该bean定义匹配的bean的请求都会导致Spring容器返回一个特定的bean实例。)
Toputitanotherway,whenyoudefineabeandefinitionanditisscopedasasingleton,theSpringIoCcontainercreatesexactlyoneinstanceoftheobjectdefinedbythatbeandefinition.Thissingleinstanceisstoredinacacheofsuchsingletonbeans,andallsubsequentrequestsandreferencesforthatnamedbeanreturnthecachedobject.
Spring’sconceptofasingletonbeandiffersfromtheSingletonpatternasdefinedintheGangofFour(GoF)patternsbook.TheGoFSingletonhard-codesthescopeofanobjectsuchthatoneandonlyoneinstanceofaparticularclassiscreatedperClassLoader.ThescopeoftheSpringsingletonisbestdescribedaspercontainerandperbean.ThismeansthatifyoudefineonebeanforaparticularclassinasingleSpringcontainer,thentheSpringcontainercreatesoneandonlyoneinstanceoftheclassdefinedbythatbeandefinition.ThesingletonscopeisthedefaultscopeinSpring.TodefineabeanasasingletoninXML,youwouldwrite,forexample:
ThefollowingdiagramillustratestheSpringprototypescope.Adataaccessobject(DAO)isnottypicallyconfiguredasaprototype,becauseatypicalDAOdoesnotholdanyconversationalstate;itwasjusteasierforthisauthortoreusethecoreofthesingletondiagram.
ThefollowingexampledefinesabeanasaprototypeinXML:
Whenyouusesingleton-scopedbeanswithdependenciesonprototypebeans,beawarethatdependenciesareresolvedatinstantiationtime.Thusifyoudependency-injectaprototype-scopedbeanintoasingleton-scopedbean,anewprototypebeanisinstantiatedandthendependency-injectedintothesingletonbean.Theprototypeinstanceisthesoleinstancethatiseversuppliedtothesingleton-scopedbean.
Therequest,session,application,andwebsocketscopesareonlyavailableifyouuseaweb-awareSpringApplicationContextimplementation(suchasXmlWebApplicationContext).IfyouusethesescopeswithregularSpringIoCcontainerssuchastheClassPathXmlApplicationContext,anIllegalStateExceptionwillbethrowncomplainingaboutanunknownbeanscope.
Tosupportthescopingofbeansattherequest,session,application,andwebsocketlevels(web-scopedbeans),someminorinitialconfigurationisrequiredbeforeyoudefineyourbeans.(Thisinitialsetupisnotrequiredforthestandardscopes,singletonandprototype.)
HowyouaccomplishthisinitialsetupdependsonyourparticularServletenvironment.
IfyouaccessscopedbeanswithinSpringWebMVC,ineffect,withinarequestthatisprocessedbytheSpringDispatcherServlet,thennospecialsetupisnecessary:DispatcherServletalreadyexposesallrelevantstate.
IfyouuseaServlet2.5webcontainer,withrequestsprocessedoutsideofSpring’sDispatcherServlet(forexample,whenusingJSForStruts),youneedtoregistertheorg.springframework.web.context.request.RequestContextListenerServletRequestListener.ForServlet3.0+,thiscanbedoneprogrammaticallyviatheWebApplicationInitializerinterface.Alternatively,orforoldercontainers,addthefollowingdeclarationtoyourwebapplication’sweb.xmlfile:
ConsiderthefollowingXMLconfigurationforabeandefinition:
Whenusingannotation-drivencomponentsorJavaConfig,the@RequestScopeannotationcanbeusedtoassignacomponenttotherequestscope.
@RequestScope@ComponentpublicclassLoginAction{//...}SessionscopeConsiderthefollowingXMLconfigurationforabeandefinition:
Whenusingannotation-drivencomponentsorJavaConfig,the@SessionScopeannotationcanbeusedtoassignacomponenttothesessionscope.
@SessionScope@ComponentpublicclassUserPreferences{//...}ApplicationscopeConsiderthefollowingXMLconfigurationforabeandefinition:
Whenusingannotation-drivencomponentsorJavaConfig,the@ApplicationScopeannotationcanbeusedtoassignacomponenttotheapplicationscope.
@ApplicationScope@ComponentpublicclassAppPreferences{//...}Scopedbeansasdependencies(将限定范围的bean作为依赖项)TheSpringIoCcontainermanagesnotonlytheinstantiationofyourobjects(beans),butalsothewiringupofcollaborators(ordependencies).Ifyouwanttoinject(forexample)anHTTPrequestscopedbeanintoanotherbeanofalonger-livedscope,youmaychoosetoinjectanAOPproxyinplaceofthescopedbean.Thatis,youneedtoinjectaproxyobjectthatexposesthesamepublicinterfaceasthescopedobjectbutthatcanalsoretrievetherealtargetobjectfromtherelevantscope(suchasanHTTPrequest)anddelegatemethodcallsontotherealobject.
Theconfigurationinthefollowingexampleisonlyoneline,butitisimportanttounderstandthe"why"aswellasthe"how"behindit.
Thisisnotthebehavioryouwantwheninjectingashorter-livedscopedbeanintoalonger-livedscopedbean,forexampleinjectinganHTTPSession-scopedcollaboratingbeanasadependencyintosingletonbean.Rather,youneedasingleuserManagerobject,andforthelifetimeofanHTTPSession,youneedauserPreferencesobjectthatisspecifictosaidHTTPSession.ThusthecontainercreatesanobjectthatexposestheexactsamepublicinterfaceastheUserPreferencesclass(ideallyanobjectthatisaUserPreferencesinstance)whichcanfetchtherealUserPreferencesobjectfromthescopingmechanism(HTTPrequest,Session,etc.).ThecontainerinjectsthisproxyobjectintotheuserManagerbean,whichisunawarethatthisUserPreferencesreferenceisaproxy.Inthisexample,whenaUserManagerinstanceinvokesamethodonthedependency-injectedUserPreferencesobject,itactuallyisinvokingamethodontheproxy.TheproxythenfetchestherealUserPreferencesobjectfrom(inthiscase)theHTTPSession,anddelegatesthemethodinvocationontotheretrievedrealUserPreferencesobject.
Thusyouneedthefollowing,correctandcomplete,configurationwheninjectingrequest-andsession-scopedbeansintocollaboratingobjects:
Alternatively,youcanconfiguretheSpringcontainertocreatestandardJDKinterface-basedproxiesforsuchscopedbeans,byspecifyingfalseforthevalueoftheproxy-target-classattributeofthe
Thebeanscopingmechanismisextensible;Youcandefineyourownscopes,orevenredefineexistingscopes,althoughthelatterisconsideredbadpracticeandyoucannotoverridethebuilt-insingletonandprototypescopes.
TheScopeinterfacehasfourmethodstogetobjectsfromthescope,removethemfromthescope,andallowthemtobedestroyed.
Thefollowingmethodreturnstheobjectfromtheunderlyingscope.Thesessionscopeimplementation,forexample,returnsthesession-scopedbean(andifitdoesnotexist,themethodreturnsanewinstanceofthebean,afterhavingboundittothesessionforfuturereference).
Objectget(Stringname,ObjectFactoryobjectFactory)Thefollowingmethodremovestheobjectfromtheunderlyingscope.Thesessionscopeimplementationforexample,removesthesession-scopedbeanfromtheunderlyingsession.Theobjectshouldbereturned,butyoucanreturnnulliftheobjectwiththespecifiednameisnotfound.
Objectremove(Stringname)Thefollowingmethodregistersthecallbacksthescopeshouldexecutewhenitisdestroyedorwhenthespecifiedobjectinthescopeisdestroyed.RefertothejavadocsoraSpringscopeimplementationformoreinformationondestructioncallbacks.
voidregisterDestructionCallback(Stringname,RunnabledestructionCallback)Thefollowingmethodobtainstheconversationidentifierfortheunderlyingscope.Thisidentifierisdifferentforeachscope.Forasessionscopedimplementation,thisidentifiercanbethesessionidentifier.
StringgetConversationId()Usingacustomscope(使用自定义范围)AfteryouwriteandtestoneormorecustomScopeimplementations,youneedtomaketheSpringcontainerawareofyournewscope(s).ThefollowingmethodisthecentralmethodtoregisteranewScopewiththeSpringcontainer:
voidregisterScope(StringscopeName,Scopescope);ThismethodisdeclaredontheConfigurableBeanFactoryinterface,whichisavailableonmostoftheconcreteApplicationContextimplementationsthatshipwithSpringviatheBeanFactoryproperty.
ThefirstargumenttotheregisterScope(..)methodistheuniquenameassociatedwithascope;examplesofsuchnamesintheSpringcontaineritselfaresingletonandprototype.ThesecondargumenttotheregisterScope(..)methodisanactualinstanceofthecustomScopeimplementationthatyouwishtoregisteranduse.
SupposethatyouwriteyourcustomScopeimplementation,andthenregisteritasbelow.
ScopethreadScope=newSimpleThreadScope();beanFactory.registerScope("thread",threadScope);YouthencreatebeandefinitionsthatadheretothescopingrulesofyourcustomScope:
Inadditiontotheinitializationanddestructioncallbacks,Spring-managedobjectsmayalsoimplementtheLifecycleinterfacesothatthoseobjectscanparticipateinthestartupandshutdownprocessasdrivenbythecontainer’sownlifecycle.
-除了初始化和销毁回调之外,spring管理的对象还可以实现生命周期接口,以便这些对象可以参与由容器自己的生命周期驱动的启动和关闭过程。
Thelifecyclecallbackinterfacesaredescribedinthissection.
Theorg.springframework.beans.factory.InitializingBeaninterfaceallowsabeantoperforminitializationworkafterallnecessarypropertiesonthebeanhavebeensetbythecontainer.TheInitializingBeaninterfacespecifiesasinglemethod:
Implementingtheorg.springframework.beans.factory.DisposableBeaninterfaceallowsabeantogetacallbackwhenthecontainercontainingitisdestroyed.TheDisposableBeaninterfacespecifiesasinglemethod:
WhenyouwriteinitializationanddestroymethodcallbacksthatdonotusetheSpring-specificInitializingBeanandDisposableBeancallbackinterfaces,youtypicallywritemethodswithnamessuchasinit(),initialize(),dispose(),andsoon.Ideally,thenamesofsuchlifecyclecallbackmethodsarestandardizedacrossaprojectsothatalldevelopersusethesamemethodnamesandensureconsistency.
YoucanconfiguretheSpringcontainertolookfornamedinitializationanddestroycallbackmethodnamesoneverybean.Thismeansthatyou,asanapplicationdeveloper,canwriteyourapplicationclassesanduseaninitializationcallbackcalledinit(),withouthavingtoconfigureaninit-method="init"attributewitheachbeandefinition.TheSpringIoCcontainercallsthatmethodwhenthebeaniscreated(andinaccordancewiththestandardlifecyclecallbackcontractdescribedpreviously).Thisfeaturealsoenforcesaconsistentnamingconventionforinitializationanddestroymethodcallbacks.
Supposethatyourinitializationcallbackmethodsarenamedinit()anddestroycallbackmethodsarenameddestroy().Yourclasswillresembletheclassinthefollowingexample.
publicclassDefaultBlogServiceimplementsBlogService{privateBlogDaoblogDao;publicvoidsetBlogDao(BlogDaoblogDao){this.blogDao=blogDao;}//thisis(unsurprisingly)theinitializationcallbackmethodpublicvoidinit(){if(this.blogDao==null){thrownewIllegalStateException("The[blogDao]propertymustbeset.");}}}
Youconfiguredestroymethodcallbackssimilarly(inXML,thatis)byusingthedefault-destroy-methodattributeonthetop-level
Whereexistingbeanclassesalreadyhavecallbackmethodsthatarenamedatvariancewiththeconvention,youcanoverridethedefaultbyspecifying(inXML,thatis)themethodnameusingtheinit-methodanddestroy-methodattributesofthe
TheSpringcontainerguaranteesthataconfiguredinitializationcallbackiscalledimmediatelyafterabeanissuppliedwithalldependencies.Thustheinitializationcallbackiscalledontherawbeanreference,whichmeansthatAOPinterceptorsandsofortharenotyetappliedtothebean.Atargetbeanisfullycreatedfirst,thenanAOPproxy(forexample)withitsinterceptorchainisapplied.Ifthetargetbeanandtheproxyaredefinedseparately,yourcodecaneveninteractwiththerawtargetbean,bypassingtheproxy.Hence,itwouldbeinconsistenttoapplytheinterceptorstotheinitmethod,becausedoingsowouldcouplethelifecycleofthetargetbeanwithitsproxy/interceptorsandleavestrangesemanticswhenyourcodeinteractsdirectlytotherawtargetbean.
Multiplelifecyclemechanismsconfiguredforthesamebean,withdifferentinitializationmethods,arecalledasfollows:
Destroymethodsarecalledinthesameorder:
TheLifecycleinterfacedefinestheessentialmethodsforanyobjectthathasitsownlifecyclerequirements(e.g.startsandstopssomebackgroundprocess):
publicinterfaceLifecycle{voidstart();voidstop();booleanisRunning();}AnySpring-managedobjectmayimplementthatinterface.Then,whentheApplicationContextitselfreceivesstartandstopsignals,e.g.forastop/restartscenarioatruntime,itwillcascadethosecallstoallLifecycleimplementationsdefinedwithinthatcontext.ItdoesthisbydelegatingtoaLifecycleProcessor:
publicinterfaceLifecycleProcessorextendsLifecycle{voidonRefresh();voidonClose();}NoticethattheLifecycleProcessorisitselfanextensionoftheLifecycleinterface.Italsoaddstwoothermethodsforreactingtothecontextbeingrefreshedandclosed.
Theorderofstartupandshutdowninvocationscanbeimportant.Ifa"depends-on"relationshipexistsbetweenanytwoobjects,thedependentsidewillstartafteritsdependency,anditwillstopbeforeitsdependency.However,attimesthedirectdependenciesareunknown.Youmayonlyknowthatobjectsofacertaintypeshouldstartpriortoobjectsofanothertype.Inthosecases,theSmartLifecycleinterfacedefinesanotheroption,namelythegetPhase()methodasdefinedonitssuper-interface,Phased.
publicinterfacePhased{intgetPhase();}publicinterfaceSmartLifecycleextendsLifecycle,Phased{booleanisAutoStartup();voidstop(Runnablecallback);}Whenstarting,theobjectswiththelowestphasestartfirst,andwhenstopping,thereverseorderisfollowed.Therefore,anobjectthatimplementsSmartLifecycleandwhosegetPhase()methodreturnsInteger.MIN_VALUEwouldbeamongthefirsttostartandthelasttostop.Attheotherendofthespectrum,aphasevalueofInteger.MAX_VALUEwouldindicatethattheobjectshouldbestartedlastandstoppedfirst(likelybecauseitdependsonotherprocessestoberunning).Whenconsideringthephasevalue,it’salsoimportanttoknowthatthedefaultphaseforany"normal"LifecycleobjectthatdoesnotimplementSmartLifecyclewouldbe0.Therefore,anynegativephasevaluewouldindicatethatanobjectshouldstartbeforethosestandardcomponents(andstopafterthem),andviceversaforanypositivephasevalue.
AsyoucanseethestopmethoddefinedbySmartLifecycleacceptsacallback.Anyimplementationmustinvokethatcallback’srun()methodafterthatimplementation’sshutdownprocessiscomplete.ThatenablesasynchronousshutdownwherenecessarysincethedefaultimplementationoftheLifecycleProcessorinterface,DefaultLifecycleProcessor,willwaituptoitstimeoutvalueforthegroupofobjectswithineachphasetoinvokethatcallback.Thedefaultper-phasetimeoutis30seconds.Youcanoverridethedefaultlifecycleprocessorinstancebydefiningabeannamed"lifecycleProcessor"withinthecontext.Ifyouonlywanttomodifythetimeout,thendefiningthefollowingwouldbesufficient:
IfyouareusingSpring’sIoCcontainerinanon-webapplicationenvironment;forexample,inarichclientdesktopenvironment;youregisterashutdownhookwiththeJVM.Doingsoensuresagracefulshutdownandcallstherelevantdestroymethodsonyoursingletonbeanssothatallresourcesarereleased.Ofcourse,youmuststillconfigureandimplementthesedestroycallbackscorrectly.
Toregisterashutdownhook,youcalltheregisterShutdownHook()methodthatisdeclaredontheConfigurableApplicationContextinterface:
importorg.springframework.context.ConfigurableApplicationContext;importorg.springframework.context.support.ClassPathXmlApplicationContext;publicfinalclassBoot{publicstaticvoidmain(finalString[]args)throwsException{ConfigurableApplicationContextctx=newClassPathXmlApplicationContext("beans.xml");//addashutdownhookfortheabovecontext...ctx.registerShutdownHook();//apprunshere...//mainmethodexits,hookiscalledpriortotheappshuttingdown...}}1.6.2.ApplicationContextAwareandBeanNameAwareWhenanApplicationContextcreatesanobjectinstancethatimplementstheorg.springframework.context.ApplicationContextAwareinterface,theinstanceisprovidedwithareferencetothatApplicationContext.
WhenanApplicationContextcreatesaclassthatimplementstheorg.springframework.beans.factory.BeanNameAwareinterface,theclassisprovidedwithareferencetothenamedefinedinitsassociatedobjectdefinition.
publicinterfaceBeanNameAware{voidsetBeanName(Stringname)throwsBeansException;}ThecallbackisinvokedafterpopulationofnormalbeanpropertiesbutbeforeaninitializationcallbacksuchasInitializingBeanafterPropertiesSetoracustominit-method.
BesidesApplicationContextAwareandBeanNameAwarediscussedabove,SpringoffersarangeofAwareinterfacesthatallowbeanstoindicatetothecontainerthattheyrequireacertaininfrastructuredependency.ThemostimportantAwareinterfacesaresummarizedbelow-asageneralrule,thenameisagoodindicationofthedependencytype:
NoteagainthatusageoftheseinterfacestiesyourcodetotheSpringAPIanddoesnotfollowtheInversionofControlstyle.Assuch,theyarerecommendedforinfrastructurebeansthatrequireprogrammaticaccesstothecontainer.
Abeandefinitioncancontainalotofconfigurationinformation,includingconstructorarguments,propertyvalues,andcontainer-specificinformationsuchasinitializationmethod,staticfactorymethodname,andsoon.Achildbeandefinitioninheritsconfigurationdatafromaparentdefinition.Thechilddefinitioncanoverridesomevalues,oraddothers,asneeded.Usingparentandchildbeandefinitionscansavealotoftyping.Effectively,thisisaformoftemplating.
IfyouworkwithanApplicationContextinterfaceprogrammatically,childbeandefinitionsarerepresentedbytheChildBeanDefinitionclass.Mostusersdonotworkwiththemonthislevel,insteadconfiguringbeandefinitionsdeclarativelyinsomethingliketheClassPathXmlApplicationContext.WhenyouuseXML-basedconfigurationmetadata,youindicateachildbeandefinitionbyusingtheparentattribute,specifyingtheparentbeanasthevalueofthisattribute.
Achildbeandefinitioninheritsscope,constructorargumentvalues,propertyvalues,andmethodoverridesfromtheparent,withtheoptiontoaddnewvalues.Anyscope,initializationmethod,destroymethod,and/orstaticfactorymethodsettingsthatyouspecifywilloverridethecorrespondingparentsettings.
Theremainingsettingsarealwaystakenfromthechilddefinition:dependson,autowiremode,dependencycheck,singleton,lazyinit.
Theprecedingexampleexplicitlymarkstheparentbeandefinitionasabstractbyusingtheabstractattribute.Iftheparentdefinitiondoesnotspecifyaclass,explicitlymarkingtheparentbeandefinitionasabstractisrequired,asfollows:
Typically,anapplicationdeveloperdoesnotneedtosubclassApplicationContextimplementationclasses.Instead,theSpringIoCcontainercanbeextendedbyplugginginimplementationsofspecialintegrationinterfaces.Thenextfewsectionsdescribetheseintegrationinterfaces.
TheBeanPostProcessorinterfacedefinescallbackmethodsthatyoucanimplementtoprovideyourown(oroverridethecontainer’sdefault)instantiationlogic,dependency-resolutionlogic,andsoforth.IfyouwanttoimplementsomecustomlogicaftertheSpringcontainerfinishesinstantiating,configuring,andinitializingabean,youcanpluginoneormoreBeanPostProcessorimplementations.
Theorg.springframework.beans.factory.config.BeanPostProcessorinterfaceconsistsofexactlytwocallbackmethods.Whensuchaclassisregisteredasapost-processorwiththecontainer,foreachbeaninstancethatiscreatedbythecontainer,thepost-processorgetsacallbackfromthecontainerbothbeforecontainerinitializationmethods(suchasInitializingBean’safterPropertiesSet()andanydeclaredinitmethod)arecalledaswellasafteranybeaninitializationcallbacks.Thepost-processorcantakeanyactionwiththebeaninstance,includingignoringthecallbackcompletely.Abeanpost-processortypicallychecksforcallbackinterfacesormaywrapabeanwithaproxy.SomeSpringAOPinfrastructureclassesareimplementedasbeanpost-processorsinordertoprovideproxy-wrappinglogic.
AnApplicationContextautomaticallydetectsanybeansthataredefinedintheconfigurationmetadatawhichimplementtheBeanPostProcessorinterface.TheApplicationContextregistersthesebeansaspost-processorssothattheycanbecalledlateruponbeancreation.Beanpost-processorscanbedeployedinthecontainerjustlikeanyotherbeans.
NotethatwhendeclaringaBeanPostProcessorusingan@Beanfactorymethodonaconfigurationclass,thereturntypeofthefactorymethodshouldbetheimplementationclassitselforatleasttheorg.springframework.beans.factory.config.BeanPostProcessorinterface,clearlyindicatingthepost-processornatureofthatbean.Otherwise,theApplicationContextwon’tbeabletoautodetectitbytypebeforefullycreatingit.SinceaBeanPostProcessorneedstobeinstantiatedearlyinordertoapplytotheinitializationofotherbeansinthecontext,thisearlytypedetectioniscritical.
Thefollowingexamplesshowhowtowrite,register,anduseBeanPostProcessorsinanApplicationContext.
Thisfirstexampleillustratesbasicusage.TheexampleshowsacustomBeanPostProcessorimplementationthatinvokesthetoString()methodofeachbeanasitiscreatedbythecontainerandprintstheresultingstringtothesystemconsole.
FindbelowthecustomBeanPostProcessorimplementationclassdefinition:
ThefollowingsimpleJavaapplicationexecutestheprecedingcodeandconfiguration:
importorg.springframework.context.ApplicationContext;importorg.springframework.context.support.ClassPathXmlApplicationContext;importorg.springframework.scripting.Messenger;publicfinalclassBoot{publicstaticvoidmain(finalString[]args)throwsException{ApplicationContextctx=newClassPathXmlApplicationContext("scripting/beans.xml");Messengermessenger=(Messenger)ctx.getBean("messenger");System.out.println(messenger);}}Theoutputoftheprecedingapplicationresemblesthefollowing:
Bean'messenger'created:org.springframework.scripting.groovy.GroovyMessenger@272961org.springframework.scripting.groovy.GroovyMessenger@272961Example:TheRequiredAnnotationBeanPostProcessor(例子:)UsingcallbackinterfacesorannotationsinconjunctionwithacustomBeanPostProcessorimplementationisacommonmeansofextendingtheSpringIoCcontainer.AnexampleisSpring’sRequiredAnnotationBeanPostProcessor-aBeanPostProcessorimplementationthatshipswiththeSpringdistributionwhichensuresthatJavaBeanpropertiesonbeansthataremarkedwithan(arbitrary)annotationareactually(configuredtobe)dependency-injectedwithavalue.
Thenextextensionpointthatwewilllookatistheorg.springframework.beans.factory.config.BeanFactoryPostProcessor.ThesemanticsofthisinterfacearesimilartothoseoftheBeanPostProcessor,withonemajordifference:BeanFactoryPostProcessoroperatesonthebeanconfigurationmetadata;thatis,theSpringIoCcontainerallowsaBeanFactoryPostProcessortoreadtheconfigurationmetadataandpotentiallychangeitbeforethecontainerinstantiatesanybeansotherthanBeanFactoryPostProcessors.
YoucanconfiguremultipleBeanFactoryPostProcessors,andyoucancontroltheorderinwhichtheseBeanFactoryPostProcessorsexecutebysettingtheorderproperty.However,youcanonlysetthispropertyiftheBeanFactoryPostProcessorimplementstheOrderedinterface.IfyouwriteyourownBeanFactoryPostProcessor,youshouldconsiderimplementingtheOrderedinterfacetoo.ConsultthejavadocsoftheBeanFactoryPostProcessorandOrderedinterfacesformoredetails.
Abeanfactorypost-processorisexecutedautomaticallywhenitisdeclaredinsideanApplicationContext,inordertoapplychangestotheconfigurationmetadatathatdefinethecontainer.Springincludesanumberofpredefinedbeanfactorypost-processors,suchasPropertyOverrideConfigurerandPropertyPlaceholderConfigurer.AcustomBeanFactoryPostProcessorcanalsobeused,forexample,toregistercustompropertyeditors.
AnApplicationContextautomaticallydetectsanybeansthataredeployedintoitthatimplementtheBeanFactoryPostProcessorinterface.Itusesthesebeansasbeanfactorypost-processors,attheappropriatetime.Youcandeploythesepost-processorbeansasyouwouldanyotherbean.
YouusethePropertyPlaceholderConfigurertoexternalizepropertyvaluesfromabeandefinitioninaseparatefileusingthestandardJavaPropertiesformat.Doingsoenablesthepersondeployinganapplicationtocustomizeenvironment-specificpropertiessuchasdatabaseURLsandpasswords,withoutthecomplexityorriskofmodifyingthemainXMLdefinitionfileorfilesforthecontainer.
ConsiderthefollowingXML-basedconfigurationmetadatafragment,whereaDataSourcewithplaceholdervaluesisdefined.TheexampleshowspropertiesconfiguredfromanexternalPropertiesfile.Atruntime,aPropertyPlaceholderConfigurerisappliedtothemetadatathatwillreplacesomepropertiesoftheDataSource.Thevaluestoreplacearespecifiedasplaceholdersoftheform${property-name}whichfollowstheAnt/log4j/JSPELstyle.
jdbc.driverClassName=org.hsqldb.jdbcDriverjdbc.url=jdbc:hsqldb:hsql://production:9002jdbc.username=sajdbc.password=rootTherefore,thestring${jdbc.username}isreplacedatruntimewiththevalue'sa',andthesameappliesforotherplaceholdervaluesthatmatchkeysinthepropertiesfile.ThePropertyPlaceholderConfigurerchecksforplaceholdersinmostpropertiesandattributesofabeandefinition.Furthermore,theplaceholderprefixandsuffixcanbecustomized.
WiththecontextnamespaceintroducedinSpring2.5,itispossibletoconfigurepropertyplaceholderswithadedicatedconfigurationelement.Oneormorelocationscanbeprovidedasacomma-separatedlistinthelocationattribute.
ConsultthePropertyPlaceholderConfigurerjavadocsformoreinformation.
ThePropertyOverrideConfigurer,anotherbeanfactorypost-processor,resemblesthePropertyPlaceholderConfigurer,butunlikethelatter,theoriginaldefinitionscanhavedefaultvaluesornovaluesatallforbeanproperties.IfanoverridingPropertiesfiledoesnothaveanentryforacertainbeanproperty,thedefaultcontextdefinitionisused.
Notethatthebeandefinitionisnotawareofbeingoverridden,soitisnotimmediatelyobviousfromtheXMLdefinitionfilethattheoverrideconfigurerisbeingused.IncaseofmultiplePropertyOverrideConfigurerinstancesthatdefinedifferentvaluesforthesamebeanproperty,thelastonewins,duetotheoverridingmechanism.
Propertiesfileconfigurationlinestakethisformat:
beanName.property=valueForexample:
dataSource.driverClassName=com.mysql.jdbc.DriverdataSource.url=jdbc:mysql:mydbThisexamplefilecanbeusedwithacontainerdefinitionthatcontainsabeancalleddataSource,whichhasdriverandurlproperties.
Compoundpropertynamesarealsosupported,aslongaseverycomponentofthepathexceptthefinalpropertybeingoverriddenisalreadynon-null(presumablyinitializedbytheconstructors).Inthisexample…
foo.fred.bob.sammy=123Specifiedoverridevaluesarealwaysliteralvalues;theyarenottranslatedintobeanreferences.ThisconventionalsoapplieswhentheoriginalvalueintheXMLbeandefinitionspecifiesabeanreference.WiththecontextnamespaceintroducedinSpring2.5,itispossibletoconfigurepropertyoverridingwithadedicatedconfigurationelement:
TheFactoryBeaninterfaceisapointofpluggabilityintotheSpringIoCcontainer’sinstantiationlogic.IfyouhavecomplexinitializationcodethatisbetterexpressedinJavaasopposedtoa(potentially)verboseamountofXML,youcancreateyourownFactoryBean,writethecomplexinitializationinsidethatclass,andthenplugyourcustomFactoryBeanintothecontainer.
TheFactoryBeaninterfaceprovidesthreemethods:
TheFactoryBeanconceptandinterfaceisusedinanumberofplaceswithintheSpringFramework;morethan50implementationsoftheFactoryBeaninterfaceshipwithSpringitself.
WhenyouneedtoaskacontainerforanactualFactoryBeaninstanceitselfinsteadofthebeanitproduces,prefacethebean’sidwiththeampersandsymbol(&)whencallingthegetBean()methodoftheApplicationContext.SoforagivenFactoryBeanwithanidofmyBean,invokinggetBean("myBean")onthecontainerreturnstheproductoftheFactoryBean;whereas,invokinggetBean("&myBean")returnstheFactoryBeaninstanceitself.
AreannotationsbetterthanXMLforconfiguringSpring
Theintroductionofannotation-basedconfigurationsraisedthequestionofwhetherthisapproachis'better'thanXML.Theshortanswerisitdepends.Thelongansweristhateachapproachhasitsprosandcons,andusuallyitisuptothedevelopertodecidewhichstrategysuitsthembetter.Duetothewaytheyaredefined,annotationsprovidealotofcontextintheirdeclaration,leadingtoshorterandmoreconciseconfiguration.However,XMLexcelsatwiringupcomponentswithouttouchingtheirsourcecodeorrecompilingthem.SomedeveloperspreferhavingthewiringclosetothesourcewhileothersarguethatannotatedclassesarenolongerPOJOsand,furthermore,thattheconfigurationbecomesdecentralizedandhardertocontrol.
Asalways,youcanregisterthemasindividualbeandefinitions,buttheycanalsobeimplicitlyregisteredbyincludingthefollowingtaginanXML-basedSpringconfiguration(noticetheinclusionofthecontextnamespace):
The@Requiredannotationappliestobeanpropertysettermethods,asinthefollowingexample:
publicclassSimpleMovieLister{privateMovieFindermovieFinder;@RequiredpublicvoidsetMovieFinder(MovieFindermovieFinder){this.movieFinder=movieFinder;}//...}Thisannotationsimplyindicatesthattheaffectedbeanpropertymustbepopulatedatconfigurationtime,throughanexplicitpropertyvalueinabeandefinitionorthroughautowiring.Thecontainerthrowsanexceptioniftheaffectedbeanpropertyhasnotbeenpopulated;thisallowsforeagerandexplicitfailure,avoidingNullPointerExceptionsorthelikelateron.Itisstillrecommendedthatyouputassertionsintothebeanclassitself,forexample,intoaninitmethod.Doingsoenforcesthoserequiredreferencesandvaluesevenwhenyouusetheclassoutsideofacontainer.
Youcanapplythe@Autowiredannotationtoconstructors:
publicclassMovieRecommender{privatefinalCustomerPreferenceDaocustomerPreferenceDao;@AutowiredpublicMovieRecommender(CustomerPreferenceDaocustomerPreferenceDao){this.customerPreferenceDao=customerPreferenceDao;}//...}AsofSpringFramework4.3,an@Autowiredannotationonsuchaconstructorisnolongernecessaryifthetargetbeanonlydefinesoneconstructortobeginwith.However,ifseveralconstructorsareavailable,atleastonemustbeannotatedtoteachthecontainerwhichonetouse.Asexpected,youcanalsoapplythe@Autowiredannotationto"traditional"settermethods:
publicclassSimpleMovieLister{privateMovieFindermovieFinder;@AutowiredpublicvoidsetMovieFinder(MovieFindermovieFinder){this.movieFinder=movieFinder;}//...}Youcanalsoapplytheannotationtomethodswitharbitrarynamesand/ormultiplearguments:
publicclassMovieRecommender{privateMovieCatalogmovieCatalog;privateCustomerPreferenceDaocustomerPreferenceDao;@Autowiredpublicvoidprepare(MovieCatalogmovieCatalog,CustomerPreferenceDaocustomerPreferenceDao){this.movieCatalog=movieCatalog;this.customerPreferenceDao=customerPreferenceDao;}//...}Youcanapply@Autowiredtofieldsaswellandevenmixitwithconstructors:
publicclassMovieRecommender{@AutowiredprivateMovieCatalog[]movieCatalogs;//...}Thesameappliesfortypedcollections:
publicclassMovieRecommender{privateMap
publicclassSimpleMovieLister{privateMovieFindermovieFinder;@Autowired(required=false)publicvoidsetMovieFinder(MovieFindermovieFinder){this.movieFinder=movieFinder;}//...}Onlyoneannotatedconstructorper-classcanbemarkedasrequired,butmultiplenon-requiredconstructorscanbeannotated.Inthatcase,eachisconsideredamongthecandidatesandSpringusesthegreediestconstructorwhosedependenciescanbesatisfied,thatistheconstructorthathasthelargestnumberofarguments.Therequiredattributeof@Autowiredisrecommendedoverthe@Requiredannotation.Therequiredattributeindicatesthatthepropertyisnotrequiredforautowiringpurposes,thepropertyisignoredifitcannotbeautowired.@Required,ontheotherhand,isstrongerinthatitenforcesthepropertythatwassetbyanymeanssupportedbythecontainer.Ifnovalueisinjected,acorrespondingexceptionisraised.Alternatively,youmayexpressthenon-requirednatureofaparticulardependencythroughJava8’sjava.util.Optional:或者,你也可以通过Java8的Java.util.optional来表达某个特定依赖项的非必需性质:
publicclassSimpleMovieLister{@AutowiredpublicvoidsetMovieFinder(Optional
publicclassSimpleMovieLister{@AutowiredpublicvoidsetMovieFinder(@NullableMovieFindermovieFinder){...}}Youcanalsouse@Autowiredforinterfacesthatarewell-knownresolvabledependencies:BeanFactory,ApplicationContext,Environment,ResourceLoader,ApplicationEventPublisher,andMessageSource.Theseinterfacesandtheirextendedinterfaces,suchasConfigurableApplicationContextorResourcePatternResolver,areautomaticallyresolved,withnospecialsetupnecessary.
publicclassMovieRecommender{@AutowiredprivateApplicationContextcontext;publicMovieRecommender(){}//...}@Autowired,@Inject,@Resource,and@ValueannotationsarehandledbySpringBeanPostProcessorimplementationswhichinturnmeansthatyoucannotapplytheseannotationswithinyourownBeanPostProcessororBeanFactoryPostProcessortypes(ifany).Thesetypesmustbe'wiredup'explicitlyviaXMLorusingaSpring@Beanmethod.1.9.3.Fine-tuningannotation-basedautowiringwith@Primary(使用@Primary微调基于注释的自动装配)Becauseautowiringbytypemayleadtomultiplecandidates,itisoftennecessarytohavemorecontrolovertheselectionprocess.OnewaytoaccomplishthisiswithSpring’s@Primaryannotation.@Primaryindicatesthataparticularbeanshouldbegivenpreferencewhenmultiplebeansarecandidatestobeautowiredtoasingle-valueddependency.Ifexactlyone'primary'beanexistsamongthecandidates,itwillbetheautowiredvalue.
Let’sassumewehavethefollowingconfigurationthatdefinesfirstMovieCatalogastheprimaryMovieCatalog.
@ConfigurationpublicclassMovieConfiguration{@Bean@PrimarypublicMovieCatalogfirstMovieCatalog(){...}@BeanpublicMovieCatalogsecondMovieCatalog(){...}//...}Withsuchconfiguration,thefollowingMovieRecommenderwillbeautowiredwiththefirstMovieCatalog.
publicclassMovieRecommender{@AutowiredprivateMovieCatalogmovieCatalog;//...}Thecorrespondingbeandefinitionsappearasfollows.
publicclassMovieRecommender{@Autowired@Qualifier("main")privateMovieCatalogmovieCatalog;//...}The@Qualifierannotationcanalsobespecifiedonindividualconstructorargumentsormethodparameters:
publicclassMovieRecommender{privateMovieCatalogmovieCatalog;privateCustomerPreferenceDaocustomerPreferenceDao;@Autowiredpublicvoidprepare(@Qualifier("main")MovieCatalogmovieCatalog,CustomerPreferenceDaocustomerPreferenceDao){this.movieCatalog=movieCatalog;this.customerPreferenceDao=customerPreferenceDao;}//...}Thecorrespondingbeandefinitionsappearasfollows.Thebeanwithqualifiervalue"main"iswiredwiththeconstructorargumentthatisqualifiedwiththesamevalue.
Qualifiersalsoapplytotypedcollections,asdiscussedabove,forexample,toSet
Youcancreateyourowncustomqualifierannotations.Simplydefineanannotationandprovidethe@Qualifierannotationwithinyourdefinition:
@Target({ElementType.FIELD,ElementType.PARAMETER})@Retention(RetentionPolicy.RUNTIME)@Qualifierpublic@interfaceGenre{Stringvalue();}Thenyoucanprovidethecustomqualifieronautowiredfieldsandparameters:
publicclassMovieRecommender{@Autowired@Genre("Action")privateMovieCatalogactionCatalog;privateMovieCatalogcomedyCatalog;@AutowiredpublicvoidsetComedyCatalog(@Genre("Comedy")MovieCatalogcomedyCatalog){this.comedyCatalog=comedyCatalog;}//...}Next,providetheinformationforthecandidatebeandefinitions.Youcanadd
Insomecases,itmaybesufficienttouseanannotationwithoutavalue.Thismaybeusefulwhentheannotationservesamoregenericpurposeandcanbeappliedacrossseveraldifferenttypesofdependencies.Forexample,youmayprovideanofflinecatalogthatwouldbesearchedwhennoInternetconnectionisavailable.Firstdefinethesimpleannotation:
@Target({ElementType.FIELD,ElementType.PARAMETER})@Retention(RetentionPolicy.RUNTIME)@Qualifierpublic@interfaceOffline{}Thenaddtheannotationtothefieldorpropertytobeautowired:
publicclassMovieRecommender{@Autowired@OfflineprivateMovieCatalogofflineCatalog;//...}Nowthebeandefinitiononlyneedsaqualifiertype:
@Target({ElementType.FIELD,ElementType.PARAMETER})@Retention(RetentionPolicy.RUNTIME)@Qualifierpublic@interfaceMovieQualifier{Stringgenre();Formatformat();}InthiscaseFormatisanenum:
publicenumFormat{VHS,DVD,BLURAY}Thefieldstobeautowiredareannotatedwiththecustomqualifierandincludevaluesforbothattributes:genreandformat.
publicclassMovieRecommender{@Autowired@MovieQualifier(format=Format.VHS,genre="Action")privateMovieCatalogactionVhsCatalog;@Autowired@MovieQualifier(format=Format.VHS,genre="Comedy")privateMovieCatalogcomedyVhsCatalog;@Autowired@MovieQualifier(format=Format.DVD,genre="Action")privateMovieCatalogactionDvdCatalog;@Autowired@MovieQualifier(format=Format.BLURAY,genre="Comedy")privateMovieCatalogcomedyBluRayCatalog;//...}Finally,thebeandefinitionsshouldcontainmatchingqualifiervalues.Thisexamplealsodemonstratesthatbeanmetaattributesmaybeusedinsteadofthe
@ConfigurationpublicclassMyConfiguration{@BeanpublicStringStorestringStore(){returnnewStringStore();}@BeanpublicIntegerStoreintegerStore(){returnnewIntegerStore();}}Assumingthatbeansaboveimplementagenericinterface,i.e.Store
@AutowiredprivateStore
Whenmultiplebeansqualifyasautowirecandidates,thedeterminationofa"primary"isthefollowing:ifexactlyonebeandefinitionamongthecandidateshasaprimaryattributesettotrue,itwillbeselected.
SpringalsosupportsinjectionusingtheJSR-250@Resourceannotationonfieldsorbeanpropertysettermethods.ThisisacommonpatterninJavaEE5and6,forexampleinJSF1.2managedbeansorJAX-WS2.0endpoints.SpringsupportsthispatternforSpring-managedobjectsaswell.
@Resourcetakesanameattribute,andbydefaultSpringinterpretsthatvalueasthebeannametobeinjected.Inotherwords,itfollowsby-namesemantics,asdemonstratedinthisexample:
publicclassSimpleMovieLister{privateMovieFindermovieFinder;@Resource(name="myMovieFinder")publicvoidsetMovieFinder(MovieFindermovieFinder){this.movieFinder=movieFinder;}}Ifnonameisspecifiedexplicitly,thedefaultnameisderivedfromthefieldnameorsettermethod.Incaseofafield,ittakesthefieldname;incaseofasettermethod,ittakesthebeanpropertyname.Sothefollowingexampleisgoingtohavethebeanwithname"movieFinder"injectedintoitssettermethod:
Thusinthefollowingexample,thecustomerPreferenceDaofieldfirstlooksforabeannamedcustomerPreferenceDao,thenfallsbacktoaprimarytypematchforthetypeCustomerPreferenceDao.The"context"fieldisinjectedbasedontheknownresolvabledependencytypeApplicationContext.
Springprovidesfurtherstereotypeannotations:@Component,@Service,and@Controller.@ComponentisagenericstereotypeforanySpring-managedcomponent.@Repository,@Service,and@Controllerarespecializationsof@Componentformorespecificusecases,forexample,inthepersistence,service,andpresentationlayers,respectively.Therefore,youcanannotateyourcomponentclasseswith@Component,butbyannotatingthemwith@Repository,@Service,or@Controllerinstead,yourclassesaremoreproperlysuitedforprocessingbytoolsorassociatingwithaspects.Forexample,thesestereotypeannotationsmakeidealtargetsforpointcuts.Itisalsopossiblethat@Repository,@Service,and@ControllermaycarryadditionalsemanticsinfuturereleasesoftheSpringFramework.Thus,ifyouarechoosingbetweenusing@Componentor@Serviceforyourservicelayer,@Serviceisclearlythebetterchoice.Similarly,asstatedabove,@Repositoryisalreadysupportedasamarkerforautomaticexceptiontranslationinyourpersistencelayer.
ManyoftheannotationsprovidedbySpringcanbeusedasmeta-annotationsinyourowncode.Ameta-annotationissimplyanannotationthatcanbeappliedtoanotherannotation.Forexample,the@Serviceannotationmentionedaboveismeta-annotatedwith@Component:
@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@Documented@Component//Springwillseethisandtreat@Serviceinthesamewayas@Componentpublic@interfaceService{//....}Meta-annotationscanalsobecombinedtocreatecomposedannotations.Forexample,the@RestControllerannotationfromSpringMVCiscomposedof@Controllerand@ResponseBody.
Inaddition,composedannotationsmayoptionallyredeclareattributesfrommeta-annotationstoallowusercustomization.Thiscanbeparticularlyusefulwhenyouwanttoonlyexposeasubsetofthemeta-annotation’sattributes.Forexample,Spring’s@SessionScopeannotationhardcodesthescopenametosessionbutstillallowscustomizationoftheproxyMode.
@Target({ElementType.TYPE,ElementType.METHOD})@Retention(RetentionPolicy.RUNTIME)@Documented@Scope(WebApplicationContext.SCOPE_SESSION)public@interfaceSessionScope{/***Aliasfor{@linkScope#proxyMode}.*
Defaultsto{@linkScopedProxyMode#TARGET_CLASS}.*/@AliasFor(annotation=Scope.class)ScopedProxyModeproxyMode()defaultScopedProxyMode.TARGET_CLASS;}@SessionScopecanthenbeusedwithoutdeclaringtheproxyModeasfollows:
@Service@SessionScopepublicclassSessionScopedService{//...}OrwithanoverriddenvaluefortheproxyModeasfollows:
SpringcanautomaticallydetectstereotypedclassesandregistercorrespondingBeanDefinitionswiththeApplicationContext.Forexample,thefollowingtwoclassesareeligibleforsuchautodetection:
@ServicepublicclassSimpleMovieLister{privateMovieFindermovieFinder;@AutowiredpublicSimpleMovieLister(MovieFindermovieFinder){this.movieFinder=movieFinder;}}@RepositorypublicclassJpaMovieFinderimplementsMovieFinder{//implementationelidedforclarity}Toautodetecttheseclassesandregisterthecorrespondingbeans,youneedtoadd@ComponentScantoyour@Configurationclass,wherethebasePackagesattributeisacommonparentpackageforthetwoclasses.(Alternatively,youcanspecifyacomma/semicolon/space-separatedlistthatincludestheparentpackageofeachclass.)
@Configuration@ComponentScan(basePackages="org.example")publicclassAppConfig{...}Forconcision,theabovemayhaveusedthevalueattributeoftheannotation,i.e.@ComponentScan("org.example")ThefollowingisanalternativeusingXML
Bydefault,classesannotatedwith@Component,@Repository,@Service,@Controller,oracustomannotationthatitselfisannotatedwith@Componentaretheonlydetectedcandidatecomponents.However,youcanmodifyandextendthisbehaviorsimplybyapplyingcustomfilters.AddthemasincludeFiltersorexcludeFiltersparametersofthe@ComponentScanannotation(orasinclude-filterorexclude-filtersub-elementsofthecomponent-scanelement).Eachfilterelementrequiresthetypeandexpressionattributes.Thefollowingtabledescribesthefilteringoptions.
Thefollowingexampleshowstheconfigurationignoringall@Repositoryannotationsandusing"stub"repositoriesinstead.
@Configuration@ComponentScan(basePackages="org.example",includeFilters=@Filter(type=FilterType.REGEX,pattern=".*Stub.*Repository"),excludeFilters=@Filter(Repository.class))publicclassAppConfig{...}andtheequivalentusingXML(使用XML的等价)
@ComponentpublicclassFactoryMethodComponent{@Bean@Qualifier("public")publicTestBeanpublicInstance(){returnnewTestBean("publicInstance");}publicvoiddoWork(){//Componentmethodimplementationomitted}}ThisclassisaSpringcomponentthathasapplication-specificcodecontainedinitsdoWork()method.However,italsocontributesabeandefinitionthathasafactorymethodreferringtothemethodpublicInstance().The@Beanannotationidentifiesthefactorymethodandotherbeandefinitionproperties,suchasaqualifiervaluethroughthe@Qualifierannotation.Othermethodlevelannotationsthatcanbespecifiedare@Scope,@Lazy,andcustomqualifierannotations.
Autowiredfieldsandmethodsaresupportedaspreviouslydiscussed,withadditionalsupportforautowiringof@Beanmethods:
@ComponentpublicclassFactoryMethodComponent{privatestaticinti;@Bean@Qualifier("public")publicTestBeanpublicInstance(){returnnewTestBean("publicInstance");}//useofacustomqualifierandautowiringofmethodparameters@BeanprotectedTestBeanprotectedInstance(@Qualifier("public")TestBeanspouse,@Value("#{privateInstance.age}")Stringcountry){TestBeantb=newTestBean("protectedInstance",1);tb.setSpouse(spouse);tb.setCountry(country);returntb;}@BeanprivateTestBeanprivateInstance(){returnnewTestBean("privateInstance",i++);}@Bean@RequestScopepublicTestBeanrequestScopedInstance(){returnnewTestBean("requestScopedInstance",3);}}TheexampleautowirestheStringmethodparametercountrytothevalueoftheagepropertyonanotherbeannamedprivateInstance.ASpringExpressionLanguageelementdefinesthevalueofthepropertythroughthenotation#{
AsofSpringFramework4.3,youmayalsodeclareafactorymethodparameteroftypeInjectionPoint(oritsmorespecificsubclassDependencyDescriptor)inordertoaccesstherequestinginjectionpointthattriggersthecreationofthecurrentbean.Notethatthiswillonlyapplytotheactualcreationofbeaninstances,nottotheinjectionofexistinginstances.Asaconsequence,thisfeaturemakesmostsenseforbeansofprototypescope.Forotherscopes,thefactorymethodwillonlyeverseetheinjectionpointwhichtriggeredthecreationofanewbeaninstanceinthegivenscope:forexample,thedependencythattriggeredthecreationofalazysingletonbean.Usetheprovidedinjectionpointmetadatawithsemanticcareinsuchscenarios.
@ComponentpublicclassFactoryMethodComponent{@Bean@Scope("prototype")publicTestBeanprototypeInstance(InjectionPointinjectionPoint){returnnewTestBean("prototypeInstancefor"+injectionPoint.getMember());}}The@BeanmethodsinaregularSpringcomponentareprocesseddifferentlythantheircounterpartsinsideaSpring@Configurationclass.Thedifferenceisthat@ComponentclassesarenotenhancedwithCGLIBtointercepttheinvocationofmethodsandfields.CGLIBproxyingisthemeansbywhichinvokingmethodsorfieldswithin@Beanmethodsin@Configurationclassescreatesbeanmetadatareferencestocollaboratingobjects;suchmethodsarenotinvokedwithnormalJavasemanticsbutrathergothroughthecontainerinordertoprovidetheusuallifecyclemanagementandproxyingofSpringbeansevenwhenreferringtootherbeansviaprogrammaticcallsto@Beanmethods.Incontrast,invokingamethodorfieldinan@Beanmethodwithinaplain@ComponentclasshasstandardJavasemantics,withnospecialCGLIBprocessingorotherconstraintsapplying.
Whenacomponentisautodetectedaspartofthescanningprocess,itsbeannameisgeneratedbytheBeanNameGeneratorstrategyknowntothatscanner.Bydefault,anySpringstereotypeannotation(@Component,@Repository,@Service,and@Controller)thatcontainsanamevaluewilltherebyprovidethatnametothecorrespondingbeandefinition.
Ifsuchanannotationcontainsnonamevalueorforanyotherdetectedcomponent(suchasthosediscoveredbycustomfilters),thedefaultbeannamegeneratorreturnstheuncapitalizednon-qualifiedclassname.Forexample,ifthefollowingcomponentclassesweredetected,thenameswouldbemyMovieListerandmovieFinderImpl:
AswithSpring-managedcomponentsingeneral,thedefaultandmostcommonscopeforautodetectedcomponentsissingleton.However,sometimesyouneedadifferentscopewhichcanbespecifiedviathe@Scopeannotation.Simplyprovidethenameofthescopewithintheannotation:
Togeneratetheindex,simplyaddanadditionaldependencytoeachmodulethatcontainscomponentsthataretargetforcomponentscandirectives:
dependencies{compileOnly("org.springframework:spring-context-indexer:5.0.8.RELEASE")}ThatprocesswillgenerateaMETA-INF/spring.componentsfilethatisgoingtobeincludedinthejar.
StartingwithSpring3.0,SpringofferssupportforJSR-330standardannotations(DependencyInjection).ThoseannotationsarescannedinthesamewayastheSpringannotations.Youjustneedtohavetherelevantjarsinyourclasspath.
Insteadof@Autowired,@javax.inject.Injectmaybeusedasfollows:
importjavax.inject.Inject;publicclassSimpleMovieLister{privateMovieFindermovieFinder;@InjectpublicvoidsetMovieFinder(MovieFindermovieFinder){this.movieFinder=movieFinder;}publicvoidlistMovies(){this.movieFinder.findMovies(...);...}}Aswith@Autowired,itispossibletouse@Injectatthefieldlevel,methodlevelandconstructor-argumentlevel.Furthermore,youmaydeclareyourinjectionpointasaProvider,allowingforon-demandaccesstobeansofshorterscopesorlazyaccesstootherbeansthroughaProvider.get()call.Asavariantoftheexampleabove:
importjavax.inject.Inject;importjavax.inject.Provider;publicclassSimpleMovieLister{privateProvider
importjavax.inject.Inject;importjavax.inject.Named;publicclassSimpleMovieLister{privateMovieFindermovieFinder;@InjectpublicvoidsetMovieFinder(@Named("main")MovieFindermovieFinder){this.movieFinder=movieFinder;}//...}Like@Autowired,@Injectcanalsobeusedwithjava.util.Optionalor@Nullable.Thisisevenmoreapplicableheresince@Injectdoesnothavearequiredattribute.
publicclassSimpleMovieLister{@InjectpublicvoidsetMovieFinder(Optional
importjavax.inject.Inject;importjavax.inject.Named;@Named("movieListener")//@ManagedBean("movieListener")couldbeusedaswellpublicclassSimpleMovieLister{privateMovieFindermovieFinder;@InjectpublicvoidsetMovieFinder(MovieFindermovieFinder){this.movieFinder=movieFinder;}//...}Itisverycommontouse@Componentwithoutspecifyinganameforthecomponent.@Namedcanbeusedinasimilarfashion:
importjavax.inject.Inject;importjavax.inject.Named;@NamedpublicclassSimpleMovieLister{privateMovieFindermovieFinder;@InjectpublicvoidsetMovieFinder(MovieFindermovieFinder){this.movieFinder=movieFinder;}//...}Whenusing@Namedor@ManagedBean,itispossibletousecomponentscanningintheexactsamewayaswhenusingSpringannotations:
@Configuration@ComponentScan(basePackages="org.example")publicclassAppConfig{...}Incontrastto@Component,theJSR-330@NamedandtheJSR-250ManagedBeanannotationsarenotcomposable.PleaseuseSpring’sstereotypemodelforbuildingcustomcomponentannotations.1.11.3.LimitationsofJSR-330standardannotations(JSR-330标准注释的限制)Whenworkingwithstandardannotations,itisimportanttoknowthatsomesignificantfeaturesarenotavailableasshowninthetablebelow:
ThecentralartifactsinSpring’snewJava-configurationsupportare@Configuration-annotatedclassesand@Bean-annotatedmethods.
The@Beanannotationisusedtoindicatethatamethodinstantiates,configuresandinitializesanewobjecttobemanagedbytheSpringIoCcontainer.ForthosefamiliarwithSpring’s
Annotatingaclasswith@Configurationindicatesthatitsprimarypurposeisasasourceofbeandefinitions.Furthermore,@Configurationclassesallowinter-beandependenciestobedefinedbysimplycallingother@Beanmethodsinthesameclass.Thesimplestpossible@Configurationclasswouldreadasfollows:
@ConfigurationpublicclassAppConfig{@BeanpublicMyServicemyService(){returnnewMyServiceImpl();}}TheAppConfigclassabovewouldbeequivalenttothefollowingSpring
When@Beanmethodsaredeclaredwithinclassesthatarenotannotatedwith@Configurationtheyarereferredtoasbeingprocessedina'lite'mode.Beanmethodsdeclaredina@Componentoreveninaplainoldclasswillbeconsidered'lite',withadifferentprimarypurposeofthecontainingclassandan@Beanmethodjustbeingasortofbonusthere.Forexample,servicecomponentsmayexposemanagementviewstothecontainerthroughanadditional@Beanmethodoneachapplicablecomponentclass.Insuchscenarios,@Beanmethodsareasimplegeneral-purposefactorymethodmechanism.
Unlikefull@Configuration,lite@Beanmethodscannotdeclareinter-beandependencies.Instead,theyoperateontheircontainingcomponent’sinternalstateandoptionallyonargumentsthattheymaydeclare.Suchan@Beanmethodshouldthereforenotinvokeother@Beanmethods;eachsuchmethodisliterallyjustafactorymethodforaparticularbeanreference,withoutanyspecialruntimesemantics.Thepositiveside-effecthereisthatnoCGLIBsubclassinghastobeappliedatruntime,sotherearenolimitationsintermsofclassdesign(i.e.thecontainingclassmayneverthelessbefinaletc).
Incommonscenarios,@Beanmethodsaretobedeclaredwithin@Configurationclasses,ensuringthat'full'modeisalwaysusedandthatcross-methodreferenceswillthereforegetredirectedtothecontainer’slifecyclemanagement.Thiswillpreventthesame@BeanmethodfromaccidentallybeinginvokedthrougharegularJavacallwhichhelpstoreducesubtlebugsthatcanbehardtotrackdownwhenoperatingin'lite'mode.
The@Beanand@Configurationannotationswillbediscussedindepthinthesectionsbelow.First,however,we’llcoverthevariouswaysofcreatingaspringcontainerusingJava-basedconfiguration.
ThesectionsbelowdocumentSpring’sAnnotationConfigApplicationContext,newinSpring3.0.ThisversatileApplicationContextimplementationiscapableofacceptingnotonly@Configurationclassesasinput,butalsoplain@ComponentclassesandclassesannotatedwithJSR-330metadata.
When@Configurationclassesareprovidedasinput,the@Configurationclassitselfisregisteredasabeandefinition,andalldeclared@Beanmethodswithintheclassarealsoregisteredasbeandefinitions.
When@ComponentandJSR-330classesareprovided,theyareregisteredasbeandefinitions,anditisassumedthatDImetadatasuchas@Autowiredor@Injectareusedwithinthoseclasseswherenecessary.
InmuchthesamewaythatSpringXMLfilesareusedasinputwheninstantiatingaClassPathXmlApplicationContext,@ConfigurationclassesmaybeusedasinputwheninstantiatinganAnnotationConfigApplicationContext.ThisallowsforcompletelyXML-freeusageoftheSpringcontainer:
publicstaticvoidmain(String[]args){ApplicationContextctx=newAnnotationConfigApplicationContext(AppConfig.class);MyServicemyService=ctx.getBean(MyService.class);myService.doStuff();}Asmentionedabove,AnnotationConfigApplicationContextisnotlimitedtoworkingonlywith@Configurationclasses.Any@ComponentorJSR-330annotatedclassmaybesuppliedasinputtotheconstructor.Forexample:
publicstaticvoidmain(String[]args){ApplicationContextctx=newAnnotationConfigApplicationContext(MyServiceImpl.class,Dependency1.class,Dependency2.class);MyServicemyService=ctx.getBean(MyService.class);myService.doStuff();}TheaboveassumesthatMyServiceImpl,Dependency1andDependency2useSpringdependencyinjectionannotationssuchas@Autowired.
AnAnnotationConfigApplicationContextmaybeinstantiatedusingano-argconstructorandthenconfiguredusingtheregister()method.ThisapproachisparticularlyusefulwhenprogrammaticallybuildinganAnnotationConfigApplicationContext.
publicstaticvoidmain(String[]args){AnnotationConfigApplicationContextctx=newAnnotationConfigApplicationContext();ctx.register(AppConfig.class,OtherConfig.class);ctx.register(AdditionalConfig.class);ctx.refresh();MyServicemyService=ctx.getBean(MyService.class);myService.doStuff();}Enablingcomponentscanningwithscan(String…)(使用scan启用组件扫描(字符串…)Toenablecomponentscanning,justannotateyour@Configurationclassasfollows:
@Configuration@ComponentScan(basePackages="com.acme")publicclassAppConfig{...}ExperiencedSpringuserswillbefamiliarwiththeXMLdeclarationequivalentfromSpring’scontext:namespace
Youcanusethe@Beanannotationina@Configuration-annotatedorina@Component-annotatedclass.
Todeclareabean,simplyannotateamethodwiththe@Beanannotation.YouusethismethodtoregisterabeandefinitionwithinanApplicationContextofthetypespecifiedasthemethod’sreturnvalue.Bydefault,thebeannamewillbethesameasthemethodname.Thefollowingisasimpleexampleofa@Beanmethoddeclaration:
@ConfigurationpublicclassAppConfig{@BeanpublicTransferServiceImpltransferService(){returnnewTransferServiceImpl();}}TheprecedingconfigurationisexactlyequivalenttothefollowingSpringXML:
transferService->com.acme.TransferServiceImplYoumayalsodeclareyour@Beanmethodwithaninterface(orbaseclass)returntype:
@ConfigurationpublicclassAppConfig{@BeanpublicTransferServicetransferService(){returnnewTransferServiceImpl();}}However,thislimitsthevisibilityforadvancetypepredictiontothespecifiedinterfacetype(TransferService)then,withthefulltype(TransferServiceImpl)onlyknowntothecontaineroncetheaffectedsingletonbeanhasbeeninstantiated.Non-lazysingletonbeansgetinstantiatedaccordingtotheirdeclarationorder,soyoumayseedifferenttypematchingresultsdependingonwhenanothercomponenttriestomatchbyanon-declaredtype(suchas@AutowiredTransferServiceImplwhichwillonlyresolveoncethe"transferService"beanhasbeeninstantiated).
A@Beanannotatedmethodcanhaveanarbitrarynumberofparametersdescribingthedependenciesrequiredtobuildthatbean.ForinstanceifourTransferServicerequiresanAccountRepositorywecanmaterializethatdependencyviaamethodparameter:
The@Beanannotationsupportsspecifyingarbitraryinitializationanddestructioncallbackmethods,muchlikeSpringXML’sinit-methodanddestroy-methodattributesonthebeanelement:
Thedefaultscopeissingleton,butyoucanoverridethiswiththe@Scopeannotation:
IfyouportthescopedproxyexamplefromtheXMLreferencedocumentation(seeprecedinglink)toour@BeanusingJava,itwouldlooklikethefollowing:
//anHTTPSession-scopedbeanexposedasaproxy@Bean@SessionScopepublicUserPreferencesuserPreferences(){returnnewUserPreferences();}@BeanpublicServiceuserService(){UserServiceservice=newSimpleUserService();//areferencetotheproxieduserPreferencesbeanservice.setUserPreferences(userPreferences());returnservice;}Customizingbeannaming(定制bean命名)Bydefault,configurationclassesusea@Beanmethod’snameasthenameoftheresultingbean.Thisfunctionalitycanbeoverridden,however,withthenameattribute.
@ConfigurationpublicclassAppConfig{@Bean(name={"dataSource","subsystemA-dataSource","subsystemB-dataSource"})publicDataSourcedataSource(){//instantiate,configureandreturnDataSourcebean...}}Beandescription(Bean描述)Sometimesitishelpfultoprovideamoredetailedtextualdescriptionofabean.Thiscanbeparticularlyusefulwhenbeansareexposed(perhapsviaJMX)formonitoringpurposes.
When@Beanshavedependenciesononeanother,expressingthatdependencyisassimpleashavingonebeanmethodcallanother:
@ConfigurationpublicclassAppConfig{@BeanpublicFoofoo(){returnnewFoo(bar());}@BeanpublicBarbar(){returnnewBar();}}Intheexampleabove,thefoobeanreceivesareferencetobarviaconstructorinjection.
publicabstractclassCommandManager{publicObjectprocess(ObjectcommandState){//grabanewinstanceoftheappropriateCommandinterfaceCommandcommand=createCommand();//setthestateonthe(hopefullybrandnew)Commandinstancecommand.setState(commandState);returncommand.execute();}//okay...butwhereistheimplementationofthismethodprotectedabstractCommandcreateCommand();}UsingJava-configurationsupport,youcancreateasubclassofCommandManagerwheretheabstractcreateCommand()methodisoverriddeninsuchawaythatitlooksupanew(prototype)commandobject:
@Bean@Scope("prototype")publicAsyncCommandasyncCommand(){AsyncCommandcommand=newAsyncCommand();//injectdependencieshereasrequiredreturncommand;}@BeanpublicCommandManagercommandManager(){//returnnewanonymousimplementationofCommandManagerwithcommand()overridden//toreturnanewprototypeCommandobjectreturnnewCommandManager(){protectedCommandcreateCommand(){returnasyncCommand();}}}FurtherinformationabouthowJava-basedconfigurationworksinternally(关于基于java的配置如何在内部工作的进一步信息)Thefollowingexampleshowsa@Beanannotatedmethodbeingcalledtwice:
Muchasthe
@ConfigurationpublicclassConfigA{@BeanpublicAa(){returnnewA();}}@Configuration@Import(ConfigA.class)publicclassConfigB{@BeanpublicBb(){returnnewB();}}Now,ratherthanneedingtospecifybothConfigA.classandConfigB.classwheninstantiatingthecontext,onlyConfigBneedstobesuppliedexplicitly:
publicstaticvoidmain(String[]args){ApplicationContextctx=newAnnotationConfigApplicationContext(ConfigB.class);//nowbothbeansAandBwillbeavailable...Aa=ctx.getBean(A.class);Bb=ctx.getBean(B.class);}Thisapproachsimplifiescontainerinstantiation,asonlyoneclassneedstobedealtwith,ratherthanrequiringthedevelopertorememberapotentiallylargenumberof@Configurationclassesduringconstruction.
Theexampleaboveworks,butissimplistic.Inmostpracticalscenarios,beanswillhavedependenciesononeanotheracrossconfigurationclasses.WhenusingXML,thisisnotanissue,perse,becausethereisnocompilerinvolved,andonecansimplydeclareref="someBean"andtrustthatSpringwillworkitoutduringcontainerinitialization.Ofcourse,whenusing@Configurationclasses,theJavacompilerplacesconstraintsontheconfigurationmodel,inthatreferencestootherbeansmustbevalidJavasyntax.
@ConfigurationpublicclassServiceConfig{@BeanpublicTransferServicetransferService(AccountRepositoryaccountRepository){returnnewTransferServiceImpl(accountRepository);}}@ConfigurationpublicclassRepositoryConfig{@BeanpublicAccountRepositoryaccountRepository(DataSourcedataSource){returnnewJdbcAccountRepository(dataSource);}}@Configuration@Import({ServiceConfig.class,RepositoryConfig.class})publicclassSystemTestConfig{@BeanpublicDataSourcedataSource(){//returnnewDataSource}}publicstaticvoidmain(String[]args){ApplicationContextctx=newAnnotationConfigApplicationContext(SystemTestConfig.class);//everythingwiresupacrossconfigurationclasses...TransferServicetransferService=ctx.getBean(TransferService.class);transferService.transfer(100.00,"A123","C456");}Thereisanotherwaytoachievethesameresult.Rememberthat@Configurationclassesareultimatelyjustanotherbeaninthecontainer:Thismeansthattheycantakeadvantageof@Autowiredand@Valueinjectionetcjustlikeanyotherbean!
IncaseswherethisambiguityisnotacceptableandyouwishtohavedirectnavigationfromwithinyourIDEfromone@Configurationclasstoanother,considerautowiringtheconfigurationclassesthemselves:
@ConfigurationpublicclassServiceConfig{@AutowiredprivateRepositoryConfigrepositoryConfig;@BeanpublicTransferServicetransferService(){//navigate'through'theconfigclasstothe@Beanmethod!returnnewTransferServiceImpl(repositoryConfig.accountRepository());}}Inthesituationabove,itiscompletelyexplicitwhereAccountRepositoryisdefined.However,ServiceConfigisnowtightlycoupledtoRepositoryConfig;that’sthetradeoff.Thistightcouplingcanbesomewhatmitigatedbyusinginterface-basedorabstractclass-based@Configurationclasses.Considerthefollowing:
ImplementationsoftheConditioninterfacesimplyprovideamatches(…)methodthatreturnstrueorfalse.Forexample,hereistheactualConditionimplementationusedfor@Profile:
Spring’s@Configurationclasssupportdoesnotaimtobea100%completereplacementforSpringXML.SomefacilitiessuchasSpringXMLnamespacesremainanidealwaytoconfigurethecontainer.IncaseswhereXMLisconvenientornecessary,youhaveachoice:eitherinstantiatethecontainerinan"XML-centric"wayusing,forexample,ClassPathXmlApplicationContext,orina"Java-centric"fashionusingAnnotationConfigApplicationContextandthe@ImportResourceannotationtoimportXMLasneeded.
ItmaybepreferabletobootstraptheSpringcontainerfromXMLandinclude@Configurationclassesinanad-hocfashion.Forexample,inalargeexistingcodebasethatusesSpringXML,itwillbeeasiertocreate@Configurationclassesonanas-neededbasisandincludethemfromtheexistingXMLfiles.Belowyou’llfindtheoptionsforusing@Configurationclassesinthiskindof"XML-centric"situation.
Rememberthat@Configurationclassesareultimatelyjustbeandefinitionsinthecontainer.Inthisexample,wecreatea@ConfigurationclassnamedAppConfigandincludeitwithinsystem-test-config.xmlasa
@ConfigurationpublicclassAppConfig{@AutowiredprivateDataSourcedataSource;@BeanpublicAccountRepositoryaccountRepository(){returnnewJdbcAccountRepository(dataSource);}@BeanpublicTransferServicetransferService(){returnnewTransferService(accountRepository());}}system-test-config.xml:
jdbc.url=jdbc:hsqldb:hsql://localhost/xdbjdbc.username=sajdbc.password=publicstaticvoidmain(String[]args){ApplicationContextctx=newClassPathXmlApplicationContext("classpath:/com/acme/system-test-config.xml");TransferServicetransferService=ctx.getBean(TransferService.class);//...}Insystem-test-config.xmlabove,theAppConfig
Because@Configurationismeta-annotatedwith@Component,@Configuration-annotatedclassesareautomaticallycandidatesforcomponentscanning.Usingthesamescenarioasabove,wecanredefinesystem-test-config.xmltotakeadvantageofcomponent-scanning.Notethatinthiscase,wedon’tneedtoexplicitlydeclare
system-test-config.xml:
Aprofileisanamed,logicalgroupofbeandefinitionstoberegisteredwiththecontaineronlyifthegivenprofileisactive.BeansmaybeassignedtoaprofilewhetherdefinedinXMLorviaannotations.TheroleoftheEnvironmentobjectwithrelationtoprofilesisindeterminingwhichprofiles(ifany)arecurrentlyactive,andwhichprofiles(ifany)shouldbeactivebydefault.
Propertiesplayanimportantroleinalmostallapplications,andmayoriginatefromavarietyofsources:propertiesfiles,JVMsystemproperties,systemenvironmentvariables,JNDI,servletcontextparameters,ad-hocPropertiesobjects,Maps,andsoon.TheroleoftheEnvironmentobjectwithrelationtopropertiesistoprovidetheuserwithaconvenientserviceinterfaceforconfiguringpropertysourcesandresolvingpropertiesfromthem.
Beandefinitionprofilesisamechanisminthecorecontainerthatallowsforregistrationofdifferentbeansindifferentenvironments.Thewordenvironmentcanmeandifferentthingstodifferentusersandthisfeaturecanhelpwithmanyusecases,including:
Let’sconsiderthefirstusecaseinapracticalapplicationthatrequiresaDataSource.Inatestenvironment,theconfigurationmaylooklikethis:
@BeanpublicDataSourcedataSource(){returnnewEmbeddedDatabaseBuilder().setType(EmbeddedDatabaseType.HSQL).addScript("my-schema.sql").addScript("my-test-data.sql").build();}Let’snowconsiderhowthisapplicationwillbedeployedintoaQAorproductionenvironment,assumingthatthedatasourcefortheapplicationwillberegisteredwiththeproductionapplicationserver’sJNDIdirectory.OurdataSourcebeannowlookslikethis:
@Bean(destroyMethod="")publicDataSourcedataSource()throwsException{Contextctx=newInitialContext();return(DataSource)ctx.lookup("java:comp/env/jdbc/datasource");}Theproblemishowtoswitchbetweenusingthesetwovariationsbasedonthecurrentenvironment.Overtime,Springusershavedevisedanumberofwaystogetthisdone,usuallyrelyingonacombinationofsystemenvironmentvariablesandXML
Ifwegeneralizetheexampleusecaseaboveofenvironment-specificbeandefinitions,weendupwiththeneedtoregistercertainbeandefinitionsincertaincontexts,whilenotinothers.YoucouldsaythatyouwanttoregisteracertainprofileofbeandefinitionsinsituationA,andadifferentprofileinsituationB.Let’sfirstseehowwecanupdateourconfigurationtoreflectthisneed.
@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@Profile("production")public@interfaceProduction{}Ifa@Configurationclassismarkedwith@Profile,allofthe@Beanmethodsand@Importannotationsassociatedwiththatclasswillbebypassedunlessoneormoreofthespecifiedprofilesareactive.Ifa@Componentor@Configurationclassismarkedwith@Profile({"p1","p2"}),thatclasswillnotberegistered/processedunlessprofiles'p1'and/or'p2'havebeenactivated.IfagivenprofileisprefixedwiththeNOToperator(!),theannotatedelementwillberegisterediftheprofileisnotactive.Forexample,given@Profile({"p1","!p2"}),registrationwilloccurifprofile'p1'isactiveorifprofile'p2'isnotactive.@Profilecanalsobedeclaredatthemethodleveltoincludeonlyoneparticularbeanofaconfigurationclass,e.g.foralternativevariantsofaparticularbean:
Nowthatwehaveupdatedourconfiguration,westillneedtoinstructSpringwhichprofileisactive.Ifwestartedoursampleapplicationrightnow,wewouldseeaNoSuchBeanDefinitionExceptionthrown,becausethecontainercouldnotfindtheSpringbeannameddataSource.
Activatingaprofilecanbedoneinseveralways,butthemoststraightforwardistodoitprogrammaticallyagainsttheEnvironmentAPIwhichisavailableviaanApplicationContext:
Notethatprofilesarenotan"either-or"proposition;itispossibletoactivatemultipleprofilesatonce.Programmatically,simplyprovidemultipleprofilenamestothesetActiveProfiles()method,whichacceptsString…varargs:
ctx.getEnvironment().setActiveProfiles("profile1","profile2");Declaratively,spring.profiles.activemayacceptacomma-separatedlistofprofilenames:
-Dspring.profiles.active="profile1,profile2"Defaultprofile(默认配置文件)Thedefaultprofilerepresentstheprofilethatisenabledbydefault.Considerthefollowing:(默认配置文件表示默认情况下启用的配置文件。考虑以下:)
@Configuration@Profile("default")publicclassDefaultDataConfig{@BeanpublicDataSourcedataSource(){returnnewEmbeddedDatabaseBuilder().setType(EmbeddedDatabaseType.HSQL).addScript("classpath:com/bank/config/sql/schema.sql").build();}}Ifnoprofileisactive,thedataSourceabovewillbecreated;thiscanbeseenasawaytoprovideadefaultdefinitionforoneormorebeans.Ifanyprofileisenabled,thedefaultprofilewillnotapply.
ThenameofthedefaultprofilecanbechangedusingsetDefaultProfiles()ontheEnvironmentordeclarativelyusingthespring.profiles.defaultproperty.
Spring’sEnvironmentabstractionprovidessearchoperationsoveraconfigurablehierarchyofpropertysources.Toexplainfully,considerthefollowing:
Concretely,whenusingtheStandardEnvironment,thecalltoenv.containsProperty("foo")willreturntrueifafoosystempropertyorfooenvironmentvariableispresentatruntime.
Mostimportantly,theentiremechanismisconfigurable.Perhapsyouhaveacustomsourceofpropertiesthatyou’dliketointegrateintothissearch.Noproblem—simplyimplementandinstantiateyourownPropertySourceandaddittothesetofPropertySourcesforthecurrentEnvironment:
Givenafile"app.properties"containingthekey/valuepairtestbean.name=myTestBean,thefollowing@Configurationclassuses@PropertySourceinsuchawaythatacalltotestBean.getName()willreturn"myTestBean".
@Configuration@PropertySource("classpath:/com/myco/app.properties")publicclassAppConfig{@AutowiredEnvironmentenv;@BeanpublicTestBeantestBean(){TestBeantestBean=newTestBean();testBean.setName(env.getProperty("testbean.name"));returntestBean;}}Any${…}placeholderspresentina@PropertySourceresourcelocationwillberesolvedagainstthesetofpropertysourcesalreadyregisteredagainsttheenvironment.Forexample:
@Configuration@PropertySource("classpath:/com/${my.placeholder:default/path}/app.properties")publicclassAppConfig{@AutowiredEnvironmentenv;@BeanpublicTestBeantestBean(){TestBeantestBean=newTestBean();testBean.setName(env.getProperty("testbean.name"));returntestBean;}}Assumingthat"my.placeholder"ispresentinoneofthepropertysourcesalreadyregistered,e.g.systempropertiesorenvironmentvariables,theplaceholderwillberesolvedtothecorrespondingvalue.Ifnot,then"default/path"willbeusedasadefault.Ifnodefaultisspecifiedandapropertycannotberesolved,anIllegalArgumentExceptionwillbethrown.
Historically,thevalueofplaceholdersinelementscouldberesolvedonlyagainstJVMsystempropertiesorenvironmentvariables.Nolongeristhisthecase.BecausetheEnvironmentabstractionisintegratedthroughoutthecontainer,it’seasytorouteresolutionofplaceholdersthroughit.Thismeansthatyoumayconfiguretheresolutionprocessinanywayyoulike:changetheprecedenceofsearchingthroughsystempropertiesandenvironmentvariables,orremovethementirely;addyourownpropertysourcestothemixasappropriate.
Concretely,thefollowingstatementworksregardlessofwherethecustomerpropertyisdefined,aslongasitisavailableintheEnvironment:
Toenableload-timeweavingaddthe@EnableLoadTimeWeavingtooneofyour@Configurationclasses:
@Configuration@EnableLoadTimeWeavingpublicclassAppConfig{}AlternativelyforXMLconfigurationusethecontext:load-time-weaverelement:
ToenhanceBeanFactoryfunctionalityinamoreframework-orientedstylethecontextpackagealsoprovidesthefollowingfunctionality:
TheApplicationContextinterfaceextendsaninterfacecalledMessageSource,andthereforeprovidesinternationalization(i18n)functionality.SpringalsoprovidestheinterfaceHierarchicalMessageSource,whichcanresolvemessageshierarchically.TogethertheseinterfacesprovidethefoundationuponwhichSpringeffectsmessageresolution.Themethodsdefinedontheseinterfacesinclude:
WhenanApplicationContextisloaded,itautomaticallysearchesforaMessageSourcebeandefinedinthecontext.ThebeanmusthavethenamemessageSource.Ifsuchabeanisfound,allcallstotheprecedingmethodsaredelegatedtothemessagesource.Ifnomessagesourceisfound,theApplicationContextattemptstofindaparentcontainingabeanwiththesamename.Ifitdoes,itusesthatbeanastheMessageSource.IftheApplicationContextcannotfindanysourceformessages,anemptyDelegatingMessageSourceisinstantiatedinordertobeabletoacceptcallstothemethodsdefinedabove.
SpringprovidestwoMessageSourceimplementations,ResourceBundleMessageSourceandStaticMessageSource.BothimplementHierarchicalMessageSourceinordertodonestedmessaging.TheStaticMessageSourceisrarelyusedbutprovidesprogrammaticwaystoaddmessagestothesource.TheResourceBundleMessageSourceisshowninthefollowingexample:
#informat.propertiesmessage=Alligatorsrock!#inexceptions.propertiesargument.required=The{0}argumentisrequired.AprogramtoexecutetheMessageSourcefunctionalityisshowninthenextexample.RememberthatallApplicationContextimplementationsarealsoMessageSourceimplementationsandsocanbecasttotheMessageSourceinterface.
publicstaticvoidmain(String[]args){MessageSourceresources=newClassPathXmlApplicationContext("beans.xml");Stringmessage=resources.getMessage("message",null,"Default",null);System.out.println(message);}Theresultingoutputfromtheaboveprogramwillbe…
Alligatorsrock!Sotosummarize,theMessageSourceisdefinedinafilecalledbeans.xml,whichexistsattherootofyourclasspath.ThemessageSourcebeandefinitionreferstoanumberofresourcebundlesthroughitsbasenamesproperty.Thethreefilesthatarepassedinthelisttothebasenamespropertyexistasfilesattherootofyourclasspathandarecalledformat.properties,exceptions.properties,andwindows.propertiesrespectively.
Thenextexampleshowsargumentspassedtothemessagelookup;theseargumentswillbeconvertedintoStringsandinsertedintoplaceholdersinthelookupmessage.
TheuserDaoargumentisrequired.Withregardtointernationalization(i18n),Spring’svariousMessageSourceimplementationsfollowthesamelocaleresolutionandfallbackrulesasthestandardJDKResourceBundle.Inshort,andcontinuingwiththeexamplemessageSourcedefinedpreviously,ifyouwanttoresolvemessagesagainsttheBritish(en-GB)locale,youwouldcreatefilescalledformat_en_GB.properties,exceptions_en_GB.properties,andwindows_en_GB.propertiesrespectively.
Typically,localeresolutionismanagedbythesurroundingenvironmentoftheapplication.Inthisexample,thelocaleagainstwhich(British)messageswillberesolvedisspecifiedmanually.
#inexceptions_en_GB.propertiesargument.required=Ebagumlad,the{0}argumentisrequired,Isay,required.publicstaticvoidmain(finalString[]args){MessageSourceresources=newClassPathXmlApplicationContext("beans.xml");Stringmessage=resources.getMessage("argument.required",newObject[]{"userDao"},"Required",Locale.UK);System.out.println(message);}Theresultingoutputfromtherunningoftheaboveprogramwillbe…
Ebagumlad,the'userDao'argumentisrequired,Isay,required.YoucanalsousetheMessageSourceAwareinterfacetoacquireareferencetoanyMessageSourcethathasbeendefined.AnybeanthatisdefinedinanApplicationContextthatimplementstheMessageSourceAwareinterfaceisinjectedwiththeapplicationcontext’sMessageSourcewhenthebeaniscreatedandconfigured.
EventhandlingintheApplicationContextisprovidedthroughtheApplicationEventclassandApplicationListenerinterface.IfabeanthatimplementstheApplicationListenerinterfaceisdeployedintothecontext,everytimeanApplicationEventgetspublishedtotheApplicationContext,thatbeanisnotified.Essentially,thisisthestandardObserverdesignpattern.
Springprovidesthefollowingstandardevents:
Youcanalsocreateandpublishyourowncustomevents.ThisexampledemonstratesasimpleclassthatextendsSpring’sApplicationEventbaseclass:
publicclassBlackListEventextendsApplicationEvent{privatefinalStringaddress;privatefinalStringtest;publicBlackListEvent(Objectsource,Stringaddress,Stringtest){super(source);this.address=address;this.test=test;}//accessorandothermethods...}TopublishacustomApplicationEvent,callthepublishEvent()methodonanApplicationEventPublisher.TypicallythisisdonebycreatingaclassthatimplementsApplicationEventPublisherAwareandregisteringitasaSpringbean.Thefollowingexampledemonstratessuchaclass:
publicclassEmailServiceimplementsApplicationEventPublisherAware{privateList
ToreceivethecustomApplicationEvent,createaclassthatimplementsApplicationListenerandregisteritasaSpringbean.Thefollowingexampledemonstratessuchaclass:
publicclassBlackListNotifierimplementsApplicationListener
Thefollowingexampleshowsthebeandefinitionsusedtoregisterandconfigureeachoftheclassesabove:
AsofSpring4.2,aneventlistenercanberegisteredonanypublicmethodofamanagedbeanviatheEventListenerannotation.TheBlackListNotifiercanberewrittenasfollows:
publicclassBlackListNotifier{privateStringnotificationAddress;publicvoidsetNotificationAddress(StringnotificationAddress){this.notificationAddress=notificationAddress;}@EventListenerpublicvoidprocessBlackListEvent(BlackListEventevent){//notifyappropriatepartiesvianotificationAddress...}}Asyoucanseeabove,themethodsignatureonceagaindeclarestheeventtypeitlistensto,butthistimewithaflexiblenameandwithoutimplementingaspecificlistenerinterface.Theeventtypecanalsobenarrowedthroughgenericsaslongastheactualeventtyperesolvesyourgenericparameterinitsimplementationhierarchy.
Ifyourmethodshouldlistentoseveraleventsorifyouwanttodefineitwithnoparameteratall,theeventtype(s)canalsobespecifiedontheannotationitself:
Forinstance,ournotifiercanberewrittentobeonlyinvokedifthetestattributeoftheeventisequaltofoo:
@EventListener(condition="#blEvent.test=='foo'")publicvoidprocessBlackListEvent(BlackListEventblEvent){//notifyappropriatepartiesvianotificationAddress...}EachSpELexpressionevaluatesagainadedicatedcontext.Thenexttableliststheitemsmadeavailabletothecontextsoonecanusethemforconditionaleventprocessing:
Notethat#root.eventallowsyoutoaccesstotheunderlyingevent,evenifyourmethodsignatureactuallyreferstoanarbitraryobjectthatwaspublished.
Ifyouneedtopublishaneventastheresultofprocessinganother,justchangethemethodsignaturetoreturntheeventthatshouldbepublished,somethinglike:
@EventListener@AsyncpublicvoidprocessBlackListEvent(BlackListEventevent){//BlackListEventisprocessedinaseparatethread}Beawareofthefollowinglimitationswhenusingasynchronousevents:
Ifyouneedthelistenertobeinvokedbeforeanotherone,justaddthe@Orderannotationtothemethoddeclaration:
@EventListener@Order(42)publicvoidprocessBlackListEvent(BlackListEventevent){//notifyappropriatepartiesvianotificationAddress...}Genericevents(通用的事件)Youmayalsousegenericstofurtherdefinethestructureofyourevent.ConsideranEntityCreatedEvent
@EventListenerpublicvoidonPersonCreated(EntityCreatedEvent
Incertaincircumstances,thismaybecomequitetediousifalleventsfollowthesamestructure(asitshouldbethecasefortheeventabove).Insuchacase,youcanimplementResolvableTypeProvidertoguidetheframeworkbeyondwhattheruntimeenvironmentprovides:
AnapplicationcontextisaResourceLoader,whichcanbeusedtoloadResources.AResourceisessentiallyamorefeaturerichversionoftheJDKclassjava.net.URL,infact,theimplementationsoftheResourcewrapaninstanceofjava.net.URLwhereappropriate.AResourcecanobtainlow-levelresourcesfromalmostanylocationinatransparentfashion,includingfromtheclasspath,afilesystemlocation,anywheredescribablewithastandardURL,andsomeothervariations.Iftheresourcelocationstringisasimplepathwithoutanyspecialprefixes,wherethoseresourcescomefromisspecificandappropriatetotheactualapplicationcontexttype.
Youcanconfigureabeandeployedintotheapplicationcontexttoimplementthespecialcallbackinterface,ResourceLoaderAware,tobeautomaticallycalledbackatinitializationtimewiththeapplicationcontextitselfpassedinastheResourceLoader.YoucanalsoexposepropertiesoftypeResource,tobeusedtoaccessstaticresources;theywillbeinjectedintoitlikeanyotherproperties.YoucanspecifythoseResourcepropertiesassimpleStringpaths,andrelyonaspecialJavaBeanPropertyEditorthatisautomaticallyregisteredbythecontext,toconvertthosetextstringstoactualResourceobjectswhenthebeanisdeployed.
ThelocationpathorpathssuppliedtoanApplicationContextconstructorareactuallyresourcestrings,andinsimpleformaretreatedappropriatelytothespecificcontextimplementation.ClassPathXmlApplicationContexttreatsasimplelocationpathasaclasspathlocation.Youcanalsouselocationpaths(resourcestrings)withspecialprefixestoforceloadingofdefinitionsfromtheclasspathoraURL,regardlessoftheactualcontexttype.
YoucancreateApplicationContextinstancesdeclarativelybyusing,forexample,aContextLoader.OfcourseyoucanalsocreateApplicationContextinstancesprogrammaticallybyusingoneoftheApplicationContextimplementations.
YoucanregisteranApplicationContextusingtheContextLoaderListenerasfollows:
ItispossibletodeployaSpringApplicationContextasaRARfile,encapsulatingthecontextandallofitsrequiredbeanclassesandlibraryJARsinaJavaEERARdeploymentunit.ThisistheequivalentofbootstrappingastandaloneApplicationContext,justhostedinJavaEEenvironment,beingabletoaccesstheJavaEEserversfacilities.RARdeploymentismorenaturalalternativetoscenarioofdeployingaheadlessWARfile,ineffect,aWARfilewithoutanyHTTPentrypointsthatisusedonlyforbootstrappingaSpringApplicationContextinaJavaEEenvironment.
RARdeploymentisidealforapplicationcontextsthatdonotneedHTTPentrypointsbutratherconsistonlyofmessageendpointsandscheduledjobs.BeansinsuchacontextcanuseapplicationserverresourcessuchastheJTAtransactionmanagerandJNDI-boundJDBCDataSourcesandJMSConnectionFactoryinstances,andmayalsoregisterwiththeplatform’sJMXserver-allthroughSpring’sstandardtransactionmanagementandJNDIandJMXsupportfacilities.Applicationcomponentscanalsointeractwiththeapplicationserver’sJCAWorkManagerthroughSpring’sTaskExecutorabstraction.
ForasimpledeploymentofaSpringApplicationContextasaJavaEERARfile:packageallapplicationclassesintoaRARfile,whichisastandardJARfilewithadifferentfileextension.AddallrequiredlibraryJARsintotherootoftheRARarchive.Adda"META-INF/ra.xml"deploymentdescriptor(asshowninSpringContextResourceAdaptersjavadoc)andthecorrespondingSpringXMLbeandefinitionfile(s)(typically"META-INF/applicationContext.xml"),anddroptheresultingRARfileintoyourapplicationserver’sdeploymentdirectory.
TheBeanFactoryprovidestheunderlyingbasisforSpring’sIoCfunctionalitybutitisonlyuseddirectlyinintegrationwithotherthird-partyframeworksandisnowlargelyhistoricalinnatureformostusersofSpring.TheBeanFactoryandrelatedinterfaces,suchasBeanFactoryAware,InitializingBean,DisposableBean,arestillpresentinSpringforthepurposesofbackwardcompatibilitywiththelargenumberofthird-partyframeworksthatintegratewithSpring.Oftenthird-partycomponentsthatcannotusemoremodernequivalentssuchas@PostConstructor@PreDestroyinordertoavoidadependencyonJSR-250.
ThissectionprovidesadditionalbackgroundintothedifferencesbetweentheBeanFactoryandApplicationContextandhowonemightaccesstheIoCcontainerdirectlythroughaclassicsingletonlookup.
UseanApplicationContextunlessyouhaveagoodreasonfornotdoingso.(使用ApplicationContext,除非你有很好的理由不这样做。)
ThefollowingtablelistsfeaturesprovidedbytheBeanFactoryandApplicationContextinterfacesandimplementations.
Toexplicitlyregisterabeanpost-processorwithaBeanFactoryimplementation,youneedtowritecodelikethis:
DefaultListableBeanFactoryfactory=newDefaultListableBeanFactory();//populatethefactorywithbeandefinitions//nowregisteranyneededBeanPostProcessorinstancesMyBeanPostProcessorpostProcessor=newMyBeanPostProcessor();factory.addBeanPostProcessor(postProcessor);//nowstartusingthefactoryToexplicitlyregisteraBeanFactoryPostProcessorwhenusingaBeanFactoryimplementation,youmustwritecodelikethis:
DefaultListableBeanFactoryfactory=newDefaultListableBeanFactory();XmlBeanDefinitionReaderreader=newXmlBeanDefinitionReader(factory);reader.loadBeanDefinitions(newFileSystemResource("beans.xml"));//bringinsomepropertyvaluesfromaPropertiesfilePropertyPlaceholderConfigurercfg=newPropertyPlaceholderConfigurer();cfg.setLocation(newFileSystemResource("jdbc.properties"));//nowactuallydothereplacementcfg.postProcessBeanFactory(factory);Inbothcases,theexplicitregistrationstepisinconvenient,whichisonereasonwhythevariousApplicationContextimplementationsarepreferredaboveplainBeanFactoryimplementationsinthevastmajorityofSpring-backedapplications,especiallywhenusingBeanFactoryPostProcessorsandBeanPostProcessors.ThesemechanismsimplementimportantfunctionalitysuchaspropertyplaceholderreplacementandAOP.