Filter概述

Filter不用于客户端请求,只用于对request,response进行修改或对context,session,request事件进行监听。

1.概述

如上图,多个filter组成一个FilterChain。

2.Filter接口

3.Filter配置

防盗链Filter

代码详解:

1.编写过滤器

public class ImageRedirectFilter implements Filter {

public void init(FilterConfig config) throws ServletException {

}

public void doFilter(ServletRequest req, ServletResponse res,

FilterChain chain) throws IOException, ServletException {

HttpServletRequest request = (HttpServletRequest) req;

HttpServletResponse response = (HttpServletResponse) res;

// 禁止缓存

response.setHeader("Cache-Control", "no-store");

response.setHeader("Pragrma", "no-cache");

response.setDateHeader("Expires", 0);

// 链接来源地址

String referer = request.getHeader("referer");

if (referer == null || !referer.contains(request.getServerName())) {

request.getServerName()//获取你的网站的域名

/**

* 如果 链接地址来自其他网站,则返回错误图片

*/

request.getRequestDispatcher("/error.gif").forward(request,

response);

} else {

/**

* 图片正常显示

*/

chain.doFilter(request, response);

}

}

public void destroy() {

}

}

过滤器继承Filter接口,重载三个方法,分别是init(),doFilter(),destroy().其中doFilter()中必须执行

chain.doFilter(request, response);以便于执行接下来的过滤器。在doFilter()中判断浏览器请求的源地址和服务器的域名是否一致。

2.过滤器配置

通过配置<url-pattern>,说明只有当浏览器访问/images或者/upload/images目录时才执行该过滤器。

字符编码Filter

public class CharacterEncodingFilter implements Filter {

private String characterEncoding;

private String enable;

private boolean enabled;

@Override

public void init(FilterConfig config) throws ServletException {

characterEncoding = config.getInitParameter("characterEncoding");

enable = config.getInitParameter("enable");

enabled = "true".equalsIgnoreCase(enable.trim())

|| "1".equalsIgnoreCase(enable.trim());

}

@Override

public void doFilter(ServletRequest request, ServletResponse response,

FilterChain chain) throws IOException, ServletException {

if (enabled&&characterEncoding != null) {

request.setCharacterEncoding(characterEncoding);

response.setCharacterEncoding(characterEncoding);

}

chain.doFilter(request, response);

}

@Override

public void destroy() {

characterEncoding = null;

}

}

个人认为编码Filter应该处于第一个Filter,这样,在tomcat解析request参数前就对参数进行编码设置了。

日志记录Filter

public class LogFilter implements Filter {

private Log log = LogFactory.getLog(this.getClass());

private String filterName;

public void init(FilterConfig config) throws ServletException {

// 获取 Filter 的 name,配置在 web.xml 中

filterName = config.getFilterName();

log.info("启动 Filter: " + filterName);

}

public void doFilter(ServletRequest req, ServletResponse res,

FilterChain chain) throws IOException, ServletException {

HttpServletRequest request = (HttpServletRequest) req;

HttpServletResponse response = (HttpServletResponse) res;

long startTime = System.currentTimeMillis();

String requestURI = request.getRequestURI();

requestURI = request.getQueryString() == null ? requestURI

: (requestURI + "?" + request.getQueryString()); //判断请求中是否含有参数,如果有,则加入到requestURI中去。

chain.doFilter(request, response);

long endTime = System.currentTimeMillis();

log.info(request.getRemoteAddr() + " 访问了 " + requestURI + ", 总用时 "

+ (endTime - startTime) + " 毫秒。");//request.getRemoteAddr()返回客户端或代理服务器的IP地址

}

public void destroy() {

log.info("关闭 Filter: " + filterName);

}

}

web配置省略。

异常捕捉Filter

