Redis和Spring的整合

因为经常用到几个工具类,因此备份一下,后续可以直接copy过去

spring配置

<bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
    <property name="maxIdle" value="${redis.pool.maxIdle}" /> <!-- 最大能够保持idel状态的对象数  -->
    <property name="maxTotal" value="${redis.pool.maxTotal}" /> <!-- 最大分配的对象数 -->
    <property name="testOnBorrow" value="${redis.pool.testOnBorrow}" /> <!-- 当调用borrow Object方法时,是否进行有效性检查 -->
</bean>  

    <!-- sprin_data_redis 单机配置 -->
    <bean id="jedisConnFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory" >
        <property name="hostName" value="${redis.host}" />
        <property name="port" value="${redis.port}" />
        <property name="timeout" value="${redis.timeout}" />
        <property name="password" value="${redis.password}" />
        <property name="poolConfig" ref="jedisPoolConfig" />
    </bean>
    <!-- key序列化 -->
<bean id="stringRedisSerializer" class="org.springframework.data.redis.serializer.StringRedisSerializer" />  

<bean id="stringRedisTemplate" class="org.springframework.data.redis.core.StringRedisTemplate"
    p:connectionFactory-ref="jedisConnFactory" />
<bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate"
    p:connectionFactory-ref="jedisConnFactory"
    p:keySerializer-ref="stringRedisSerializer"
    p:hashKeySerializer-ref="stringRedisSerializer" />
<!-- spring自己的缓存管理器 -->
   <bean id="cacheManager" class="org.springframework.cache.support.SimpleCacheManager">
       <property name="caches">
           <set>
            <bean class="com.rd.ifaes.common.jedis.RdRedisCache" p:redis-template-ref="redisTemplate" p:name="sysCache"/>
           </set>
       </property>
   </bean>    

<!-- 启用缓存注解功能,这个是必须的,否则注解不会生效,另外,该注解一定要声明在spring主配置文件中才会生效 -->
<cache:annotation-driven cache-manager="cacheManager" proxy-target-class="true" /> 

缓存工具类

import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;  

import org.springframework.data.redis.core.BoundHashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.util.CollectionUtils;  

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.rd.ifaes.common.dict.ExpireTime;
import com.lh.common.util.JsonMapper;
import com.rd.ifaes.common.util.SpringContextHolder;
import com.rd.ifaes.common.util.StringUtils;  

/**
 * 通用缓存工具类
 * @author lh
 * @version 3.0
 * @since 2016-6-22
 *
 */
public class CacheUtils {  

    private static StringRedisTemplate stringRedisTemplate = SpringContextHolder.getBean("stringRedisTemplate");
    private static RedisTemplate<String, Object> redisTemplate = SpringContextHolder.getBean("redisTemplate");  

    /**
     * 删除缓存<br>
     * 根据key精确匹配删除
     * @param key
     */
    @SuppressWarnings("unchecked")
    public static void del(String... key){
        if(key!=null && key.length > 0){
            if(key.length == 1){
                redisTemplate.delete(key[0]);
            }else{
                redisTemplate.delete(CollectionUtils.arrayToList(key));
            }
        }
    }  

    /**
     * 批量删除<br>
     * (该操作会执行模糊查询,请尽量不要使用,以免影响性能或误删)
     * @param pattern
     */
    public static void batchDel(String... pattern){
        for (String kp : pattern) {
            redisTemplate.delete(redisTemplate.keys(kp + "*"));
        }
    }  

    /**
     * 取得缓存(int型)
     * @param key
     * @return
     */
    public static Integer getInt(String key){
        String value = stringRedisTemplate.boundValueOps(key).get();
        if(StringUtils.isNotBlank(value)){
            return Integer.valueOf(value);
        }
        return null;
    }  

    /**
     * 取得缓存(字符串类型)
     * @param key
     * @return
     */
    public static String getStr(String key){
        return stringRedisTemplate.boundValueOps(key).get();
    }  

    /**
     * 取得缓存(字符串类型)
     * @param key
     * @return
     */
    public static String getStr(String key, boolean retain){
        String value = stringRedisTemplate.boundValueOps(key).get();
        if(!retain){
            redisTemplate.delete(key);
        }
        return value;
    }  

