基于spring的web项目启动时预加载数据到servletcontext

木心      2022-04-30     303

关键词:

1、要在web启动时预加载数据到ServletContext,实现方法有很多,一种比较简单的方案就是:

  1)新建一个bean,定义其初始化方法:

    <bean id="beanId" class="beanClassName" init-method="初始化方法" />或者使用@PostConstruct注解到初始化方法上面

  2)获取ServletContext实例对象,如何获取呢?

    方法1:

      @Autowired
      private ServletContext application;

    方法2:

      实现 ServletContextAware接口,重写setServletContext(ServletContext)方法

  3)在初始化方法里面写代码,调用service层或dao层方法,查询数据库得到数据;将数据设置给ServletContext实例对象;

  

2、案例

  jdk: 1.8.0_111;

  tomcat: apache-tomcat-9.0.13;

  依赖jar包:

  项目结构:

 

  在service层定义一个类InitComponent,定义初始化方法init()。在spring容器加载这个bean时,会调用init()方法。

package com.oy.service.impl;

import javax.annotation.PostConstruct;
import javax.servlet.ServletContext;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import com.oy.service.BookService;

@Component
public class InitComponent {
    @Autowired
    private BookService bookService;

    @Autowired
    private ServletContext application;

    @PostConstruct
    public void init() {
        System.out.println("向application域中添加数据。。。");
        application.setAttribute("bookList", bookService.findAll());
        System.out.println(application.getAttribute("bookList"));
    }
}

   为了测试方便,dao层不查询数据库,而是使用模拟数据:

@Service
public class BookServiceImpl implements BookService {
    @Autowired
    private BookDao bookDao;
    
    @Override
    public List<Book> findAll() {
        return bookDao.findAll();
    }
}


@Repository
public class BookDaoImpl implements BookDao {
    private List<Book> bookList = new ArrayList<>();
    
    @Override
    public List<Book> findAll() {
        Book book1 = new Book();
        book1.setId(1);
        book1.setName("java编程思想");
        bookList.add(book1);
        
        Book book2 = new Book();
        book2.setId(2);
        book2.setName("java从入门到精通");
        bookList.add(book2);
        
        return bookList;
    }
}

 

  以后数据改变了,想要更新缓存怎么办?写一个controller,调用InitComponent#init()。在页面上定义一个按钮,点击访问 initController#refreshCache(),即可更新缓存。记得更新前先删除缓存。

package com.oy.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import com.oy.service.impl.InitComponent;

@Controller
public class InitController {
    @Autowired
    private InitComponent initComponent;
    
    @RequestMapping("/refreshCache")
    @ResponseBody
    public String refreshCache() {
        initComponent.init();
        return "controller刷新缓存成功!";
    }
}

 

  配置文件web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns="http://java.sun.com/xml/ns/javaee"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
    id="WebApp_ID" version="2.5">
    <welcome-file-list>
        <welcome-file>index.jsp</welcome-file>
    </welcome-file-list>

    <!-- spring配置文件 -->
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:applicationContext.xml</param-value>
    </context-param>

    <!-- spring监听器,初始化spring IoC容器 -->
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

    <!-- springmvc前端控制器 -->
    <servlet>
        <servlet-name>springmvc</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <!-- 如果不配置springmvc配置文件路径,默认在/WEB-INF/[servlet-name]-servlet.xml -->
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:springmvc.xml</param-value>
        </init-param>
    </servlet>
    <servlet-mapping>
        <servlet-name>springmvc</servlet-name>
        <url-pattern>*.do</url-pattern>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

    <!-- post请求的编码过滤器 -->
    <filter>
        <filter-name>characterEncodingFilter</filter-name>
        <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
        <init-param>
            <param-name>encoding</param-name>
            <param-value>utf-8</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>characterEncodingFilter</filter-name>
        <url-pattern>*.do</url-pattern>
        <url-pattern>/</url-pattern>
    </filter-mapping>
</web-app>

 

  配置文件applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>    
<beans xmlns="http://www.springframework.org/schema/beans"    
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"   
    xmlns:p="http://www.springframework.org/schema/p"  
    xmlns:aop="http://www.springframework.org/schema/aop"   
    xmlns:context="http://www.springframework.org/schema/context"  
    xmlns:jee="http://www.springframework.org/schema/jee"  
    xmlns:tx="http://www.springframework.org/schema/tx"  
    xsi:schemaLocation="    
    http://www.springframework.org/schema/aop 
    http://www.springframework.org/schema/aop/spring-aop-4.0.xsd     http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-4.0.xsd     http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context-4.0.xsd     http://www.springframework.org/schema/jee
    http://www.springframework.org/schema/jee/spring-jee-4.0.xsd     http://www.springframework.org/schema/tx
    http://www.springframework.org/schema/tx/spring-tx-4.0.xsd"
