Как добавить привязку к событию инициализации контекста приложения?
Для обычного сервлета, я думаю, вы могли бы объявить прослушиватель контекста, но для Spring MVC Spring сделать это проще?
Кроме того, если я определяю прослушиватель контекста, а затем должен получить доступ к beans, определенному в моих servlet.xml
или applicationContext.xml
, как я могу получить к ним доступ?
Ответы
Ответ 1
Spring имеет несколько стандартных событий, которые вы можете обрабатывать.
Чтобы сделать это, вы должны создать и зарегистрировать bean, который реализует интерфейс ApplicationListener
, примерно так:
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(...)
// ...
}
}
}
Затем вы регистрируете этот bean в файле servlet.xml
или applicationContext.xml
:
<bean id="eventListenerBean" class="test.pack.age.ApplicationListenerBean" />
и Spring сообщают об этом при инициализации контекста приложения.
В 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(...)
// ...
}
}
Ответ 2
Так как Spring 4.2, вы можете использовать @EventListener
(документация)
@Component
class MyClassWithEventListeners {
@EventListener({ContextRefreshedEvent.class})
void contextRefreshedEvent() {
System.out.println("a context refreshed event happened");
}
}
Ответ 3
Создайте свою аннотацию
@Retention(RetentionPolicy.RUNTIME)
public @interface AfterSpringLoadComplete {
}
Создать класс
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) {
}
}
}
}
Зарегистрируйте этот класс с помощью аннотации @Component или в xml
<bean class="ua.adeptius.PostProxyInvokerContextListener"/>
и используйте аннотацию, где вы хотите использовать любой метод, который вы хотите запустить после инициализации контекста, например:
@AfterSpringLoadComplete
public void init() {}
Ответ 4
У меня было одностраничное приложение при вводе URL-адреса, в котором он создавал HashMap (используемый моей веб-страницей), который содержал данные из нескольких баз данных.
Я выполнил следующие действия, чтобы загрузить все во время запуска сервера -
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- Добавлена запись ниже в web.xml
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<listener>
<listener-class>com.context.listener.MyAppContextListener</listener-class>
</listener>
3- В моем классе контроллера обновлен код для первой проверки карты в servletContext
@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";
}
С этим большим изменением, когда я запускаю свой tomcat, он загружает dataMap во время startTime и помещает все в servletContext, который затем используется классом Controller для получения результатов из уже заполненного servletContext.