    /**
     * 获取缓存<br>
     * 注:基本数据类型(Character除外),请直接使用get(String key, Class<T> clazz)取值
     * @param key
     * @return
     */
    public static Object getObj(String key){
        return redisTemplate.boundValueOps(key).get();
    }  

    /**
     * 获取缓存<br>
     * 注:java 8种基本类型的数据请直接使用get(String key, Class<T> clazz)取值
     * @param key
     * @param retain    是否保留
     * @return
     */
    public static Object getObj(String key, boolean retain){
        Object obj = redisTemplate.boundValueOps(key).get();
        if(!retain){
            redisTemplate.delete(key);
        }
        return obj;
    }  

    /**
     * 获取缓存<br>
     * 注:该方法暂不支持Character数据类型
     * @param key   key
     * @param clazz 类型
     * @return
     */
    @SuppressWarnings("unchecked")
    public static <T> T get(String key, Class<T> clazz) {
        return (T)redisTemplate.boundValueOps(key).get();
    }  

    /**
     * 获取缓存json对象<br>
     * @param key   key
     * @param clazz 类型
     * @return
     */
    public static <T> T getJson(String key, Class<T> clazz) {
        return JsonMapper.fromJsonString(stringRedisTemplate.boundValueOps(key).get(), clazz);
    }  

    /**
     * 将value对象写入缓存
     * @param key
     * @param value
     * @param time 失效时间(秒)
     */
    public static void set(String key,Object value,ExpireTime time){
        if(value.getClass().equals(String.class)){
            stringRedisTemplate.opsForValue().set(key, value.toString());
        }else if(value.getClass().equals(Integer.class)){
            stringRedisTemplate.opsForValue().set(key, value.toString());
        }else if(value.getClass().equals(Double.class)){
            stringRedisTemplate.opsForValue().set(key, value.toString());
        }else if(value.getClass().equals(Float.class)){
            stringRedisTemplate.opsForValue().set(key, value.toString());
        }else if(value.getClass().equals(Short.class)){
            stringRedisTemplate.opsForValue().set(key, value.toString());
        }else if(value.getClass().equals(Long.class)){
            stringRedisTemplate.opsForValue().set(key, value.toString());
        }else if(value.getClass().equals(Boolean.class)){
            stringRedisTemplate.opsForValue().set(key, value.toString());
        }else{
            redisTemplate.opsForValue().set(key, value);
        }
        if(time.getTime() > 0){
            redisTemplate.expire(key, time.getTime(), TimeUnit.SECONDS);
        }
    }  

    /**
     * 将value对象以JSON格式写入缓存
     * @param key
     * @param value
     * @param time 失效时间(秒)
     */
    public static void setJson(String key,Object value,ExpireTime time){
        stringRedisTemplate.opsForValue().set(key, JsonMapper.toJsonString(value));
        if(time.getTime() > 0){
            stringRedisTemplate.expire(key, time.getTime(), TimeUnit.SECONDS);
        }
    }  

    /**
     * 更新key对象field的值
     * @param key   缓存key
     * @param field 缓存对象field
     * @param value 缓存对象field值
     */
    public static void setJsonField(String key, String field, String value){
        JSONObject obj = JSON.parseObject(stringRedisTemplate.boundValueOps(key).get());
        obj.put(field, value);
        stringRedisTemplate.opsForValue().set(key, obj.toJSONString());
    }  

    /**
     * 递减操作
     * @param key
     * @param by
     * @return
     */
    public static double decr(String key, double by){
        return redisTemplate.opsForValue().increment(key, -by);
    }  

    /**
     * 递增操作
     * @param key
     * @param by
     * @return
     */
    public static double incr(String key, double by){
        return redisTemplate.opsForValue().increment(key, by);
    }  

    /**
     * 获取double类型值
     * @param key
     * @return
     */
    public static double getDouble(String key) {
        String value = stringRedisTemplate.boundValueOps(key).get();
        if(StringUtils.isNotBlank(value)){
            return Double.valueOf(value);
        }
        return 0d;
    }  

