把握Spring事情监听器的外部逻辑与成功-深化了解Spring运行程序生命周期中事情的颁布和监听机制 (把握共同富裕的深刻内涵)
1.事情的档次流传
在Spring中,licationContext可以构成一个档次结构,通常由主容器和多个子容器组成。一个经常出现的不懂是:当一个事情在其中一个容器中颁布时,这个事情会如何在这个档次结构中流传?
为了讨论这个疑问,咱们创立了一个名为HierarchicalEventPropagationEvent的事情类和一个对应的监听器HierarchicalEventPropagationListener。
所有代码如下:
packagecom.example.demo.event;importorg.springframework.context.ApplicationEvent;//事情类publicclassHierarchicalEventPropagationEventextendsApplicationEvent{privateStringmessage;publicHierarchicalEventPropagationEvent(Objectsource,Stringmessage){super(source);this.message=message;}publicStringgetMessage(){returnmessage;}}
相应地,为HierarchicalEventPropagationEvent定义一个监听器HierarchicalEventPropagationListener:
packagecom.example.demo.listener;importcom.example.demo.event.HierarchicalEventPropagationEvent;importorg.springframework.context.ApplicationListener;//监听器类publicclassHierarchicalEventPropagationListenerimplementsApplicationListener<HierarchicalEventPropagationEvent>{privateStringlistenerId;publicHierarchicalEventPropagationListener(StringlistenerId){this.listenerId=listenerId;}@OverridepublicvoidonApplicationEvent(HierarchicalEventPropagationEventevent){System.out.println(listenerId+"receivedevent-"+event.getMessage());}}
为了测试承袭机制,咱们须要构建主容器和子容器,并为每个容器注册了一个监听器。初始化容器后,咱们在两个容器中区分颁布事情。
请留意,首先须要刷新主容器,而后刷新子容器。否则会出现意外:Exceptioninthread"mn".lang.IllegalStateException:ApplicationEventMulticasternotinitialized-call'refresh'beforemulticastingeventsviathecontext:org.springframework.context.annotation.AnnotationConfigApplicationContext@somehashcode
主程序如下:
ackagecom.example.demo;importcom.example.demo.event.HierarchicalEventPropagationEvent;importcom.example.demo.listener.HierarchicalEventPropagationListener;importorg.springframework.context.annotation.AnnotationConfigApplicationContext;publicclassDemoApplication{publicstaticvoidmain(String[]args){//创立父容器,注册监听器AnnotationConfigApplicationContextparentCtx=newAnnotationConfigApplicationContext();parentCtx.addApplicationListener(newHierarchicalEventPropagationListener("ParentListener"));parentCtx.refresh();//创立子容器,注册监听器AnnotationConfigApplicationContextchildCtx=newAnnotationConfigApplicationContext();childCtx.setParent(parentCtx);childCtx.addApplicationListener(newHierarchicalEventPropagationListener("ChildListener"));childCtx.refresh();//颁布事情HierarchicalEventPropagationEventevent1=newHierarchicalEventPropagationEvent(parentCtx,"Eventfromparent");parentCtx.publishEvent(event1);HierarchicalEventPropagationEventevent2=newHierarchicalEventPropagationEvent(childCtx,"Eventfromchild");childCtx.publishEvent(event2);}}
运转结果
主容器颁布的事情只触发了一次性监听,而子容器颁布的事情触发了两次监听。父容器和子容器都监听到了来自子容器的事情,而只要父容器监听到了来自父容器的事情。
所以得出论断:在Spring的父子容器结构中,事情会从子容器向上行播至其父容器,但父容器中颁布的事情不会向下流传至子容器。这种设计可以协助开发者在父容器中集中处置一切的事情,而不用担忧事情在多个子容器之间的流传。
2.PayloadApplicationEvent的经常使用
PayloadApplicationEvent是Spring提供的一种不凡事情,用于传递数据(称为"payload")。所以不须要自定义事情,PayloadApplicationEvent可以间接传递任何类型的数据,只要要指定它的类型即可。
所有代码如下:
首先,咱们来看怎样定义一个监听器来接纳这个事情:
通用监听器-会监听到一切种类的PayloadApplicationEvent:
packagecom.example.demo.listener;importorg.springframework.context.ApplicationListener;importorg.springframework.context.PayloadApplicationEvent;/***通用监听器,能监听到一切类型的PayloadApplicationEvent*/publicclassCustomObjectApplicationListenerimplementsApplicationListener<PayloadApplicationEvent>{@OverridepublicvoidonApplicationEvent(PayloadApplicationEventevent){System.out.println("收到PayloadApplicationEvent,数据是:"+event.getPayload());}}
特定数据类型的监听器-只会监听指定类型的数据。例如,假设咱们只对字符串数据感兴味,咱们可以如此定义:
packagecom.example.demo.listener;importorg.springframework.context.ApplicationListener;importorg.springframework.context.PayloadApplicationEvent;/***特定数据类型的监听器。这个监听器专门监听String类型的PayloadApplicationEvent*/publicclassCustomStringApplicationListenerimplementsApplicationListener<PayloadApplicationEvent<String>>{@OverridepublicvoidonApplicationEvent(PayloadApplicationEvent<String>event){System.out.println("收到了字符串数据:"+event.getPayload());}}
要看这两种监听器如何上班,咱们来写一个测试。
执行结果如下:
从输入可以看出:
第一种监听器(通用的)接纳到了一切三个事情,由于它不关心数据的详细类型。
第二种监听器(字符串公用的)只接纳到了字符串类型的事情。
3.为什么选用自定义事情?
只管PayloadApplicationEvent提供了简化事情监听的才干,但其或者无余以满足特定的业务需求,尤其是当须要更多高低文和数据时。上方是一个经常使用自定义事情ArticlePublishedEvent的例子。
所有代码如下:
自定义事情:ArticlePublishedEvent
packagecom.example.demo.event;importorg.springframework.context.ApplicationEvent;publicclassArticlePublishedEventextendsApplicationEvent{privateStringtitle;privateStringauthor;privateStringpublishedDate;publicArticlePublishedEvent(Objectsource,Stringtitle,Stringauthor,StringpublishedDate){super(source);this.title=title;this.author=author;this.publishedDate=publishedDate;}publicStringgetTitle(){returntitle;}publicStringgetAuthor(){returnauthor;}publicStringgetPublishedDate(){returnpublishedDate;}}
自定义监听器:ArticlePublishedListener
这个监听器专门照应ArticlePublishedEvent,执行特定的业务逻辑,例如通知订阅者、降级搜查索引等。
importorg.springframework.context.ApplicationListener;publicclassArticlePublishedListenerimplementsApplicationListener<ArticlePublishedEvent>{@OverridepublicvoidonApplicationEvent(ArticlePublishedEventevent){System.out.println("Anewarticlehasbeenpublished!");System.out.println("Title:"+event.getTitle());System.out.println("Author:"+event.getAuthor());System.out.println("PublishedDate:"+event.getPublishedDate());//NotifysubscribersaboutthenewarticlenotifySubscribers(event);//UpdatesearchengineindexwithnewarticledetailsupdateSearchIndex(event);//Updatestatistical>packagecom.example.demo;importcom.example.demo.event.ArticlePublishedEvent;importcom.example.demo.listener.ArticlePublishedListener;importorg.springframework.context.annotation.AnnotationConfigApplicationContext;publicclassDemoApplication{publicstaticvoidmain(String[]args){//InitializeSpringApplicationContextAnnotationConfigApplicationContextctx=newAnnotationConfigApplicationContext();//Registerlistenerctx.addApplicationListener(newArticlePublishedListener());ctx.refresh();//Simulatepublishinganarticlectx.publishEvent(newArticlePublishedEvent(ctx,"SpringEvents","JohnDoe","2023-09-15"));}}
运转结果如下:
咱们可以看到ArticlePublishedEvent比PayloadApplicationEvent具有更多的业务含意和高低文。这样的设计使咱们能够更详细地照应和处置特定的业务事情。
实践上,在企业级运行中,文章颁布或者会触发多种不同的后续举措,经常使用Spring的事情监听器形式可以带来如下长处:
解耦:事情颁布者(即新文章颁布性能)不用关心详细的后续处置步骤。它只要颁布事情,而后其余感兴味的监听器会相应地做出照应。这种设计有助于各特性能之间的解耦。
可裁减性:假设未来须要为新文章颁布参与更多的后续处置,只要参与更多的监听器即可,无需修正原有的业务逻辑。
保养性:由于性能之间的解耦,每特性能模块都可以独立保养,这有助于提高代码的可保养性。
Spring为开发者提供了弱小的事情监听机制,无论是经常使用自定义事情还是应用PayloadApplicationEvent启动极速开发,都使咱们能够构建一个高度解耦、可裁减且易于保养的系统。
4.事情广播原理
4.1Spring5.x的事情模型概述
外围概念:
ApplicationEvent:这是一切Spring事情的超类。用户可以经过承袭此类来创立自定义事情。
ApplicationListener:这是一切事情监听器的接口。它定义了一个onApplicationEvent方法,用于处置特定类型的事情。
ApplicationEventPublisher:这是一个接口,定义了颁布事情的方法。ApplicationContext承袭了这个接口,因此任何Springbean都可以颁布事情。
ApplicationEventMulticaster:这个组件担任将事情广播到一切婚配的监听器。
事情颁布:
用户可以经过ApplicationEventPublisher接口或ApplicationContext来颁布事情。通常状况下,当咱们在Springbean中须要颁布事情时,可以让这个bean成功ApplicationEventPublisherAware接口,这样Spring容器会注入一个事情颁布器。
异步事情:
从Spring4.2开局,咱们可以轻松地使事情监听器异步化。在Spring5中,这一性能依然失掉支持。只要要在监听器方法上参与@Async注解并确保启用了异步支持。这使得事情处置可以在独自的线程中执行,不阻塞颁布者。
泛型事情:
Spring4.2引入了对泛型事情的支持,这在Spring5中失掉了保养。这象征着监听器如今可以依据事情的泛型类型启动过滤。例如,一个ApplicationListener<ApplicationEvent<String>>将只接纳到携带String负载的事情。
事情的排序:
监听器可以成功Ordered接口或经常使用@Order注解来指定事情的执行顺序。
新的事情类型:
Spring5引入了新的事情类型,如ServletRequestHandledEvent,为web恳求处置提供更多的钩子。而像ContextRefreshedEvent这样的事情,只管不是Spring5新引入的,但它为特定的生命周期回调提供了钩子。
Reactive事情模型:
与Spring5引入的WebFlux一同,还引入了对反响式编程模型的事情监听和颁布的支持。
总结:
在Spring5.x中,事情模型失掉了进一步的增强和优化,参与了对异步、泛型和反响式编程的支持,提供了更弱小、灵敏和高效的机制来处置运行程序事情。关于开发者来说,这为在解耦的同时成功复杂的业务逻辑提供了便利。
4.2颁布事情publishEvent源码剖析
上图,这里是Spring5.3.7的源码,上方讲独自抽进去剖析
publicvoidpublishEvent(ApplicationEventevent){this.publishEvent(event,(ResolvableType)null);}
剖析:
该方法接受一个ApplicationEvent对象并调用其重载版本publishEvent(Objectevent,@NullableResolvableTypeeventType),为其传递null作为事情类型。这是为了简化用户经常使用,用户可以间接传递一个ApplicationEvent对象而无需思考其详细的类型。
publicvoidpublishEvent(Objectevent){this.publishEvent(event,(ResolvableType)null);}
剖析:
与上一个方法相似,但它接受任何Object作为事情,并将其与null的eventType一同传递给外围方法。这参与了灵敏性,用户可以发送任何对象作为事情,而不只仅是ApplicationEvent对象。
protectedvoidpublishEvent(Objectevent,@NullableResolvableTypeeventType){//审核事情对象能否为空,确保颁布的事情是无心义的Assert.notNull(event,"Eventmustnotbenull");//判别传入的事情能否曾经是ApplicationEvent类型,假设是,则无需再启动包装ObjectapplicationEvent;if(eventinstanceofApplicationEvent){applicationEvent=(ApplicationEvent)event;}else{//假设传入的事情不是ApplicationEvent类型,则将其包装为PayloadApplicationEventapplicationEvent=newPayloadApplicationEvent(this,event);//假设未指定事情类型,那么从包装后的事情中失掉其实在类型if(eventType==null){eventType=((PayloadApplicationEvent)applicationEvent).getResolvableType();}}//判别以后能否存在earlyApplicationEvents列表if(this.earlyApplicationEvents!=null){//假设存在,说明ApplicationContext还未齐全初始化,将事情参与到此列表中,稍后再启动处置this.earlyApplicationEvents.add(applicationEvent);}else{//假设ApplicationContext曾经初始化,那么间接经过事情多播器广播事情this.getApplicationEventMulticaster().multicastEvent((ApplicationEvent)applicationEvent,eventType);}//假设存在父ApplicationContext,则也将事情颁布到父容器中if(this.parent!=null){if(this.parentinstanceofAbstractApplicationContext){//假设父容器是AbstractApplicationContext类型,则带上事情类型启动颁布((AbstractApplicationContext)this.parent).publishEvent(event,eventType);}else{//否则,只传递事情对象启动颁布this.parent.publishEvent(event);}}}
这个方法终究做了什么?
事情非空审核:为了确保事情对象不为空,启动了初步的断言审核。这是一个经常出现的做法,以防止有效的事情被广播。
事情类型审核与封装:Spring准许经常使用恣意类型的对象作为事情。假设传入的不是ApplicationEvent的实例,它会经常使用PayloadApplicationEvent来启动封装。这种设计提供了更大的灵敏性。
早期事情的处置:在Spring的生命周期中,ApplicationContext或者还没有齐全初始化,这时会有一些早期的事情。假设earlyApplicationEvents不为空,这些事情会被参与到此列表中,稍后再广播。
事情广播:假设ApplicationContext已初始化,事情会被广播给一切的监听器。这是经过ApplicationEventMulticaster成功的,它是Spring中担任事情广播的外围组件。
处置父ApplicationContext:在有些运行中,可以存在父子ApplicationContext。当子容器广播一个事情时,也可以思考在父容器中广播这个事情。这是为了确保在整个高低文档次结构中的一切感兴味的监听器都能收到事情。
经过这种形式,Spring的事情颁布机制确保了事情在不同的高低文和生命周期阶段都能被正确处置和广播。
上方说到事情广播是ApplicationEventMulticaster成功的,这个是什么?上方来看看
4.3Spring事情广播:从ApplicationEventMulticaster开局
当咱们在Spring中讨论事情,咱们实践上是在讨论两件事:事情(即出现的事情)和监听器(即对这些事情感兴味并作出反响的实体)。
ApplicationEventMulticaster的关键职责是治理事情监听器并广播事情给这些监听器。咱们关键关注SimpleApplicationEventMulticaster,由于这是自动的成功,但请留意,Spring准许交流为自定义的ApplicationEventMulticaster。
以下是SimpleApplicationEventMulticaster中的相关代码与剖析,有兴味的小同伴可以自行检查:
publicclassSimpleApplicationEventMulticasterextendsAbstractApplicationEventMulticaster{//可选的义务执行器,用于异步伐用事情监听器。@NullableprivateExecutortaskExecutor;//可选的失误处置器,用于处置在广播事情环节中出现的失误。@NullableprivateErrorHandlererrorHandler;//用于记载日志的logger,它是提早初始化的。@NullableprivatevolatileLoglazyLogger;//自动结构函数。publicSimpleApplicationEventMulticaster(){}//带有BeanFactory参数的结构函数,通罕用于更复杂的运行高低文性能中。publicSimpleApplicationEventMulticaster(BeanFactorybeanFactory){this.setBeanFactory(beanFactory);}//设置义务执行器。可以是任何JavaExecutor,比如Spring的SimpleAsyncTaskExecutor或Java的FixedThreadPool。publicvoidsetTaskExecutor(@NullableExecutortaskExecutor){this.taskExecutor=taskExecutor;}//失掉以后设置的义务执行器。@NullableprotectedExecutorgetTaskExecutor(){returnthis.taskExecutor;}//设置失误处置器。publicvoidsetErrorHandler(@NullableErrorHandlererrorHandler){this.errorHandler=errorHandler;}//失掉以后设置的失误处置器。@NullableprotectedErrorHandlergetErrorHandler(){returnthis.errorHandler;}//这是广播事情的关键方法。它首先解析事情的类型,而后调用具有额外参数的重载方法。publicvoidmulticastEvent(ApplicationEventevent){this.multicastEvent(event,this.resolveDefaultEventType(event));}//这个方法是真正执行广播操作的方法。publicvoidmulticastEvent(ApplicationEventevent,@NullableResolvableTypeeventType){//确定事情类型。ResolvableTypetype=(eventType!=null)?eventType:this.resolveDefaultEventType(event);//失掉义务执行器。Executorexecutor=this.getTaskExecutor();//失掉婚配此事情类型的一切监听器。Iterator<ApplicationListener<?>>listeners=this.getApplicationListeners(event,type).iterator();//遍历每个监听器并调用它。while(listeners.hasNext()){ApplicationListener<?>listener=listeners.next();//假设有设置义务执行器,则异步伐用监听器。if(executor!=null){executor.execute(()->this.invokeListener(listener,event));}else{//假设没有设置义务执行器,则同步伐用监听器。this.invokeListener(listener,event);}}}//为给定的事情解析自动的事情类型。privateResolvableTyperesolveDefaultEventType(ApplicationEventevent){returnResolvableType.forInstance(event);}//调用指定的监听器来处置给定的事情,并依据须要处置失误。protectedvoidinvokeListener(ApplicationListener<?>listener,ApplicationEventevent){//失掉以后的失误处置器。ErrorHandlererrorHandler=this.getErrorHandler();if(errorHandler!=null){try{//尝试调用监听器。this.doInvokeListener(listener,event);}catch(Throwableex){//假设出现失误,经常使用失误处置器处置。errorHandler.handleError(ex);}}else{//假设没有设置失误处置器,则间接调用监听器。this.doInvokeListener(listener,event);}}//间接调用监听器,捕捉任何类型不婚配的意外。privatevoiddoInvokeListener(ApplicationListenerlistener,ApplicationEventevent){try{listener.onApplicationEvent(event);}catch(ClassCastExceptionex){//捕捉类型转换意外,并依据须要启动处置。//这可以确保假设监听器不能处置特定类型的事情,不会造成整个广播操作失败。Stringmsg=ex.getMessage();if(msg!=null&&!this.matchesClassCastMessage(msg,event.getClass())&&(!(eventinstanceofPayloadApplicationEvent)||!this.matchesClassCastMessage(msg,((PayloadApplicationEvent)event).getPayload().getClass()))){throwex;}//在预期状况下捕捉并记载意外,而不是抛出它。LogloggerToUse=this.lazyLogger;if(loggerToUse==null){loggerToUse=LogFactory.getLog(this.getClass());this.lazyLogger=loggerToUse;}if(loggerToUse.isTraceEnabled()){loggerToUse.trace("Non-matchingeventtypeforlistener:"+listener,ex);}}}//依据给定的类型失误信息和事情类来审核ClassCastException能否是预期的。privatebooleanmatchesClassCastMessage(StringclassCastMessage,Class<?>eventClass){if(classCastMessage.startsWith(eventClass.getName())){returntrue;}elseif(classCastMessage.startsWith(eventClass.toString())){returntrue;}else{intmoduleSeparatorIndex=classCastMessage.indexOf(47);returnmoduleSeparatorIndex!=-1&&classCastMessage.startsWith(eventClass.getName(),moduleSeparatorIndex+1);}}}
关于外围方法multicastEvent须要作出特意说明:
//这个方法是真正执行广播操作的方法。publicvoidmulticastEvent(ApplicationEventevent,@NullableResolvableTypeeventType){//确定事情类型。ResolvableTypetype=(eventType!=null)?eventType:this.resolveDefaultEventType(event);......//失掉婚配此事情类型的一切监听器。Iterator<ApplicationListener<?>>listeners=this.getApplicationListeners(event,type).iterator();......}-----------------------------------©著述权归作者一切:来自51CTO博客作者华为云开发者联盟的原创作品,请咨询作者失掉转载授权,否则将清查法律责任把握Spring事情监听器的外部逻辑与成功
方法第一行失掉一个ResolvableType类型的对象,为什么Spring选用经常使用ResolvableType而不是间接经常使用Java类型?最关键的要素是Java的泛型擦除。在Java中,泛型只存在于编译时,一旦代码被编译,泛型信息就会被擦除,运转时就不能间接失掉到泛型的实践类型。
为了处置这个疑问,Spring引入了ResolvableType,一个能够解析泛型类型信息的工具类。
举个例子:
假定有如下的类定义:
publicclassSample{privateList<String>names;}
咱们可以这样失掉names字段的泛型类型:
ResolvableTypet=ResolvableType.forField(Sample.class.getDeclaredField("names"));Class<?>genericType=t.getGeneric(0).resolve();//失掉String.class
在Spring事情中的经常使用
ResolvableType在Spring事情中的运行关键是确定事情的类型和监听器监听的事情类型。当咱们颁布一个事情:
ApplicationEventevent=newMyCustomEvent(this,"data");applicationContext.publishEvent(event);
Spring外部会经常使用ResolvableType.forInstance(event)来失掉这个事情的类型。而后,它会找到一切注册的监听器,检查它们监听的事情类型能否与此事情婚配(经过比拟ResolvableType)。婚配的监听器会被调用。
关于一个监听器:
publicclassMyListenerimplementsApplicationListener<MyCustomEvent>{@OverridepublicvoidonApplicationEvent(MyCustomEventevent){//handletheevent}}
Spring外部会经常使用ResolvableType来解析这个监听器监听的事情类型(在这个例子中是MyCustomEvent)。
总之,ResolvableType在Spring中的关键用途是提供了一种形式来解析和操作运转时的泛型类型信息,特意是在事情颁布和监听中。
4.4Spring事情颁布与处置流程图
假设看不清,倡导在新标签页中关上图片后加大看
4.5监听器外部逻辑
再来看看监听器外部逻辑,咱们来剖析在multicastEvent方法中调用的getApplicationListeners(event,type)来剖析下
protectedCollection<ApplicationListener<?>>getApplicationListeners(ApplicationEventevent,ResolvableTypeeventType){//失掉事情起源对象Objectsource=event.getSource();//判别事情起源对象能否为null,是则前往null,否则前往事情起源对象的类Class<?>sourceType=source!=null?source.getClass():null;//经常使用事情类型和源类型作为缓存键AbstractApplicationEventMulticaster.ListenerCacheKeycacheKey=newAbstractApplicationEventMulticaster.ListenerCacheKey(eventType,sourceType);//初始化一个新的监听器检索器为nullAbstractApplicationEventMulticaster.CachedListenerRetrievernewRetriever=null;//尝试从缓存中经常使用键取得一个已存在的检索器AbstractApplicationEventMulticaster.CachedListenerRetrieverexistingRetriever=(AbstractApplicationEventMulticaster.CachedListenerRetriever)this.retrieverCache.get(cacheKey);//假设没有从缓存中失掉到检索器,并且满足缓存安保性条件if(existingRetriever==null&&(this.beanClassLoader==null||ClassUtils.isCacheSafe(event.getClass(),this.beanClassLoader)&&(sourceType==null||ClassUtils.isCacheSafe(sourceType,this.beanClassLoader)))){//创立一个新的检索器newRetriever=newAbstractApplicationEventMulticaster.CachedListenerRetriever();//尝试将新检索器参与到缓存中existingRetriever=(AbstractApplicationEventMulticaster.CachedListenerRetriever)this.retrieverCache.putIfAbsent(cacheKey,newRetriever);//假设缓存中曾经有了一个值(由于并发的要素),则将新检索器设回nullif(existingRetriever!=null){newRetriever=null;}}//假设有现有的检索器if(existingRetriever!=null){//尝试从检索器中失掉监听器汇合Collection<ApplicationListener<?>>result=existingRetriever.getApplicationListeners();//假设结果不为null,则间接前往if(result!=null){returnresult;}}//假设上述步骤都没有前往,调用retrieveApplicationListeners启动实践的监听器检索returnthis.retrieveApplicationListeners(eventType,sourceType,newRetriever);}privateCollection<ApplicationListener<?>>retrieveApplicationListeners(ResolvableTypeeventType,@NullableClass<?>sourceType,@NullableAbstractApplicationEventMulticaster.CachedListenerRetrieverretriever){//初始化一个空的监听器列表List<ApplicationListener<?>>allListeners=newArrayList();//若retriever非null,则初始化汇合来保留过滤进去的监听器和Bean名Set<ApplicationListener<?>>filteredListeners=retriever!=null?newLinkedHashSet():null;Set<String>filteredListenerBeans=retriever!=null?newLinkedHashSet():null;LinkedHashSetlisteners;LinkedHashSetlistenerBeans;//同步从defaultRetriever中失掉已注册的监听器和其Bean称号synchronized(this.defaultRetriever){listeners=newLinkedHashSet(this.defaultRetriever.applicationListeners);listenerBeans=newLinkedHashSet(this.defaultRetriever.applicationListenerBeans);}//遍历一切的监听器for(ApplicationListener<?>listener:listeners){//审核该监听器能否支持此事情类型和源类型if(this.supportsEvent(listener,eventType,sourceType)){if(retriever!=null){//假设支持并且retriever非null,参与到过滤监听器汇合filteredListeners.add(listener);}//将支持的监听器参与到allListeners列表allListeners.add(listener);}}//假设存在监听器Bean称号if(!listenerBeans.isEmpty()){ConfigurableBeanFactorybeanFactory=this.getBeanFactory();for(StringlistenerBeanName:listenerBeans){try{//审核Bean工厂中的Bean能否支持该事情if(this.supportsEvent(beanFactory,listenerBeanName,eventType)){ApplicationListener<?>listener=(ApplicationListener)beanFactory.getBean(listenerBeanName,ApplicationListener.class);//再次审核确保Bean实例支持事情,并且它还没有被参与allListeners列表if(!allListeners.contains(listener)&&this.supportsEvent(listener,eventType,sourceType)){if(retriever!=null){//若该Bean是单例并且retriever非null,参与到过滤监听器汇合if(beanFactory.isSingleton(listenerBeanName)){filteredListeners.add(listener);}else{filteredListenerBeans.add(listenerBeanName);}}//参与到allListeners列表allListeners.add(listener);}}else{//若不支持该事情,从allListeners中移除该BeanObjectlistener=beanFactory.getSingleton(listenerBeanName);if(retriever!=null){filteredListeners.remove(listener);}allListeners.remove(listener);}}catch(NoSuchBeanDefinitionExceptione){//若Bean不存在,间接继续下一个}}}//对allListeners列表启动排序,确保监听器的执行顺序AnnotationAwareOrderComparator.sort(allListeners);//假设retriever非null,降级其外部汇合以后续经常使用if(retriever!=null){if(filteredListenerBeans.isEmpty()){retriever.applicationListeners=newLinkedHashSet(allListeners);retriever.applicationListenerBeans=filteredListenerBeans;}else{retriever.applicationListeners=filteredListeners;retriever.applicationListenerBeans=filteredListenerBeans;}}//前往allListeners作为结果returnallListeners;}-----------------------------------©著述权归作者一切:来自51CTO博客作者华为云开发者联盟的原创作品,请咨询作者失掉转载授权,否则将清查法律责任把握Spring事情监听器的外部逻辑与成功
监听器外部做了什么?
在getApplicationListeners方法中,驳回了一种优化检索的缓存机制来提高性能并确保线程安保性。
详细剖析如下:
初次审核:
AbstractApplicationEventMulticaster.CachedListenerRetrieverexistingRetriever=(AbstractApplicationEventMulticaster.CachedListenerRetriever)this.retrieverCache.get(cacheKey);
这里,咱们首先从retrieverCache中检索existingRetriever。
判别能否须要进入同步代码块:
if(existingRetriever==null&&(this.beanClassLoader==null||...)){...}
假设existingRetriever为空,那么咱们或者须要创立一个新的CachedListenerRetriever并放入缓存中。然而,为了确保线程安保性,咱们必定在这之行启动进一步的审核。
双重审核:
在创立新的CachedListenerRetriever之前,咱们经常使用了putIfAbsent方法。这个方法会尝试参与一个新值,但假设该值已存在,它只会前往现有的值。该机制驳回了一种缓存优化战略:经过ConcurrentMap的putIfAbsent方法,即使多个线程同时抵达这个代码段,也确保只要一个线程能够成功地放入新的值,从而保证线程安保性。
newRetriever=newAbstractApplicationEventMulticaster.CachedListenerRetriever();existingRetriever=(AbstractApplicationEventMulticaster.CachedListenerRetriever)this.retrieverCache.putIfAbsent(cacheKey,newRetriever);
这里的逻辑经常使用了ConcurrentMap的putIfAbsent方法来确保线程安保性,而没有经常使用传统的synchronized块。
所以,咱们可以说getApplicationListeners中的这局部逻辑驳回了一种优化检索的缓存机制。它应用了并发容器的原子性操作putIfAbsent来保证线程安保,而不是依赖于传统的双重审核锁定形式。
总体概括一下,关于getApplicationListeners和retrieveApplicationListeners两个方法的性能可以总结为以下三个步骤:
从自动检索器挑选监听器:
这局部代码间接从defaultRetriever中失掉监听器,并审核它们能否支持以后事情。在retrieveApplicationListeners方法中,代码首先从defaultRetriever中失掉曾经编程式注入的监听器,并审核每个监听器能否支持以后的事情类型。
listeners=newLinkedHashSet(this.defaultRetriever.applicationListeners);for(ApplicationListener<?>listener:listeners){if(this.supportsEvent(listener,eventType,sourceType)){...//参与到挑选进去的监听器列表}}
从IOC容器中挑选监听器:
在retrieveApplicationListeners方法中,除了从defaultRetriever中失掉曾经编程式注入的监听器,代码还会尝试从IOC容器(经过bean称号)失掉监听器,并审核它们能否支持以后的事情。
if(!listenerBeans.isEmpty()){ConfigurableBeanFactorybeanFactory=this.getBeanFactory();for(StringlistenerBeanName:listenerBeans){...//审核并参与到挑选进去的监听器列表}}
监听器排序:
最后,为确保监听器依照预约的顺序照应事情,挑选出的一切监听器会经过排序。排序基于Spring的@Order注解或Ordered接口,如AnnotationAwareOrderComparator.sort(allListeners)所示
AnnotationAwareOrderComparator.sort(allListeners);
4.6Spring事情监听器检索流程图
5.Spring事情流传、异步处置等机制的详细图示
说明:
容器和事情广播:
ApplicationContext是Spring的运行高低文容器。在图中,咱们有一个主容器和一个子容器。
当咱们想颁布一个事情时,咱们调用publishEvent方法。
ApplicationEventMulticaster担任实践地将事情广播到各个监听器。
主容器和子容器相关:
在Spring中,可以有多个容器,其中一个是主容器,其余的则是子容器。
通常,子容器可以访问主容器中的bean,但反之则不行。但在事情流传的高低文中,子容器颁布的事情自动不会在主容器中流传。这一点由Note1注释标明。
异步处置:
当事情被颁布时,它可以被异步地流传到监听器,这取决于能否性能了异步执行器。
能否经常使用异步执行器?这个决策点说明了基于性能,事情可以同步或异步地流传到监听器。
事情生命周期:
在Spring容器的生命周期中,有些事情在容器初始化前触发,这些被称为earlyevents。这些事情会被缓存起来,直到容器初始化成功。
一旦容器初始化成功,这些早期的事情会被处置,并开局处置惯例事情。
在容器销毁时,也或者触发事情。
留意事项(Note1):
这个局部强调了一个特定的行为,即在某些性能下,子容器颁布的事情或者也会在主容器中流传,但这并不是自动行为。
聊聊Spring Boot面试相关问题
Spring Boot 是 Spring 开源组织下的子项目,是 Spring 组件一站式解决方案,主要是简化了使用 Spring 的难度,简省了繁重的配置,提供了各种启动器,开发者能快速上手。
Spring Boot 主要有如下优点: 启动类上面的注解是@SpringBootApplication,它也是 Spring Boot 的核心注解,主要组合包含了以下 3 个注解: @SpringBootConfiguration:组合了 @Configuration 注解,实现配置文件的功能。
@EnableAutoConfiguration:打开自动配置的功能,也可以关闭某个自动配置的选项,如关闭数据源自动配置功能:@SpringBootApplication(exclude= { })。
@ComponentScan:Spring组件扫描。
Spring JavaConfig 是 Spring 社区的产品,它提供了配置 Spring IoC 容器的纯Java 方法。
因此它有助于避免使用 XML 配置。
使用 JavaConfig 的优点在于: (1) 面向对象的配置。
由于配置被定义为 JavaConfig 中的类,因此用户可以 充分利用 Java 中的面向对象的功能。
一个配置类可以继承另一个,重写它的 @Bean 方法等。
(2) 减少或消除 XML 配置。
基于依赖注入原则的外化配置的好处已被证明。
但是,许多开发人员不希望在 XML 和 Java 之间来回切换。
JavaConfig 为开发人员提供了一种纯粹 Java 方法来配置与 XML 配置概念相似的 Spring 容器。
从 技术角度来讲,只是使用 JavaConfig 配置类来配置容器是可行的,但实际上很多人认为将JavaConfig 与 XML 混合匹配是理想的。
(3)类型安全和重构友好。
JavaConfig 提供了一种类型安全的方法来配置 Spring容器。
由于 Java 5.0 对泛型的支持,现在可以按类型而不是按名称检索 bean,不需要任何强制转换或基于字符串的查找。
BFPP:BeanFactoryPostProcessor BPP:BeanPostProcessor BDRPP:BeanDefinitionRegistryPostProcessor 表达的总体思路是:总-分-总 1、springboot自动装配是什么,解决了什么问题 2、自动装配实现的原理: 1、当启动springboot应用程序的时候, 会先创建SpringApplication的对象,在对象的构造方法中会进行某些参数的初始化工作,最主要的是判断当前应用程序的类型以及初始化器和监听器,在这个过程中会加载整个应用程序中的文件,将文件的内容放到缓存对象中,方便后续获取。
2、SpringApplication对象创建完成之后,开始执行run方法,来完成整个启动,启动过程中最主要的有两个方法,第一个叫做prepareContext,第二个叫做refreshContext,在这两个关键步骤中完整了自动装配的核心功能,前面的处理逻辑包含了上下文对象的创建,banner的打印,异常报告期的准备等各个准备工作,方便后续来进行调用。
3、在prepareContext方法中主要完成的是对上下文对象的初始化操作,包括了属性值的设置,比如环境对象,在整个过程中有一个非常重要的方法,叫做load,load主要完成一件事,将当前启动类做为一个beanDefinition注册到registry中,方便后续在进行BeanFactoryPostProcessor调用执行的时候,找到对应的主类,来完成@SpringBootApplicaiton,@EnableAutoConfiguration等注解的解析工作 4、在refreshContext方法中会进行整个容器刷新过程,会调用中spring中的refresh方法,refresh中有13个非常关键的方法,来完成整个spring应用程序的启动,在自动装配过程中,会调用invokeBeanFactoryPostProcessor方法,在此方法中主要是对ConfigurationClassPostProcessor类的处理,这次是BFPP的子类也是BDRPP的子类,在调用的时候会先调用BDRPP中的postProcessBeanDefinitionRegistry方法,然后调用postProcessBeanFactory方法,在执行postProcessBeanDefinitionRegistry的时候回解析处理各种注解,包含@PropertySource,@ComponentScan,@ComponentScans,@Bean,@Import等注解,最主要的是@Import注解的解析。
5、在解析@Import注解的时候,会有一个getImports的方法,从主类开始递归解析注解,把所有包含@Import的注解都解析到,然后在processImport方法中对Import的类进行分类,此处主要识别的时候AutoConfigurationImportSelect归属于ImportSelect的子类,在后续过程中会调用deferredImportSelectorHandler中的process方法,来完整EnableAutoConfiguration的加载。
6、上面是我对springboot自动装配的简单理解,面试官您看一下,我回答有没有问题,帮我指点一下! 在 Spring Boot 里面,可以使用以下几种方式来加载配置。
1) properties文件; 2) YAML文件; 3) 系统环境变量; 等等…… YAML 是一种人类可读的数据序列化语言。
它通常用于配置文件。
与属性文件相比,如果我们想要在配置文件中添加复杂的属性,YAML 文件就更加结构化,而且更少混淆。
可以看出 YAML 具有分层配置数据。
YAML 现在可以算是非常流行的一种配置文件格式了,无论是前端还是后端,都可以见到 YAML 配置。
那么 YAML 配置和传统的 properties 配置相比到底有哪些优势呢? 相比 properties 配置文件,YAML 还有一个缺点,就是不支持 @PropertySource 注解导入自定义的 YAML 配置。
Spring Boot 推荐使用 Java 配置而非 XML 配置,但是 Spring Boot 中也可以使用 XML 配置,通过 @ImportResource 注解可以引入一个 XML 配置。
spring boot 核心配置文件是什么? 和 有何区别 ? 单纯做 Spring Boot 开发,可能不太容易遇到 配置文 件,但是在结合 Spring Cloud 时,这个配置就会经常遇到了,特别是在需要加载一些远程配置文件的时侯。
spring boot 核心的两个配置文件: bootstrap (. yml 或者 . properties):boostrap 由父 ApplicationContext 加载的,比 applicaton 优先加载,配置在应用程序上下文的引导阶段生效。
一般来说我们在 Spring Cloud Config 或者 Nacos 中会用到它。
且 boostrap 里面的属性不 能被覆盖; application (. yml 或者 . properties): 由ApplicatonContext 加载,用于 spring boot 项目的自动化配置。
Spring Profiles 允许用户根据配置文件(dev,test,prod 等)来注册 bean。
因此,当应用程序在开发中运行时,只有某些 bean 可以加载,而在 PRODUCTION中,某些其他 bean 可以加载。
假设我们的要求是 Swagger 文档仅适用于 QA 环境,并且禁用所有其他文档。
这可以使用配置文件来完成。
Spring Boot 使得使用配置文件非常简单。
为了在自定义端口上运行 Spring Boot 应用程序,您可以在 中指定端口。
= 8090 为了实现 Spring Boot 的安全性,我们使用 spring-boot-starter-security 依赖项,并且必须添加安全配置。
它只需要很少的代码。
配置类将必须扩展 WebSecurityConfigurerAdapter 并覆盖其方法。
跨域可以在前端通过 JSONP 来解决,但是 JSONP 只可以发送 GET 请求,无法发送其他类型的请求,在 RESTful 风格的应用中,就显得非常鸡肋,因此我们推荐在后端通过 (CORS,Cross-origin resource sharing) 来解决跨域问题。
这种解决方案并非 Spring Boot 特有的,在传统的 SSM 框架中,就可以通过 CORS 来解决跨域问题,只不过之前我们是在 XML 文件中配置 CORS ,现在可以通过实现WebMvcConfigurer接口然后重写addCorsMappings方法解决跨域问题。
项目中前后端分离部署,所以需要解决跨域的问题。
我们使用cookie存放用户登录的信息,在spring拦截器进行权限控制,当权限不符合时,直接返回给用户固定的json结果。
当用户登录以后,正常使用;当用户退出登录状态时或者token过期时,由于拦截器和跨域的顺序有问题,出现了跨域的现象。
我们知道一个http请求,先走filter,到达servlet后才进行拦截器的处理,如果我们把cors放在filter里,就可以优先于权限拦截器执行。
CSRF 代表跨站请求伪造。
这是一种攻击,迫使 终用户在当前通过身份验证的 Web 应用程序上执行不需要的操作。
CSRF 攻击专门针对状态改变请求,而不是数据窃取,因为攻击者无法查看对伪造请求的响应。
Spring boot actuator 是 spring 启动框架中的重要功能之一。
Spring boot 监视器可帮助您访问生产环境中正在运行的应用程序的当前状态。
有几个指标必须在生产环境中进行检查和监控。
即使一些外部应用程序可能正在使用这些服务来向相关人员触发警报消息。
监视器模块公开了一组可直接作为 HTTP URL 访问 的REST 端点来检查状态。
如何在 Spring Boot 中禁用 Actuator 端点安全性? 默认情况下,所有敏感的 HTTP 端点都是安全的,只有具有 ACTUATOR 角色 的用户才能访问它们。
安全性是使用标准的 方法实施的。
我们可以使用来禁用安全性。
只有在执行机构端点在防火墙后访问时,才建议禁用安全性。
Spring Boot 提供监视器端点以监控各个微服务的度量。
这些端点对于获取有关应用程序的信息(如它们是否已启动)以及它们的组件(如数据库等)是否正常运行很有帮助。
但是,使用监视器的一个主要缺点或困难是,我们必须单独打开应用程序的知识点以了解其状态或 健康 状况。
想象一下涉及 50 个应用程序的微服务,管理员将不得不击中所有 50 个应用程序的执行终端。
为了帮助我们处理这种情况,我们将使用位于的开源项目。
它建立在 Spring Boot Actuator 之上,它提供了一个 Web UI,使我们能够可视化多个应用程序的度量。
WebSocket 是一种计算机通信协议,通过单个 TCP 连接提供全双工通信信道。
1、 WebSocket 是双向的 -使用 WebSocket 客户端或服务器可以发起消息发送。
2、 WebSocket 是全双工的 -客户端和服务器通信是相互独立的。
3、 单个 TCP 连接 -初始连接使用 HTTP,然后将此连接升级到基于套接字的连接。
然后这个单一连接用于所有未来的通信 4、 Light -与 http 相比,WebSocket 消息数据交换要轻得多。
什么是 Spring Data ? Spring Data 是 Spring 的一个子项目。
用于简化数据库访问,支持NoSQL 和 关系数据存储。
其主要目标是使数据库的访问变得方便快捷。
Spring Data 具有如下特点: SpringData 项目支持 NoSQL 存储: SpringData 项目所支持的关系数据存储技术: Spring Boot Batch 提供可重用的函数,这些函数在处理大量记录时非常重要,包括日志/跟踪,事务管理,作业处理统计信息,作业重新启动,跳过和资源管理。
它还提供了更先进的技术服务和功能,通过优化和分区技术,可以实现极高批量和高性能批处理作业。
简单以及复杂的大批量批处理作业可以高度可扩展的方式利用框架处理重要大量的信息。
FreeMarker 是一个基于 Java 的模板引擎, 初专注于使用 MVC 软件架构进行动态网页生成。
使用 Freemarker 的主要优点是表示层和业务层的完全分离。
程序员可以处理应用程序代码,而设计人员可以处理 html 页面设计。
后使用 freemarker 可以将这些结合起来,给出 终的输出页面。
对于集成 Spring Boot 和 ActiveMQ,我们使用依赖关系。
它只需要很少的配置,并且不需要样板代码。
Swagger 广泛用于可视化 API,使用 Swagger UI 为前端开发人员提供在线沙箱。
Swagger 是用于生成 RESTful Web 服务的可视化表示的工具,规范和完整框架实现。
它使文档能够以与服务器相同的速度更新。
当通过 Swagger 正确定义时,消费者可以使用 少量的实现逻辑来理解远程服务并与其进行交互。
因此,Swagger消除了调用服务时的猜测。
前后端分离,如何维护接口文档 ? 前后端分离开发日益流行,大部分情况下,我们都是通过 Spring Boot 做前后端分离开发,前后端分离一定会有接口文档,不然会前后端会深深陷入到扯皮中。
一个比较笨的方法就是使用 word 或者 md 来维护接口文档,但是效率太低,接口一变,所有人手上的文档都得变。
在 Spring Boot 中,这个问题常见 的解决方案是 Swagger ,使用 Swagger 我们可以快速生成一个接口文档网 站,接口一旦发生变化,文档就会自动更新,所有开发工程师访问这一个在线网站就可以获取到 新的接口文档,非常方便。
这可以使用 DEV 工具来实现。
通过这种依赖关系,您可以节省任何更改,嵌入式tomcat 将重新启动。
Spring Boot 有一个开发工具(DevTools)模块,它有助于提高开发人员的生产力。
Java 开发人员面临的一个主要挑战是将文件更改自动部署到服务器并自动重启服务器。
开发人员可以重新加载 Spring Boot 上的更改,而无需重新启动服务器。
这将消除每次手动部署更改的需要。
Spring Boot 在发布它的第一个版本时没有这个功能。
这是开发人员 需要的功能。
DevTools 模块完全满足开发人员的需求。
该模块将在生产环境中被禁用。
它还提供 H2 数据库控制台以更好地测试应用程序。
使用了下面的一些依赖项 spring-boot-starter-activemq spring-boot-starter-security 这有助于增加更少的依赖关系,并减少版本的冲突。
Spring Boot 中的 starter 到底是什么 ? 首先,这个 Starter 并非什么新的技术点,基本上还是基于 Spring 已有功能来实现的。
首先它提供了一个自动化配置类,一般命名为 XXXAutoConfiguration ,在这个配置类中通过条件注解来决定一个配置是否生效(条件注解就是 Spring 中原本就有的),然后它还会提供一系列的默认配置,也允许开发者根据实际情况自定义相关配置,然后通过类型安全的属性注入将这些配置属性注入进来,新注入的属性会代替掉默认属性。
正因为如此,很多第三方框架,我们只需要引入依赖就可以直接使用了。
当然,开发者也可以自定义 Starter spring-boot-starter-parent 有什么用 ? 我们都知道,新创建一个 Spring Boot 项目,默认都是有 parent 的,这个 parent 就是 spring-boot-starter-parent ,spring-boot-starter-parent 主要有如下作用: Spring Boot 打成的 jar 和普通的 jar 有什么区别 ? Spring Boot 项目 终打包成的 jar 是可执行 jar ,这种 jar 可以直接通过 java jar 命令来运行,这种 jar 不可以作为普通的 jar 被其他项目依赖,即使依赖了也无法使用其中的类。
Spring Boot 的 jar 无法被其他项目依赖,主要还是他和普通 jar 的结构不同。
普通的 jar 包,解压后直接就是包名,包里就是我们的代码,而 Spring Boot 打包成的可执行 jar 解压后,在 BOOT-INFclasses 目录下才是我们的代码,因此无法被直接引用。
如果非要引用,可以在 文件中增加配置,将 Spring Boot 项目打包成两个 jar ,一个可执行,一个可引用。
1) 打包用命令或者放到容器中运行 2) 用 Maven/ Gradle 插件运行 3)直接执行 main 方法运行 1) 继承spring-boot-starter-parent项目 2) 导入spring-boot-dependencies项目依赖 Spring 提供了一种使用 ControllerAdvice 处理异常的非常有用的方法。
我们通过实现一个 ControlerAdvice 类,来处理控制器类抛出的所有异常。
使用 Spring Boot 实现分页非常简单。
使用 Spring Data-JPA 可以实现将可分页的传递给存储库方法。
微服务中如何实现 session 共享 ? 在微服务中,一个完整的项目被拆分成多个不相同的独立的服务,各个服务独立部署在不同的服务器上,各自的 session 被从物理空间上隔离开了,但是经 常,我们需要在不同微服务之间共享 session ,常见的方案就是 Spring Session + Redis 来实现 session 共享。
将所有微服务的 session 统一保存在 Redis 上,当各个微服务对 session 有相关的读写操作时,都去操作 Redis 上的 session 。
这样就实现了 session 共享,Spring Session 基于 Spring 中的代理过滤器实现,使得 session 的同步操作对开发人员而言是透明的,非常简便。
定时任务也是一个常见的需求,Spring Boot 中对于定时任务的支持主要还是来自 Spring 框架。
在 Spring Boot 中使用定时任务主要有两种不同的方式,一个就是使用 Spring 中的 @Scheduled 注解,另一个则是使用第三方框架 Quartz。
使用 Spring 中的 @Scheduled 的方式主要通过 @Scheduled 注解来实现。
使用 Quartz ,则按照 Quartz 的方式,定义 Job 和 Trigger 即可。
有什么好书讲解spring框架的原理和用法的麽
有什么好书讲解spring框架的原理和用法的麽
spring框架与无spring框架有什么区别?
spring的两大核心功能就是依赖注入DI和AOP,依赖注入实现的功能是不需要自己去new对象而是通过set方法注入,例如把DAO等注入到一个业务逻辑的类中来实现数据库操作,从而使类与类之间的联系更小,耦合度就小。AOP一般用于事务管理。spring功能很强大,如果不用的话可能实现以上功能很复杂的。
spring框架有什么用?
spring框架的作用就是让对象与对象(模块与模块)之间的关系没有通过代码来关联,都是通过配置类说明 管理的(Spring根据这些配置 内部通过反射去动态的组装对象) Spring是一个容器,凡是在容器里的对象才会有Spring所提供的这些服务和功能。
内部最核心的就是IOC了, 动态注入,让一个对象的创建不用new了,可以自动的生产,这其实就是利用java里的反射 反射其实就是在运行时动态的去创建、调用对象,Spring就是在运行时,跟xml Spring的配置 文件来动态的创建对象,和调用对象里的方法的 。 Spring还有一个核心就是AOP这个就是面向切面编程,可以为某一类对象 进行监督和控制(也就是 在调用这类对象的具体方法的前后去调用你指定的 模块)从而达到对一个模块扩充的功能。
Spring框架是由于软件开发的复杂性而创建的。Spring使用的是基本的JavaBean来完成以前只可能由EJB完成的事情。然而,Spring的用途不仅仅限于服务器端的开发。从简单性、可测试性和松耦合性的角度而言,绝大部分Java应用都可以从Spring中受益。
目的:解决企业应用开发的复杂性
功能:使用基本的JavaBean代替EJB,并提供了更多的企业应用功能
范围:任何Java应用Spring是一个轻量级控制反转(IoC)和面向切面(AOP)的容器框架。
java spring框架有什么用
Spring的好处至少包括: 1, Spring能有效地组织你的中间层对象; 2, Spring能消除在许多工程中常见的对Singleton的过多使用; 3, 通过一种在不同应用程序和项目间一致的方法来处理配置文件,消除各种自定义格式的属性文件的需要
Spring框架IOC和AOP要怎么理解?, 简述你对Spring框架IOC和AOP的理解。
IoC就是对象的创建,依赖都由Spring及配置文件控制;AOP就是统一的给一些类似的方法加上同样的功能,比如日志,事务。
spring框架和hibernate框架有什么区别?那个更好一点?
hibernate帮你搞定对象和数据库的映射。spring则帮你完成应用程序各部分的组装。初学的话你可以把hibernate理解为代替你对数据库进行select insert update delete底层操作的框架。从而只需要对hibernate进行简单的配置就可以完成数据库之间的切换(从mysql切换到mssql,如果使用hibernate,则你不需要修改任何应用程序就,只需要修改hibernate的配置文件即可)。spring你可以认为是一条把整个应用串联起来的线,比如你有个类,里面有个成员是一个“回叫的接口”,如果把这个程序给一只鸟用的话,需要实例化一只会叫的鸟,如果给一只狗则需要实例化一只会叫的狗。没有spring的情况下你需要修改源代码,把原本实例化鸟的地方实例化狗。当是用了spring就可以通过修改配置把一只鸟或者一直狗分配给这个成员变量。正是由于这种特性,才使得spring成为轻量级架构中的核心框架,是实现应用分层机构的基础。
Java中的spring框架给详细讲讲
Spring是一个开源框架,Spring是于2003 年兴起的一个轻量级的Java 开发框架,由Rod Johnson 在其著作Expert One-On-One J2EE Development and Design中阐述的部分理念和原型衍生而来。它是为了解决企业应用开发的复杂性而创建的。框架的主要优势之一就是其分层架构,分层架构允许使用者选择使用哪一个组件,同时为 J2EE 应用程序开发提供集成的框架。Spring使用基本的JavaBean来完成以前只可能由EJB完成的事情。然而,Spring的用途不仅限于服务器端的开发。从简单性、可测试性和松耦合的角度而言,任何Java应用都可以从Spring中受益。Spring的核心是控制反转(IoC)和面向切面(AOP)。简单来说,Spring是一个分层的JavaSE/EEfull-stack(一站式) 轻量级开源框架。
免责声明:本文转载或采集自网络,版权归原作者所有。本网站刊发此文旨在传递更多信息,并不代表本网赞同其观点和对其真实性负责。如涉及版权、内容等问题,请联系本网,我们将在第一时间删除。同时,本网站不对所刊发内容的准确性、真实性、完整性、及时性、原创性等进行保证,请读者仅作参考,并请自行核实相关内容。对于因使用或依赖本文内容所产生的任何直接或间接损失,本网站不承担任何责任。