zoukankan      html  css  js  c++  java
  • Spring源码之Springboot中监听器介绍

    https://www.bilibili.com/video/BV12C4y1s7dR?p=11

    监听器模式要素

    • 事件
    • 监听器
    • 广播器
    • 触发机制

    Springboot中监听模式总结

    1. 在SpringApplication初始化中从META_INF/spring.factories获取Listeners
    2. 创建监听器
    3. 创建广播器
    4. 将监听器在广播器中进行注册
    5. 事件触发时,监听器监听到,同时将创建的事件传给广播器,执行广播器,通过回调调用实际事件中方法

    Springboot中监听器各要素

    事件
    public abstract class ApplicationEvent extends EventObject {
    
    	/** use serialVersionUID from Spring 1.2 for interoperability. */
    	private static final long serialVersionUID = 7099057708183571937L;
    
    	/** System time when the event happened. */
    	private final long timestamp;
    
    
    	/**
    	 * Create a new {@code ApplicationEvent}.
    	 * @param source the object on which the event initially occurred or with
    	 * which the event is associated (never {@code null})
    	 */
    	public ApplicationEvent(Object source) {
    		super(source);
    		this.timestamp = System.currentTimeMillis();
    	}
    
    
    	/**
    	 * Return the system time in milliseconds when the event occurred.
    	 */
    	public final long getTimestamp() {
    		return this.timestamp;
    	}
    
    }
    
    
    监听器
    @FunctionalInterface
    public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
    
    	/**
    	 * Handle an application event.
    	 * @param event the event to respond to
    	 */
    	void onApplicationEvent(E event);
    
    }
    
    广播器
    public interface ApplicationEventMulticaster {
    	void addApplicationListener(ApplicationListener<?> listener);
    	void addApplicationListenerBean(String listenerBeanName);
    	void removeApplicationListener(ApplicationListener<?> listener);
    	void removeApplicationListenerBean(String listenerBeanName);
    	void removeAllListeners();
    	void multicastEvent(ApplicationEvent event);
    	void multicastEvent(ApplicationEvent event, @Nullable ResolvableType eventType);
    }
    
    
    事件触发,如:

    SpringAppliation中listeners.starting();

    源码梳理

    在SpringApplication初始化中setListeners

    在SpringApplication构造函数中,执行setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));

    public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
    	this.resourceLoader = resourceLoader;
    	Assert.notNull(primarySources, "PrimarySources must not be null");
    	this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
    	this.webApplicationType = WebApplicationType.deduceFromClasspath();
    	setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
    	setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
    	this.mainApplicationClass = deduceMainApplicationClass();
    }
    

    执行SpringFactoriesLoader类中loadSpringFactories方法,将jar包中所有META_INF/spring.factories文件中的属性进行加载(在setInitializers中已经加载,所以在setListeners直接从MultiValueMap缓存中取)

    Enumeration<URL> urls = (classLoader != null ?
    		classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
    		ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
    

    调用链:

    SpringApplication#init() --> SpringApplication#getSpringFactoriesInstances() --> SpringFactoriesLoader#loadFactoryNames() --> SpringFactoriesLoader#loadSpringFactories()

    loadSpringFactories代码:

    	private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
    		MultiValueMap<String, String> result = cache.get(classLoader);
    		if (result != null) {
    			return result;
    		}
    
    		try {
    			Enumeration<URL> urls = (classLoader != null ?
    					classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
    					ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
    			result = new LinkedMultiValueMap<>();
    			while (urls.hasMoreElements()) {
    				URL url = urls.nextElement();
    				UrlResource resource = new UrlResource(url);
    				Properties properties = PropertiesLoaderUtils.loadProperties(resource);
    				for (Map.Entry<?, ?> entry : properties.entrySet()) {
    					String factoryTypeName = ((String) entry.getKey()).trim();
    					for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
    						result.add(factoryTypeName, factoryImplementationName.trim());
    					}
    				}
    			}
    			cache.put(classLoader, result);
    			return result;
    		}
    		catch (IOException ex) {
    			throw new IllegalArgumentException("Unable to load factories from location [" +
    					FACTORIES_RESOURCE_LOCATION + "]", ex);
    		}
    	}
    

    调用createSpringFactoriesInstances方法进行实例化

    private <T> List<T> createSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes,
    		ClassLoader classLoader, Object[] args, Set<String> names) {
    	List<T> instances = new ArrayList<>(names.size());
    	for (String name : names) {
    		try {
    			Class<?> instanceClass = ClassUtils.forName(name, classLoader);
    			Assert.isAssignable(type, instanceClass);
    			Constructor<?> constructor = instanceClass.getDeclaredConstructor(parameterTypes);
    			T instance = (T) BeanUtils.instantiateClass(constructor, args);
    			instances.add(instance);
    		}
    		catch (Throwable ex) {
    			throw new IllegalArgumentException("Cannot instantiate " + type + " : " + name, ex);
    		}
    	}
    	return instances;
    }
    

    将获取到的listeners注册到List<ApplicationListener<?>> listeners中

    public void setListeners(Collection<? extends ApplicationListener<?>> listeners) {
    	this.listeners = new ArrayList<>(listeners);
    }
    
    获取SpringApplicationRunListener
    SpringApplicationRunListeners listeners = getRunListeners(args);
    
    private SpringApplicationRunListeners getRunListeners(String[] args) {
    	Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
    	return new SpringApplicationRunListeners(logger,
    			getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args));
    }
    

    key为org.springframework.boot.SpringApplicationRunListener,从之前缓存的map中取出SpringApplicationRunListener,value为EventPublishingRunListener

    对EventPublishingRunListener进行初始化,创建SimpleApplicationEventMulticaster广播器

    public EventPublishingRunListener(SpringApplication application, String[] args) {
    	this.application = application;
    	this.args = args;
    	this.initialMulticaster = new SimpleApplicationEventMulticaster();
    	for (ApplicationListener<?> listener : application.getListeners()) {
    		this.initialMulticaster.addApplicationListener(listener);
    	}
    }
    

    将监听器注册到广播器中:

    for (ApplicationListener<?> listener : application.getListeners()) {
    	this.initialMulticaster.addApplicationListener(listener);
    }
    
    事件触发

    SpringApplication中listeners.starting();
    执行到EventPublishingRunListener中

    @Override
    public void starting() {
    	this.initialMulticaster.multicastEvent(new ApplicationStartingEvent(this.application, this.args));
    }
    

    进行广播事件:

    @Override
    public void multicastEvent(ApplicationEvent event) {
    	multicastEvent(event, resolveDefaultEventType(event));
    }
    
    @Override
    public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
    	ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
    	Executor executor = getTaskExecutor();
    	for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
    		if (executor != null) {
    			executor.execute(() -> invokeListener(listener, event));
    		}
    		else {
    			invokeListener(listener, event);
    		}
    	}
    }
    

    然后去调用具体事件的onApplicationEvent方法

    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出。
  • 相关阅读:
    QTP知识总结(一)
    QTP中DataTable操作大全
    QTP DataTable全攻略(1)
    QTP脚本不能录制怎么办?
    每天一个linux命令(20):find命令之exec
    bash下几个替换运算符的区分
    linux_shell 特殊符号的介绍
    Linux dirname、basename 指令
    Liunx readlink命令
    微信公众账号开发教程(四)自定义菜单(转)
  • 原文地址:https://www.cnblogs.com/caozibiao/p/13964994.html
Copyright © 2011-2022 走看看