public class ExceptionHandlerFilter implements Filter {

public void destroy() {

}

public void doFilter(ServletRequest request, ServletResponse response,

FilterChain chain) throws IOException, ServletException {

try { //捕获异常

chain.doFilter(request, response);

} catch (Exception e) { //得到异常后,根据不同异常作相应的处理

Throwable rootCause = e;

while (rootCause.getCause() != null) {

rootCause = rootCause.getCause();

}

String message = rootCause.getMessage();

message = message == null ? "异常:" + rootCause.getClass().getName()

: message;

request.setAttribute("message", message);

request.setAttribute("e", e);

if (rootCause instanceof AccountException) {//判断rootCause是否是AccountException的一个实例

request.getRequestDispatcher("/accountException.jsp").forward(

request, response);

} else if (rootCause instanceof BusinessException) {

request.getRequestDispatcher("/businessException.jsp").forward(

request, response);

} else {

request.getRequestDispatcher("/exception.jsp").forward(request,

response);

}

}

}

public void init(FilterConfig arg0) throws ServletException {

}

}

权限验证Filter

public class PrivilegeFilter implements Filter {

private Properties pp = new Properties();

public void init(FilterConfig config) throws ServletException {

// 从 初始化参数 中获取权 限配置文件 的位置

String file = config.getInitParameter("file");

String realPath = config.getServletContext().getRealPath(file);

try {

pp.load(new FileInputStream(realPath));

} catch (Exception e) {

config.getServletContext().log("读取权限控制文件失败。", e);

}

}

public void doFilter(ServletRequest req, ServletResponse res,

FilterChain chain) throws IOException, ServletException {

HttpServletRequest request = (HttpServletRequest) req;

// 获取访问的路径,例如:admin.jsp

String requestURI = request.getRequestURI().replace(

request.getContextPath() + "/", "");

// 获取 action 参数,例如:add

String action = req.getParameter("action");

action = action == null ? "" : action;

// 拼接成 URI。例如:log.do?action=list

String uri = requestURI + "?action=" + action;

// 从 session 中获取用户权限角色。

String role = (String) request.getSession(true).getAttribute("role");

role = role == null ? "guest" : role;

boolean authentificated = false;

// 开始检查该用户角色是否有权限访问 uri

for (Object obj : pp.keySet()) {

String key = ((String) obj);

// 使用正则表达式验证 需要将 ? . 替换一下,并将通配符 * 处理一下

if (uri.matches(key.replace("?", "\\?").replace(".", "\\.")

.replace("*", ".*"))) {

// 如果 role 匹配

if (role.equals(pp.get(key))) {

authentificated = true;

break;

}

}

}

if (!authentificated) {

throw new RuntimeException(new AccountException(

"您无权访问该页面。请以合适的身份登陆后查看。"));

}

// 继续运行

chain.doFilter(req, res);

}

public void destroy() {

pp = null;

}

}

内容替换Filter

public class HttpCharacterResponseWrapper extends HttpServletResponseWrapper {

private CharArrayWriter charArrayWriter = new CharArrayWriter();

public HttpCharacterResponseWrapper(HttpServletResponse response) {

super(response);

}

@Override

public PrintWriter getWriter() throws IOException {

return new PrintWriter(charArrayWriter);//将response的输出目标变为charArrayWriter

}

public CharArrayWriter getCharArrayWriter() {

return charArrayWriter;

}

}

public class OutputReplaceFilter implements Filter {

private Properties pp = new Properties();

public void init(FilterConfig config) throws ServletException {

String file = config.getInitParameter("file");

String realPath = config.getServletContext().getRealPath(file);

try {

pp.load(new FileInputStream(realPath));

} catch (IOException e) {

}

}

public void doFilter(ServletRequest req, ServletResponse res,

FilterChain chain) throws IOException, ServletException {

// 自定义的 response

HttpCharacterResponseWrapper response = new HttpCharacterResponseWrapper(

(HttpServletResponse) res);

// 提交给 Servlet 或者下一个 Filter

chain.doFilter(req, response);

// 得到缓存在自定义 response 中的输出内容

String output = response.getCharArrayWriter().toString();

// 修改,替换

for (Object obj : pp.keySet()) {

String key = (String) obj;

output = output.replace(key, pp.getProperty(key));

}

// 输出

PrintWriter out = res.getWriter();

out.write(output);

out.println("<!-- Generated at " + new java.util.Date() + " -->");

}

public void destroy() {

}

}

