淺談Spring事件監聽
聲明:筆者以下所有的代碼和實驗都是基於Spring boot 的 2.0.0.RELEASE 版本。
另外筆者能力有限,文中但凡有不對或者用詞不當之處,望不吝指出。
在談Spring的事件監聽之前,讓我們先了解一下Spring容器,什麼是ApplicationContext ?
它是Spring的核心,Context我們通常解釋為上下文環境,但是理解成容器會更好些。
ApplicationContext則是應用的容器。
Spring把Bean(object)放在容器中,需要用就通過get方法取出來。
下面我們結合源碼注視和ApplicationContext的類圖來看看它到底提供給我們哪些能力
源碼:
/*
* Copyright 2002-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.context;
import org.springframework.beans.factory.HierarchicalBeanFactory;
import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.core.env.EnvironmentCapable;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.lang.Nullable;
/**
* Central interface to provide configuration for an application.
* This is read-only while the application is running, but may be
* reloaded if the implementation supports this.
*
* <p>An ApplicationContext provides:
* <ul>
* <li>Bean factory methods for accessing application components.
* Inherited from {@link org.springframework.beans.factory.ListableBeanFactory}.
* <li>The ability to load file resources in a generic fashion.
* Inherited from the {@link org.springframework.core.io.ResourceLoader} interface.
* <li>The ability to publish events to registered listeners.
* Inherited from the {@link ApplicationEventPublisher} interface.
* <li>The ability to resolve messages, supporting internationalization.
* Inherited from the {@link MessageSource} interface.
* <li>Inheritance from a parent context. Definitions in a descendant context
* will always take priority. This means, for example, that a single parent
* context can be used by an entire web application, while each servlet has
* its own child context that is independent of that of any other servlet.
* </ul>
*
* <p>In addition to standard {@link org.springframework.beans.factory.BeanFactory}
* lifecycle capabilities, ApplicationContext implementations detect and invoke
* {@link ApplicationContextAware} beans as well as {@link ResourceLoaderAware},
* {@link ApplicationEventPublisherAware} and {@link MessageSourceAware} beans.
*
* @author Rod Johnson
* @author Juergen Hoeller
* @see ConfigurableApplicationContext
* @see org.springframework.beans.factory.BeanFactory
* @see org.springframework.core.io.ResourceLoader
*/
public interface ApplicationContext extends EnvironmentCapable, ListableBeanFactory, HierarchicalBeanFactory,
MessageSource, ApplicationEventPublisher, ResourcePatternResolver {
/**
* Return the unique id of this application context.
* @return the unique id of the context, or {@code null} if none
*/
@Nullable
String getId();
/**
* Return a name for the deployed application that this context belongs to.
* @return a name for the deployed application, or the empty String by default
*/
String getApplicationName();
/**
* Return a friendly name for this context.
* @return a display name for this context (never {@code null})
*/
String getDisplayName();
/**
* Return the timestamp when this context was first loaded.
* @return the timestamp (ms) when this context was first loaded
*/
long getStartupDate();
/**
* Return the parent context, or {@code null} if there is no parent
* and this is the root of the context hierarchy.
* @return the parent context, or {@code null} if there is no parent
*/
@Nullable
ApplicationContext getParent();
/**
* Expose AutowireCapableBeanFactory functionality for this context.
* <p>This is not typically used by application code, except for the purpose of
* initializing bean instances that live outside of the application context,
* applying the Spring bean lifecycle (fully or partly) to them.
* <p>Alternatively, the internal BeanFactory exposed by the
* {@link ConfigurableApplicationContext} interface offers access to the
* {@link AutowireCapableBeanFactory} interface too. The present method mainly
* serves as a convenient, specific facility on the ApplicationContext interface.
* <p><b>NOTE: As of 4.2, this method will consistently throw IllegalStateException
* after the application context has been closed.</b> In current Spring Framework
* versions, only refreshable application contexts behave that way; as of 4.2,
* all application context implementations will be required to comply.
* @return the AutowireCapableBeanFactory for this context
* @throws IllegalStateException if the context does not support the
* {@link AutowireCapableBeanFactory} interface, or does not hold an
* autowire-capable bean factory yet (e.g. if {@code refresh()} has
* never been called), or if the context has been closed already
* @see ConfigurableApplicationContext#refresh()
* @see ConfigurableApplicationContext#getBeanFactory()
*/
AutowireCapableBeanFactory getAutowireCapableBeanFactory() throws IllegalStateException;
}
類圖:
由源碼注釋:
此介面提供給Spring應用配置的能力,當應用啟動時,此介面的實現是只讀的,但是如果該實現支持,其內容也是可以重新載入的。
ApplicationContext大概提功能如下能力:
1.獲取應用組件的bean工廠方法,此能力繼承自org.springframework.beans.factory.ListableBeanFactory。
2.載入資源文件的能力,此能力繼承自org.springframework.core.io.ResourceLoader
3.發布事件到已註冊的監聽器,此能力繼承自ApplicationEventPublisher
4.提供國際化的消息訪問,此能力繼承自MessageSource
好對ApplicationContext有一定了解之後我們再來看看Spring提供的事件監聽。
為了實現事件監聽的能力Spring為我們提供了兩個頂層介面/抽象類
ApplcationEvent:是個抽象類,裡面只有一個構造函數和一個長整型的timestamp。我們自定義的Application event 需要繼承這個抽象類.
ApplicationListener:是一個介面,裡面只有一個方法onApplicationEvent ,每一個實現改介面的類都要自己實現這個方法。
Spring的事件監聽是基於標準的觀察者模式的,如果在ApplicationContext部署了一個實現了ApplicationListener的bean,那麼當一個ApplicationEvent發布到
ApplicationContext時,這個bean得到通知並作特定的處理。
從上面這段話我們很容易產生兩點思考:1.實現了ApplicationListener的bean如何部署到ApplicationContext 2.一個ApplicationEvent如何發布到ApplicationContext
下面我們就通過具體的代碼來看看這兩個問題
廢話少說,先看代碼!
先自定義一個MsgEvent,它本身提供一個print()方法:
package com.snow.event;
import org.springframework.context.ApplicationEvent;
/**
* Created by snowwolf-louis on 18/3/15.
*/
public class MsgEvent extends ApplicationEvent {
private String text;
public MsgEvent(Object source) {
super(source);
}
public MsgEvent(Object source, String text) {
super(source);
this.text = text;
}
public void print(){
System.out.println("print even content:" + this.text);
}
}
再自定義一個PringListener實現ApplicationListener:
package com.snow.listener;
import com.snow.event.MsgEvent;
import org.springframework.context.ApplicationListener;
/**
* Created by snowwolf-louis on 18/3/15.
*/
public class PrintListener implements ApplicationListener<MsgEvent> {
/**
* Handle an application event.
*
* @param event the event to respond to
*/
@Override
public void onApplicationEvent(MsgEvent event) {
System.out.print("調用MsgEvent的print方法輸出其內容:");
event.print();
}
}
現在自定義事件和監聽器都好了,我們就來看看第一個問題,監聽器如何部署到ApplicationContext,有四種方式可以實現,我們一個一個看:
1.應用啟動之前調用SpringApplication的addListeners方法將自定義的監聽器加入。
package com.snow;
import com.snow.event.MsgEvent;
import com.snow.listener.PrintListener;
import com.snow.publisher.SnowEventPublisher;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@SpringBootApplication
@RestController
public class SnowComponentApplication {
public static void main(String[] args) {
//創建SpringApplication
SpringApplication application = new SpringApplication(SnowComponentApplication.class);
//配置事件監聽
application.addListeners(new PrintListener());
//啟動應用並獲得上下文信息
ConfigurableApplicationContext context = application.run(args);
//發布事件
context.publishEvent(new MsgEvent(new Object(), "你好!"));
context.close();
}
}
運行結果:
2.監聽器部署到ApplicationContext,實際上就是將將監聽器交給Spring 容器管理,所以最簡單的方法只需在自定義的PrintListener上加上@Component註解就行了,代碼如下
package com.snow.listener;
import com.snow.event.MsgEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;
/**
* Created by snowwolf-louis on 18/3/15.
*/
@Component
public class PrintListener implements ApplicationListener<MsgEvent> {
/**
* Handle an application event.
*
* @param event the event to respond to
*/
@Override
public void onApplicationEvent(MsgEvent event) {
System.out.print("調用MsgEvent的print方法輸出其內容:");
event.print();
}
}
啟動處的代碼注掉配置事件監聽一行,如下:
package com.snow;
import com.snow.event.MsgEvent;
import com.snow.listener.PrintListener;
import com.snow.publisher.SnowEventPublisher;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@SpringBootApplication
@RestController
public class SnowComponentApplication {
public static void main(String[] args) {
//創建SpringApplication
SpringApplication application = new SpringApplication(SnowComponentApplication.class);
//配置事件監聽
//application.addListeners(new PrintListener());
//啟動應用並獲得上下文信息
ConfigurableApplicationContext context = application.run(args);
//發布事件
context.publishEvent(new MsgEvent(new Object(), "你好!"));
context.close();
}
}
啟動運行,結果如下:
3.在配置文件中加入context.listener.classes: com.snow.listener.PrintListener
注釋掉PrintListener上的@Component註解
源碼分析:
Spring內置DelegatingApplicationListener會監聽ApplicationEnvironmentPreparedEvent事件(Environment已經準備好但是Context還沒有創建事件),
讀取Environment中的context.listener.classes屬性值(監聽器類全路徑,多個以逗號隔開)來創建ApplicationListener,詳情見如下源碼中我的注釋。
/*
* Copyright 2012-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.context.config;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.springframework.beans.BeanUtils;
import org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent;
import org.springframework.context.ApplicationContextException;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.SimpleApplicationEventMulticaster;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;
/**
* {@link ApplicationListener} that delegates to other listeners that are specified under
* a {@literal context.listener.classes} environment property.
*
* @author Dave Syer
* @author Phillip Webb
*/
public class DelegatingApplicationListener
implements ApplicationListener<ApplicationEvent>, Ordered {
// NOTE: Similar to org.springframework.web.context.ContextLoader
private static final String PROPERTY_NAME = "context.listener.classes」;//配置文件中配置的配置
private int order = 0;
private SimpleApplicationEventMulticaster multicaster;
@Override
public void onApplicationEvent(ApplicationEvent event) {
if (event instanceof ApplicationEnvironmentPreparedEvent) {//監聽ApplicationEnvironmentPreparedEvent事件
List<ApplicationListener<ApplicationEvent>> delegates = getListeners(
((ApplicationEnvironmentPreparedEvent) event).getEnvironment());
if (delegates.isEmpty()) {
return;
}
this.multicaster = new SimpleApplicationEventMulticaster();
for (ApplicationListener<ApplicationEvent> listener : delegates) {
this.multicaster.addApplicationListener(listener);//將監聽事件加入spring容器中
}
}
if (this.multicaster != null) {
this.multicaster.multicastEvent(event);
}
}
@SuppressWarnings("unchecked")
private List<ApplicationListener<ApplicationEvent>> getListeners(
ConfigurableEnvironment environment) {
if (environment == null) {
return Collections.emptyList();
}
String classNames = environment.getProperty(PROPERTY_NAME);//獲取配置文件中配置的監聽器
List<ApplicationListener<ApplicationEvent>> listeners = new ArrayList<>();
if (StringUtils.hasLength(classNames)) {
for (String className : StringUtils.commaDelimitedListToSet(classNames)) {
try {
Class<?> clazz = ClassUtils.forName(className,
ClassUtils.getDefaultClassLoader());
Assert.isAssignable(ApplicationListener.class, clazz, "class ["
+ className + "] must implement ApplicationListener");
listeners.add((ApplicationListener<ApplicationEvent>) BeanUtils
.instantiateClass(clazz));
}
catch (Exception ex) {
throw new ApplicationContextException(
"Failed to load context listener class [" + className + "]",
ex);
}
}
}
AnnotationAwareOrderComparator.sort(listeners);
return listeners;
}
public void setOrder(int order) {
this.order = order;
}
@Override
public int getOrder() {
return this.order;
}
}
運行結果:
4.使用@EventListener註解,先看代碼,建立一個普通的java類並交給spring容器,其中一個處理event的方法,加上該註解,刪掉配置文件中的配置。
package com.snow.handle;
import com.snow.event.MsgEvent;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
/**
* Created by snowwolf-louis on 18/3/17.
*/
@Component
public class MsgEventHandle {
@EventListener
public void handle(MsgEvent event){
event.print();
}
}
運行結果:
源碼分析:
我們首先看@EventListener註解的源碼,裡面有這樣一段注釋:
它指引我們去看EventListenerMethodProcessor類,這個類是application啟動時自動註冊執行的。該類的功能是掃描@EventListener註解並生成一個ApplicationListener實例。
其中有個這樣的方法:就是用來掃描容器中bean的方法上所有的@EventListener,循環創建ApplicationListener。
protected void processBean(
final List<EventListenerFactory> factories, final String beanName, final Class<?> targetType) {
if (!this.nonAnnotatedClasses.contains(targetType)) {
Map<Method, EventListener> annotatedMethods = null;
try {
annotatedMethods = MethodIntrospector.selectMethods(targetType,
(MethodIntrospector.MetadataLookup<EventListener>) method ->
AnnotatedElementUtils.findMergedAnnotation(method, EventListener.class));
}
catch (Throwable ex) {
// An unresolvable type in a method signature, probably from a lazy bean - let"s ignore it.
if (logger.isDebugEnabled()) {
logger.debug("Could not resolve methods for bean with name "" + beanName + """, ex);
}
}
if (CollectionUtils.isEmpty(annotatedMethods)) {
this.nonAnnotatedClasses.add(targetType);
if (logger.isTraceEnabled()) {
logger.trace("No @EventListener annotations found on bean class: " + targetType.getName());
}
}
else {
// Non-empty set of methods
ConfigurableApplicationContext context = getApplicationContext();
for (Method method : annotatedMethods.keySet()) {
for (EventListenerFactory factory : factories) {
if (factory.supportsMethod(method)) {
Method methodToUse = AopUtils.selectInvocableMethod(method, context.getType(beanName));
ApplicationListener<?> applicationListener =
factory.createApplicationListener(beanName, targetType, methodToUse);
if (applicationListener instanceof ApplicationListenerMethodAdapter) {
((ApplicationListenerMethodAdapter) applicationListener).init(context, this.evaluator);
}
context.addApplicationListener(applicationListener);
break;
}
}
}
if (logger.isDebugEnabled()) {
logger.debug(annotatedMethods.size() + " @EventListener methods processed on bean "" +
beanName + "": " + annotatedMethods);
}
}
}
}
好以上四點就回答了我們開頭提出的第一個問題:實現了ApplicationListener的bean如何部署到ApplicationContext。接下來輪到第二個問題了,
一個ApplicationEvent如何發布到ApplicationContext。我們還是來看測試的主代碼:
package com.snow;
import com.snow.event.MsgEvent;
import com.snow.listener.PrintListener;
import com.snow.publisher.SnowEventPublisher;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@SpringBootApplication
@RestController
public class SnowComponentApplication {
public static void main(String[] args) {
//創建SpringApplication
SpringApplication application = new SpringApplication(SnowComponentApplication.class);
//配置事件監聽
//application.addListeners(new PrintListener());
//啟動應用並獲得上下文信息
ConfigurableApplicationContext context = application.run(args);
//發布事件
context.publishEvent(new MsgEvent(new Object(), "你好!"));
context.close();
}
}
上面的這段代碼可以看出ConfigurableApplicationContext具有發布事件的能力,而通過下面的類圖我們知道ConfigurableApplicationContext是
ApplicationContext的子介面,文章開頭對ApplicationContext的分析我們知道ApplicationContext具有發布事件的能力,這項能力是通過繼承
ApplicationEventPublisher獲得的。
通過以上分析,我們不難發現,只要我們在一個普通的java bean注入ApplicationContext的實例,那麼這個bean就獲得了發布事件的能力。
那麼怎樣才能在一個普通的bean中注入ApplicationContext的實例呢?看ApplicationContext的源碼,源碼中有這一段注釋:
這段注釋大概的意思是ApplicationContext的實例可以探測和喚起AppliationContextAware ,ResourceLoaderAware,ApplicationEventPublisherAware,MessageSourceAware等,
我們順勢看下AppliationContextAware的代碼,如下:
/*
* Copyright 2002-2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.context;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.Aware;
/**
* Interface to be implemented by any object that wishes to be notified
* of the {@link ApplicationContext} that it runs in.
*
* <p>Implementing this interface makes sense for example when an object
* requires access to a set of collaborating beans. Note that configuration
* via bean references is preferable to implementing this interface just
* for bean lookup purposes.
*
* <p>This interface can also be implemented if an object needs access to file
* resources, i.e. wants to call {@code getResource}, wants to publish
* an application event, or requires access to the MessageSource. However,
* it is preferable to implement the more specific {@link ResourceLoaderAware},
* {@link ApplicationEventPublisherAware} or {@link MessageSourceAware} interface
* in such a specific scenario.
*
* <p>Note that file resource dependencies can also be exposed as bean properties
* of type {@link org.springframework.core.io.Resource}, populated via Strings
* with automatic type conversion by the bean factory. This removes the need
* for implementing any callback interface just for the purpose of accessing
* a specific file resource.
*
* <p>{@link org.springframework.context.support.ApplicationObjectSupport} is a
* convenience base class for application objects, implementing this interface.
*
* <p>For a list of all bean lifecycle methods, see the
* {@link org.springframework.beans.factory.BeanFactory BeanFactory javadocs}.
*
* @author Rod Johnson
* @author Juergen Hoeller
* @author Chris Beams
* @see ResourceLoaderAware
* @see ApplicationEventPublisherAware
* @see MessageSourceAware
* @see org.springframework.context.support.ApplicationObjectSupport
* @see org.springframework.beans.factory.BeanFactoryAware
*/
public interface ApplicationContextAware extends Aware {
/**
* Set the ApplicationContext that this object runs in.
* Normally this call will be used to initialize the object.
* <p>Invoked after population of normal bean properties but before an init callback such
* as {@link org.springframework.beans.factory.InitializingBean#afterPropertiesSet()}
* or a custom init-method. Invoked after {@link ResourceLoaderAware#setResourceLoader},
* {@link ApplicationEventPublisherAware#setApplicationEventPublisher} and
* {@link MessageSourceAware}, if applicable.
* @param applicationContext the ApplicationContext object to be used by this object
* @throws ApplicationContextException in case of context initialization errors
* @throws BeansException if thrown by application context methods
* @see org.springframework.beans.factory.BeanInitializationException
*/
void setApplicationContext(ApplicationContext applicationContext) throws BeansException;
}
通過ApplicationContextAware的源碼我們可以看出其有一個setApplicationContext 方法可以將ApplicationContext實例注入,下面我們
寫一個java類去實現 ApplicationContextAware看看。代碼如下:
package com.snow;
import com.snow.event.MsgEvent;
import com.snow.listener.PrintListener;
import com.snow.publisher.SnowApplcationContextAware;
import com.snow.publisher.SnowEventPublisher;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@SpringBootApplication
@RestController
public class SnowComponentApplication {
public static void main(String[] args) {
//創建SpringApplication
SpringApplication application = new SpringApplication(SnowComponentApplication.class);
//配置事件監聽
//application.addListeners(new PrintListener());
//啟動應用並獲得上下文信息
ConfigurableApplicationContext context = application.run(args);
//發布事件
//context.publishEvent(new MsgEvent(new Object(), "你好!"));
//context.close();
}
@Autowired
private SnowApplcationContextAware applcationContextAware;
@RequestMapping(value = "/hello")
public String sayHello(){
applcationContextAware.publish(new MsgEvent(new Object(),"有人訪問我,我得跟他們說:hello"));
return "hello";
}
}
運行代碼,打開瀏覽器訪問http:localhost:8080/hello,結果如下:
控制台信息如下:
通過這種方式事件的確發布了。
--難道這樣就完了嗎?
--嗯,別人的博客也許這樣就完了,但是雪夜蒼狼的不會。
我們注意到ApplicationContext的事件發布能力是繼承自ApplicationEventPublisher,並且ApplicationContextAware中有這樣一段注釋:
這段注釋可以看出,如果僅僅想獲取發布事件的能力,只需要實現ApplicationEventPublisherAware就行了。
照著這樣的思路代碼如下:定義一個事件發布器
package com.snow.publisher;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.stereotype.Component;
/**
* Created by snowwolf-louis on 18/3/17.
*/
@Component
public class SnowEventPublisher implements ApplicationEventPublisherAware {
private ApplicationEventPublisher publisher;
@Override
public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
this.publisher = applicationEventPublisher;
}
public void publish(ApplicationEvent event){
publisher.publishEvent(event);
}
}
主測試代碼改成這樣:
package com.snow;
import com.snow.event.MsgEvent;
import com.snow.listener.PrintListener;
import com.snow.publisher.SnowApplcationContextAware;
import com.snow.publisher.SnowEventPublisher;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@SpringBootApplication
@RestController
public class SnowComponentApplication {
public static void main(String[] args) {
//創建SpringApplication
SpringApplication application = new SpringApplication(SnowComponentApplication.class);
//配置事件監聽
//application.addListeners(new PrintListener());
//啟動應用並獲得上下文信息
ConfigurableApplicationContext context = application.run(args);
//發布事件
//context.publishEvent(new MsgEvent(new Object(), "你好!"));
//context.close();
}
// @Autowired
// private SnowApplcationContextAware applcationContextAware;
@Autowired
private SnowEventPublisher snowEventPublisher;
@RequestMapping(value = "/hello")
public String sayHello(){
snowEventPublisher.publish(new MsgEvent(new Object(),"有人訪問我,我得跟他們說:hello"));
return "hello";
}
}
運行代碼,打開瀏覽器訪問http:localhost:8080/hello,結果如下:
控制台依然列印了如下信息,證明我們的猜想沒錯。
※求助:threejs+qml+json模型載入失敗
※SpringBoot:SpringDataRedis緩存改造
TAG:程序員小新人學習 |