`
luckaway
  • 浏览: 136465 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

Servlet的Filter实现页面缓存

阅读更多
java有多个开源的缓存系统都支持页面缓存的,如OScache、Ehcache。
这个例子就是从Ehcache里挖出来的,并做了些改造和简化,但原理在此例子中都是完全体现出来了。该例子只供大家学习用,企业应用还是需要做一些修改的。因为页面数据只是直接存放到HashMap里。

CacheFilter.java
页面数据就是存放到HashMap里,key是url。
public class CacheFilter implements Filter {

	public static final String HEADER_LAST_MODIFIED = "Last-Modified";
	public static final String HEADER_CONTENT_TYPE = "Content-Type";
	public static final String HEADER_CONTENT_ENCODING = "Content-Encoding";
	public static final String HEADER_EXPIRES = "Expires";
	public static final String HEADER_IF_MODIFIED_SINCE = "If-Modified-Since";
	public static final String HEADER_CACHE_CONTROL = "Cache-Control";
	public static final String HEADER_ACCEPT_ENCODING = "Accept-Encoding";

	private static final String REQUEST_FILTERED = "cache_filter_" + CacheFilter.class.getName();

	private final Map<String, ResponseContent> cache = new HashMap<String, ResponseContent>();

	// Last Modified parameter
	private static final long LAST_MODIFIED_INITIAL = -1;

	// Expires parameter
	private static final long EXPIRES_ON = 1;

	private int time = 60 * 60;
	private long lastModified = LAST_MODIFIED_INITIAL;
	private long expires = EXPIRES_ON;
	private long cacheControlMaxAge = -60;

	@Override
	public void destroy() {

	}

	@Override
	public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException,
			ServletException {
		HttpServletRequest request = (HttpServletRequest) req;
		//避免重复调用
		if (isFilteredBefore(request)) {
			chain.doFilter(request, res);
			return;
		}
		request.setAttribute(REQUEST_FILTERED, Boolean.TRUE);

		String key = getCacheKey(request);

		ResponseContent responseContent = cache.get(key);
		if (responseContent != null) {//如果当前的URL已经有对应的响应内容
			responseContent.writeTo(res);
			return;
		}
//用CacheHttpServletResponseWrapper来代替HttpServletResponse,用于记录HttpServletResponse输出的内容。
		CacheHttpServletResponseWrapper cacheResponse = new CacheHttpServletResponseWrapper((HttpServletResponse) res,
				time * 1000L, lastModified, expires, cacheControlMaxAge);
		chain.doFilter(request, cacheResponse);
		cacheResponse.flushBuffer();
		// Store as the cache content the result of the response
		cache.put(key, cacheResponse.getContent());
	}

	private String getCacheKey(HttpServletRequest request) {
		StringBuilder builder = new StringBuilder(request.getRequestURI());
		if (StringUtils.isNotEmpty(request.getQueryString())) {
			builder.append("_").append(request.getQueryString());
		}
		return builder.toString();
	}

	/**
	 * Checks if the request was filtered before, so guarantees to be executed
	 * once per request. You can override this methods to define a more specific
	 * behaviour.
	 * 
	 * @param request checks if the request was filtered before.
	 * @return true if it is the first execution
	 */
	public boolean isFilteredBefore(ServletRequest request) {
		return request.getAttribute(REQUEST_FILTERED) != null;
	}

	@Override
	public void init(FilterConfig arg0) throws ServletException {
		// TODO Auto-generated method stub
	}
}



HttpServletResponseWrapper.java
用ResponseContent记录HttpServletResponse输出的信息
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.util.Locale;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * 缓存的HttpServletResponseWrapper,它会把{@link HttpServletResponse}的部分数据记录到{@link ResponseContent}里。
 */
public class CacheHttpServletResponseWrapper extends HttpServletResponseWrapper {
	private final Log log = LogFactory.getLog(this.getClass());

	private PrintWriter cachedWriter = null;
	private ResponseContent result = null;
	private SplitServletOutputStream cacheOut = null;
	private int status = SC_OK;
	private long cacheControl = -60;

	public CacheHttpServletResponseWrapper(HttpServletResponse response) {
		this(response, Long.MAX_VALUE, CacheFilter.EXPIRES_ON, CacheFilter.LAST_MODIFIED_INITIAL, -60);
	}


	public CacheHttpServletResponseWrapper(HttpServletResponse response, long time, long lastModified, long expires,
			long cacheControl) {
		super(response);
		this.result = new ResponseContent();
		this.cacheControl = cacheControl;

		// setting a default last modified value based on object creation and
		// remove the millis
		if (lastModified == CacheFilter.LAST_MODIFIED_INITIAL) {
			long current = System.currentTimeMillis();
			current = current - (current % 1000);
			result.setLastModified(current);
			super.setDateHeader(CacheFilter.HEADER_LAST_MODIFIED, result.getLastModified());
		}
		// setting the expires value
		if (expires == CacheFilter.EXPIRES_TIME) {
			result.setExpires(result.getLastModified() + time);
			super.setDateHeader(CacheFilter.HEADER_EXPIRES, result.getExpires());
		}
		// setting the cache control with max-age
		if (this.cacheControl == CacheFilter.MAX_AGE_TIME) {
			// set the count down
			long maxAge = System.currentTimeMillis();
			maxAge = maxAge - (maxAge % 1000) + time;
			result.setMaxAge(maxAge);
			super.addHeader(CacheFilter.HEADER_CACHE_CONTROL, "max-age=" + time / 1000);
		} else if (this.cacheControl != CacheFilter.MAX_AGE_NO_INIT) {
			result.setMaxAge(this.cacheControl);
			super.addHeader(CacheFilter.HEADER_CACHE_CONTROL, "max-age=" + (-this.cacheControl));
		} else if (this.cacheControl == CacheFilter.MAX_AGE_NO_INIT) {
			result.setMaxAge(this.cacheControl);
		}
	}
	/**
	 * Get a response content
	 * 
	 * @return The content
	 */
	public ResponseContent getContent() {
		// Flush the buffer
		try {
			flush();
		} catch (IOException ignore) {
		}
		// Create the byte array
		result.commit();
		// Return the result from this response
		return result;
	}

	/**
	 * Set the content type
	 * 
	 * @param value The content type
	 */
	public void setContentType(String value) {
		if (log.isDebugEnabled()) {
			log.debug("ContentType: " + value);
		}
		super.setContentType(value);
		result.setContentType(value);
	}

	/**
	 * Set a header field
	 * 
	 * @param name The header name
	 * @param value The header value
	 */
	public void setHeader(String name, String value) {
		if (log.isDebugEnabled()) {
			log.debug("header: " + name + ": " + value);
		}
		if (CacheFilter.HEADER_CONTENT_TYPE.equalsIgnoreCase(name)) {
			result.setContentType(value);
		}

		if (CacheFilter.HEADER_CONTENT_ENCODING.equalsIgnoreCase(name)) {
			result.setContentEncoding(value);
		}
		super.setHeader(name, value);
	}

	/**
	 * Add a header field
	 * 
	 * @param name The header name
	 * @param value The header value
	 */
	public void addHeader(String name, String value) {
		if (log.isDebugEnabled()) {
			log.debug("header: " + name + ": " + value);
		}

		if (CacheFilter.HEADER_CONTENT_TYPE.equalsIgnoreCase(name)) {
			result.setContentType(value);
		}

		if (CacheFilter.HEADER_CONTENT_ENCODING.equalsIgnoreCase(name)) {
			result.setContentEncoding(value);
		}

		super.addHeader(name, value);
	}

	/**
	 * We override this so we can catch the response status. Only responses with
	 * a status of 200 (<code>SC_OK</code>) will be cached.
	 */
	public void setStatus(int status) {
		super.setStatus(status);
		this.status = status;
	}

	/**
	 * We override this so we can catch the response status. Only responses with
	 * a status of 200 (<code>SC_OK</code>) will be cached.
	 */
	public void sendError(int status, String string) throws IOException {
		super.sendError(status, string);
		this.status = status;
	}

	/**
	 * We override this so we can catch the response status. Only responses with
	 * a status of 200 (<code>SC_OK</code>) will be cached.
	 */
	public void sendError(int status) throws IOException {
		super.sendError(status);
		this.status = status;
	}

	/**
	 * We override this so we can catch the response status. Only responses with
	 * a status of 200 (<code>SC_OK</code>) will be cached.
	 */
	public void setStatus(int status, String string) {
		super.setStatus(status, string);
		this.status = status;
	}

	/**
	 * We override this so we can catch the response status. Only responses with
	 * a status of 200 (<code>SC_OK</code>) will be cached.
	 */
	public void sendRedirect(String location) throws IOException {
		this.status = SC_MOVED_TEMPORARILY;
		super.sendRedirect(location);
	}

	/**
	 * Retrieves the captured HttpResponse status.
	 */
	public int getStatus() {
		return status;
	}

	/**
	 * Set the locale
	 * 
	 * @param value The locale
	 */
	public void setLocale(Locale value) {
		super.setLocale(value);
		result.setLocale(value);
	}

	/**
	 * Get an output stream
	 * 
	 * @throws IOException
	 */
	public ServletOutputStream getOutputStream() throws IOException {
		// Pass this faked servlet output stream that captures what is sent
		if (cacheOut == null) {
			cacheOut = new SplitServletOutputStream(result.getOutputStream(), super.getOutputStream());
		}
		return cacheOut;
	}

	/**
	 * Get a print writer
	 * 
	 * @throws IOException
	 */
	public PrintWriter getWriter() throws IOException {
		if (cachedWriter == null) {
			String encoding = getCharacterEncoding();
			if (encoding != null) {
				cachedWriter = new PrintWriter(new OutputStreamWriter(getOutputStream(), encoding));
			} else { // using the default character encoding
				cachedWriter = new PrintWriter(new OutputStreamWriter(getOutputStream()));
			}
		}

		return cachedWriter;
	}

	/**
	 * Flushes all streams.
	 * 
	 * @throws IOException
	 */
	private void flush() throws IOException {
		if (cacheOut != null) {
			cacheOut.flush();
		}

		if (cachedWriter != null) {
			cachedWriter.flush();
		}
	}

	public void flushBuffer() throws IOException {
		super.flushBuffer();
		flush();
	}
}





ResponseContent.java
响应后的内容,就是需要cache的对象,包含页面内容和Content-Type、Last-Modified、Content-Encoding等一些响应头的信息。
import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.Serializable;
import java.util.Locale;
import java.util.zip.GZIPInputStream;

import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletResponse;

/**
 * Holds the servlet response in a byte array so that it can be held in the
 * cache (and, since this class is serializable, optionally persisted to disk).
 * 
 * @version $Revision: 362 $
 * @author <a href="mailto:sergek@lokitech.com">Serge Knystautas</a>
 */
public class ResponseContent implements Serializable {
	private static final long serialVersionUID = 1L;
	private transient ByteArrayOutputStream bout = new ByteArrayOutputStream(1000);
	private Locale locale = null;
	private String contentEncoding = null;
	private String contentType = null;
	private byte[] content = null;
	private long expires = Long.MAX_VALUE;
	private long lastModified = -1;
	private long maxAge = -60;

	public String getContentType() {
		return contentType;
	}

	/**
	 * Set the content type. We capture this so that when we serve this data
	 * from cache, we can set the correct content type on the response.
	 */
	public void setContentType(String value) {
		contentType = value;
	}

	public long getLastModified() {
		return lastModified;
	}

	public void setLastModified(long value) {
		lastModified = value;
	}

	public String getContentEncoding() {
		return contentEncoding;
	}

	public void setContentEncoding(String contentEncoding) {
		this.contentEncoding = contentEncoding;
	}

	/**
	 * Set the Locale. We capture this so that when we serve this data from
	 * cache, we can set the correct locale on the response.
	 */
	public void setLocale(Locale value) {
		locale = value;
	}

	/**
	 * @return the expires date and time in miliseconds when the content will be
	 *         stale
	 */
	public long getExpires() {
		return expires;
	}

	/**
	 * Sets the expires date and time in miliseconds.
	 * 
	 * @param value time in miliseconds when the content will expire
	 */
	public void setExpires(long value) {
		expires = value;
	}

	/**
	 * Returns the max age of the content in miliseconds. If expires header and
	 * cache control are enabled both, both will be equal.
	 * 
	 * @return the max age of the content in miliseconds, if -1 max-age is
	 *         disabled
	 */
	public long getMaxAge() {
		return maxAge;
	}

	/**
	 * Sets the max age date and time in miliseconds. If the parameter is -1,
	 * the max-age parameter won't be set by default in the Cache-Control
	 * header.
	 * 
	 * @param value sets the intial
	 */
	public void setMaxAge(long value) {
		maxAge = value;
	}

	/**
	 * Get an output stream. This is used by the
	 * {@link SplitServletOutputStream} to capture the original (uncached)
	 * response into a byte array.
	 * 
	 * @return the original (uncached) response, returns null if response is
	 *         already committed.
	 */
	public OutputStream getOutputStream() {
		return bout;
	}

	/**
	 * Gets the size of this cached content.
	 * 
	 * @return The size of the content, in bytes. If no content exists, this
	 *         method returns <code>-1</code>.
	 */
	public int getSize() {
		return (content != null) ? content.length : (-1);
	}

	/**
	 * Called once the response has been written in its entirety. This method
	 * commits the response output stream by converting the output stream into a
	 * byte array.
	 */
	public void commit() {
		if (bout != null) {
			content = bout.toByteArray();
			bout = null;
		}
	}

	/**
	 * Writes this cached data out to the supplied <code>ServletResponse</code>.
	 * 
	 * @param response The servlet response to output the cached content to.
	 * @throws IOException
	 */
	public void writeTo(ServletResponse response) throws IOException {
		writeTo(response, false, false);
	}

	/**
	 * Writes this cached data out to the supplied <code>ServletResponse</code>.
	 * 
	 * @param response The servlet response to output the cached content to.
	 * @param fragment is true if this content a fragment or part of a page
	 * @param acceptsGZip is true if client browser supports gzip compression
	 * @throws IOException
	 */
	public void writeTo(ServletResponse response, boolean fragment, boolean acceptsGZip) throws IOException {
		// Send the content type and data to this response
		if (contentType != null) {
			response.setContentType(contentType);
		}

		if (fragment) {
			// Don't support gzip compression if the content is a fragment of a
			// page
			acceptsGZip = false;
		} else {
			// add special headers for a complete page
			if (response instanceof HttpServletResponse) {
				HttpServletResponse httpResponse = (HttpServletResponse) response;

				// add the last modified header
				if (lastModified != -1) {
					httpResponse.setDateHeader(CacheFilter.HEADER_LAST_MODIFIED, lastModified);
				}

				// add the expires header
				if (expires != Long.MAX_VALUE) {
					httpResponse.setDateHeader(CacheFilter.HEADER_EXPIRES, expires);
				}

				// add the cache-control header for max-age
				if (maxAge == CacheFilter.MAX_AGE_NO_INIT || maxAge == CacheFilter.MAX_AGE_TIME) {
					// do nothing
				} else if (maxAge > 0) { // set max-age based on life time
					long currentMaxAge = maxAge / 1000 - System.currentTimeMillis() / 1000;
					if (currentMaxAge < 0) {
						currentMaxAge = 0;
					}
					httpResponse.addHeader(CacheFilter.HEADER_CACHE_CONTROL, "max-age=" + currentMaxAge);
				} else {
					httpResponse.addHeader(CacheFilter.HEADER_CACHE_CONTROL, "max-age=" + (-maxAge));
				}

			}
		}

		if (locale != null) {
			response.setLocale(locale);
		}

		OutputStream out = new BufferedOutputStream(response.getOutputStream());

		if (isContentGZiped()) {
			if (acceptsGZip) {
				((HttpServletResponse) response).addHeader(CacheFilter.HEADER_CONTENT_ENCODING, "gzip");
				response.setContentLength(content.length);
				out.write(content);
			} else {
				// client doesn't support, so we have to uncompress it
				ByteArrayInputStream bais = new ByteArrayInputStream(content);
				GZIPInputStream zis = new GZIPInputStream(bais);

				ByteArrayOutputStream baos = new ByteArrayOutputStream();
				int numBytesRead = 0;
				byte[] tempBytes = new byte[4196];

				while ((numBytesRead = zis.read(tempBytes, 0, tempBytes.length)) != -1) {
					baos.write(tempBytes, 0, numBytesRead);
				}

				byte[] result = baos.toByteArray();

				response.setContentLength(result.length);
				out.write(result);
			}
		} else {
			// the content isn't compressed
			// regardless if the client browser supports gzip we will just
			// return the content
			response.setContentLength(content.length);
			out.write(content);
		}
		out.flush();
	}

	/**
	 * @return true if the content is GZIP compressed
	 */
	public boolean isContentGZiped() {
		return "gzip".equals(contentEncoding);
	}


SplitServletOutputStream.java

package com.dukuai.metis.search.servlet;

import java.io.IOException;
import java.io.OutputStream;

import javax.servlet.ServletOutputStream;

/**
 * Extends the base <code>ServletOutputStream</code> class so that the stream
 * can be captured as it gets written. This is achieved by overriding the
 * <code>write()</code> methods and outputting the data to two streams - the
 * original stream and a secondary stream that is designed to capture the
 * written data.
 * 
 * @version $Revision: 393 $
 * @author <a href="mailto:sergek@lokitech.com">Serge Knystautas</a>
 */
public class SplitServletOutputStream extends ServletOutputStream {
	OutputStream captureStream = null;
	OutputStream passThroughStream = null;

	/**
	 * Constructs a split output stream that both captures and passes through
	 * the servlet response.
	 * 
	 * @param captureStream The stream that will be used to capture the data.
	 * @param passThroughStream The pass-through
	 *            <code>ServletOutputStream</code> that will write the
	 *            response to the client as originally intended.
	 */
	public SplitServletOutputStream(OutputStream captureStream, OutputStream passThroughStream) {
		this.captureStream = captureStream;
		this.passThroughStream = passThroughStream;
	}

	/**
	 * Writes the incoming data to both the output streams.
	 * 
	 * @param value The int data to write.
	 * @throws IOException
	 */
	public void write(int value) throws IOException {
		captureStream.write(value);
		passThroughStream.write(value);
	}

	/**
	 * Writes the incoming data to both the output streams.
	 * 
	 * @param value The bytes to write to the streams.
	 * @throws IOException
	 */
	public void write(byte[] value) throws IOException {
		captureStream.write(value);
		passThroughStream.write(value);
	}

	/**
	 * Writes the incoming data to both the output streams.
	 * 
	 * @param b The bytes to write out to the streams.
	 * @param off The offset into the byte data where writing should begin.
	 * @param len The number of bytes to write.
	 * @throws IOException
	 */
	public void write(byte[] b, int off, int len) throws IOException {
		captureStream.write(b, off, len);
		passThroughStream.write(b, off, len);
	}

	/**
	 * Flushes both the output streams.
	 * 
	 * @throws IOException
	 */
	public void flush() throws IOException {
		super.flush();
		captureStream.flush(); // why not?
		passThroughStream.flush();
	}

	/**
	 * Closes both the output streams.
	 * 
	 * @throws IOException
	 */
	public void close() throws IOException {
		super.close();
		captureStream.close();
		passThroughStream.close();
	}

}



分享到:
评论

相关推荐

    serlvet 的过滤器实现缓存机制

    NULL 博文链接:https://bushyou.iteye.com/blog/1187666

    Web应用与开发作业

    (2)有3个http响应头字段可以禁止浏览器缓存当前页面,它们在Servlet中的示例代码如下。 response.setDateHeader("Expires",-1); response.setHeader("Cache-Control","no-cache"); response.setHeader("Pragma",...

    idc网管系统

    本项目结 构上分为表现层、业务层和数据访问层,层次间的依赖关系自下到上。采用的技术有Struts,Spring,Hibernate,Log4J,JDom 等。其中表现层采用Struts框架开发;...为提高性能,采用Servlet Filter实现了缓存代理

    Listener、Filter、Servlet与Java Web项目初始化项目例子 源代码

    通常JavaWeb项目启动时我们需要... 下面代码是模拟初始化的一个示例,可以在控制台看到程序的输出,和Listener、Filter、Servlet的启动先后顺序,强烈建议跟我一样喜欢动手的Coder操作一下,废话不多说,直接上代码了。

    servlet四个有用的过滤器

    servlet四个有用的过滤器,包括中文转码,缓存过滤等等

    JEE-Cache-Filter:Tomcat的JEE缓存过滤器

    最优缓存策略制定参见我的文章: ####使用参考 DisableETagFilter web.xml filter配置 &lt;filter&gt; &lt;filter&gt;disableETagFilter&lt;/filter&gt; &lt;filter&gt;com.huawei.universe.ckm.web.filter.DisableETagFilter&lt;/filter&gt; ...

    xsession:A java web servlet filter for distributed session cached . 分布式Java Web Session缓存

    基于filter机制。 优点 支持多种序列化方式,默认使用Java自带的Serializable方式(兼容性最好); 支持多种Session存储方案,默认是redis; 合并session的写操作,一个request里的多次session操作,只会写一次缓存...

    springboot+权限管理系统 shiro + ssm实现 实现菜单,自用

    springboot+Listener(监听器),Filter(过滤器),Interceptor(拦截器),Servlet,springmvc静态资源,文件上传下载,多数据源切换,缓存,quartz定时任务(没有具体业务实现)等技术点都在项目中实现了,可谓是麻雀虽小五脏俱全!...

    Java 权限管理系统 shiro + ssm实现

    springboot+Listener(监听器),Filter(过滤器),Interceptor(拦截器),Servlet,springmvc静态资源,文件上传下载,多数据源切换,缓存,quartz定时任务(没有具体业务实现)等技术点都在项目中实现了,可谓是麻雀虽小五脏俱全!...

    权限管理系统 shiro + ssm实现

    springboot+Listener(监听器),Filter(过滤器),Interceptor(拦截器),Servlet,springmvc静态资源,文件上传下载,多数据源切换,缓存,quartz定时任务(没有具体业务实现)等技术点都在项目中实现了,可谓是麻雀虽小五脏俱全!...

    JSP-Servlet-CartDemo:Javaweb基础案例之JSP+Servlet实现在线课程网站购物车功能

    学习如何设计并实现本地JVM缓存 在线学习网站购物车功能 1、课程列表显示 2、加入购物车、加入收藏 3、购物车列表显示及删除、计算 4、收藏列表显示及删除 应用到的技术点 JSP+JSTL Servlet及内部判断 LocalCache ...

    5个Servlet过滤器实例源码(JSP)

    Servlet过滤器大全,各种详细使用的代码! 一、字符编码的过滤器 二、使浏览器不缓存页面的过滤器 三、检测用户是否登陆的过滤器 四、资源保护过滤器 五 利用Filter限制用户浏览权限

    gwt-cache-filter:一个servlet过滤器,它基于* .cache。*和* .nocache。*命名模式向GWT生成的文件中添加适当的http缓存头。

    GWTCacheControlFilter基于GWTCacheControlFilter将适当的缓存属性添加到GWT生成的文件中。 和.nocache。 命名模式。 如果文件系统上存在一个PreEncodedGzipFilter它将提供静态文件的gzip变体,并且请求指定“ ...

    spring boot+mvc+mybatis(通用mapper)+druid+jsp+bootstrap实现后台权限管理系统源码

    项目基于jdk1.8整合了springboot+mvc+mybatis(通用mapper)+druid+jsp+bootstrap等技术,springboot+Listener(监听器),Filter(过滤器),Interceptor(拦截器),Servlet,springmvc静态资源,文件上传下载,多数据源切换,缓存,...

    springboot+mybatis+druid+jsp+bootstrap实现后台权限管理系统源码.zip

    springboot+Listener(监听器),Filter(过滤器),Interceptor(拦截器),Servlet,springmvc静态资源,文件上传下载,多数据源切换,缓存,quartz定时任务(没有具体业务实现)等技术点都在项目中实现了,可谓是麻雀虽小五脏俱全!...

    整合springboot+mvc+mybatis(通用mapper)+druid+jsp+bootstrap实现权限管理文件上传下载多数据源切换等功能

    springboot+Listener(监听器),Filter(过滤器),Interceptor(拦截器),Servlet,springmvc静态资源,文件上传下载,多数据源切换,缓存,quartz定时任务(没有具体业务实现)等技术点都在项目中实现了,可谓是麻雀虽小五脏俱全!...

    springboot权限控制系统

    springboot+Listener(监听器),Filter(过滤器),Interceptor(拦截器),Servlet,springmvc静态资源,文件上传下载,多数据源切换,缓存,quartz定时任务(没有具体业务实现)等技术点都在项目中实现了,可谓是麻雀虽小五脏俱全!...

    JAVA EE 开发手册.CHM

    JAVA EE 开发手册 JavaEE是一个开放的、 基于标准的平台,用以开发、...3、Servlet2 之缓存技术 4、JSP和El表达式 5、Filter过滤器 6、JDBC之基础介绍 7、30之基础介绍 8、监听器Listener 9、注解( Annotation)

    EHCache 配置说明

    It features memory and disk stores, replicate by copy and invalidate, listeners, cache loaders, cache extensions, cache exception handlers, a gzip caching servlet filter and much more...

    travel.zip

    基于html+ajax的旅游网站实现,实现前后端分离,用到的技术:html+css+jquery+ajax异步请求+servlet+mysql数据库+redis缓存优化,通过filter实现自动登录,用jdbcTemplate实现数据库的增、删、改、查、

Global site tag (gtag.js) - Google Analytics