在Spring中使用Cache缓存方法返回

刚好在项目中用到了Spring中的Cache缓存服务层方法的返回值

使用相当简单,直接用@Cacheable注解标注需要缓存返回值的方法,如下

@Cacheable(value = "billfoldCache", key = "'fetchByGroupName' + #groupName")
Collection<Game> fetchByGroupName(final String groupName) {
}

其中value的值是缓存的名字,在ehcache配置文件中指定(如果使用ehcache作为底层缓存),key是缓存的键,并支持Spring Expression Language (SpEL)。

配置
目前Spring中带了两种实现,一是基于ehcache,另一个是基于Java中的ConcurrentMap实现。项目是使用的是ehcache

配置也很简单,在Spring配置文件中加入

    <cache :annotation-driven cache-manager="billfoldCacheManager"></cache>
 
    <bean id="billfoldCacheManager" class="org.springframework.cache.ehcache.EhCacheCacheManager"
          p:cacheManager-ref="billfoldEhCacheManagerFactoryBean"></bean>
 
    <bean id="billfoldEhCacheManagerFactoryBean"
          class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean"
          p:configLocation="classpath:/ctx-ehcache.xml"></bean>

并且为ehcache创建名为ctx-ehcache.xml的配置文件

< ?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:noNamespaceSchemaLocation="ehcache.xsd" updateCheck="false">
    <diskstore path="java.io.tmpdir"></diskstore>
    <defaultcache maxEntriesLocalHeap="100000" eternal="false"
                  timeToIdleSeconds="120" timeToLiveSeconds="120" maxEntriesLocalDisk="1000000"
                  diskExpiryThreadIntervalSeconds="3600" memoryStoreEvictionPolicy="LFU">
    </defaultcache>
 
    <cache name="billfoldCache" eternal="false" maxEntriesLocalHeap="100000"
           timeToLiveSeconds="60" timeToIdleSeconds="60">
        <persistence strategy="none"></persistence>
    </cache>
</ehcache>

需要注意的是:

  • Spring推荐在实现类上使用@Cache*注解,而不是在接口上使用
  • 在默认的配置下(proxy),@Cache*只对外部调用具有public可见性的方法起作用,protected, private, package-visible以及内部方法调用,缓存都不会生效。除非使用在配置时明确指定mode为aspectj。详细见Spring CacheSpring Configuration – load time weaver

解决自定义文件上传处理与Spring MultipartResolver的冲突问题

  在原项目中,对文件上传的处理并不是使用Spring的MultipartResolver,而是使用自定义的MultiPartFilter和HttpServletRequestWrapper结合来进行处理。

  MultiPartFilter是一个定义在web.xml中的<filter>,原理是通过判断HttpServletRequest中的contentType是否包含”multipart/form-data”,若代码中包含此字符品,则表明是一个File Upload请求,就使用JakartaMultiPartRequest来对HttpServletRequest对其进行包装。

  其代码如下:

public class MultiPartFilter extends GenericFilterBean {
 
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
            throws IOException, ServletException {
 
		HttpServletRequest request = (HttpServletRequest) servletRequest;
 
		String content_type = request.getContentType();
		if (content_type != null && content_type.indexOf("multipart/form-data") != -1) {
			JakartaMultiPartRequest jakartaMultiPartRequest = new JakartaMultiPartRequest(request,
					getFilterConfig().getInitParameter("saveDir"), Integer.valueOf(
							getFilterConfig().getInitParameter("maxUploadSize")).intValue());
 
			request = jakartaMultiPartRequest;
		}
 
		filterChain.doFilter(request, servletResponse);
    }
 
}

  JakartaMultiPartRequest类继承自HttpServletRequestWrapper,在其构造方法中,我们对Request进行分析,并且保存用户上传的文件,将文件名等信息放到Request的attribute中,从而在后续的代码中可以对上传的文件进行处理。

  但在后来客户进出的对产品图片处理的新需求中,因为JakartaMultiPartRequest的问题,没有办法满足我们的要求,所以考虑到使用Spring的MultipartResolver来进行上传处理,但之前的代码不能再进行改动,否则所有处理文件上传的Controller必须更改,代价很大。但如果在Spring中进行配置MultipartResolver的配置后,发现我们旧的文件上传代码都失效了,无法上传文件。
继续阅读“解决自定义文件上传处理与Spring MultipartResolver的冲突问题”

在Spring中使用ActiveMQ发送邮件

环境:Spring 2.5, ActiveMQ 5.1

  项目的后台要求在更改密码后发送邮件通知用户,为了避免发送邮件时程序对用户操作的阻塞,之前中文版中使用了线程来发送邮件,而在英文版中,我决定使用JMS来异步发送邮件,让用户更改密码的操作和发送邮件的操作更进一步解耦,也在实际环境中试试JMS。

  我们的环境是Spring 2.5, Tomcat 5.5,使用ActiveMQ来实现JMS传送和接收。

  首先,我们在Spring中加入ActiveMQ Broker的配置:

    <bean id="broker"
        class="org.apache.activemq.xbean.BrokerFactoryBean">
        <property name="config"
            value="classpath:activemq.xml"></property>
        <property name="start"
            value="true"></property>
    </bean>

  我们在此处配置了BrokerFactoryBean,此Bean实现在Spring中配置嵌入式Broker,并且支持XBean方式的配置。Broker的配置文件由config属性指定,此处定义配置文件位于classpath中的activemq.xml。

继续阅读“在Spring中使用ActiveMQ发送邮件”