这个问题是我上一个问题我的EventBus系统的后续问题,它包含了@rolfl的回答的大部分要点。
它包括但不限于:
Collections.synchronizedSet优于手动synchronized { }。守则:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Event { }public interface EventBus {
void registerListenersOfObject(final Object callbackObject);
<T> void registerListener(final Class<T> eventClass, final Consumer<? extends T> eventListener);
void executeEvent(final Object event);
void removeListenersOfObject(final Object callbackObject);
<T> void removeListener(final Class<T> eventClass, final Consumer<? extends T> eventListener);
void removeAllListenersOfEvent(final Class<?> eventClass);
void removeAllListeners();
}public class SimpleEventBus implements EventBus {
private final static Set<EventHandler> EMPTY_SET = new HashSet<>();
private final static EventHandler[] EMPTY_ARRAY = new EventHandler[0];
private final ConcurrentMap<Class<?>, Set<EventHandler>> eventMapping = new ConcurrentHashMap<>();
private final Class<?> eventClassConstraint;
public SimpleEventBus() {
this(Object.class);
}
public SimpleEventBus(final Class<?> eventClassConstraint) {
this.eventClassConstraint = Objects.requireNonNull(eventClassConstraint);
}
@Override
public void registerListenersOfObject(final Object callbackObject) {
Arrays.stream(callbackObject.getClass().getMethods())
.filter(this::isEligibleMethod)
.forEach(method -> {
Class<?> eventClass = method.getParameterTypes()[0];
addEventHandler(eventClass, new MethodEventHandler(method, callbackObject, eventClass));
});
}
@Override
@SuppressWarnings("unchecked")
public <T> void registerListener(final Class<T> eventClass, final Consumer<? extends T> eventListener) {
Objects.requireNonNull(eventClass);
Objects.requireNonNull(eventListener);
if (eventClassConstraint.isAssignableFrom(eventClass)) {
addEventHandler(eventClass, new ConsumerEventHandler((Consumer<Object>)eventListener));
}
}
@Override
public void executeEvent(final Object event) {
if (eventClassConstraint.isAssignableFrom(event.getClass())) {
getCopyOfEventHandlers(event.getClass()).forEach(eventHandler -> eventHandler.invoke(event));
}
}
@Override
public void removeListenersOfObject(final Object callbackObject) {
Arrays.stream(callbackObject.getClass().getMethods())
.filter(this::isEligibleMethod)
.forEach(method -> {
Class<?> eventClass = method.getParameterTypes()[0];
removeEventHandler(eventClass, new MethodEventHandler(method, callbackObject, eventClass));
});
}
@Override
@SuppressWarnings("unchecked")
public <T> void removeListener(final Class<T> eventClass, final Consumer<? extends T> eventListener) {
Objects.requireNonNull(eventClass);
Objects.requireNonNull(eventListener);
if (eventClassConstraint.isAssignableFrom(eventClass)) {
removeEventHandler(eventClass, new ConsumerEventHandler((Consumer<Object>)eventListener));
}
}
@Override
public void removeAllListenersOfEvent(final Class<?> eventClass) {
eventMapping.remove(Objects.requireNonNull(eventClass));
}
@Override
public void removeAllListeners() {
eventMapping.clear();
}
private boolean isEligibleMethod(final Method method) {
return (method.getAnnotation(Event.class) != null
&& method.getReturnType().equals(void.class)
&& method.getParameterCount() == 1
&& eventClassConstraint.isAssignableFrom(method.getParameterTypes()[0]));
}
private void addEventHandler(final Class<?> eventClass, final EventHandler eventHandler) {
Objects.requireNonNull(eventClass);
Objects.requireNonNull(eventHandler);
eventMapping.putIfAbsent(eventClass, Collections.synchronizedSet(new HashSet<>()));
eventMapping.get(eventClass).add(eventHandler);
}
private void removeEventHandler(final Class<?> eventClass, final EventHandler eventHandler) {
Objects.requireNonNull(eventClass);
Objects.requireNonNull(eventHandler);
eventMapping.getOrDefault(eventClass, EMPTY_SET).remove(eventHandler);
}
private Stream<EventHandler> getCopyOfEventHandlers(final Class<?> eventClass) {
Set<EventHandler> eventHandlers = eventMapping.get(Objects.requireNonNull(eventClass));
return (eventHandlers == null)
? Stream.empty()
: Arrays.stream(eventHandlers.toArray(EMPTY_ARRAY));
}
private static interface EventHandler {
void invoke(final Object event);
}
private static class MethodEventHandler implements EventHandler {
private final Method method;
private final Object callbackObject;
private final Class<?> eventClass;
public MethodEventHandler(final Method method, final Object object, final Class<?> eventClass) {
this.method = Objects.requireNonNull(method);
this.callbackObject = Objects.requireNonNull(object);
this.eventClass = Objects.requireNonNull(eventClass);
}
@Override
public void invoke(final Object event) {
try {
method.setAccessible(true);
method.invoke(callbackObject, Objects.requireNonNull(event));
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) {
throw new RuntimeException(ex);
}
}
@Override
public int hashCode() {
int hash = 7;
hash = 71 * hash + Objects.hashCode(this.method);
hash = 71 * hash + Objects.hashCode(this.callbackObject);
hash = 71 * hash + Objects.hashCode(this.eventClass);
return hash;
}
@Override
public boolean equals(final Object obj) {
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final MethodEventHandler other = (MethodEventHandler)obj;
if (!Objects.equals(this.method, other.method)) {
return false;
}
if (!Objects.equals(this.callbackObject, other.callbackObject)) {
return false;
}
if (!Objects.equals(this.eventClass, other.eventClass)) {
return false;
}
return true;
}
}
private static class ConsumerEventHandler implements EventHandler {
private final Consumer<Object> eventListener;
public ConsumerEventHandler(final Consumer<Object> consumer) {
this.eventListener = Objects.requireNonNull(consumer);
}
@Override
public void invoke(final Object event) {
eventListener.accept(Objects.requireNonNull(event));
}
@Override
public int hashCode() {
int hash = 5;
hash = 19 * hash + Objects.hashCode(this.eventListener);
return hash;
}
@Override
public boolean equals(final Object obj) {
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final ConsumerEventHandler other = (ConsumerEventHandler)obj;
if (!Objects.equals(this.eventListener, other.eventListener)) {
return false;
}
return true;
}
}
}旧版本的单元测试仍然有效,并且仍然通过。
发布于 2014-05-05 22:29:50
虽然同步现在是正确的,但Collections.synchronizedSet(new HashSet<>()))仍然是一种相当粗粒度的同步方式。您可以用Collections.newSetFromMap(new ConcurrentHashMap<>())替换它,以便从ConcurrentHashMap的更好的锁定策略中获益。
使用Map.computeIfAbsent()在Java8中使用lambdas可以更容易地从Map中获取值,或者在不存在的情况下输入一个值,您可以替换:
eventMapping.putIfAbsent(eventClass, Collections.newSetFromMap(new ConcurrentHashMap<>()));
eventMapping.get(eventClass).add(eventHandler);通过
eventMapping.computeIfAbsent(eventClass, k -> Collections.newSetFromMap(new ConcurrentHashMap<>())).add(eventHandler);https://codereview.stackexchange.com/questions/48970
复制相似问题