    /**
     * 设置double类型值
     * @param key
     * @param value
     * @param time 失效时间(秒)
     */
    public static void setDouble(String key, double value, ExpireTime time) {
        stringRedisTemplate.opsForValue().set(key, String.valueOf(value));
        if(time.getTime() > 0){
            stringRedisTemplate.expire(key, time.getTime(), TimeUnit.SECONDS);
        }
    }  

    /**
     * 设置double类型值
     * @param key
     * @param value
     * @param time 失效时间(秒)
     */
    public static void setInt(String key, int value, ExpireTime time) {
        stringRedisTemplate.opsForValue().set(key, String.valueOf(value));
        if(time.getTime() > 0){
            stringRedisTemplate.expire(key, time.getTime(), TimeUnit.SECONDS);
        }
    }  

    /**
     * 将map写入缓存
     * @param key
     * @param map
     * @param time 失效时间(秒)
     */
    public static <T> void setMap(String key, Map<String, T> map, ExpireTime time){
        redisTemplate.opsForHash().putAll(key, map);
    }  

    /**
     * 将map写入缓存
     * @param key
     * @param map
     * @param time 失效时间(秒)
     */
    @SuppressWarnings("unchecked")
    public static <T> void setMap(String key, T obj, ExpireTime time){
        Map<String, String> map = (Map<String, String>)JsonMapper.parseObject(obj, Map.class);
        redisTemplate.opsForHash().putAll(key, map);
    }  

    /**
     * 向key对应的map中添加缓存对象
     * @param key
     * @param map
     */
    public static <T> void addMap(String key, Map<String, T> map){
        redisTemplate.opsForHash().putAll(key, map);
    }  

    /**
     * 向key对应的map中添加缓存对象
     * @param key   cache对象key
     * @param field map对应的key
     * @param value     值
     */
    public static void addMap(String key, String field, String value){
        redisTemplate.opsForHash().put(key, field, value);
    }  

    /**
     * 向key对应的map中添加缓存对象
     * @param key   cache对象key
     * @param field map对应的key
     * @param obj   对象
     */
    public static <T> void addMap(String key, String field, T obj){
        redisTemplate.opsForHash().put(key, field, obj);
    }  

    /**
     * 获取map缓存
     * @param key
     * @param clazz
     * @return
     */
    public static <T> Map<String, T> mget(String key, Class<T> clazz){
        BoundHashOperations<String, String, T> boundHashOperations = redisTemplate.boundHashOps(key);
        return boundHashOperations.entries();
    }  

    /**
     * 获取map缓存
     * @param key
     * @param clazz
     * @return
     */
    public static <T> T getMap(String key, Class<T> clazz){
        BoundHashOperations<String, String, String> boundHashOperations = redisTemplate.boundHashOps(key);
        Map<String, String> map = boundHashOperations.entries();
        return JsonMapper.parseObject(map, clazz);
    }  

    /**
     * 获取map缓存中的某个对象
     * @param key
     * @param field
     * @param clazz
     * @return
     */
    @SuppressWarnings("unchecked")
    public static <T> T getMapField(String key, String field, Class<T> clazz){
        return (T)redisTemplate.boundHashOps(key).get(field);
    }  

    /**
     * 删除map中的某个对象
     * @author lh
     * @date 2016年8月10日
     * @param key   map对应的key
     * @param field map中该对象的key
     */
    public void delMapField(String key, String... field){
        BoundHashOperations<String, String, ?> boundHashOperations = redisTemplate.boundHashOps(key);
        boundHashOperations.delete(field);
    }  

    /**
     * 指定缓存的失效时间
     *
     * @author FangJun
     * @date 2016年8月14日
     * @param key 缓存KEY
     * @param time 失效时间(秒)
     */
    public static void expire(String key, ExpireTime time) {
        if(time.getTime() > 0){
            redisTemplate.expire(key, time.getTime(), TimeUnit.SECONDS);
        }
    }  

    /**
     * 添加set
     * @param key
     * @param value
     */
    public static void sadd(String key, String... value) {
        redisTemplate.boundSetOps(key).add(value);
    }  