代码解析:

1.定义一个类HttpCharacterResponseWrapper用于替换原来的response。

2.在过滤器中用res声明一个新的response:

HttpCharacterResponseWrapper response = new HttpCharacterResponseWrapper(

(HttpServletResponse) res);

3.调用chain.doFilter(req, response)时,如果有输出就会调用response.getWriter(), 此时调用的是覆盖后的PrintWriter(),该PrintWriter()将会把数据写到charArrayWriter中去。

4.执行完chain.doFilter(req, response)后,获取charArrayWriter中的数据:

String output = response.getCharArrayWriter().toString();

5.修改output数据

6.再通过原始的res来输出output中的数据

PrintWriter out = res.getWriter();

out.write(output);

以上代码的功能是先将res包装成response(此时response的输出目标不再是客户端,而是charArrayWriter缓存区),接着在读取charArrayWriter内的内用,再通过res的写出方法写到客户端。

GZIP压缩Filter

public class GZipFilter implements Filter {

public void destroy() {

}

public void doFilter(ServletRequest req, ServletResponse res,

FilterChain chain) throws IOException, ServletException {

HttpServletRequest request = (HttpServletRequest) req;

HttpServletResponse response = (HttpServletResponse) res;

String acceptEncoding = request.getHeader("Accept-Encoding");

System.out.println("Accept-Encoding: " + acceptEncoding);

if (acceptEncoding != null

&& acceptEncoding.toLowerCase().indexOf("gzip") != -1) {

// 如果客户浏览器支持 GZIP 格式, 则使用 GZIP 压缩数据

GZipResponseWrapper gzipResponse = new GZipResponseWrapper(response);

chain.doFilter(request, gzipResponse);

// 输出压缩数据

gzipResponse.finishResponse();

} else {

// 否则, 不压缩

chain.doFilter(request, response);

}

}

public void init(FilterConfig arg0) throws ServletException {

}

}

类GZipResponseWrapper代码如下:

public class GZipResponseWrapper extends HttpServletResponseWrapper {

// 默认的 response

private HttpServletResponse response;

// 自定义的 outputStream, 执行close()的时候对数据压缩,并输出

private GZipOutputStream gzipOutputStream;

// 自定义 printWriter,将内容输出到 GZipOutputStream 中

private PrintWriter writer;

public GZipResponseWrapper(HttpServletResponse response) throws IOException {

super(response);

this.response = response;

}

public ServletOutputStream getOutputStream() throws IOException {

if (gzipOutputStream == null)

gzipOutputStream = new GZipOutputStream(response);

return gzipOutputStream;

}

public PrintWriter getWriter() throws IOException {

if (writer == null)

writer = new PrintWriter(new OutputStreamWriter(

new GZipOutputStream(response), "UTF-8"));

return writer;

}

// 压缩后数据长度会发生变化 因此将该方法内容置空

public void setContentLength(int contentLength) {

}

public void flushBuffer() throws IOException {

gzipOutputStream.flush();

}

public void finishResponse() throws IOException {

if (gzipOutputStream != null)

gzipOutputStream.close();

if (writer != null)

writer.close();

}

}

类GZipOutputStream代码如下:

