spring 如何在应用程序上下文初始化事件中添加钩子?

zphenhs4  于 2023-09-29  发布在  Spring
关注(0)|答案(6)|浏览(164)

对于一个普通的Servlet,我猜你可以声明一个context listener,但是对于Spring MVC,Spring会让这更容易吗?
此外,如果我定义了一个上下文侦听器,然后需要访问在我的servlet.xmlapplicationContext.xml中定义的bean,我将如何访问它们?

xqkwcwgp

xqkwcwgp1#

Spring有一些你可以处理的标准事件。

为此,您必须创建并注册一个实现ApplicationListener接口的bean,类似于以下内容:

  1. package test.pack.age;
  2. import org.springframework.context.ApplicationContext;
  3. import org.springframework.context.ApplicationEvent;
  4. import org.springframework.context.ApplicationListener;
  5. import org.springframework.context.event.ContextRefreshedEvent;
  6. public class ApplicationListenerBean implements ApplicationListener {
  7. @Override
  8. public void onApplicationEvent(ApplicationEvent event) {
  9. if (event instanceof ContextRefreshedEvent) {
  10. ApplicationContext applicationContext = ((ContextRefreshedEvent) event).getApplicationContext();
  11. // now you can do applicationContext.getBean(...)
  12. // ...
  13. }
  14. }
  15. }

然后在servlet.xmlapplicationContext.xml文件中注册此bean:

  1. <bean id="eventListenerBean" class="test.pack.age.ApplicationListenerBean" />

Spring会在application context初始化时通知它。
在Spring 3中(如果你使用的是这个版本),ApplicationListener class is generic和你可以声明你感兴趣的事件类型,事件将被相应地过滤。你可以像这样简化你的bean代码:

  1. public class ApplicationListenerBean implements ApplicationListener<ContextRefreshedEvent> {
  2. @Override
  3. public void onApplicationEvent(ContextRefreshedEvent event) {
  4. ApplicationContext applicationContext = event.getApplicationContext();
  5. // now you can do applicationContext.getBean(...)
  6. // ...
  7. }
  8. }
展开查看全部
wqlqzqxt

wqlqzqxt2#

从Spring 4.2开始,您可以使用@EventListenerdocumentation

  1. @Component
  2. class MyClassWithEventListeners {
  3. @EventListener({ContextRefreshedEvent.class})
  4. void contextRefreshedEvent() {
  5. System.out.println("a context refreshed event happened");
  6. }
  7. }
camsedfj

camsedfj3#

创建注解

  1. @Retention(RetentionPolicy.RUNTIME)
  2. public @interface AfterSpringLoadComplete {
  3. }

创建类

  1. public class PostProxyInvokerContextListener implements ApplicationListener<ContextRefreshedEvent> {
  2. @Autowired
  3. ConfigurableListableBeanFactory factory;
  4. @Override
  5. public void onApplicationEvent(ContextRefreshedEvent event) {
  6. ApplicationContext context = event.getApplicationContext();
  7. String[] names = context.getBeanDefinitionNames();
  8. for (String name : names) {
  9. try {
  10. BeanDefinition definition = factory.getBeanDefinition(name);
  11. String originalClassName = definition.getBeanClassName();
  12. Class<?> originalClass = Class.forName(originalClassName);
  13. Method[] methods = originalClass.getMethods();
  14. for (Method method : methods) {
  15. if (method.isAnnotationPresent(AfterSpringLoadComplete.class)){
  16. Object bean = context.getBean(name);
  17. Method currentMethod = bean.getClass().getMethod(method.getName(), method.getParameterTypes());
  18. currentMethod.invoke(bean);
  19. }
  20. }
  21. } catch (Exception ignored) {
  22. }
  23. }
  24. }
  25. }

通过@Component注解或在xml中注册该类

  1. <bean class="ua.adeptius.PostProxyInvokerContextListener"/>

并在你想要的任何方法上使用注解,你想在上下文初始化后运行,比如:

  1. @AfterSpringLoadComplete
  2. public void init() {}
展开查看全部
8ftvxx2r

8ftvxx2r4#