    /**
     * 删除set集合中的对象
     * @param key
     * @param value
     */
    public static void srem(String key, String... value) {
        redisTemplate.boundSetOps(key).remove(value);
    }  

    /**
     * set重命名
     * @param oldkey
     * @param newkey
     */
    public static void srename(String oldkey, String newkey){
        redisTemplate.boundSetOps(oldkey).rename(newkey);
    }  

    /**
     * 短信缓存
     * @author fxl
     * @date 2016年9月11日
     * @param key
     * @param value
     * @param time
     */
    public static void setIntForPhone(String key,Object value,int time){
        stringRedisTemplate.opsForValue().set(key, JsonMapper.toJsonString(value));
        if(time > 0){
            stringRedisTemplate.expire(key, time, TimeUnit.SECONDS);
        }
    }  

    /**
     * 模糊查询keys
     * @param pattern
     * @return
     */
    public static Set<String> keys(String pattern){
        return redisTemplate.keys(pattern);
    }  

}

JsonMaper的基础封装

import java.io.IOException;
import java.util.TimeZone;  

import org.apache.commons.lang3.StringEscapeUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;  

import com.alibaba.fastjson.JSON;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser.Feature;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.util.JSONPObject;
import com.fasterxml.jackson.module.jaxb.JaxbAnnotationModule;  

/**
 * 简单封装Jackson,实现JSON String<->Java Object的Mapper.
 *
 */
public class JsonMapper extends ObjectMapper {  

        private static final long serialVersionUID = 1L;  

        private final static Logger LOGGER = LoggerFactory.getLogger(JsonMapper.class);  

        private static JsonMapper mapper;  

        public JsonMapper() {
            this(Include.NON_EMPTY);
        }  

        public JsonMapper(Include include) {
            // 设置输出时包含属性的风格
            if (include != null) {
                this.setSerializationInclusion(include);
            }
            // 设置输入时忽略在JSON字符串中存在但Java对象实际没有的属性
            this.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
            // 空值处理为空串
            this.getSerializerProvider().setNullValueSerializer(new JsonSerializer<Object>(){
                @Override
                public void serialize(Object value, JsonGenerator jgen,
                        SerializerProvider provider) throws IOException,
                        JsonProcessingException {
                    jgen.writeString("");
                }
            });
            // 进行HTML解码。
            this.registerModule(new SimpleModule().addSerializer(String.class, new JsonSerializer<String>(){
                @Override
                public void serialize(String value, JsonGenerator jgen,
                        SerializerProvider provider) throws IOException,
                        JsonProcessingException {
                    jgen.writeString(StringEscapeUtils.unescapeHtml4(value));
                }
            }));
            // 设置时区
            this.setTimeZone(TimeZone.getDefault());//getTimeZone("GMT+8:00")
        }  

        /**
         * 创建只输出非Null且非Empty(如List.isEmpty)的属性到Json字符串的Mapper,建议在外部接口中使用.
         */
        public static JsonMapper getInstance() {
            if (mapper == null){
                mapper = new JsonMapper().enableSimple();
            }
            return mapper;
        }  

        /**
         * 创建只输出初始值被改变的属性到Json字符串的Mapper, 最节约的存储方式,建议在内部接口中使用。
         */
        public static JsonMapper nonDefaultMapper() {
            if (mapper == null){
                mapper = new JsonMapper(Include.NON_DEFAULT);
            }
            return mapper;
        }  

        /**
         * Object可以是POJO,也可以是Collection或数组。
         * 如果对象为Null, 返回"null".
         * 如果集合为空集合, 返回"[]".
         */
        public String toJson(Object object) {
            try {
                return this.writeValueAsString(object);
            } catch (IOException e) {
                if(LOGGER.isWarnEnabled()){
                    LOGGER.warn("write to json string error:" + object, e);
                }
                return null;
            }
        }  

