spring 如何为应用程序上下文初始化事件添加一个钩子?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/8686507/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to add a hook to the application context initialization event?
提问by teddy teddy
For a regular Servlet, I guess you could declare a context listener, but for Spring MVC would Spring make this any easier?
对于常规 Servlet,我想您可以声明一个上下文侦听器,但是对于 Spring MVC,Spring 会使这变得更容易吗?
Furthermore, if I define a context listener and then would need to access the beans defined in my servlet.xmlor applicationContext.xml, how would I get access to them?
此外,如果我定义了一个上下文侦听器,然后需要访问在我的servlet.xmlor 中定义的 bean,我applicationContext.xml将如何访问它们?
回答by Bogdan
Spring has some standard events which you can handle.
To do that, you must create and register a bean that implements the ApplicationListenerinterface, something like this:
为此,您必须创建并注册一个实现ApplicationListener接口的 bean,如下所示:
package test.pack.age;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
public class ApplicationListenerBean implements ApplicationListener {
@Override
public void onApplicationEvent(ApplicationEvent event) {
if (event instanceof ContextRefreshedEvent) {
ApplicationContext applicationContext = ((ContextRefreshedEvent) event).getApplicationContext();
// now you can do applicationContext.getBean(...)
// ...
}
}
}
You then register this bean within your servlet.xmlor applicationContext.xmlfile:
然后您在您的servlet.xml或applicationContext.xml文件中注册此 bean :
<bean id="eventListenerBean" class="test.pack.age.ApplicationListenerBean" />
and Spring will notify it when the application context is initialized.
当应用程序上下文被初始化时,Spring 会通知它。
In Spring 3 (if you are using this version), the ApplicationListenerclass is genericand you can declare the event type that you are interested in, and the event will be filtered accordingly. You can simplify a bit your bean code like this:
在 Spring 3 中(如果你使用这个版本),ApplicationListener类是泛型的,你可以声明你感兴趣的事件类型,事件会被相应地过滤。您可以像这样简化 bean 代码:
public class ApplicationListenerBean implements ApplicationListener<ContextRefreshedEvent> {
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
ApplicationContext applicationContext = event.getApplicationContext();
// now you can do applicationContext.getBean(...)
// ...
}
}
回答by David Groomes
Since Spring 4.2 you can use @EventListener(documentation)
从 Spring 4.2 开始,您可以使用@EventListener(文档)
@Component
class MyClassWithEventListeners {
@EventListener({ContextRefreshedEvent.class})
void contextRefreshedEvent() {
System.out.println("a context refreshed event happened");
}
}
回答by Adeptius
Create your annotation
创建您的注释
@Retention(RetentionPolicy.RUNTIME)
public @interface AfterSpringLoadComplete {
}
Create class
创建类
public class PostProxyInvokerContextListener implements ApplicationListener<ContextRefreshedEvent> {
@Autowired
ConfigurableListableBeanFactory factory;
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
ApplicationContext context = event.getApplicationContext();
String[] names = context.getBeanDefinitionNames();
for (String name : names) {
try {
BeanDefinition definition = factory.getBeanDefinition(name);
String originalClassName = definition.getBeanClassName();
Class<?> originalClass = Class.forName(originalClassName);
Method[] methods = originalClass.getMethods();
for (Method method : methods) {
if (method.isAnnotationPresent(AfterSpringLoadComplete.class)){
Object bean = context.getBean(name);
Method currentMethod = bean.getClass().getMethod(method.getName(), method.getParameterTypes());
currentMethod.invoke(bean);
}
}
} catch (Exception ignored) {
}
}
}
}
Register this class by @Component annotation or in xml
通过@Component注解或者在xml中注册这个类
<bean class="ua.adeptius.PostProxyInvokerContextListener"/>
and use annotation where you wan on any method that you want to run after context initialized, like:
并在您想要在上下文初始化后运行的任何方法上使用注释,例如:
@AfterSpringLoadComplete
public void init() {}
回答by Amit Singh
I had a single page application on entering URL it was creating a HashMap (used by my webpage) which contained data from multiple databases. I did following things to load everything during server start time-
我有一个输入 URL 的单页应用程序,它正在创建一个 HashMap(由我的网页使用),其中包含来自多个数据库的数据。我做了以下事情来在服务器启动期间加载所有内容-
1- Created ContextListenerClass
1- 创建 ContextListenerClass
public class MyAppContextListener implements ServletContextListener
@Autowired
private MyDataProviderBean myDataProviderBean;
public MyDataProviderBean getMyDataProviderBean() {
return MyDataProviderBean;
}
public void setMyDataProviderBean(MyDataProviderBean MyDataProviderBean) {
this.myDataProviderBean = MyDataProviderBean;
}
@Override
public void contextDestroyed(ServletContextEvent arg0) {
System.out.println("ServletContextListener destroyed");
}
@Override
public void contextInitialized(ServletContextEvent context) {
System.out.println("ServletContextListener started");
ServletContext sc = context.getServletContext();
WebApplicationContext springContext = WebApplicationContextUtils.getWebApplicationContext(sc);
MyDataProviderBean MyDataProviderBean = (MyDataProviderBean)springContext.getBean("myDataProviderBean");
Map<String, Object> myDataMap = MyDataProviderBean.getDataMap();
sc.setAttribute("myMap", myDataMap);
}
2- Added below entry in web.xml
2- 在 web.xml 中添加以下条目
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<listener>
<listener-class>com.context.listener.MyAppContextListener</listener-class>
</listener>
3- In my Controller Class updated code to first check for Map in servletContext
3- 在我的控制器类中更新代码以首先检查 servletContext 中的 Map
@RequestMapping(value = "/index", method = RequestMethod.GET)
public String index(@ModelAttribute("model") ModelMap model) {
Map<String, Object> myDataMap = new HashMap<String, Object>();
if (context != null && context.getAttribute("myMap")!=null)
{
myDataMap=(Map<String, Object>)context.getAttribute("myMap");
}
else
{
myDataMap = myDataProviderBean.getDataMap();
}
for (String key : myDataMap.keySet())
{
model.addAttribute(key, myDataMap.get(key));
}
return "myWebPage";
}
With this much change when I start my tomcat it loads dataMap during startTime and puts everything in servletContext which is then used by Controller Class to get results from already populated servletContext .
当我启动我的 tomcat 时,有了这么多变化,它会在 startTime 期间加载 dataMap 并将所有内容放在 servletContext 中,然后控制器类使用它从已经填充的 servletContext 中获取结果。
回答by Dilip Dumania
Please follow below step to do some processing after Application Context get loaded i.e application is ready to serve.
请按照以下步骤在应用程序上下文加载后进行一些处理,即应用程序已准备好提供服务。
Create below annotation i.e
@Retention(RetentionPolicy.RUNTIME) @Target(value= {ElementType.METHOD, ElementType.TYPE}) public @interface AfterApplicationReady {}
创建以下注释即
@Retention(RetentionPolicy.RUNTIME) @Target(value= {ElementType.METHOD, ElementType.TYPE}) public @interface AfterApplicationReady {}
2.Create Below Class which is a listener which get call on application ready state.
2.创建下面的类,它是一个在应用程序就绪状态下调用的侦听器。
@Component
public class PostApplicationReadyListener implements ApplicationListener<ApplicationReadyEvent> {
public static final Logger LOGGER = LoggerFactory.getLogger(PostApplicationReadyListener.class);
public static final String MODULE = PostApplicationReadyListener.class.getSimpleName();
@Override
public void onApplicationEvent(ApplicationReadyEvent event) {
try {
ApplicationContext context = event.getApplicationContext();
String[] beans = context.getBeanNamesForAnnotation(AfterAppStarted.class);
LOGGER.info("bean found with AfterAppStarted annotation are : {}", Arrays.toString(beans));
for (String beanName : beans) {
Object bean = context.getBean(beanName);
Class<?> targetClass = AopUtils.getTargetClass(bean);
Method[] methods = targetClass.getMethods();
for (Method method : methods) {
if (method.isAnnotationPresent(AfterAppStartedComplete.class)) {
LOGGER.info("Method:[{} of Bean:{}] found with AfterAppStartedComplete Annotation.", method.getName(), beanName);
Method currentMethod = bean.getClass().getMethod(method.getName(), method.getParameterTypes());
LOGGER.info("Going to invoke method:{} of bean:{}", method.getName(), beanName);
currentMethod.invoke(bean);
LOGGER.info("Invocation compeleted method:{} of bean:{}", method.getName(), beanName);
}
}
}
} catch (Exception e) {
LOGGER.warn("Exception occured : ", e);
}
}
}
Finally when you start your Spring application just before log stating application started your listener will be called.
最后,当您在日志说明应用程序启动之前启动 Spring 应用程序时,您的侦听器将被调用。

