Otto Source Code Analysis
Otto's source code is very simple, altogether nine classes.
Project structure
- Annotatedhandlerfinder (Find and cache all annotation methods)
- Bus (Otto core business class, including registration, anti-registration, sending events, etc.)
- deadevent (built-in events, no subscribers, cannot be passed)
- EventHandler (Package @subscribe method and its class)
- eventproducer (Package @produce method and its class)
- Handlerfinder (call Annotatedhandlerfinder method to get the annotation method, encapsulated into EventHandler and Eventproducer collection)
- Produce (@Produce)
- Subscribe (@Subscribe)
- Threadenforcer (for switching threads)
Register implementation Processing @Produce logic
- 1. Find @Produce method by Annotatedhandlerfinder
- 2. Then handlerfinder through the recovered package into a
Map<Class<?>, EventProducer>
set
- 3. Iterate through the
Map<Class<?>, EventProducer>
collection and get all the cached EventHandler for that class one by one
- 4. @Subscribe and @Produce method
- 5. Call Dispatchproducerresulttohandler Processing: The so-called process of providing @Produce @Subscribe consumption by itself
/** * Find all @Produce methods in the * object by Handlerfinder * Retrieve the package into Eventproducer * and finally return map<class<?>, even tproducer> * *Map<class<?> eventproducer> foundproducers = Handlerfinder.findallproducers (object); for(class<?> Type:foundProducers.keySet ()) {/** * Get Eventproducer one by one * * FinalEventproducer producer = Foundproducers.get (type);/** * Concurrentmap putifabsent Safe put * prevents concurrency * view cache concurrentmap<class<?> eventproducer> has wood Have * /Eventproducer previousproducer = producersbytype.putifabsent (type, producer);//checking If the previous producer existed /** * There is a cache first "fried" * * if(Previousproducer! =NULL) {Throw NewIllegalArgumentException ("Producer method for Type"+ Type +"found on type"+ producer.target.getClass () +", but already registered by type"+ previousProducer.target.getClass () +"."); }/** * Get all cache EventHandler ( @Subscribe Method Encapsulation Class) */set<eventhandler> handlers = Handlersbytype.get (type);if(Handlers! =NULL&&!handlers.isempty ()) { for(EventHandler handler:handlers) {/** * Call @subscribe and @Produce one by one * *Dispatchproducerresulttohandler (handler, producer); } }}
Handling @SubScribe Logic
- 1. Find @Subscribe method by Annotatedhandlerfinder
- 2. Then handlerfinder through the recovered package into a
Map<Class<?>, Set<EventHandler>>
set
- 3. Go through
Map<Class<?>, Set<EventHandler>>
the collection and get the Set<EventHandler>
refresh cache
- 4. Cycle the
Map<Class<?>, Set<EventHandler>>
collection again to find the cache for the corresponding class Set<EventProducer>
.
- 6. Call Dispatchproducerresulttohandler processing, once again: the so-called process of providing @Produce @Subscribe consumption by itself
/** * Find all @SubScribe methods in the * object by Handlerfinder * Retrieved package into EventHandler * MAP<CLASS<?> set< eventhandler>> * *Map<class<?> set<eventhandler>> Foundhandlersmap = Handlerfinder.findallsubscribers (object);/** * Get EventHandler one by one * * for(class<?> Type:foundHandlersMap.keySet ()) {/** * Get Cache EventHandler Collection * /set<eventhandler> handlers = Handlersbytype.get (type);if(Handlers = =NULL) {//concurrent put if absentSet<eventhandler> handlerscreation =NewCopyonwritearrayset<eventhandler> ();/** * Put a copy of the type corresponding to the Eventhandle set in the cache EventHandler Map * *handlers = Handlersbytype.putifabsent (type, handlerscreation);if(Handlers = =NULL) {handlers = Handlerscreation; } }Finalset<eventhandler> foundhandlers = Foundhandlersmap.get (type);if(!handlers.addall (Foundhandlers)) {Throw NewIllegalArgumentException ("Object already registered."); }}/** * Traverse all found EventHandler ( @Subscribe method) */ for(Map.entry<class<?>, set<eventhandler>> Entry:foundHandlersMap.entrySet ()) {class<?> type = Entry.getkey ();/** * Take one More time eventproducer cache * If there is @Producer method in the object, the logic of the loop EventHandler is called * Dispat Chproducerresulttohandler method * /Eventproducer producer = Producersbytype.get (type);if(Producer! =NULL&& Producer.isvalid ()) {set<eventhandler> foundhandlers = Entry.getvalue (); for(EventHandler foundhandler:foundhandlers) {if(!producer.isvalid ()) { Break; }if(Foundhandler.isvalid ()) {Dispatchproducerresulttohandler (Foundhandler, producer); } } }}
Unregister implementation
- 1. Find @Produce method by Annotatedhandlerfinder
- 2. Then handlerfinder through the recovered package into a
Map<Class<?>, EventProducer>
set
- 3. Iterate through the
Map<Class<?>, EventProducer>
collection, get Eventproducer one by one, and go to find out if the Eventproducer cache exists.
- 4. Cache exists, removed from Eventproducer cache map, call Eventproducer.invalidate () method
/** * Find all @Produce methods in the * object by Handlerfinder * Retrieve the package into Eventproducer * and finally return map<class<?>, even tproducer> * *Map<class<?> eventproducer> Producersinlistener = Handlerfinder.findallproducers (object); for(Map.entry<class<?>, eventproducer> Entry:producersInListener.entrySet ()) {Finalclass<?> key = Entry.getkey ();/** * Get the corresponding Eventproducer * Here is only one to show: * An object only exists a eventproducer * Producer represents the cached object All Eventproducer * value represents all EventHandler found through the Finder */Eventproducer producer = Getproducerforeventtype (key); Eventproducer value = Entry.getvalue ();if(Value = =NULL|| !value.equals (producer)) {Throw NewIllegalArgumentException ("Missing event producer for an annotated method." Is "+ object.getclass () +"Registered?"); }/** * Remove * from Eventproducer cache map and call Eventproducer.invalidate () method * Set the Eventproducer illegal * *Producersbytype.remove (Key). Invalidate ();}
- 1. Find @Subscribe method by Annotatedhandlerfinder
- 2. Then handlerfinder through the recovered package into a
Map<Class<?>, Set<EventHandler>>
set
- 3. Iterate through the
Map<Class<?>, Set<EventHandler>>
collection, get the Set<EventHandler>
collection, and see if the cache exists.
- 4. There is a cache, continue to traverse
Set<EventHandler>
the collection, get EventHandler.
- 5. If the cache is EventHandler, there is a Finder first check in the EventHandler, marked as illegal.
- 6. Delete the
Set<EventHandler>
cache for this collection
Post implementation
First of all, here's a way to do all the parent classes of a class, including yourself, stored as a set set.
Also Otto a tool method .
Getclassesfor
/** * Find a class All parent classes include themselves as a set set * * @param concreteclass concreteclass * @return set<class& lt;? >> * *PrivateSet<class<?>>getclassesfor(class<?> Concreteclass) {List<class<?>> parents =NewLinkedlist<class<?>> (); set<class<?>> classes =NewHashset<class<?>> (); Parents.add (Concreteclass); while(!parents.isempty ()) {class<?> clazz = Parents.remove (0); Classes.add (Clazz); class<?> parent = Clazz.getsuperclass ();if(Parent! =NULL) {Parents.add (parent); } }returnClasses;}
Then talk about the implementation of the post -specific process:
- 1. Use the
flattenHierarchy
method to deal with the family tree collection of events (self + Parent class collection), return a Set<Class<?>>
collection
- 1.1.
flattenHierarchy
method First look at whether there is a family tree collection of the event in the flattenhierarchycache cache (self + parent class collection)
- The cache of the event exists in 1.2.Flattenhierarchycache and returns directly
- 1.3.Flattenhierarchycache does not exist in the cache of the event, it is called
getClassesFor
to collect the family tree collection of that class (itself + the collection of parent classes), caching a copy to Flattenhierarchycache , and then return
- 2. Walk through this
Set<Class<?>>
collection (Family tree collection), then get the cache collection of the Listener class, then Set<EventHandler>
iterate through the Set<EventHandler>
collection, enter into the enqueueEvent
inside with event + EventHandler wrapped into Eventwithhandler object, and Execute Queue Logic
- 3. Determine if the event has no subscribers and is not a deadevent event. Match, give a deadevent event
- 4. Finally will slip
dispatchQueuedEvents
into the processing event queue
/** * Get the event + the set set of all parent classes of the event */set<class<?>> dispatchtypes = FlattenHierarchy (Event.getclass ());Booleandispatched =false;/** * Iterates over the event + the set set of all parent classes of the event */ for(class<?> eventtype:dispatchtypes) {/** * Get the object all cache EventHandler */set<eventhandler> wrappers = Gethandlersforeventtype (EventType);/** * Start to go through the EventHandler of the cache * and handle the event * Enter enqueueevent logic */ if(Wrappers! =NULL&&!wrappers.isempty ()) {dispatched =true; for(EventHandler wrapper:wrappers) {/** * Event + EventHandler packed into Eventwithhandler * * *Enqueueevent (event, wrapper); } }}/** * According to the above loop can be known * If an object exists a EventHandler * and the post event is not deadevent * The post is executed once (new Deadevent (this, event)) */< /c3>if(!dispatched &&!) (EventinstanceofDeadevent)) {post (NewDeadevent ( This, event));} Dispatchqueuedevents ();
Annotated source code
Annotated source code
52.otto Source Code Analysis