        /**
         * 反序列化POJO或简单Collection如List<String>.
         *
         * 如果JSON字符串为Null或"null"字符串, 返回Null.
         * 如果JSON字符串为"[]", 返回空集合.
         *
         * 如需反序列化复杂Collection如List<MyBean>, 请使用fromJson(String,JavaType)
         * @see #fromJson(String, JavaType)
         */
        public <T> T fromJson(String jsonString, Class<T> clazz) {
            if (StringUtils.isEmpty(jsonString)) {
                return null;
            }
            try {
                return this.readValue(jsonString, clazz);
            } catch (IOException e) {
                if(LOGGER.isWarnEnabled()){
                    LOGGER.warn("parse json string error:" + jsonString, e);
                }
                return null;
            }
        }  

        /**
         * 反序列化复杂Collection如List<Bean>, 先使用函數createCollectionType构造类型,然后调用本函数.
         * @see #createCollectionType(Class, Class...)
         */
        @SuppressWarnings("unchecked")
        public <T> T fromJson(String jsonString, JavaType javaType) {
            if (StringUtils.isEmpty(jsonString)) {
                return null;
            }
            try {
                return (T) this.readValue(jsonString, javaType);
            } catch (IOException e) {
                if(LOGGER.isWarnEnabled()){
                    LOGGER.warn("parse json string error:" + jsonString, e);
                }
                return null;
            }
        }  

        /**
         * 構造泛型的Collection Type如:
         * ArrayList<MyBean>, 则调用constructCollectionType(ArrayList.class,MyBean.class)
         * HashMap<String,MyBean>, 则调用(HashMap.class,String.class, MyBean.class)
         */
        public JavaType createCollectionType(Class<?> collectionClass, Class<?>... elementClasses) {
            return this.getTypeFactory().constructParametricType(collectionClass, elementClasses);
        }  

        /**
         * 當JSON裡只含有Bean的部分屬性時,更新一個已存在Bean,只覆蓋該部分的屬性.
         */
        @SuppressWarnings("unchecked")
        public <T> T update(String jsonString, T object) {
            try {
                return (T) this.readerForUpdating(object).readValue(jsonString);
            } catch (JsonProcessingException e) {
                if(LOGGER.isWarnEnabled()){
                    LOGGER.warn("update json string:" + jsonString + " to object:" + object + " error.", e);
                }
            } catch (IOException e) {
                if(LOGGER.isWarnEnabled()){
                    LOGGER.warn("update json string:" + jsonString + " to object:" + object + " error.", e);
                }
            }
            return null;
        }  

        /**
         * 輸出JSONP格式數據.
         */
        public String toJsonP(String functionName, Object object) {
            return toJson(new JSONPObject(functionName, object));
        }  

        /**
         * 設定是否使用Enum的toString函數來讀寫Enum,
         * 為False時時使用Enum的name()函數來讀寫Enum, 默認為False.
         * 注意本函數一定要在Mapper創建後, 所有的讀寫動作之前調用.
         */
        public JsonMapper enableEnumUseToString() {
            this.enable(SerializationFeature.WRITE_ENUMS_USING_TO_STRING);
            this.enable(DeserializationFeature.READ_ENUMS_USING_TO_STRING);
            return this;
        }  

        /**
         * 支持使用Jaxb的Annotation,使得POJO上的annotation不用与Jackson耦合。
         * 默认会先查找jaxb的annotation,如果找不到再找jackson的。
         */
        public JsonMapper enableJaxbAnnotation() {
            JaxbAnnotationModule module = new JaxbAnnotationModule();
            this.registerModule(module);
            return this;
        }  

        /**
         * 允许单引号
         * 允许不带引号的字段名称
         */
        public JsonMapper enableSimple() {
            this.configure(Feature.ALLOW_SINGLE_QUOTES, true);
            this.configure(Feature.ALLOW_UNQUOTED_FIELD_NAMES, true);
            return this;
        }  

        /**
         * 取出Mapper做进一步的设置或使用其他序列化API.
         */
        public ObjectMapper getMapper() {
            return this;
        }  

        /**
         * 对象转换为JSON字符串
         * @param object
         * @return
         */
        public static String toJsonString(Object object){
            return JsonMapper.getInstance().toJson(object);
        }  

        /**
         * JSON字符串转换为对象
         * @param jsonString
         * @param clazz
         * @return
         */
        public static <T> T fromJsonString(String jsonString, Class<T> clazz){
            return JsonMapper.getInstance().fromJson(jsonString, clazz);
        }  