public class GZipOutputStream extends ServletOutputStream {

private HttpServletResponse response;

// JDK 自带的压缩数据的类

private GZIPOutputStream gzipOutputStream;

// 将压缩后的数据存放到 ByteArrayOutputStream 对象中

private ByteArrayOutputStream byteArrayOutputStream;

public GZipOutputStream(HttpServletResponse response) throws IOException {

this.response = response;

byteArrayOutputStream = new ByteArrayOutputStream();

gzipOutputStream = new GZIPOutputStream(byteArrayOutputStream);

}

public void write(int b) throws IOException {

gzipOutputStream.write(b);

}

public void close() throws IOException {

// 压缩完毕 一定要调用该方法

gzipOutputStream.finish();

// 将压缩后的数据输出到客户端

byte[] content = byteArrayOutputStream.toByteArray();

// 设定压缩方式为 GZIP, 客户端浏览器会自动将数据解压

response.addHeader("Content-Encoding", "gzip");

response.addHeader("Content-Length", Integer.toString(content.length));

// 输出

ServletOutputStream out = response.getOutputStream();

out.write(content);

out.close();

}

public void flush() throws IOException {

gzipOutputStream.flush();

}

public void write(byte[] b, int off, int len) throws IOException {

gzipOutputStream.write(b, off, len);

}

public void write(byte[] b) throws IOException {

gzipOutputStream.write(b);

}

}

代码详解:

1.在过滤器中有如下代码:

// 如果客户浏览器支持 GZIP 格式, 则使用 GZIP 压缩数据

GZipResponseWrapper gzipResponse = new GZipResponseWrapper(response);

chain.doFilter(request, gzipResponse);

// 输出压缩数据

gzipResponse.finishResponse();

第一行是将response包装,目的是压缩response中的数据

第三行是将response中压缩的数据输出到客户端

2.构造一个GZipResponseWrapper类,在此类中重写getWriter方法改变其输出目的地。

public PrintWriter getWriter() throws IOException {

if (writer == null)

writer = new PrintWriter(new OutputStreamWriter(

new GZipOutputStream(response), "UTF-8"));

return writer;

}

因此过滤器中gzipResponse获取的writer将会把数据输出到GZipOutputStream(response)流中。

3.GZipOutputStream流继承于ServletOutputStream,在此类中创建一个缓存区ByteArrayOutputStream并生成GZIPOutputStream的对象gzipOutputStream,并将该流的输出指向缓存区,该类的write方法自动将数据压缩并写到指定目的地。GZipOutputStream的输出都是通过GZIPOutputStream的gzipOutputStream写入到缓存区。

4.当过滤器执行chain.doFilter(request, gzipResponse)期间,假如有数据输出则会调用gzipResponse.getWriter()方法得到PrintWriter out对象。执行out.write("String"),则会进一步调用OutputStreamWriter的write()方法,又会进一步调用GZipOutputStream的write()方法,在

GZipOutputStream的write()方法中,调用GZIPOutputStream的write方法把需要输出的数据压缩并缓存到ByteArrayOutputStream中。

5.当过滤器执行gzipResponse.finishResponse()时,便会关闭PrintWriter writer对象对应的流,即进一步关闭OutputStreamWriter流,进一步关闭GZipOutputStream流,在GZipOutputStream的close方法中将缓存区的ByteArrayOutputStream通过response输出到客户端。

图像水印Filter

思路和上一节GZIP压缩是类似的。

1.WaterMarkFilter代码如下:

public class WaterMarkFilter implements Filter {

// 水印图片,配置在初始化参数中

private String waterMarkFile;

public void init(FilterConfig config) throws ServletException {

String file = config.getInitParameter("waterMarkFile");

waterMarkFile = config.getServletContext().getRealPath(file);

}

public void doFilter(ServletRequest req, ServletResponse res,

FilterChain chain) throws IOException, ServletException {

HttpServletRequest request = (HttpServletRequest) req;

HttpServletResponse response = (HttpServletResponse) res;

// 自定义的response

WaterMarkResponseWrapper waterMarkRes = new WaterMarkResponseWrapper(

response, waterMarkFile);

chain.doFilter(request, waterMarkRes);

// 打水印,输出到客户端浏览器

waterMarkRes.finishResponse();

}

public void destroy() {

}

}

2.WaterMarkResponseWrapper代码如下:

