Spring高級特性-監聽事件[觀察者模式]

1、定義一個事件

     訂單支付之後的擴展業務,可直接動態擴展不用改動之前任何的代碼

package com.milla.navicat.spring.study.event;

import org.springframework.context.ApplicationEvent;

/**
 * @Package: com.milla.navicat.spring.study.event
 * @Description: <賣出事件[觀察者模式,可擴展業務:推送郵件、推送訂單完成信息、推送物流信息、推送獲取優惠券信息]>
 * @Author: MILLA
 * @CreateDate: 2019/11/1 19:04
 * @UpdateUser: MILLA
 * @UpdateDate: 2019/11/1 19:04
 * @UpdateRemark: <>
 * @Version: 1.0
 */
public class OrderServiceEvent extends ApplicationEvent {
    /**
     * Create a new ApplicationEvent.
     *
     * @param source the object on which the event initially occurred (never {@code null})
     */
    public OrderServiceEvent(Object source) {
        super(source);
    }
}

2、定義監聽器

    2.1 定義郵件監聽器

 

package com.milla.navicat.spring.study.event.listener;

import com.milla.navicat.spring.study.event.OrderServiceEvent;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;

/**
 * @Package: com.milla.navicat.spring.study.event.listener
 * @Description: <發送郵件業務>
 * @Author: MILLA
 * @CreateDate: 2019/11/1 19:07
 * @UpdateUser: MILLA
 * @UpdateDate: 2019/11/1 19:07
 * @UpdateRemark: <>
 * @Version: 1.0
 */
@Component
@Slf4j
public class EmailListener implements ApplicationListener<OrderServiceEvent> {
    @Override
    public void onApplicationEvent(OrderServiceEvent event) {
        Object source = event.getSource();
        log.info("郵件發送服務,參數:{}", source);
        log.info("Email...1.根據帳號獲取用戶郵箱信息");
        log.info("Email...2.發送郵件");

    }
}

    2.2 定義優惠券發放監聽器

package com.milla.navicat.spring.study.event.listener;

import com.milla.navicat.spring.study.event.OrderServiceEvent;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;

/**
 * @Package: com.milla.navicat.spring.study.event.listener
 * @Description: <優惠券業務類>
 * @Author: MILLA
 * @CreateDate: 2019/11/4 16:04
 * @UpdateUser: MILLA
 * @UpdateDate: 2019/11/4 16:04
 * @UpdateRemark: <>
 * @Version: 1.0
 */
@Slf4j
@Component
public class DiscountCouponListener implements ApplicationListener<OrderServiceEvent> {
    @Override
    public void onApplicationEvent(OrderServiceEvent event) {
        Object source = event.getSource();
        log.info("優惠券業務,參數:{}", source);
        log.info("優惠券...1.根據帳號獲取用戶信息");
        log.info("優惠券...2.分發優惠券");
        log.info("優惠券...3.其他優惠券業務");

    }
}

3、發佈事件

package com.milla.navicat.spring.study.service.impl;

import com.google.common.collect.Maps;
import com.milla.navicat.spring.study.event.OrderServiceEvent;
import com.milla.navicat.spring.study.service.CalculateAmountService;
import com.milla.navicat.spring.study.service.OrderService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;

import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

/**
 * @Package: com.milla.navicat.spring.study.service.impl
 * @Description: <六大設計原則>
 * @Author: MILLA
 * @CreateDate: 2019/11/1 16:32
 * @UpdateUser: MILLA
 * @UpdateDate: 2019/11/1 16:32
 * @UpdateRemark: <>
 * @Version: 1.0
 */
@Service
@Slf4j
public class OrderServiceImpl implements OrderService {
    @Autowired
    private Map<String, CalculateAmountService> calculateAmountServiceMap;

    @Autowired
    private ApplicationContext applicationContext;

    @Override
    public double getPaymentAmount(String userType, double amount) {
        log.debug("所有的折扣對象:{}", calculateAmountServiceMap);
        CalculateAmountService service = calculateAmountServiceMap.get(userType);
        if (Objects.isNull(service)) {
            return amount;
        }
        return service.getPaymentAmountByDisCount(amount);
    }

    @Override
    public boolean payment(String userType, double amount, double discountAmount) {
        double paymentAmount = this.getPaymentAmount(userType, amount);
        Assert.isTrue(discountAmount != paymentAmount, "折扣計算有誤");
        HashMap<Object, Object> map = Maps.newHashMap();
        map.put("account", "唯一賬戶信息");//用以獲取業務中需要的信息,比如電話號、郵箱、訂單信息等等
        map.put("userType", userType);
        map.put("payAmount", discountAmount);
        //支付操作;支付成功,做推送業務
        OrderServiceEvent event = new OrderServiceEvent(map);
        applicationContext.publishEvent(event);//發佈事件
        return true;
    }

}

4、只要是監聽了OrderServiceEvent事件的監聽器,都會執行對應的void onApplicationEvent(OrderServiceEvent event)方法,然後執行擴展的業務邏輯

跟蹤代碼執行的順序及邏輯如下:


package org.springframework.context;

/**
 * Interface that encapsulates event publication functionality.
 *
 * <p>Serves as a super-interface for {@link ApplicationContext}.
 *
 * @author Juergen Hoeller
 * @author Stephane Nicoll
 * @since 1.1.1
 * @see ApplicationContext
 * @see ApplicationEventPublisherAware
 * @see org.springframework.context.ApplicationEvent
 * @see org.springframework.context.event.ApplicationEventMulticaster
 * @see org.springframework.context.event.EventPublicationInterceptor
 */