        /**
         * 将obj对象转换成 class类型的对象
         * @param obj
         * @param clazz
         * @return
         */
        public static <T> T parseObject(Object obj, Class<T> clazz){
            return JSON.parseObject(JSON.toJSONString(obj), clazz);
        }  

}
时间: 2024-09-26 04:48:27

Redis和Spring的整合的相关文章

Redis 缓存 + Spring 的集成示例

SpringSession和Redis实现Session跨域 http://www.ithao123.cn/content-11111681.html   tomcat中创建session很耗服务器内存 原生session与session in redis对比下面是从stackoverflow上找到的一些观点: Using something like Redis for storing sessions is a great way to get more performance out of

Redis 缓存 + Spring 的集成示例(转)

<整合 spring 4(包括mvc.context.orm) + mybatis 3 示例>一文简要介绍了最新版本的 Spring MVC.IOC.MyBatis ORM 三者的整合以及声明式事务处理.现在我们需要把缓存也整合进来,缓存我们选用的是 Redis,本文将在该文示例基础上介绍 Redis 缓存 + Spring 的集成.关于 Redis 服务器的搭建请参考博客<Redhat5.8 环境下编译安装 Redis 并将其注册为系统服务>. 1. 依赖包安装 pom.xml

maven 整合springmvc spring myatis整合报错

问题描述 maven 整合springmvc spring myatis整合报错 java.util.concurrent.ExecutionException: org.apache.catalina.LifecycleException: Failed to start component [StandardEngine[Tomcat].StandardHost[localhost].StandardContext[]] at java.util.concurrent.FutureTask.

Spring Boot 整合 Elasticsearch,实现 function score query 权重分查询

摘要: 原创出处 www.bysocket.com 「泥瓦匠BYSocket 」欢迎转载,保留摘要,谢谢! 『 预见未来最好的方式就是亲手创造未来 – <史蒂夫·乔布斯传> 』 运行环境:JDK 7 或 8,Maven 3.0+技术栈:SpringBoot 1.5+,ElasticSearch 2.3.2 本文提纲 一.ES 的使用场景 二.运行 springboot-elasticsearch 工程 三.springboot-elasticsearch 工程代码详解 一.ES 的使用场景 简

maven intellij-mybatis+spring mvc整合出的错误!

问题描述 mybatis+spring mvc整合出的错误! 10C 在myeclipse中的配置文件转到intellij用maven创建web项目就出现下面的异常.UserMapper.xml中的方法和UserMapper.java中的一样.改用原生JDBC连接没问题,就是不知道是什么错,请大神帮忙解答.在myeclipse中都没问题的,改maven就不行了,纳闷了. type Exception report message Request processing failed; nested

java类的问题哈哈哈哈-hibernate和spring怎么整合的

问题描述 hibernate和spring怎么整合的 hibernate和spring添加切面配置是如何搞得,请各位大神指点我,谢谢啦 解决方案 http://wenku.baidu.com/link?url=JWek_B9UHh9ZkM1l80KvA1nmq6ePWUTq94zbWQVTe_2rL89R-pWdR3y3uBM5m2aRZBcruy2k2jsniuR3CTUtbLeoGbG4UswO6qECEumiFzu 解决方案二: http://blog.163.com/cui_zhouya

Spring hibernate 整合报的错

问题描述 Spring hibernate 整合报的错 SEVERE: Exception sending context initialized event to listener instance of class org.springframework.web.context.ContextLoaderListenerorg.springframework.beans.factory.BeanCreationException: Error creating bean with name

struts2+spring+ibatis整合需要哪些jar包

问题描述 struts2+spring+ibatis整合需要哪些jar包 struts2+spring+ibatis整合需要哪些jar包

求解:关于struts1+spring+ibatis整合的问题

问题描述 struts1+spring+ibatis整合,都配置好了,可是老报错说找不到方法struts1的配置<action name="timePlanForm" path="/TimePlan"type="org.springframework.web.struts.DelegatingActionProxy" scope="request"parameter="cmd"><forw