Upload
antoine-sabot-durand
View
689
Download
1
Embed Size (px)
Citation preview
GoingfurtherwithCDI1.2
AntoineSabot-Durand·AntoninStefanutti
AntoninStefanutti
[email protected]/astefanutti
AntoineSabot-Durand
RedHatCDIspeclead@antoine_sdnext-presso.comgithub.com/antoinesd
ShouldIstayorshouldIgo?
AtalkaboutadvancedCDI
Mightbehardforbeginners
Don’tneedtobeaCDIguru
@Inject @ProducesEvent<T> @Observes@Qualifier InjectionPoint
ShouldIstayorshouldIgo?
Ifyouknowmostoftheseyoucanstay
Moreconcretely
What’sincluded:
1. Realusecasesfromreallifewithrealusers2. Newapproachtointroduceportableextensionconcepts3. CodeinIDEwithtests
What’snotincluded:
1. IntroductiontoCDI2. Oldcontentonextension3. WorkwithContext(need2morehours)
Toolsusedinthecode1/2
ApacheDeltaspike
1. ApacheDeltaSpikeisagreatCDItoolbox
2. Providehelperstodevelopextension3. Andacollectionofmoduleslike:
1. Security2. Data3. Scheduler
4. Moreinfoondeltaspike.apache.org
Toolsusedinthecode2/2
Arquillian
1. Arquillianisanintegrationtestplatform2. ItintegrateswithJUnit3. Createyourdeploymentinadedicated
method4. Andlaunchyourtestsagainstthecontainer
ofyourchoice5. We’llusethe weld-se-embedded and
weld-ee-embedded container6. TherightsolutiontotestJavaEEcode7. Moreinfoonarquillian.org
Agenda
Slidesavailableatastefanutti.github.io/further-cdi
MeetCDISPIIntroducingCDIExtensionsMetricsCDICamelCDI
MeetCDISPI
SPIcanbesplitin4parts
SPIcanbesplitin4parts
Typemeta-model
SPIcanbesplitin4parts
CDImeta-model
SPIcanbesplitin4parts
CDIentrypoints
SPIcanbesplitin4parts
SPIdedicatedtoextensions
Whyhavingatypemeta-model?
Because @Annotations areconfiguration
buttheyarealsoread-only
Sotoconfigureweneedamutablemeta-model…
… forannotatedtypes
SPIfortypemeta-model
Annotated
TypegetBaseType()Set<Type>getTypeClosure()<TextendsAnnotation>getAnnotation(Class<T>)Set<Annotation>getAnnotations()booleanisAnnotationPresent(Class<?extendsAnnotation>)
AnnotatedMemberX
MembergetJavaMember()booleanisStatic()AnnotatedType<X>getDeclaringType()
AnnotatedParameterX
intgetPosition()AnnotatedCallable<X>getDeclaringCallable()
AnnotatedTypeX
Class<X>getJavaClass()Set<AnnotatedConstructor<X>>getConstructors()Set<AnnotatedMethod<?superX>>getMethods()Set<AnnotatedField<?superX>>getFields()
AnnotatedCallableX
List<AnnotatedParameter<X>>getParameters()
AnnotatedFieldX
FieldgetJavaMember()
AnnotatedConstructorX
Constructor<X>getJavaMember()
AnnotatedMethodX
MethodgetJavaMember()
SPIdedicatedtoCDImeta-modelBeanAttributes
T
Set<Type>getTypes()Set<Annotation>getQualifiers()Class<?extendsAnnotation>getScope()StringgetName()Set<Class<?extendsAnnotation>>getStereotypes()booleanisAlternative()
BeanT
Class<?>getBeanClass()Set<InjectionPoint>getInjectionPoints()booleanisNullable()
InterceptorT
Set<Annotation>getInterceptorBindings()booleanintercepts(InterceptionTypetype)Objectintercept(InterceptionType,T,InvocationContext)
DecoratorT
TypegetDelegateType()Set<Annotation>getDelegateQualifiers()Set<Type>getDecoratedTypes()
ProducerT
Tproduce(CreationalContext<T>)voiddispose(T)Set<InjectionPoint>getInjectionPoints()
InjectionTargetT
voidinject(T,CreationalContext<T>)voidpostConstruct(T)voidpreDestroy(T)
InjectionPoint
TypegetType()Set<Annotation>getQualifiers()Bean<?>getBean()MembergetMember()AnnotatedgetAnnotated()booleanisDelegate()booleanisTransient()
ObserverMethodT
Class<?>getBeanClass()TypegetObservedType()Set<Annotation>getObservedQualifiers()ReceptiongetReception()TransactionPhasegetTransactionPhase()voidnotify(T)
EventMetadata
Set<Annotation>getQualifiers()InjectionPointgetInjectionPoint()TypegetType()
ThisSPIcanbeusedinyourcode(1/2)
InjectionPoint canbeusedtogetinfoaboutwhat’sbeinginjected
@Qualifier@Retention(RetentionPolicy.RUNTIME)public@interfaceHttpParam{@NonbindingpublicStringvalue();}
@Produces@HttpParam("")StringgetParamValue(InjectionPointip,HttpServletRequestreq){returnreq.getParameter(ip.getAnnotated().getAnnotation(HttpParam.class).value());}
@Inject@HttpParam("productId")StringproductId;
ThisSPIcanbeusedinyourcode(2/2)
InjectionPoint containsinfoaboutrequestedtypeat @Inject
classMyMapProducer(){
@Produces<K,V>Map<K,V>produceMap(InjectionPointip){if(valueIsNumber(((ParameterizedType)ip.getType())))returnnewTreeMap<K,V>();returnnewHashMap<K,V>();}
booleanvalueIsNumber(ParameterizedTypetype){Class<?>valueClass=(Class<?>)type.getActualTypeArguments()[1];returnNumber.class.isAssignableFrom(valueClass)}}
SPIprovidingCDIentrypoints
UnmanagedT
Unmanaged(BeanManager,Class<T>)Unmanaged(Class<T>)UnmanagedInstance<T>newInstance()
UnmanagedInstanceT
Tget()UnmanagedInstance<T>produce()UnmanagedInstance<T>inject()UnmanagedInstance<T>postConstruct()UnmanagedInstance<T>preDestroy()UnmanagedInstance<T>dispose()
CDIProvider
CDI<Object>getCDI()
BeanManager
ObjectgetReference(Bean<?>,Type,CreationalContext<?>)ObjectgetInjectableReference(InjectionPoint,CreationalContext<?>)Set<Bean<?>>getBeans(Type,Annotation[])Bean<?extendsX>resolve(Set<Bean<?extendsX>>)voidvalidate(InjectionPoint)voidfireEvent(Object,Annotation[])
booleanisQualifier(Class<?extendsAnnotation>)booleanisStereotype(Class<?extendsAnnotation>)booleanareQualifiersEquivalent(Annotation,Annotation)booleanareInterceptorBindingsEquivalent(Annotation,Annotation)ContextgetContext(Class<?extendsAnnotation>)ELResolvergetELResolver()ExpressionFactorywrapExpressionFactory(ExpressionFactory)AnnotatedType<T>createAnnotatedType(Class<T>)InjectionTarget<T>createInjectionTarget(AnnotatedType<T>)InjectionTargetFactory<T>getInjectionTargetFactory(AnnotatedType<T>)BeanAttributes<T>createBeanAttributes(AnnotatedType<T>)Bean<T>createBean(BeanAttributes<T>,Class<X>,ProducerFactory<X>)InjectionPointcreateInjectionPoint(AnnotatedField<?>)
somemethodsskipped
CDIT
Set<CDIProvider>discoveredProvidersCDIProviderconfiguredProvider
CDI<Object>current()voidsetCDIProvider(CDIProviderprovider)BeanManagergetBeanManager()
SPIdedicatedtoextensions
BeforeBeanDiscovery
addQualifier(Class<?extendsAnnotation>)addScope(Class<?extendsAnnotation>,boolean,boolean)addStereotype(Class<?extendsAnnotation>,Annotation[])addInterceptorBinding(Class<?extendsAnnotation>,Annotation[])addAnnotatedType(AnnotatedType<?>)
AfterTypeDiscovery
List<Class<?>>getAlternatives()List<Class<?>>getInterceptors()List<Class<?>>getDecorators()addAnnotatedType(AnnotatedType<?>,String)
AfterDeploymentValidation BeforeShutdown
AfterBeanDiscovery
addBean(Bean<?>)addObserverMethod(ObserverMethod<?>)addContext(Context)AnnotatedType<T>getAnnotatedType(Class<T>,String)Iterable<AnnotatedType<T>>getAnnotatedTypes(Class<T>)
ProcessAnnotatedTypeX
AnnotatedType<X>getAnnotatedType()voidsetAnnotatedType(AnnotatedType<X>)veto()
ProcessBeanX
AnnotatedgetAnnotated()Bean<X>getBean()
ProcessBeanAttributesT
AnnotatedgetAnnotated()BeanAttributes<T>getBeanAttributes()setBeanAttributes(BeanAttributes<T>)veto()
ProcessInjectionPointT,X
InjectionPointgetInjectionPoint()setInjectionPoint(InjectionPoint)
ProcessInjectionTargetX
AnnotatedType<X>getAnnotatedType()InjectionTarget<X>getInjectionTarget()setInjectionTarget(InjectionTarget<X>)
ProcessObserverMethodT,X
AnnotatedMethod<X>getAnnotatedMethod()ObserverMethod<T>getObserverMethod()
ProcessProducerT,X
AnnotatedMember<T>getAnnotatedMember()Producer<X>getProducer()setProducer(Producer<X>)
AlltheseSPIinterfacesareeventscontainingmeta-modelSPI
TheseeventsfiredatboottimecanonlybeobservedinCDIextensions
Forinstance:
A ProcessAnnotatedType<T> eventisfiredforeachtypebeingdiscoveredatboottime
Observing ProcessAnnotatedType<Foo>allowsyoutoprevent Foo tobedeployedasabeanbycallingProcessAnnotatedType#veto()
IntroducingCDIPortableExtensions
Portableextensions
OneofthemostpowerfulfeatureoftheCDIspecification
Notreallypopularized,partlydueto:
1. Theirhighlevelofabstraction2. ThegoodknowledgeonBasicCDIandSPI3. Lackofinformation(CDIisoftenreducedtoabasicDIsolution)
Extensions,whatfor?
Tointegrate3rdpartylibraries,frameworksorlegacycomponents
Tochangeexistingconfigurationorbehavior
ToextendCDIandJavaEE
Thankstothem,JavaEEcanevolvebetweenmajorreleases
Extensions,how?
ObservingSPIeventsatboottimerelatedtothebeanmanagerlifecycle
Checkingwhatmeta-dataarebeingcreated
Modifyingthesemeta-dataorcreatingnewones
Moreconcretely
Serviceprovideroftheservicejavax.enterprise.inject.spi.Extension declaredinMETA-INF/services
Justputthefullyqualifiednameofyourextensionclassinthisfile
importjavax.enterprise.event.Observes;importjavax.enterprise.inject.spi.Extension;
publicclassCdiExtensionimplementsExtension{
voidbeforeBeanDiscovery(@ObservesBeforeBeanDiscoverybbd){}//...
voidafterDeploymentValidation(@ObservesAfterDeploymentValidationadv){}}
InternalStep HappenOnce LooponElements
Beanmanagerlifecycle
DeploymentStart
BeforeBean
Discovery
ScanArchive
ProcessAnnotated
Type
AfterType
Discovery
BeanEligibility
Check
ProcessInjectionPoint
ProcessInjectionTarget
ProcessBean
Attributes
ProcessBean
ProcessProducer
ProcessObserverMethod
AfterBean
Discovery
AfterDeploymentValidation
ApplicationRunning
BeforeShutdown
UndeployApplication
Example:IgnoringJPAentities
ThefollowingextensionpreventsCDItomanageentities
Thisisacommonlyadmittedgoodpractice
publicclassVetoEntityimplementsExtension{
voidvetoEntity(@Observes@WithAnnotations(Entity.class)ProcessAnnotatedType<?>pat){pat.veto();}}
ExtensionsarelaunchedduringbootstrapandarebasedonCDIevents
Oncetheapplicationisbootstrapped,theBeanManagerisinread-onlymode(noruntimebeanregistration)
Youonlyhaveto@Observes built-inCDIeventstocreateyourextensions
Remember
IntegratingDropwizardMetricsinCDI
MetricsCDI
DropwizardMetricsprovides
Differentmetrictypes: Counter , Gauge , Meter , Timer ,…
Differentreporter:JMX,console,SLF4J,CSV,servlet,…
MetricRegistry objectwhichcollectsallyourappmetrics
AnnotationsforAOPframeworks: @Counted , @Timed ,…
… butdoesnotincludeintegrationwiththeseframeworks
Moreatdropwizard.github.io/metrics
DiscoverhowwecreatedCDIintegrationmoduleforMetrics
Metricsoutofthebox(withoutCDI)classMetricsHelper{publicstaticMetricRegistryregistry=newMetricRegistry();}
classTimedMethodClass{
voidtimedMethod(){Timertimer=MetricsHelper.registry.timer("timer");Timer.Contexttime=timer.time();try{/*...*/}finally{time.stop();}}}
Notethatifa Timer named "timer" doesn’texist, MetricRegistry willcreateadefaultoneandregisterit
1
1
BasicCDIintegrationclassMetricRegistryBean{@Produces@ApplicationScopedMetricRegistryregistry=newMetricRegistry();}
classTimedMethodBean{@InjectMetricRegistryregistry;
voidtimedMethod(){Timertimer=registry.timer("timer");Timer.Contexttime=timer.time();try{/*...*/}finally{time.stop();}}}
WecouldhavealotmorewithadvancedCDIfeatures
OurgoalstoachievefullCDIintegration
Produceandinjectmultiplemetricsofthesametype
ApplyMetricswithprovidedannotations
Accesssame Metric throughinjector MetricRegistry getter
GOAL1Produceandinjectmultiplemetricsofthesametype
What’stheproblemwithmultipleMetricsofthesametype?
Thiscodecreateadeploymenterror(ambiguousdependency)
@ProducesTimertimer=newTimer(newSlidingTimeWindowReservoir(1L,MINUTES));
@ProducesTimertimer=newTimer(newSlidingTimeWindowReservoir(1L,HOURS));
@InjectTimertimer;
Thistimerthatonlykeepsmeasurementoflastminuteisproducedasabeanoftype Timer
Thistimerthatonlykeepsmeasurementoflasthourisproducedasabeanoftype Timer
Thisinjectionpointisambiguoussince2eligiblebeansexist
1
2
3
1
2
3
Solvingtheambiguity
Wecouldusetheprovided @Metric annotationtoqualifyourbeans
@Produces@Metric(name="myTimer")Timertimer=newTimer(newSlidingTimeWindowReservoir(1L,MINUTES));
@Produces@Metric(name="mySecondTimer")Timertimer=newTimer(newSlidingTimeWindowReservoir(1L,HOURS));
@Inject@Metric(name="myTimer")Timertimer;
Thatwon’tworkoutoftheboxsince @Metric isnotaqualifier
Howtomake @Metric aqualifier?
Byobserving BeforeBeanDiscovery lifecycleeventinanextension
publicinterfaceBeforeBeanDiscovery{
addQualifier(Class<?extendsAnnotation>qualifier);addQualifier(AnnotatedType<?extendsAnnotation>qualifier);addScope(Class<?extendsAnnotation>scopeType,booleannormal,booleanpassivation);addStereotype(Class<?extendsAnnotation>stereotype,Annotation...stereotypeDef);addInterceptorBinding(AnnotatedType<?extendsAnnotation>bindingType);addInterceptorBinding(Class<?extendsAnnotation>bindingType,Annotation...bindingTypeDef);addAnnotatedType(AnnotatedType<?>type);addAnnotatedType(AnnotatedType<?>type,Stringid);}
Thismethodistheoneweneedtodeclare @Metric asqualifier
Anduse addQualifier() methodintheevent
1
1
InternalStep HappenOnce LooponElements
BeforeBeanDiscovery isfirstinlifecycle
DeploymentStart
BeforeBean
Discovery
ScanArchive
ProcessAnnotated
Type
AfterType
Discovery
BeanEligibility
Check
ProcessInjectionPoint
ProcessInjectionTarget
ProcessBean
Attributes
ProcessBean
ProcessProducer
ProcessObserverMethod
AfterBean
Discovery
AfterDeploymentValidation
ApplicationRunning
BeforeShutdown
UndeployApplication
Ourfirstextension
ACDIextensionisaclassimplementingthe Extension taginterface
org.cdi.further.metrics.MetricsExtension
publicclassMetricsExtensionimplementsExtension{
voidaddMetricAsQualifier(@ObservesBeforeBeanDiscoverybdd){bdd.addQualifier(Metric.class);}}
Extensionisactivatedbyaddingthisfileto META-INF/services
javax.enterprise.inject.spi.Extension
org.cdi.further.metrics.MetricsExtension
Goal1achieved Wecannowwrite:
@Produces@Metric(name="myTimer")Timertimer=newTimer(newSlidingTimeWindowReservoir(1L,MINUTES));
@Produces@Metric(name="mySecondTimer")Timertimer=newTimer(newSlidingTimeWindowReservoir(1L,HOURS));
@Inject@Metric(name="myTimer")Timertimer;
Andhavethe Timer injectionpointssatisfied
GOAL2ApplyMetricswithprovidedannotations
Goal2indetail Wewanttobeabletowrite:
@Timed("timer")voidtimedMethod(){//...}
Andhavethetimer "timer" activatedduringmethodinvocation
Thesolutionistobuildandinterceptorandbinditto @Timed
1
Goal2stepbystep
Createaninterceptorforthetimertechnicalcode
Make @Timed (providedbyMetrics)avalidinterceptorbinding
Programmaticallyadd @Timed asaninterceptorbinding
Usethemagic
Preparinginterceptorcreation
Weshouldfindthetechnicalcodethatwillwrapthebusinesscode
classTimedMethodBean{
@InjectMetricRegistryregistry;
voidtimedMethod(){Timertimer=registry.timer("timer");Timer.Contexttime=timer.time();try{//Businesscode}finally{time.stop();}}}
Creatingtheinterceptor
Interceptorcodeishighlightedbelow
@InterceptorclassTimedInterceptor{@InjectMetricRegistryregistry;@AroundInvokeObjecttimedMethod(InvocationContextcontext)throwsException{Timertimer=registry.timer(context.getMethod().getAnnotation(Timed.class).name());Timer.Contexttime=timer.time();try{returncontext.proceed();}finally{time.stop();}}}
InCDIaninterceptorisabean,youcaninjectotherbeansinit
Herethebusinessoftheapplicationiscalled.Allthecodearoundisthetechnicalone.
1
2
1
2
Activatingtheinterceptor@Interceptor@Priority(Interceptor.Priority.LIBRARY_BEFORE)classTimedInterceptor{
@InjectMetricRegistryregistry;
@AroundInvokeObjecttimedMethod(InvocationContextcontext)throwsException{Timertimer=registry.timer(context.getMethod().getAnnotation(Timed.class).name());Timer.Contexttime=timer.time();try{returncontext.proceed();}finally{time.stop();}}}
Givinga @Priority toaninterceptoractivatesandordersit
1
1
Addabindingtotheinterceptor@Timed@Interceptor@Priority(Interceptor.Priority.LIBRARY_BEFORE)classTimedInterceptor{
@InjectMetricRegistryregistry;
@AroundInvokeObjecttimedMethod(InvocationContextcontext)throwsException{Timertimer=registry.timer(context.getMethod().getAnnotation(Timed.class).name());Timer.Contexttime=timer.time();try{returncontext.proceed();}finally{time.stop();}}}
We’lluseMetrics @Timed annotationasinterceptorbinding
1
1
Backoninterceptorbinding
Aninterceptorbindingisanannotationusedin2places:
1. Ontheinterceptorclasstobindittothisannotation2. Onthemethodsorclassestobeinterceptedbythisinterceptor
Aninterceptorbindingshouldhavethe @InterceptorBindingannotationorshouldbedeclaredprogrammatically
Iftheinterceptorbindingannotationhasmembers:
1. Theirvaluesaretakenintoaccounttoresolveinterceptor2. Unlessmembersareannotatedwith @NonBinding
@Timed sourcecodeisnotaninterceptorbinding
@Documented@Retention(RetentionPolicy.RUNTIME)@Target({ElementType.TYPE,ElementType.CONSTRUCTOR,ElementType.METHOD,ElementType.ANNOTATION_TYPE})
public@interfaceTimed{
Stringname()default"";
booleanabsolute()defaultfalse;}
Lackof @InterceptorBinding annotation
Noneofthemembershavethe @NonBinding annotation,so @Timed(name="timer1") and@Timed(name="timer2") willbe2differentinterceptorbindings
1
2
2
1
2
Therequired @Timed sourcecodetomakeitaninterceptorbinding
@Documented@Retention(RetentionPolicy.RUNTIME)@Target({ElementType.TYPE,ElementType.CONSTRUCTOR,ElementType.METHOD,ElementType.ANNOTATION_TYPE})@InterceptorBindingpublic@interfaceTimed{
@NonBindingStringname()default"";
@NonBindingbooleanabsolute()defaultfalse;}
Howtoobtaintherequired@Timed ?
Wecannottouchthecomponentsource/binary!
Usingthe AnnotatedType SPI
ThankstoDeltaSpikewecaneasilycreatetherequired AnnotatedType
AnnotatedTypecreateTimedAnnotatedType()throwsException{AnnotationnonBinding=newAnnotationLiteral<Nonbinding>(){};returnnewAnnotatedTypeBuilder().readFromType(Timed.class).addToMethod(Timed.class.getMethod("name"),nonBinding).addToMethod(Timed.class.getMethod("absolute"),nonBinding).create();}
Thiscreatesaninstanceof @NonBinding annotation
Itwouldhavebeenpossiblebutfarmoreverbosetocreatethis AnnotatedType withoutthehelpofDeltaSpike.The AnnotatedTypeBuilder isinitializedfromtheMetrics @Timed annotation.
@NonBinding isaddedtobothmembersofthe @Timed annotation
123
3
1
2
3
Thisextensionwilldothejob
Weobserve BeforeBeanDiscovery toaddanewinterceptorbinding
publicclassMetricsExtensionimplementsExtension{
voidaddTimedBinding(@ObservesBeforeBeanDiscoverybdd)throwsException{AnnotationnonBinding=newAnnotationLiteral<Nonbinding>(){};
bdd.addInterceptorBinding(newAnnotatedTypeBuilder<Timed>().readFromType(Timed.class).addToMethod(Timed.class.getMethod("name"),nonBinding).addToMethod(Timed.class.getMethod("absolute"),nonBinding).create());}}
Goal2achieved Wecannowwrite:
@Timed("timer")voidtimedMethod(){//Businesscode}
AndhaveaMetricsTimer appliedtothemethod
Interceptorshouldbeenhancedtosupport @Timed onaclass
Otherinterceptorshouldbedevelopedforothermetrics
Ourgoals1. ApplyametricwiththeprovidedannotationinAOPstyle
@Timed("timer")voidtimedMethod(){//...}
2. Registerautomaticallyproducedcustommetrics@Produces@Metric(name="myTimer")Timertimer=newTimer(newSlidingTimeWindowReservoir(1L,MINUTES));//...@Timed("myTimer")voidtimedMethod(){/*...*/}
AnnotationsprovidedbyMetrics
1
1
1
1
GOAL3AccesssameMetricthrough@InjectorMetricRegistry getter
Goal3indetail WhenWriting:
@Inject@Metric(name="myTimer")Timertimer1;
@InjectMetricRegistryregistry;
Timertimer2=registry.timer("myTimer");
… Wewantthat timer1==timer2
Goal3indetail@Produces@Metric(name="myTimer")Timertimer=newTimer(newSlidingTimeWindowReservoir(1L,TimeUnit.MINUTES));
@Inject@Metric(name="myTimer")Timertimer;
@InjectMetricRegistryregistry;
Timertimer=registry.timer("myTimer");
Produced Timer shouldbeaddedtoMetricRegistrywhenproduced
Whenretrievedfromregistrya Metric shouldbeidenticaltotheproducedinstanceandviceversa
Thereare2 Metric :the com.codahale.metrics.Metric interfaceandthe com.codahale.metrics.annotation.Metric annotation
1
2
1
2
Goal3stepbystep
Weneedtowriteanextensionthatwill:
1. Changehowa Metric instanceisproducedbylookingitupintheregistryfirstandproducing(andregistering)itonlyifit’snotfound.We’lldothisby:1. observingthe ProcessProducer lifecycleevent2. decoratingMetric Producer toaddthisnewbehavior
2. Produceall Metric instancesattheendofboottimetohavetheminregistryforruntime1. we’lldothisbyobserving AfterDeploymentValidation event
InternalStep HappenOnce LooponElements
Sowewill @Observes these2eventstoaddourfeatures
DeploymentStart
BeforeBean
Discovery
ScanArchive
ProcessAnnotated
Type
AfterType
Discovery
BeanEligibility
Check
ProcessInjectionPoint
ProcessInjectionTarget
ProcessBean
Attributes
ProcessBean
ProcessProducer
ProcessObserverMethod
AfterBean
Discovery
AfterDeploymentValidation
ApplicationRunning
BeforeShutdown
UndeployApplication
Customizing Metric producingprocess
Wefirstneedtocreateourimplementationofthe Producer<X> SPI
classMetricProducer<XextendsMetric>implementsProducer<X>{
finalProducer<X>decorate;
finalStringmetricName;
MetricProducer(Producer<X>decorate,StringmetricName){this.decorate=decorate;this.metricName=metricName;}
//...
Customizing Metric producingprocess(continued)//...publicXproduce(CreationalContext<X>ctx){MetricRegistryreg=BeanProvider.getContextualReference(MetricRegistry.class,false);if(!reg.getMetrics().containsKey(metricName))reg.register(metricName,decorate.produce(ctx));
return(X)reg.getMetrics().get(metricName);}
publicvoiddispose(Xinstance){}
publicSet<InjectionPoint>getInjectionPoints(){returndecorate.getInjectionPoints();}}
Producemethodwillbeusedbythecontaineratruntimetodecoratedeclaredproducerwithourlogic
BeanProvider isaDeltaSpikehelperclasstoeasilyretrieveabeanorbeaninstance
Ifmetricnameisnotintheregistry,theoriginalproduceriscalledanditsresultisaddedtoregistry
12
3
1
2
3
We’lluseour MetricProducer ina ProcessProducer observer
Thiseventallowustosubstitutetheoriginalproducerbyours
publicinterfaceProcessProducer<T,X>{
AnnotatedMember<T>getAnnotatedMember();
Producer<X>getProducer();
voidsetProducer(Producer<X>producer);
voidaddDefinitionError(Throwablet);}
Getsthe AnnotatedMember associatedtothe @Produces fieldormethod
Getsthedefaultproducer(usefultodecorateit)
Overridestheproducer
1
2
3
1
2
3
Customizing Metric producingprocess(end)
Here’stheextensioncodetodothisproducerdecoration
publicclassMetricsExtensionimplementsExtension{//...<XextendsMetric>voiddecorateMetricProducer(@ObservesProcessProducer<?,X>pp){Stringname=pp.getAnnotatedMember().getAnnotation(Metric.class).name();pp.setProducer(newMetricProducer<>(pp.getProducer(),name));}//...}
Weretrievemetricnamefromthe name() memberin @Metric
Wereplacetheoriginalproducerbyourproducer(whichdecoratestheformer)
12
1
2
Producingallthe Metric instancesattheendofboottime
Wedothatbyobservingthe AfterDeploymentValidation event
publicclassMetricsExtensionimplementsExtension{//...voidregisterProducedMetrics(@ObservesAfterDeploymentValidationadv){List<Metric>metrics=BeanProvider.getContextualReferences(Metric.class,true);}//...}
getContextualReferences() isamethodinDeltaSpike BeanProvider helperclass.Itcreatesthelistofbeaninstancesforagivenbeantype(ignoringqualifiers)
1
1
Goal3achieved
Wecannowwrite:
@Produces@Metric(name="myTimer")Timertimer=newTimer(newSlidingTimeWindowReservoir(1L,TimeUnit.MINUTES));
@InjectMetricRegistryregistry;
@Inject@Metric("myTimer")Metrictimer;
Andbesurethat registry.getMetrics().get("myTimer") andtimer arethesameobject(ourcustom Timer )
CompleteextensioncodepublicclassMetricsExtensionimplementsExtension{
voidaddMetricAsQualifier(@ObservesBeforeBeanDiscoverybdd){bdd.addQualifier(Metric.class);}
voidaddTimedBinding(@ObservesBeforeBeanDiscoverybdd)throwsException{AnnotationnonBinding=newAnnotationLiteral<Nonbinding>(){};bdd.addInterceptorBinding(newAnnotatedTypeBuilder<Timed>().readFromType(Timed.class).addToMethod(Timed.class.getMethod("name"),nonBinding).addToMethod(Timed.class.getMethod("absolute"),nonBinding).create());}
<Textendscom.codahale.metrics.Metric>voiddecorateMetricProducer(@ObservesProcessProducer<?,T>pp){if(pp.getAnnotatedMember().isAnnotationPresent(Metric.class)){Stringname=pp.getAnnotatedMember().getAnnotation(Metric.class).name();pp.setProducer(newMetricProducer(pp.getProducer(),name));}}
voidregisterProducedMetrics(@ObservesAfterDeploymentValidationadv){List<com.codahale.metrics.Metric>metrics=BeanProvider.getContextualReferences(com.codahale.metrics.Metric.class,true);}}
HowtouseCDIasdependencyinjectioncontainerforanintegrationframework(ApacheCamel)
CamelCDI
AboutApacheCamel
Open-sourceintegrationframeworkbasedonknownEnterpriseIntegrationPatterns
ProvidesavarietyofDSLstowriteroutingandmediationrules
ProvidessupportforbeanbindingandseamlessintegrationwithDIframeworks
DiscoverhowwecreatedCDIintegrationmoduleforCamel
Cameloutofthebox(withoutCDI)publicstaticvoidmain(String[]args){CamelContextcontext=newDefaultCamelContext();context.addRoutes(newRouteBuilder(){publicvoidconfigure(){from("file:target/input?delay=1000").log("Sendingmessage[${body}]toJMS...").to("sjms:queue:output");}});PropertiesComponentproperties=newPropertiesComponent();properties.setLocation("classpath:camel.properties");context.addComponent("properties",properties);//Registersthe"properties"component
SjmsComponentcomponent=newSjmsComponent();component.setConnectionFactory(newActiveMQConnectionFactory("vm://broker?broker.persistent=false"));jms.setConnectionCount(Integer.valueOf(context.resolvePropertyPlaceholders("{{jms.maxConnections}}")));context.addComponent("sjms",jms);//Registersthe"sjms"component
context.start();}
ThisroutewatchesadirectoryeverysecondandsendsnewfilescontenttoaJMSqueue
1
1
WhyCDI?
BasicCDIintegration(1/3)1. CamelcomponentsandroutebuilderasCDIbeans2. BindtheCamelcontextlifecycletothatoftheCDIcontainer
classFileToJmsRouteBeanextendsRouteBuilder{
@Overridepublicvoidconfigure(){from("file:target/input?delay=1000").log("Sendingmessage[${body}]toJMS...").to("sjms:queue:output");}}
BasicCDIintegration(2/3)classPropertiesComponentFactoryBean{
@Produces@ApplicationScopedPropertiesComponentpropertiesComponent(){PropertiesComponentproperties=newPropertiesComponent();properties.setLocation("classpath:camel.properties");returnproperties;}}
classJmsComponentFactoryBean{
@Produces@ApplicationScopedSjmsComponentsjmsComponent(PropertiesComponentproperties)throwsException{SjmsComponentjms=newSjmsComponent();jms.setConnectionFactory(newActiveMQConnectionFactory("vm://broker?broker.persistent=false"));jms.setConnectionCount(Integer.valueOf(properties.parseUri("{{jms.maxConnections}}")));returncomponent;}}
BasicCDIintegration(3/3)@ApplicationScopedclassCamelContextBeanextendsDefaultCamelContext{
@InjectCamelContextBean(FileToJmsRouteBeanroute,SjmsComponentjms,PropertiesComponentproperties){addComponent("properties",properties);addComponent("sjms",jms);addRoutes(route);}@PostConstructvoidstartContext(){super.start();}@PreDestroyvoidpreDestroy(){super.stop();}}
WecouldhavealotmorewithadvancedCDIfeatures
Ourgoals1. Avoidassemblingandconfiguringthe CamelContext manually2. AccessCDIbeansfromtheCamelDSLautomatically
.to("sjms:queue:output");//Lookupbyname(sjms)andtype(Component)
context.resolvePropertyPlaceholders("{{jms.maxConnections}}");//Lookupbyname(properties)andtype(Component)
3. SupportCamelannotationsinCDIbeans@PropertyInject(value="jms.maxConnections",defaultValue="10")intmaxConnections;
StepstointegrateCamelandCDI
Managethecreationandtheconfigurationofthe CamelContextbean
Bindthe CamelContext lifecycletothatoftheCDIcontainer
ImplementtheCamelregistrySPItolookupCDIbeanreferences
Useacustom InjectionTarget forCDIbeanscontainingCamelannotations
Usethemagic
Howtoachievethis?
Weneedtowriteanextensionthatwill:
1. Declarea CamelContext beanbyobservingthe AfterBeanDiscoverylifecycleevent
2. Instantiatethebeansoftype RouteBuilder andaddthemtotheCamelcontext
3. Start(resp.stop)theCamelcontextwhentheAfterDeploymentValidation eventisfired(resp.the BeforeShutdownevent)
4. CustomizetheCamelcontexttoquerythe BeanManager tolookupCDIbeansbynameandtype
5. DetectCDIbeanscontainingCamelannotationsbyobservingtheProcessAnnotatedType eventandmodifyhowtheygetinjectedbyobservingthe ProcessInjectionTarget lifecycleevent
InternalStep HappenOnce LooponElements
Sowewill @Observes these5eventstoaddourfeatures
DeploymentStart
BeforeBean
Discovery
ScanArchive
ProcessAnnotated
Type
AfterType
Discovery
BeanEligibility
Check
ProcessInjectionPoint
ProcessInjectionTarget
ProcessBean
Attributes
ProcessBean
ProcessProducer
ProcessObserverMethod
AfterBean
Discovery
AfterDeploymentValidation
ApplicationRunning
BeforeShutdown
UndeployApplication
Addingthe CamelContext bean
Automaticallyadda CamelContext beaninthedeploymentarchive
Howtoaddabeanprogrammatically?
Declaringabeanprogrammatically
Weneedtoimplementthe Bean SPI
publicinterfaceBean<T>extendsContextual<T>,BeanAttributes<T>{
Class<?>getBeanClass();Set<InjectionPoint>getInjectionPoints();Tcreate(CreationalContext<T>creationalContext);//Contextual<T>voiddestroy(Tinstance,CreationalContext<T>creationalContext);Set<Type>getTypes();//BeanAttributes<T>Set<Annotation>getQualifiers();Class<?extendsAnnotation>getScope();StringgetName();Set<Class<?extendsAnnotation>>getStereotypes();booleanisAlternative();}
Implementingthe Bean SPIclassCamelContextBeanimplementsBean<CamelContext>{
publicClass<?extendsAnnotation>getScope(){returnApplicationScoped.class;}
publicSet<Annotation>getQualifiers(){returnCollections.<Annotation>singleton(newAnnotationLiteral<Default>(){});}publicSet<Type>getTypes(){returnCollections.<Type>singleton(CamelContext.class);}
publicCamelContextcreate(CreationalContext<CamelContext>creational){returnnewDefaultCamelContext();}publicvoiddestroy(CamelContextinstance,CreationalContext<CamelContext>creational){}
publicClass<?>getBeanClass(){returnDefaultCamelContext.class;}
publicSet<InjectionPoint>getInjectionPoints(){returnCollections.emptySet();}
publicSet<Class<?extendsAnnotation>>getStereotypes(){returnCollections.emptySet();}publicStringgetName(){returnnull;}//Onlycalledfor@NamedbeanpublicbooleanisAlternative(){returnfalse;}publicbooleanisNullable(){returnfalse;}}
Addingaprogrammaticbeantothedeployment
Thenaddthe CamelContextBean beanprogrammaticallybyobservingthe AfterBeanDiscovery lifecycleevent
publicclassCamelExtensionimplementsExtension{
voidaddCamelContextBean(@ObservesAfterBeanDiscoveryabd){abd.addBean(newCamelContextBean());}}
InstantiateandassembletheCamelcontext
Instantiatethe CamelContext beanandthe RouteBuilder beansintheAfterDeploymentValidation lifecycleevent
publicclassCamelExtensionimplementsExtension{//...voidconfigureContext(@ObservesAfterDeploymentValidationadv,BeanManagerbm){CamelContextcontext=getReference(bm,CamelContext.class);for(Bean<?>bean:bm.getBeans(RoutesBuilder.class))context.addRoutes(getReference(bm,RouteBuilder.class,bean));}<T>TgetReference(BeanManagerbm,Class<T>type){returngetReference(bm,type,bm.resolve(bm.getBeans(type)));}<T>TgetReference(BeanManagerbm,Class<T>type,Bean<?>bean){return(T)bm.getReference(bean,type,bm.createCreationalContext(bean));}}
ManagedtheCamelcontextlifecycle
Start(resp.stop)theCamelcontextwhentheAfterDeploymentValidation eventisfired(resp.the BeforeShutdown )
publicclassCamelExtensionimplementsExtension{//...voidconfigureContext(@ObservesAfterDeploymentValidationadv,BeanManagerbm){CamelContextcontext=getReference(bm,CamelContext.class);for(Bean<?>bean:bm.getBeans(RoutesBuilder.class)context.addRoutes(getReference(bm,RouteBuilder.class,bean);context.start();}voidstopCamelContext(@ObservesBeforeShutdownbs,BeanManagerbm){CamelContextcontext=getReference(bm,CamelContext.class);context.stop();}}
Firstgoalachieved
Wecangetridofthefollowingcode:
@ApplicationScopedclassCamelContextBeanextendsDefaultCamelContext{@InjectCamelContextBean(FileToJmsRouteBeanroute,SjmsComponentjms,PropertiesComponentproperties){addComponent("properties",propertiesComponent);addComponent("sjms",sjmsComponent);addRoutes(route);}@PostConstructvoidstartContext(){super.start();}@PreDestroyvoidstopContext(){super.stop();}}
Secondgoal:AccessCDIbeansfromtheCamelDSL
HowtoretrieveCDIbeansfromtheCamelDSL?
.to("sjms:queue:output");//Lookupbyname(sjms)andtype(Component)context.resolvePropertyPlaceholders("{{jms.maxConnections}}");//Lookupbyname(properties)andtype(Component)
//Andalso....bean(MyBean.class);//LookupbytypeandDefaultqualifier.beanRef("beanName");//Lookupbyname
ImplementtheCamelregistrySPIandusethe BeanManager tolookupforCDIbeancontextualreferencesbynameandtype
ImplementtheCamelregistrySPIclassCamelCdiRegistryimplementsRegistry{privatefinalBeanManagerbm;
CamelCdiRegistry(BeanManagerbm){this.bm=bm;}
public<T>TlookupByNameAndType(Stringname,Class<T>type){returngetReference(bm,type,bm.resolve(bm.getBeans(name)));}public<T>Set<T>findByType(Class<T>type){returngetReference(bm,type,bm.resolve(bm.getBeans(type)));}publicObjectlookupByName(Stringname){returnlookupByNameAndType(name,Object.class);}<T>TgetReference(BeanManagerbm,Typetype,Bean<?>bean){return(T)bm.getReference(bean,type,bm.createCreationalContext(bean));}}
Addthe CamelCdiRegistry totheCamelcontext
classCamelContextBeanimplementsBean<CamelContext>{privatefinalBeanManagerbm;
CamelContextBean(BeanManagerbm){this.bm=bm;}//...publicCamelContextcreate(CreationalContext<CamelContext>creational){returnnewDefaultCamelContext(newCamelCdiRegistry(bm));}}
publicclassCamelExtensionimplementsExtension{//...voidaddCamelContextBean(@ObservesAfterBeanDiscoveryabd,BeanManagerbm){abd.addBean(newCamelContextBean(bm));}}
Secondgoalachieved1/3
Wecandeclarethe sjms componentwiththe @Named qualifier
classJmsComponentFactoryBean{
@Produces@Named("sjms")@ApplicationScopedSjmsComponentsjmsComponent(PropertiesComponentproperties){SjmsComponentjms=newSjmsComponent();jms.setConnectionFactory(newActiveMQConnectionFactory("vm://broker?..."));jms.setConnectionCount(Integer.valueOf(properties.parseUri("{{jms.maxConnections}}")));returncomponent;}}
…
Secondgoalachieved2/3
Declarethe properties componentwiththe @Named qualifier
classPropertiesComponentFactoryBean{
@Produces@Named("properties")@ApplicationScopedPropertiesComponentpropertiesComponent(){PropertiesComponentproperties=newPropertiesComponent();properties.setLocation("classpath:camel.properties");returnproperties;}}
…
Secondgoalachieved3/3
Andgetridofthecoderelatedtothe properties and sjmscomponentsregistration
@ApplicationScopedclassCamelContextBeanextendsDefaultCamelContext{@InjectCamelContextBean(FileToJmsRouteBeanroute,SjmsComponentjms,PropertiesComponentproperties){addComponent("properties",propertiesComponent);addComponent("sjms",sjmsComponent);addRoutes(route);}@PostConstructvoidstartContext(){super.start();}@PreDestroyvoidstopContext(){super.stop();}}
Thirdgoal:SupportCamelannotationsinCDIbeans
CamelprovidesasetofDIframeworkagnosticannotationsforresourceinjection
@PropertyInject(value="jms.maxConnections",defaultValue="10")intmaxConnections;
//Butalso...@EndpointInject(uri="jms:queue:foo")Endpointendpoint;
@BeanInject("foo")FooBeanfoo;
Howtosupportcustomannotationsinjection?
Howtosupportcustomannotationsinjection?
Createacustom InjectionTarget thatusesthedefaultCamelbeanpostprocessor DefaultCamelBeanPostProcessor
publicinterfaceInjectionTarget<T>extendsProducer<T>{voidinject(Tinstance,CreationalContext<T>ctx);voidpostConstruct(Tinstance);voidpreDestroy(Tinstance);}
HookitintotheCDIinjectionmechanismbyobservingtheProcessInjectionTarget lifecycleevent
OnlyforbeanscontainingCamelannotationsbyobservingtheProcessAnnotatedType lifecycleandusing @WithAnnotations
Createacustom InjectionTargetclassCamelInjectionTarget<T>implementsInjectionTarget<T>{
finalInjectionTarget<T>delegate;
finalDefaultCamelBeanPostProcessorprocessor;
CamelInjectionTarget(InjectionTarget<T>target,finalBeanManagerbm){delegate=target;processor=newDefaultCamelBeanPostProcessor(){publicCamelContextgetOrLookupCamelContext(){returngetReference(bm,CamelContext.class);}};}publicvoidinject(Tinstance,CreationalContext<T>ctx){processor.postProcessBeforeInitialization(instance,null);delegate.inject(instance,ctx);}//...}
CalltheCameldefaultbeanpost-processorbeforeCDIinjection
1
1
Registerthecustom InjectionTarget
Observethe ProcessInjectionTarget lifecycleeventandsettheInjectionTarget
publicinterfaceProcessInjectionTarget<X>{AnnotatedType<X>getAnnotatedType();InjectionTarget<X>getInjectionTarget();voidsetInjectionTarget(InjectionTarget<X>injectionTarget);voidaddDefinitionError(Throwablet);}
Todecorateitwiththe CamelInjectionTarget
classCamelExtensionimplementsExtension{
<T>voidcamelBeansPostProcessor(@ObservesProcessInjectionTarget<T>pit,BeanManagerbm){pit.setInjectionTarget(newCamelInjectionTarget<>(pit.getInjectionTarget(),bm));}}
ButonlyforbeanscontainingCamelannotationsclassCamelExtensionimplementsExtension{finalSet<AnnotatedType<?>>camelBeans=newHashSet<>();
voidcamelAnnotatedTypes(@Observes@WithAnnotations(PropertyInject.class)ProcessAnnotatedType<?>pat){camelBeans.add(pat.getAnnotatedType());}
<T>voidcamelBeansPostProcessor(@ObservesProcessInjectionTarget<T>pit,BeanManagerbm){if(camelBeans.contains(pit.getAnnotatedType()))pit.setInjectionTarget(newCamelInjectionTarget<>(pit.getInjectionTarget(),bm));}}
DetectallthetypescontainingCamelannotationswith @WithAnnotations
Decoratethe InjectionTarget correspondingtothesetypes
1
2
1
2
Thirdgoalachieved1/2
Insteadofinjectingthe PropertiesComponent beantoresolveaconfigurationproperty
classJmsComponentFactoryBean{
@Produces@Named("sjms")@ApplicationScopedSjmsComponentsjmsComponent(PropertiesComponentproperties){SjmsComponentjms=newSjmsComponent();jms.setConnectionFactory(newActiveMQConnectionFactory("vm://broker?..."));jms.setConnectionCount(Integer.valueOf(properties.parseUri("{{jms.maxConnections}}")));returncomponent;}}
Thirdgoalachieved2/2
Wecandirectlyrelyonthe @PropertyInject CamelannotationinCDIbeans
classJmsComponentFactoryBean{
@PropertyInject("jms.maxConnections")intmaxConnections;
@Produces@Named("sjms")@ApplicationScopedSjmsComponentsjmsComponent(){SjmsComponentcomponent=newSjmsComponent();jms.setConnectionFactory(newActiveMQConnectionFactory("vm://broker?..."));component.setConnectionCount(maxConnections);returncomponent;}}
Bonusgoal:CamelDSLAOP
AOPinstrumentationoftheCamelDSL
from("file:target/input?delay=1000").log("Sendingmessage[${body}]toJMS...").to("sjms:queue:output");
WithCDIobservers
from("file:target/input?delay=1000").to("sjms:queue:output").id("joinpoint");}voidadvice(@Observes@NodeId("joinpoint")Exchangeexchange){logger.info("Sendingmessage[{}]toJMS...",exchange.getIn().getBody());}
Howtoachievethis?
WecancreateaCDIqualifiertoholdtheCamelnodeidmetadata:
@Qualifier@Retention(RetentionPolicy.RUNTIME)public@interfaceNodeId{Stringvalue();}
Andcreateanextensionthatwill:
1. DetecttheCDIbeanscontainingobservermethodswiththe @NodeIdqualifierbyobservingthe ProcessObserverMethod eventandcollecttheCamelprocessornodestobeinstrumented
2. CustomizetheCamelcontextbyprovidinganimplementationoftheCamel InterceptStrategy interfacethatwillfireaCDIeventeachtimean Exchange isprocessedbytheinstrumentednodes
DetecttheCamelDSLAOPobservermethods
Observethe ProcessObserverMethod lifecycleevent
publicinterfaceProcessObserverMethod<T,X>{AnnotatedMethod<X>getAnnotatedMethod();ObserverMethod<T>getObserverMethod();voidaddDefinitionError(Throwablet);}
Andcollecttheobservermethodmetadata
classCamelExtensionimplementsExtension{finalSet<NodeId>joinPoints=newHashSet<>();
voidpointcuts(@ObservesProcessObserverMethod<Exchange,?>pom){for(Annotationqualifier:pom.getObserverMethod().getObservedQualifiers())if(qualifierinstanceofNodeId)joinPoints.add(NodeId.class.cast(qualifier));}}
InstrumenttheCamelcontext
InterceptmatchingnodesandfireaCDIevent
voidconfigureCamelContext(@ObservesAfterDeploymentValidationadv,finalBeanManagermanager){context.addInterceptStrategy(newInterceptStrategy(){publicProcessorwrapProcessorInInterceptors(CamelContextcontext,ProcessorDefinition<?>definition,Processortarget,ProcessornextTarget)throwsException{if(definition.hasCustomIdAssigned()){for(finalNodenode:joinPoints){if(node.value().equals(definition.getId())){returnnewDelegateAsyncProcessor(target){publicbooleanprocess(Exchangeexchange,AsyncCallbackcallback){manager.fireEvent(exchange,node);returnsuper.process(exchange,callback);}};}}}returntarget;}});}
Bonusgoalachieved
WecandefinejoinpointsintheCamelDSL
from("file:target/input?delay=1000").to("sjms:queue:output").id("joinpoint");}
AndadvisethemwithCDIobservers
voidadvice(@Observes@NodeId("joinpoint")Exchangeexchange){List<MessageHistory>history=exchange.getProperty(Exchange.MESSAGE_HISTORY,List.class);logger.info("Sendingmessage[{}]to[{}]...",exchange.getIn().getBody(String.class),history.get(history.size()-1).getNode().getLabel());}
Conclusion
References
CDISpecification-cdi-spec.org
Slidessources-github.com/astefanutti/further-cdi
MetricsCDIsources-github.com/astefanutti/metrics-cdi
CamelCDIsources-github.com/astefanutti/camel-cdi
SlidesgeneratedwithAsciidoctor,PlantUMLandDZSlidesbackend
Originalslidetemplate-DanAllen&SarahWhite
AntoineSabot-DurandAntoninStefanutti@antoine_sd@astefanut
Annexes
CompletelifecycleeventsApplication lifecycle
BeforeBeanDiscovery
AfterTypeDiscovery
AfterBeanDiscovery
AfterDeploymentValidation
Type Discovery
ProcessAnnotatedType<X>
yes
whiletypesindeploymentarchive?
no
Bean Discovery
For each discovered types during type discovery
ProcessInjectionPoint<T,X>
ProcessInjectionTarget<X>
ProcessBeanAttributes<T>
ProcessManagedBean<X>
For each producer methods / fields of enabled beans
ProcessInjectionPoint<T,X>
ProcessProducer<T,X>
ProcessBeanAttributes<T>
ProcessProducerMethod<T,X>ProcessProducerField<T,X>
For each observer methods of enabled beans
ProcessInjectionPoint<T,X>
ProcessObserverMethod<T,X>