public class WaterMarkResponseWrapper extends HttpServletResponseWrapper {

// 水印图片位置

private String waterMarkFile;

// 原response

private HttpServletResponse response;

// 自定义servletOutputStream,用于缓冲图像数据

private WaterMarkOutputStream waterMarkOutputStream;

public WaterMarkResponseWrapper(HttpServletResponse response,

String waterMarkFile) throws IOException {

super(response);

this.response = response;

this.waterMarkFile = waterMarkFile;

this.waterMarkOutputStream = new WaterMarkOutputStream();

}

// 覆盖getOutputStream(),返回自定义的waterMarkOutputStream

public ServletOutputStream getOutputStream() throws IOException {

return waterMarkOutputStream;

}

public void flushBuffer() throws IOException {

waterMarkOutputStream.flush();

}

// 将图像数据打水印,并输出到客户端浏览器

public void finishResponse() throws IOException {

// 原图片数据

byte[] imageData = waterMarkOutputStream.getByteArrayOutputStream()

.toByteArray();

// 打水印后的图片数据

byte[] image = ImageUtil.waterMark(imageData, waterMarkFile);

// 将图像输出到浏览器

response.setContentLength(image.length);

response.getOutputStream().write(image);

waterMarkOutputStream.close();

}

}

3.WaterMarkOutputStream代码如下:

public class WaterMarkOutputStream extends ServletOutputStream {

// 缓冲图片数据

private ByteArrayOutputStream byteArrayOutputStream;

public WaterMarkOutputStream() throws IOException {

byteArrayOutputStream = new ByteArrayOutputStream();

}

public void write(int b) throws IOException {

byteArrayOutputStream.write(b);

}

public void close() throws IOException {

byteArrayOutputStream.close();

}

public void flush() throws IOException {

byteArrayOutputStream.flush();

}

public void write(byte[] b, int off, int len) throws IOException {

byteArrayOutputStream.write(b, off, len);

}

public void write(byte[] b) throws IOException {

byteArrayOutputStream.write(b);

}

public ByteArrayOutputStream getByteArrayOutputStream() {

return byteArrayOutputStream;

}

}

4.ImageUtil代码如下:

public class ImageUtil {

/**

*

* @param imageData

* JPG 图像文件

* @param waterMarkFile

* 水印图片

* @return 加水印后的图像数据

* @throws IOException

*/

public static byte[] waterMark(byte[] imageData, String waterMarkFile)

throws IOException {

// 水印图片的右边距 下边距

int paddingRight = 10;

int paddingBottom = 10;

// 原始图像

Image image = new ImageIcon(imageData).getImage();

int imageWidth = image.getWidth(null);

int imageHeight = image.getHeight(null);

// 水印图片

Image waterMark = ImageIO.read(new File(waterMarkFile));

int waterMarkWidth = waterMark.getWidth(null);

int waterMarkHeight = waterMark.getHeight(null);

// 如果图片尺寸过小,则不打水印,直接返回

if (imageWidth < waterMarkWidth + 2 * paddingRight

|| imageHeight < waterMarkHeight + 2 * paddingBottom) {

return imageData;

}

BufferedImage bufferedImage = new BufferedImage(imageWidth,

imageHeight, BufferedImage.TYPE_INT_RGB);

Graphics g = bufferedImage.createGraphics();

// 绘制原始图像

g.drawImage(image, 0, 0, imageWidth, imageHeight, null);

// 绘制水印图片

g.drawImage(waterMark, imageWidth - waterMarkWidth - paddingRight,

imageHeight - waterMarkHeight - paddingBottom, waterMarkWidth,

waterMarkHeight, null);

g.dispose();

// 转成JPEG格式

ByteArrayOutputStream out = new ByteArrayOutputStream();

JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(out);

encoder.encode(bufferedImage);

byte[] data = out.toByteArray();

out.close();

return data;

}

}

代码详解:

1.过滤器中如下代码:

// 自定义的response

WaterMarkResponseWrapper waterMarkRes = new WaterMarkResponseWrapper(

response, waterMarkFile);

对response进行包装,使得response的输出数据放入缓存,并对图像数据添加水印

2.chain.doFilter(request, waterMarkRes)执行过程中,如果输出图片,则会调用waterMarkRes.getOutputStream()方法,由于在WaterMarkResponseWrapper中重写了该方法,故得到的是waterMarkOutputStream流。waterMarkOutputStream流中将数据写入到缓存区byteArrayOutputStream流中。