> <bean class="com.oy.controller.BeanPostProcessorImpl"/> <!-- 自动扫描 --> <context:component-scan base-package="com.oy.service,com.oy.dao" /> </beans>

 

  配置文件springmvc.xml

<?xml version="1.0" encoding="UTF-8"?>    
<beans xmlns="http://www.springframework.org/schema/beans"    
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"   
    xmlns:p="http://www.springframework.org/schema/p"  
    xmlns:aop="http://www.springframework.org/schema/aop"   
    xmlns:context="http://www.springframework.org/schema/context"  
    xmlns:jee="http://www.springframework.org/schema/jee"  
    xmlns:tx="http://www.springframework.org/schema/tx"  
    xmlns:mvc="http://www.springframework.org/schema/mvc"
    xsi:schemaLocation="    
    http://www.springframework.org/schema/aop 
    http://www.springframework.org/schema/aop/spring-aop-4.0.xsd     http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-4.0.xsd     http://www.springframework.org/schema/mvc
    http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd     http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context-4.0.xsd     http://www.springframework.org/schema/jee
    http://www.springframework.org/schema/jee/spring-jee-4.0.xsd     http://www.springframework.org/schema/tx
    http://www.springframework.org/schema/tx/spring-tx-4.0.xsd"
> <!-- 加载properties文件 --> <!-- <context:property-placeholder location="classpath:params.properties"/> --> <!-- 使用注解的包,包括子集 --> <context:component-scan base-package="com.oy.controller" /> <!-- 处理器映射器,处理器适配器 --> <!-- <mvc:annotation-driven conversion-service="conversionService"/> --> <mvc:annotation-driven /> <!-- Converter转换器工厂 注: 需要在 适配器 中进行配置 --> <!-- <bean id="conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean"> 日期 去掉前后空格 <property name="converters"> <set> <bean class="com.oy.converter.CustomConverter"></bean> </set> </property> </bean> --> <!-- 静态资源映射 <mvc:resources mapping="/static/**" location="/static/"/> --> <!-- 如果使用了RESTful形式的拦截,那么对于静态资源的处理上,就需要加上此句,静态资源(没有映射的)
     就会交给默认的web容器中的servlet进行处理
--> <mvc:default-servlet-handler /> <!-- 视图解析器 --> <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/" /> <property name="suffix" value=".jsp"></property> </bean> </beans>

 

  另外,贴一个BeanPostProcessor的实现类,这个类可以帮助我们查看spring容器中加载了哪些bean。利用这个类还可以实现bean的代理,这里就不详细讲了。如果不想使用这个类,为了避免报错,需要将applicationContext.xml中<bean class="com.oy.controller.BeanPostProcessorImpl"/>这一句删掉。

package com.oy.controller;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;

/**
 * @author oy
 * @version 1.0
 * @date 2018年12月29日
 * @time 下午9:30:53
 */
public class BeanPostProcessorImpl implements BeanPostProcessor {

    public BeanPostProcessorImpl() {
        System.out.println();
        System.out.println("==========调用BeanPostProcessorImpl无参构造==========");
    }

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("前处理方法,beanName为:" + beanName);
        return bean;
    }

    /**
     * 参数bean:目标对象,即被代理的bean对象 参数beanName:被代理对象的名字,即bean的id属性值
     */
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("后处理方法,bean的类型为:" + bean.getClass().getName());

        // 通过beanName过滤,对不同的bean进行处理
        if ("userService".equals(beanName)) {
            // 生成jdk代理
            return Proxy.newProxyInstance(BeanPostProcessorImpl.class.getClassLoader(), bean.getClass().getInterfaces(),
                    new InvocationHandler() {

                        @Override
                        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                            System.out.println("===开启事务===");
                            Object obj = method.invoke(bean, args);
                            System.out.println("===提交事务===");
                            return obj;
                        }

                    });
        } else if ("people".equals(beanName)) {
            // 生成jdk代理
            return Proxy.newProxyInstance(BeanPostProcessorImpl.class.getClassLoader(), bean.getClass().getInterfaces(),
                    new InvocationHandler() {

                        @Override
                        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

                            // System.out.println(proxy.getClass().getName()); // com.sun.proxy.$Proxy6

                            long start = System.currentTimeMillis();
                            Object obj = method.invoke(bean, args);
                            long time = System.currentTimeMillis() - start;

                            System.out.println("方法" + method.getName() + "()共耗时:" + time + "毫秒");
                            return obj;
                        }

                    });
        } else {
            // 直接返回bean,不生成代理对象
            return bean;
        }
    }
}

  

  在项目根路径下新建一个index.jsp:

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
    <h1>index页面</h1>
    <c:forEach items="${bookList}" var="book">
        ${book.id} -- ${book.name}
    </c:forEach>