请按照下面的步骤做一些处理后,应用程序上下文得到加载,即应用程序准备服务。
1.创建以下注解,即
@Retention(RetentionPolicy.RUNTIME)@Target(value= {ElementType.METHOD,ElementType.TYPE})public @interface AfterApplicationReady {}
2.创建下面的类,这是一个监听器,它在应用程序就绪状态下获得调用。

  1. @Component
  2. public class PostApplicationReadyListener implements ApplicationListener<ApplicationReadyEvent> {
  3. public static final Logger LOGGER = LoggerFactory.getLogger(PostApplicationReadyListener.class);
  4. public static final String MODULE = PostApplicationReadyListener.class.getSimpleName();
  5. @Override
  6. public void onApplicationEvent(ApplicationReadyEvent event) {
  7. try {
  8. ApplicationContext context = event.getApplicationContext();
  9. String[] beans = context.getBeanNamesForAnnotation(AfterAppStarted.class);
  10. LOGGER.info("bean found with AfterAppStarted annotation are : {}", Arrays.toString(beans));
  11. for (String beanName : beans) {
  12. Object bean = context.getBean(beanName);
  13. Class<?> targetClass = AopUtils.getTargetClass(bean);
  14. Method[] methods = targetClass.getMethods();
  15. for (Method method : methods) {
  16. if (method.isAnnotationPresent(AfterAppStartedComplete.class)) {
  17. LOGGER.info("Method:[{} of Bean:{}] found with AfterAppStartedComplete Annotation.", method.getName(), beanName);
  18. Method currentMethod = bean.getClass().getMethod(method.getName(), method.getParameterTypes());
  19. LOGGER.info("Going to invoke method:{} of bean:{}", method.getName(), beanName);
  20. currentMethod.invoke(bean);
  21. LOGGER.info("Invocation compeleted method:{} of bean:{}", method.getName(), beanName);
  22. }
  23. }
  24. }
  25. } catch (Exception e) {
  26. LOGGER.warn("Exception occured : ", e);
  27. }
  28. }
  29. }

最后,当你启动你的Spring应用程序,就在log声明应用程序启动之前,你的监听器将被调用。

展开查看全部
tzxcd3kk

tzxcd3kk5#

我有一个单页应用程序在输入URL时,它正在创建一个HashMap(由我的网页使用),其中包含来自多个数据库的数据。我在服务器启动时做了以下事情来加载所有内容-
1-已创建ContextListenerClass

  1. public class MyAppContextListener implements ServletContextListener
  2. @Autowired
  3. private MyDataProviderBean myDataProviderBean;
  4. public MyDataProviderBean getMyDataProviderBean() {
  5. return MyDataProviderBean;
  6. }
  7. public void setMyDataProviderBean(MyDataProviderBean MyDataProviderBean) {
  8. this.myDataProviderBean = MyDataProviderBean;
  9. }
  10. @Override
  11. public void contextDestroyed(ServletContextEvent arg0) {
  12. System.out.println("ServletContextListener destroyed");
  13. }
  14. @Override
  15. public void contextInitialized(ServletContextEvent context) {
  16. System.out.println("ServletContextListener started");
  17. ServletContext sc = context.getServletContext();
  18. WebApplicationContext springContext = WebApplicationContextUtils.getWebApplicationContext(sc);
  19. MyDataProviderBean MyDataProviderBean = (MyDataProviderBean)springContext.getBean("myDataProviderBean");
  20. Map<String, Object> myDataMap = MyDataProviderBean.getDataMap();
  21. sc.setAttribute("myMap", myDataMap);
  22. }

2-在web.xml中添加以下条目

  1. <listener>
  2. <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  3. </listener>
  4. <listener>
  5. <listener-class>com.context.listener.MyAppContextListener</listener-class>
  6. </listener>

3-在我的控制器类中,更新了代码,以首先检查servletContext中的Map

  1. @RequestMapping(value = "/index", method = RequestMethod.GET)
  2. public String index(@ModelAttribute("model") ModelMap model) {
  3. Map<String, Object> myDataMap = new HashMap<String, Object>();
  4. if (context != null && context.getAttribute("myMap")!=null)
  5. {
  6. myDataMap=(Map<String, Object>)context.getAttribute("myMap");
  7. }
  8. else
  9. {
  10. myDataMap = myDataProviderBean.getDataMap();
  11. }
  12. for (String key : myDataMap.keySet())
  13. {
  14. model.addAttribute(key, myDataMap.get(key));
  15. }
  16. return "myWebPage";
  17. }

有了这么多的变化,当我启动我的tomcat时,它在startTime期间加载dataMap,并将所有内容放入servletContext中,然后由Controller Class使用,以从已经填充的servletContext中获取结果。

展开查看全部
uoifb46i

uoifb46i6#

您的bean还可以实现SmartInitializingSingleton.afterSingletonsInstantiated()。这是Spring文档(v 6.0.12)中列出的可能性之一:https://docs.spring.io/spring-framework/reference/core/beans/factory-nature.html#beans-factory-lifecycle-initializingbean
请注意,@PostConstruct和初始化方法通常在容器的单例创建锁中执行。bean示例只有在从@PostConstruct方法返回后才被视为完全初始化并准备好发布给其他人。这些单独的初始化方法仅用于验证配置状态,并可能基于给定的配置准备一些数据结构,但不用于外部bean访问的进一步活动。否则,存在初始化死锁的风险。
对于要触发昂贵的初始化后活动的场景,例如异步数据库准备步骤,您的bean应该实现SmartInitializingSingleton.afterSingletonsInstantiated()或依赖于上下文刷新事件:实现ApplicationException或声明其注解等效项@ EventException(ContextRefreshedEvent.class)。这些变体在所有常规单例初始化之后出现,因此在任何单例创建锁之外。
或者,您可以实现(智能)生命周期接口并与容器的整体生命周期管理集成,包括自动启动机制,预销毁停止步骤和潜在的停止/重新启动回调(见下文)。

相关问题