3.过滤器执行waterMarkRes.finishResponse()时,将会把waterMarkOutputStream中的数据取出并添加水印后,通过response.getOutputStream().write(image)将图片写出。

缓存Filter

具体代码详见《JavaWeb整合开发王者归来》

XSLT转换Filter

具体代码详见《JavaWeb整合开发王者归来》

文件上传Filter

1.UploadRequestWrapper代码如下

public class UploadRequestWrapper extends HttpServletRequestWrapper {

private static final String MULTIPART_HEADER = "Content-type";

// 是否是上传文件

private boolean multipart;

// map,保存所有的域

private Map<String, Object> params = new HashMap<String, Object>();

@SuppressWarnings("all")

public UploadRequestWrapper(HttpServletRequest request) {

super(request);

// 判断是否为上传文件

multipart = request.getHeader(MULTIPART_HEADER) != null

&& request.getHeader(MULTIPART_HEADER).startsWith(

"multipart/form-data");

if (multipart) {

try {

// 使用apache的工具解析

DiskFileUpload upload = new DiskFileUpload();

upload.setHeaderEncoding("utf8");

// 解析,获得所有的文本域与文件域

List<FileItem> fileItems = upload.parseRequest(request);

for (Iterator<FileItem> it = fileItems.iterator(); it.hasNext();) {

// 遍历

FileItem item = it.next();

if (item.isFormField()) {

// 如果是文本域,直接放到map里

params.put(item.getFieldName(), item.getString("utf8"));

} else {

// 否则,为文件,先获取文件名称

String filename = item.getName().replace("\\", "/");

filename = filename

.substring(filename.lastIndexOf("/") + 1);

// 保存到系统临时文件夹中

File file = new File(System

.getProperty("java.io.tmpdir"), filename);

// 保存文件内容

OutputStream ous = new FileOutputStream(file);

ous.write(item.get());

ous.close();

// 放到map中

params.put(item.getFieldName(), file);

}

}

} catch (Exception e) {

e.printStackTrace();

}

}

}

@Override

public Object getAttribute(String name) {

// 如果为上传文件,则从map中取值

if (multipart && params.containsKey(name)) {

return params.get(name);

}

return super.getAttribute(name);

}

@Override

public String getParameter(String name) {

// 如果为上传文件,则从map中取值

if (multipart && params.containsKey(name)) {

return params.get(name).toString();

}

return super.getParameter(name);

}

public static void main(String[] args) {

System.out.println(System.getProperties().toString().replace(", ",

"\r\n"));

}

}

以上代码自定义了request并重写其相关方法。并且判断request中的数据类型,如果是multipart,则把数据名和对应的值放入map集合。通过request.getParameter()和request.getAttribute()方法便可方便获取request的数据。