</body>
</html>

 

3、测试

  3.1、项目启动,控制台打印结果:

    ==========调用BeanPostProcessorImpl无参构造==========
    前处理方法,beanName为:bookDaoImpl
    后处理方法,bean的类型为:com.oy.dao.impl.BookDaoImpl
    前处理方法,beanName为:bookServiceImpl
    后处理方法,bean的类型为:com.oy.service.impl.BookServiceImpl
    前处理方法,beanName为:initComponent
    向application域中添加数据。。。
    [Book [id=1, name=java编程思想], Book [id=2, name=java从入门到精通]]
    后处理方法,bean的类型为:com.oy.service.impl.InitComponent
    前处理方法,beanName为:initController
    后处理方法,bean的类型为:com.oy.controller.InitController
    前处理方法,beanName为:org.springframework.context.event.internalEventListenerProcessor
    后处理方法,bean的类型为:org.springframework.context.event.EventListenerMethodProcessor
    前处理方法,beanName为:org.springframework.context.event.internalEventListenerFactory
    后处理方法,bean的类型为:org.springframework.context.event.DefaultEventListenerFactory

 

  3.2、浏览器访问:http://localhost:8080/04_Demo001/refreshCache, 控制台打印结果:

    向application域中添加数据。。。
    [Book [id=1, name=java编程思想], Book [id=2, name=java从入门到精通], Book [id=1, name=java编程思想], Book [id=2, name=java从入门到精通]]

 

4、在上面案例中将启动初始数据和更新数据写在两个类中,其实没有必要。

  4.1、首先要知道spring可以有多个容器,spring主容器加载applicationContext.xml中配置或扫描的bean,springmvc子容器加载springmvc.xml中配置或扫描的bean;父子容器的关系这里不详细讲。可以参考:

  spring与springMVC的细节问题:父子容器关系,加载controller,404错误

  Spring的父子容器问题和坑

  spring父子容器----子容器不能获取父容器的属性

  4.2、第一次惩罚:默认情况下,服务器是在第一次收到对controller#handler的请求时,会初始化DispatcherServlet,创建springmvc子容器,加载springmvc.xml中配置或扫描的bean;我上面的案例就是这种情况。

  4.3、如果在web.xml配置DispatcherServlet时添加了<load-on-startup>1</load-on-startup>,则服务器启动时就会初始化DispatcherServlet,创建springmvc子容器,加载springmvc.xml中配置或扫描的bean;

  4.4、所以在web.xml配置DispatcherServlet时添加了<load-on-startup>1</load-on-startup>后,可以将案例中initComponent类和initController类合并成一个类:

package com.oy.controller;

import javax.annotation.PostConstruct;
import javax.servlet.ServletContext;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import com.oy.service.BookService;

@Controller
public class InitController {
    @Autowired
    private BookService bookService;

    @Autowired
    private ServletContext application;

    @PostConstruct
    public void init() {
        System.out.println("向application域中添加数据。。。");
        application.setAttribute("bookList", bookService.findAll());
        System.out.println(application.getAttribute("bookList"));
    }

    @RequestMapping("/refreshCache")
    @ResponseBody
    public String refreshCache() {
        init();
        return "controller refreshCache ok!";
    }
}

 

在应用程序启动时预加载 UITabBar 选项卡时出现问题(崩溃)

】在应用程序启动时预加载UITabBar选项卡时出现问题(崩溃)【英文标题】:TroublepreloadingUITabBartabsonapplicationlaunch(crash)【发布时间】:2014-03-0703:10:33【问题描述】:我在RSS新闻阅读器中有8个标签。每个选项卡链接到一个导航控... 查看详情

web项目启动spring容器

    今天在重新搭建一套web框架,只想采用spring,其它框架均用不到。使用java应用程序进行测试,完全没问题。考虑到做为web应用程序,那就必须要在服务器启动时,能够将spring的上下文配置加载进去,... 查看详情

spring在web.xml中的配置

...ContextLoaderServlet。这两种在功能上完全相同,只是一种是基于Servlet2.3 查看详情

spring框架整合web解决配置文件加载多次的问题

1.创建JavaWEB项目,引入Spring的开发包。编写具体的类和方法。*环境搭建好后,启动服务器来测试项目,发送每访问一次都会加载一次配置文件,这样效率会非常非常慢!!2.解决上面的问题*将工厂创建好了以后放入到ServletContext... 查看详情