@FunctionalInterface
public interface ApplicationEventPublisher {

	/**
	 * Notify all <strong>matching</strong> listeners registered with this
	 * application of an application event. Events may be framework events
	 * (such as ContextRefreshedEvent) or application-specific events.
	 * <p>Such an event publication step is effectively a hand-off to the
	 * multicaster and does not imply synchronous/asynchronous execution
	 * or even immediate execution at all. Event listeners are encouraged
	 * to be as efficient as possible, individually using asynchronous
	 * execution for longer-running and potentially blocking operations.
	 * @param event the event to publish
	 * @see #publishEvent(Object)
	 * @see org.springframework.context.event.ContextRefreshedEvent
	 * @see org.springframework.context.event.ContextClosedEvent
	 */
	default void publishEvent(ApplicationEvent event) {//1.第一步
		publishEvent((Object) event);
	}

	/**
	 * Notify all <strong>matching</strong> listeners registered with this
	 * application of an event.
	 * <p>If the specified {@code event} is not an {@link ApplicationEvent},
	 * it is wrapped in a {@link PayloadApplicationEvent}.
	 * <p>Such an event publication step is effectively a hand-off to the
	 * multicaster and does not imply synchronous/asynchronous execution
	 * or even immediate execution at all. Event listeners are encouraged
	 * to be as efficient as possible, individually using asynchronous
	 * execution for longer-running and potentially blocking operations.
	 * @param event the event to publish
	 * @since 4.2
	 * @see #publishEvent(ApplicationEvent)
	 * @see PayloadApplicationEvent
	 */
	void publishEvent(Object event);//2.第二步

}
public abstract class AbstractApplicationContext extends DefaultResourceLoader
		implements ConfigurableApplicationContext {
	/**
	 * Publish the given event to all listeners.
	 * <p>Note: Listeners get initialized after the MessageSource, to be able
	 * to access it within listener implementations. Thus, MessageSource
	 * implementations cannot publish events.
	 * @param event the event to publish (may be an {@link ApplicationEvent}
	 * or a payload object to be turned into a {@link PayloadApplicationEvent})
	 */
	@Override
	public void publishEvent(Object event) {//3.第三部
		publishEvent(event, null);
	}

	/**
	 * Publish the given event to all listeners.
	 * @param event the event to publish (may be an {@link ApplicationEvent}
	 * or a payload object to be turned into a {@link PayloadApplicationEvent})
	 * @param eventType the resolved event type, if known
	 * @since 4.2
	 */
	protected void publishEvent(Object event, @Nullable ResolvableType eventType) {//4、第四步
		Assert.notNull(event, "Event must not be null");

		// Decorate event as an ApplicationEvent if necessary
		ApplicationEvent applicationEvent;
		if (event instanceof ApplicationEvent) {
			applicationEvent = (ApplicationEvent) event;
		}
		else {
			applicationEvent = new PayloadApplicationEvent<>(this, event);
			if (eventType == null) {
				eventType = ((PayloadApplicationEvent<?>) applicationEvent).getResolvableType();
			}
		}

		// Multicast right now if possible - or lazily once the multicaster is initialized
		if (this.earlyApplicationEvents != null) {
			this.earlyApplicationEvents.add(applicationEvent);
		}
		else {
                         //校驗的邏輯可以忽略,主要是下面的廣播方法
			getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
		}

		// Publish event via parent context as well...
		if (this.parent != null) {
			if (this.parent instanceof AbstractApplicationContext) {
				((AbstractApplicationContext) this.parent).publishEvent(event, eventType);
			}
			else {
				this.parent.publishEvent(event);
			}
		}
	}
}
public class SimpleApplicationEventMulticaster extends AbstractApplicationEventMulticaster {

	@Nullable
	private Executor taskExecutor;

	@Nullable
	private ErrorHandler errorHandler;
	@Override
	public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
		ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
		Executor executor = getTaskExecutor();
                                               //5、根據事件類型獲取所有的事件列表,然後分別調用監聽器
		for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
			if (executor != null) {
				executor.execute(() -> invokeListener(listener, event));
			}
			else {
				invokeListener(listener, event);
			}
		}
	}

	/**
	 * 6、使用給定的事件調用指定的監聽器
	 * @param listener the ApplicationListener to invoke
	 * @param event the current event to propagate
	 * @since 4.1
	 */
	protected void invokeListener(ApplicationListener<?> listener, ApplicationEvent event) {
		ErrorHandler errorHandler = getErrorHandler();
		if (errorHandler != null) {
			try {
				doInvokeListener(listener, event);
			}
			catch (Throwable err) {
				errorHandler.handleError(err);
			}
		}
		else {
			doInvokeListener(listener, event);
		}
	}
    //7、真正執行調用的方法
	private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
		try {
            //調用自定義監聽器的onApplicationEvent方法
			listener.onApplicationEvent(event);
		}
		catch (ClassCastException ex) {
			String msg = ex.getMessage();
			if (msg == null || matchesClassCastMessage(msg, event.getClass())) {
				// Possibly a lambda-defined listener which we could not resolve the generic event type for
				// -> let's suppress the exception and just log a debug message.
				Log logger = LogFactory.getLog(getClass());
				if (logger.isTraceEnabled()) {
					logger.trace("Non-matching event type for listener: " + listener, ex);
				}
			}
			else {
				throw ex;
			}
		}
	}
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章