第六章 过滤器Filter的更多相关文章

  1. ASP.NET MVC 第六回 过滤器Filter

    在Asp.netMvc中当你有以下及类似以下需求时你可以使用Filter功能 判断登录与否或用户权限 决策输出缓存 防盗链 防蜘蛛 本地化与国际化设置 实现动态Action Filter是一种声明式编 ...

  2. 第七章 过滤器 Filter(二)

    一  过滤器API 由容器实现的接口 –javax.servlet.Filter –javax.servlet.FilterChain –javax.servlet.FilterConfig 四个包装 ...

  3. 一、变量.二、过滤器(filter).三、标签(tag).四、条件分支tag.五、迭代器tag.六、自定义过滤器与标签.七、全系统过滤器(了解)

    一.变量 ''' 1.视图函数可以通过两种方式将变量传递给模板页面 -- render(request, 'test_page.html', {'变量key1': '变量值1', ..., '变量ke ...

  4. 第七章 过滤器基础 Filter

    简介:SUN从Servlet2.3开始添加一项激动人心的功能,就是过滤器(Filter).WEB开发人员通过Filter技术,对web服务器管理的所有web资源:例如Jsp, Servlet, 静态图 ...

  5. 一起来学Spring Cloud | 第六章:服务网关 ( Zuul)

    本章节,我们讲解springcloud重要组件:微服务网关Zuul.如果有同学从第一章看到本章的,会发现我们已经讲解了大部分微服务常用的基本组件. 已经讲解过的: 一起来学Spring Cloud | ...

  6. 第十六章 综合实例——《跟我学Shiro》

    简单的实体关系图 简单数据字典 用户(sys_user) 名称 类型 长度 描述 id bigint 编号 主键 username varchar 100 用户名 password varchar 1 ...

  7. Android群英传》读书笔记 (3) 第六章 Android绘图机制与处理技巧 + 第七章 Android动画机制与使用技巧

    第六章 Android绘图机制与处理技巧 1.屏幕尺寸信息屏幕大小:屏幕对角线长度,单位“寸”:分辨率:手机屏幕像素点个数,例如720x1280分辨率:PPI(Pixels Per Inch):即DP ...

  8. Gradle 1.12 翻译——第十六章. 使用文件

    有关其它已翻译的章节请关注Github上的项目:https://github.com/msdx/gradledoc/tree/1.12,或訪问:http://gradledoc.qiniudn.com ...

  9. 《Django By Example》第六章 中文 翻译 (个人学习,渣翻)

    书籍出处:https://www.packtpub.com/web-development/django-example 原作者:Antonio Melé (译者注:无他,祝大家年会都中奖!) 第六章 ...

随机推荐

  1. Spark Streaming 进阶与案例实战

    Spark Streaming 进阶与案例实战 1.带状态的算子: UpdateStateByKey 2.实战:计算到目前位置累积出现的单词个数写入到MySql中 1.create table CRE ...

  2. python教程(四)·序列

    距离上次的小项目已经休息了很长一段时间,是时候来继续本系列教程了.这一节开始我们将深入python中的数据结构. 序列的概念 在python中,最基本的数据结构是序列,序列包含一个或多个元素,每个元素 ...

  3. vs code 写C心得

    用命令行的话可能比较简单: g++ -o [目标文件名] [原文件名] 然后在当前路径下直接执行这个文件,查看程序执行结果 例如: g++ -o a.out test.cpp ./a.out 默认是a ...

  4. 20155338 2016-2017-2 《JAVA程序设计》课程总结

    ---恢复内容开始--- 20155338 2016-2017-2 <JAVA程序设计>课程总结 每周作业汇总: 预备作业一 预备作业二 预备作业三 <JAVA程序设计>第一周 ...

  5. 同步备份工具之 rsync

    1.常用同步方法 SCP. NFS. SFTP. http. samba. rsync. drbd(基于文件系统同步,效率高) 2.rsync 介绍 rsync,英文全称是 remote synchr ...

  6. angularjs 路由机制

    前言 AngularJS路由主要有内置的ngRoute和一个基于ngRoute开发的第三方路由模块ui-router,内置的ngRoute有时满足开发需求,使用ui-router可以解决很多原生ngR ...

  7. com.genuitec.runtime.generic.jee60 is not defined 导入项目的异常

    系统加载工程后,报错Target runtime com.genuitec.runtime.generic.jee60 is not defined,在发布工程的同事电脑上正常 新导入的工程,出问题很 ...

  8. 一起来做chrome扩展《页面右键菜单》

    本文主要内容 contextMenus的设置 打开权限 创建菜单 点击菜单 background script向content script发送消息 1. contextMenus的设置 1.1 打开 ...

  9. zigbee路由(报文实例)

    4855 广播  routeRequestId = 6, pathCost = 0 radius=1E 62BB 继续广播 routeRequestId = 6, pathCost = 1 radiu ...

  10. 换Mac了,迈入了终端的大门

    多终端其实本质和多线程一样,所有终端其实都共享着同一个内存只不过不同终端对共享内存不同部分的权限不同罢了所以对终端的数量必须要有限制 我这里开启了四个线程,很明显四个线程都在跑同一个内存而且四个线程都 ...