web项目启动spring容器

    今天在重新搭建一套web框架,只想采用spring,其它框架均用不到。使用java应用程序进行测试,完全没问题。考虑到做为web应用程序,那就必须要在服务器启动时,能够将spring的上下文配置加载进去,... 查看详情

springboot运行原理

参考技术ASpringBoot是一个基于Spring开发,集成了大量第三方库配置的javaweb开发框架pom.xml父依赖其中它主要是依赖一个父项目,主要是管理项目的资源过滤及插件。以后我们导入依赖默认是不需要写版本的。启动器spring-boot-starters... 查看详情

在应用程序启动时预加载 Ext JS 和自定义 JavaScript 文件

】在应用程序启动时预加载ExtJS和自定义JavaScript文件【英文标题】:PreloadingExtJS&customJavaScriptfilesatapplicationboottime【发布时间】:2012-08-3003:53:13【问题描述】:我正在创建一个Intranet应用程序,其UI主要使用ExtJS4.1.1我创建了几... 查看详情

spring学习————整合web项目(ssh)

     清楚了spring的IOC和AOP,最后一篇就来整合SSH框架把,记录下来,以后应该会用的到。                    --WH一、web项目中如何使用spring?      当tomcat启动时,就应该加载spring的配置... 查看详情

整合web项目(代码片段)

首先还是我们的需求式开场白。在web项目中如何使用spring开发。如果使用 new ClassPathXmlApplicationContext 将每一次都加载xml,相当于每一次都创建spring容器。而真实开发中,容器只有一份。之后重复的从spring容器中获取内... 查看详情

spring源码:web容器启动(li)

  web项目中可以集成spring的ApplicationContext进行bean的管理,这样使用起来bean更加便捷,能够利用到很多spring的特性。我们比较常用的web容器有jetty,tomcat,jboss等,以jetty为例,我们看一下web容器是如何初始化和启动spring的context... 查看详情

从 web.xml 迁移到基于 Java 的配置 - 无法启动 Tomcat 8

】从web.xml迁移到基于Java的配置-无法启动Tomcat8【英文标题】:Migratingfromweb.xmltoJavabasedconfiguration-unabletostartTomcat8【发布时间】:2015-05-0503:52:19【问题描述】:我正在尝试将我的应用程序的web.xml迁移到基于Java的配置。我们使用的... 查看详情

ideassm项目迁移到另一台机器上时出现不能正常启动项目的解决方案

  首先右下角提示关联spring文件,关联之,然后启动,发现项目无法启动,然后开始排错  首先从这个日志里发现了这么一条提示信息 然后百度了一下,答案都是说 web.xml 之类的 spring拦截器问题,... 查看详情

springboot框架的理解

...的:说使用SpringBoot可以构造任何东西,SpringBoot是构造所有基于Spring的应用程序的起点,SpringBoot在于通过最少的配置为你启动程序。2.我的理解SpringBoot是Spring开源项目的一个子项目,是Spring组件的一站式解决发案,其目的是简化Spring应... 查看详情

springboot框架的理解

...的:说使用SpringBoot可以构造任何东西,SpringBoot是构造所有基于Spring的应用程序的起点,SpringBoot在于通过最少的配置为你启动程序。2.我的理解SpringBoot是Spring开源项目的一个子项目,是Spring组件的一站式解决发案,其目的是简化Spring应... 查看详情

spring启动加载资源到内存

前言      在一些业务场景中,当容器初始化完成之后,需要处理一些操作,比如一些数据的加载、初始化缓存、特定任务的注册等等。我找到了三种方式解决下面的问题。1、使用PostConstruct注解  这种解... 查看详情

spring在web.xml中的配置

Spring在web.xml中进行配置1.1加载Spring配置文件我们首先指定我们需要加载的Spring配置文件,在tomcat容器启动后,会寻找项目中的web.xml文件,加载其中的信息,并创建一个ServletContext上下文对象,以后再web应用中可以获得其中的值... 查看详情

Spring - 从依赖 JAR 文件加载服务和存储库

...据库(使用休眠)的Web项目和独立应用程序项目。两者都基于Spring并使用服务和存储库。由于这两个项目经常与相同的实体进行交互,因此我曾经在每个项目中拥有几乎每个存储库和服务 查看详情

基于spring-boot的kettle调度

...更好地集成到自己的项目中去。于是我觉得将它抽出来,基于spring-boot,具体的业务也分离出来,kettle作为一个组件。renren-kettle项目说明项目实现功能项目结构实时websocket的接入例子部署指南如需加入项目,请邮件823894716@qq.com 查看详情