首页 文章 精选 留言 我的

精选列表

搜索[后台],共10004篇文章
优秀的个人博客,低调大师

后台(23)——Listener

探索Android软键盘的疑难杂症 深入探讨Android异步精髓Handler 详解Android主流框架不可或缺的基石 站在源码的肩膀上全解Scroller工作机制 Android多分辨率适配框架(1)— 核心基础 Android多分辨率适配框架(2)— 原理剖析 Android多分辨率适配框架(3)— 使用指南 自定义View系列教程00–推翻自己和过往,重学自定义View 自定义View系列教程01–常用工具介绍 自定义View系列教程02–onMeasure源码详尽分析 自定义View系列教程03–onLayout源码详尽分析 自定义View系列教程04–Draw源码分析及其实践 自定义View系列教程05–示例分析 自定义View系列教程06–详解View的Touch事件处理 自定义View系列教程07–详解ViewGroup分发Touch事件 自定义View系列教程08–滑动冲突的产生及其处理 版权声明 本文原创作者:谷哥的小弟 作者博客地址:http://blog.csdn.net/lfdfhl Listener简介 JavaWeb开发中我们有时需要监听域对象HttpServletRequest、HttpSession、ServletContext的创建与销毁及其属性变化;除此以外,有时还需要监听session对JavaBean的绑定和解绑。这些操作我们都可以用Listener来实现。 监听域对象的创建与销毁 在此涉及到三个监听器ServletContextListener、HttpSessionListener以及ServletRequestListener。 监听器的使用步骤 1、创建类,实现指定的监听器接口 2、实现接口中的方法 3、在web.xml文件中注册监听器 鉴于这三个监听器的使用非常类似,故在此以ServletContextListener为例说明其使用方式。 首先创建一个类实现ServletContextListener接口且实现其中的方法 /** * 本文作者:谷哥的小弟 * 博客地址:http://blog.csdn.net/lfdfhl */ package cn.com; import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; public class TestServletContextListener implements ServletContextListener { @Override public void contextInitialized(ServletContextEvent sce) { System.out.println("ServletContext对象被创建"); } @Override public void contextDestroyed(ServletContextEvent sce) { System.out.println("ServletContext对象被销毁"); } } 然后我们在web.xml中注册该监听器即可 <?xml version="1.0" encoding="UTF-8"?> <web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"> <listener> <listener-class>cn.com.TestServletContextListener</listener-class> </listener> <welcome-file-list> <welcome-file>index.jsp</welcome-file> </welcome-file-list> </web-app> 监听域对象的属性变化 在此涉及到三个监听器ServletContextAttributeListener、HttpSessionAttributeListener以及ServletRequestAttributeListener 监听器的使用步骤 1、创建类,实现指定的监听器接口 2、实现接口中的方法 3、在web.xml文件中注册监听器 鉴于这三个监听器的使用非常类似,故在此以ServletRequestAttributeListener为例说明其使用方式。 首先创建一个类实现ServletRequestAttributeListener接口且实现其中的方法 /** * 本文作者:谷哥的小弟 * 博客地址:http://blog.csdn.net/lfdfhl */ package cn.com; import javax.servlet.ServletRequestAttributeEvent; import javax.servlet.ServletRequestAttributeListener; public class TestServletRequestAttributeListener implements ServletRequestAttributeListener { public void attributeAdded(ServletRequestAttributeEvent srae) { System.out.println("ServletRequest添加属性"); } public void attributeRemoved(ServletRequestAttributeEvent srae) { System.out.println("ServletRequest移除属性"); } public void attributeReplaced(ServletRequestAttributeEvent event) { System.out.println("ServletRequest替换属性"); System.out.println(event.getName()+" "+event.getValue()); } } 然后我们在web.xml中注册该监听器即可 <listener> <listener-class>cn.com.TestServletRequestAttributeListener</listener-class> </listener> 嗯哼,我们来在index.jsp中测试该监听器 <%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="UTF-8"%> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"> <title>测试</title> <style type="text/css"> p { font-size: 30px; font-family: 宋体; color: red; background-color: pink; } </style> </head> <body> <p>原创作者:谷哥的小弟</p> <p>博客地址:http://blog.csdn.net/lfdfhl</p> <% request.setAttribute("name", "小泽玛利亚"); request.setAttribute("name", "大泽玛利亚"); request.removeAttribute("name"); %> </body> </html> 监听session对JavaBean的绑定和解绑 可利用HttpSessionBindingListener监听session对JavaBean的绑定和解绑 使用步骤 1、创建JavaBean实现Serializable和HttpSessionBindingListener 2、实现接口中的方法 请看如下示例 我们先创建一个JavaBean /** * 本文作者:谷哥的小弟 * 博客地址:http://blog.csdn.net/lfdfhl */ package cn.com; import java.io.Serializable; import javax.servlet.http.HttpSessionBindingEvent; import javax.servlet.http.HttpSessionBindingListener; public class Student implements Serializable,HttpSessionBindingListener { private int studentID; private String studentName; public int getStudentID() { return studentID; } public void setStudentID(int studentID) { this.studentID = studentID; } public String getStudentName() { return studentName; } public void setStudentName(String studentName) { this.studentName = studentName; } @Override public String toString() { return "Student [studentID=" + studentID + ", studentName=" + studentName + "]"; } @Override public void valueBound(HttpSessionBindingEvent event) { System.out.println("Student对象被绑定"); } @Override public void valueUnbound(HttpSessionBindingEvent event) { System.out.println("Student对象被解绑"); } } 嗯哼,我们再在index.jsp中测试该监听器 <%@page import="cn.com.Student"%> <%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="UTF-8"%> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"> <title>测试</title> <style type="text/css"> p { font-size: 30px; font-family: 宋体; color: red; background-color: pink; } </style> </head> <body> <p>原创作者:谷哥的小弟</p> <p>博客地址:http://blog.csdn.net/lfdfhl</p> <% session.setAttribute("student", new Student()); %> </body> </html>

优秀的个人博客,低调大师

后台(21)——DBUtils

探索Android软键盘的疑难杂症 深入探讨Android异步精髓Handler 详解Android主流框架不可或缺的基石 站在源码的肩膀上全解Scroller工作机制 Android多分辨率适配框架(1)— 核心基础 Android多分辨率适配框架(2)— 原理剖析 Android多分辨率适配框架(3)— 使用指南 自定义View系列教程00–推翻自己和过往,重学自定义View 自定义View系列教程01–常用工具介绍 自定义View系列教程02–onMeasure源码详尽分析 自定义View系列教程03–onLayout源码详尽分析 自定义View系列教程04–Draw源码分析及其实践 自定义View系列教程05–示例分析 自定义View系列教程06–详解View的Touch事件处理 自定义View系列教程07–详解ViewGroup分发Touch事件 自定义View系列教程08–滑动冲突的产生及其处理 版权声明 本文原创作者:谷哥的小弟 作者博客地址:http://blog.csdn.net/lfdfhl DBUtils简介 DBUtils是Apache提供的一个开源的数据库操作工具,它是对JDBC的简单封装,极大简化JDBC编码的工作量。比如:查询数据时它可以把结果转换成List,Array,Set等集合,非常便于开发人员操作 DBUtils的三个核心对象 QueryRunner类 主要用于增,删,改,查 ResultSetHandler接口 主要用于处理结果集 DBUtils类 该工具类主要用于关闭连接、装载JDBC驱动程序等等 嗯哼,我们将在后续的篇幅中重点介绍QueryRunner类和ResultSetHandler接口,请继续往下看。 QueryRunner QueryRunner类提供了两个构造方法 new QueryRunner() 如果采用该方法创建QueryRunner,那么数据库的事务可由我们自己手动控制。正因为该构造方法无参,所以在调用该对象的query、update、batch时需要传入参数Connection。 new QueryRunner(DataSource ds); 如果采用该方法创建QueryRunner,那么数据库的事务由DBUtils自动控制。正因为该构造方法传入了参数QueryRunner,所以在调用该对象的query、update、batch时无需传入参数Connection。 QueryRunner的常用方法如下: public Object query(Connection conn, String sql, Object[] params, ResultSetHandler rsh) throws SQLException 执行查询操作,在该查询中Object数组里的每个值被用来作为查询语句的置换参数。该方法会自行处理PreparedStatement和ResultSet的创建和关闭。 public Object query(String sql, Object[] params, ResultSetHandler rsh) throws SQLException 该方法与上面的这个方法基本一样;不同的是它不用将Connection提供给该方法 public Object query(Connection conn, String sql, ResultSetHandler rsh) throws SQLException 该方法与上面的两个方法基本一样,它用于执行一个不需要置换参数的查询操作。 public int update(Connection conn, String sql, Object[] params) throws SQLException 执行更新操作,在该查询中Object数组里的每个元素值被用来作为更新语句的置换参数。 public int update(Connection conn, String sql) throws SQLException 该方法与上面的这个方法基本一样,用来执行一个不需要置换参数的更新操作。 现在,我们通过一个例子来了解DBUtils的简单使用 /** * 本文作者:谷哥的小弟 * 博客地址:http://blog.csdn.net/lfdfhl */ package cn.com; import java.sql.SQLException; import java.util.List; import org.apache.commons.dbutils.QueryRunner; import org.apache.commons.dbutils.handlers.BeanListHandler; import org.junit.Test; public class TestCRUD { @Test public void testInsert() throws SQLException{ QueryRunner qr = new QueryRunner(C3P0Util.getDataSource()); qr.update("insert into student(studentid,studentname) values(?,?)", 5,"吉泽暗步"); } @Test public void testUpdate() throws SQLException{ QueryRunner qr = new QueryRunner(C3P0Util.getDataSource()); qr.update("update student set studentname=? where studentid=?", "明步吉泽",5); } @Test public void testDelete() throws SQLException{ QueryRunner qr = new QueryRunner(C3P0Util.getDataSource()); qr.update("delete from student where studentid=?",2); } @Test public void testSelect()throws SQLException{ QueryRunner qr = new QueryRunner(C3P0Util.getDataSource()); BeanListHandler<Student> beanListHandler=new BeanListHandler<Student>(Student.class); List<Student> list = qr.query("select * from student",beanListHandler); for(Student s: list){ System.out.println(s); } } } 嗯哼,看到了吧,我们只用了很少的代码就实现了增删改查。尤其是最后一个例子中把查询结果直接转换成了List从而给开发带来了极大的方便! ResultSetHandler ResultSetHandler接口常用的实现类如下: ArrayHandler 把结果集中的第一行数据转成对象数组 ArrayListHandler 把结果集中的每一行数据都转成一个数组存放到List中 BeanHandler 将结果集中的第一行数据封装到一个对应的JavaBean实例中。 BeanListHandler 将结果集中的每一行数据都封装到一个对应的JavaBean实例中,存放到List里。 ColumnListHandler 将结果集中某一列的数据存放到List中 KeyedHandler(name) 将结果集中的每一行数据都封装到一个Map<列名,列值>里,再把这些map再存到一个map里,其key为指定的key。 MapHandler 将结果集中的第一行数据封装到一个Map里,key是列名,value就是对应的值 MapListHandler 将结果集中的每一行数据都封装到一个Map里,然后再存放到List 关于ResultSetHandler接口实现类的用法,请看如下示例: /** * 本文作者:谷哥的小弟 * 博客地址:http://blog.csdn.net/lfdfhl */ package cn.com; import java.sql.SQLException; import java.util.List; import java.util.Map; import org.apache.commons.dbutils.QueryRunner; import org.apache.commons.dbutils.handlers.ArrayHandler; import org.apache.commons.dbutils.handlers.ArrayListHandler; import org.apache.commons.dbutils.handlers.BeanHandler; import org.apache.commons.dbutils.handlers.BeanListHandler; import org.apache.commons.dbutils.handlers.ColumnListHandler; import org.apache.commons.dbutils.handlers.KeyedHandler; import org.apache.commons.dbutils.handlers.MapHandler; import org.apache.commons.dbutils.handlers.MapListHandler; import org.apache.commons.dbutils.handlers.ScalarHandler; import org.junit.Test; public class TestResultSetHandler { @Test public void teseArrayHandler() throws SQLException{ QueryRunner qr = new QueryRunner(C3P0Util.getDataSource()); Object[] arr = qr.query("select * from student", new ArrayHandler()); for (Object o : arr) { System.out.println(o); } } @Test public void teseArrayListHandler() throws SQLException{ QueryRunner qr = new QueryRunner(C3P0Util.getDataSource()); List<Object[]> query = qr.query("select * from student", new ArrayListHandler()); for (Object[] os : query) { System.out.println(os); for (Object o : os) { System.out.println(o); } System.out.println("----------"); } } @Test public void teseColumnListHandler() throws SQLException{ QueryRunner qr = new QueryRunner(C3P0Util.getDataSource()); List<Object> list = qr.query("select * from student", new ColumnListHandler(1)); for (Object o : list) { System.out.println(o); } } @Test public void testKeyedHandler() throws SQLException{ QueryRunner qr = new QueryRunner(C3P0Util.getDataSource()); Map<Object,Map<String,Object>> map = qr.query("select * from student", new KeyedHandler(1)); for (Map.Entry<Object, Map<String,Object>> m : map.entrySet()) { System.out.println(m.getKey()); for (Map.Entry<String, Object> mm : m.getValue().entrySet()) { System.out.println(mm.getKey()+"\t"+mm.getValue()); } System.out.println("----------"); } } @Test public void teseMapHandler() throws SQLException{ QueryRunner qr = new QueryRunner(C3P0Util.getDataSource()); Map<String,Object> map = qr.query("select * from student where studentid=?", new MapHandler(),3); for (Map.Entry<String, Object> m : map.entrySet()) { System.out.println(m.getKey()+"\t"+m.getValue()); } } @Test public void teseMapListHandler() throws SQLException{ QueryRunner qr = new QueryRunner(C3P0Util.getDataSource()); List<Map<String,Object>> list = qr.query("select * from student", new MapListHandler()); for (Map<String, Object> map : list) { for (Map.Entry<String, Object> m : map.entrySet()) { System.out.println(m.getKey()+"\t"+m.getValue()); } System.out.println("----------"); } } @Test public void teseScalarHandler() throws SQLException{ QueryRunner qr = new QueryRunner(C3P0Util.getDataSource()); Object o = qr.query("select count(*) from student", new ScalarHandler(1)); System.out.println(o.getClass().getName()); } @Test public void teseBeanHandler() throws SQLException{ QueryRunner qr = new QueryRunner(C3P0Util.getDataSource()); BeanHandler<Student> beanHandler=new BeanHandler<Student>(Student.class); Student student = qr.query("select * from student where studentid=?",beanHandler,3); System.out.println(student); } @Test public void teseBeanListHandler() throws SQLException{ QueryRunner qr = new QueryRunner(C3P0Util.getDataSource()); BeanListHandler<Student> beanListHandler=new BeanListHandler<Student>(Student.class); List<Student> list = qr.query("select * from student where studentid=?", beanListHandler,3); for(Student s:list){ System.out.println(s); } } } DBUtils DBUtils类提供了关闭连接、装载JDBC驱动程序等等于资源有关的操作。 DBUtils主要方法如下: public static void close( ) throws java.sql.SQLException 用于关闭Connection、Statement和ResultSet public static void closeQuietly( ): 这一类方法不仅能关闭Connection、Statement和ResultSet,还能隐藏一些在程序中抛出的SQLException。如果不想捕捉这些异常的话,这将是一个不错的选择 public static void commitAndCloseQuietly(Connection conn): 提交并关闭连接,并且在关闭连接时不抛出SQL异常。 public static boolean loadDriver(java.lang.String driverClassName) 装载并注册JDBC驱动程序,若成功就返回true

优秀的个人博客,低调大师

后台(22)——AJAX

探索Android软键盘的疑难杂症 深入探讨Android异步精髓Handler 详解Android主流框架不可或缺的基石 站在源码的肩膀上全解Scroller工作机制 Android多分辨率适配框架(1)— 核心基础 Android多分辨率适配框架(2)— 原理剖析 Android多分辨率适配框架(3)— 使用指南 自定义View系列教程00–推翻自己和过往,重学自定义View 自定义View系列教程01–常用工具介绍 自定义View系列教程02–onMeasure源码详尽分析 自定义View系列教程03–onLayout源码详尽分析 自定义View系列教程04–Draw源码分析及其实践 自定义View系列教程05–示例分析 自定义View系列教程06–详解View的Touch事件处理 自定义View系列教程07–详解ViewGroup分发Touch事件 自定义View系列教程08–滑动冲突的产生及其处理 版权声明 本文原创作者:谷哥的小弟 作者博客地址:http://blog.csdn.net/lfdfhl AJAX简介 AJAX(Asynchronous JavaScript And XML)不是一种新的编程语言,它指的是异步JavaScript。利用AJAX可在不重新加载整个页面的情况下与服务器通信并更新部分网页。 XMLHttpRequest AJAX 的核心是XMLHttpRequest,它是一种支持异步请求的技术。也就是说:在JavaScript中利用XMLHttpRequest可向服务器提出请求并处理响应且不阻塞用户当前操作。 XMLHttpRequest的创建 目前,市面上的大部分浏览器都支持XMLHttpRequest ,但是有的较老的浏览器比如:IE5和IE6却不支持。 所以,可以这么做: var xmlhttp; if (window.XMLHttpRequest) {//code for IE7+, Firefox, Chrome, Opera, Safari xmlhttp=new XMLHttpRequest(); } else {//code for IE6, IE5 xmlhttp=new ActiveXObject("Microsoft.XMLHTTP"); } XMLHttpRequest的常用方法 open(method,url,async) 建立与服务器的连接 method:指定请求方式,比如GET、POST;常用GET方式 url:请求的地址 async:是否使用异步方式请求,默认为true send(string) 将请求发送到服务器 如果是GET请求那么参数string为null,如果是POST请求那么利用string携带请求参数 setRequestHeader(header,value) 设置请求消息头;很少用到 XMLHttpRequest的常用属性 readyState readyState用于标识XMLHttpRequest的状态信息 readyState=0表示XMLHttpRequest对象未完成初始化,即刚创建。 readyState=1表示XMLHttpRequest对象开始发送请求;即调用了open方法,但还没有调用send方法,请求还没有发出 readyState=2表示XMLHttpRequest对象的请求发送完成;即send方法已经调用,数据已经提交到服务器,但没有收到任何响应 readyState=3表示XMLHttpRequest对象开始读取响应,还没有结束;即收到了响应消息头,但响应正文还没有收到 readyState=4表示XMLHttpRequest对象读取响应结束,响应消息头和响应正文都已收到 responseText 获得响应的文本内容 responseXML 获得响应的XML文档对象 status status用于标识Http请求的响应状态码 200 服务器响应正常 400 无法找到请求的资源 403 没有访问权限 404 访问的资源不存在 500 服务器内部错误 XMLHttpRequest的回调函数 每当readyState属性值改变时onreadystatechange函数就会被执行。所以我们可以在该函数中执行一些监听,比如:readyState=4而且status=200时处理服务器返回的数据 XMLHttpRequest使用示例 首先,我们写一个简单的jsp <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <script type="text/javascript" src="${pageContext.request.contextPath}/js/xmlhttprequest.js"></script> <title>测试AJAX</title> </head> <script type="text/javascript"> window.onload = function() { //第一步:获取XMLHttpRequest var req = getXMLHttpRequest(); //第四步:处理结果 req.onreadystatechange = function(){ if(req.readyState==4){ if(req.status==200){ alert(req.responseText); } } } //第二步:建立连接 req.open("get","${pageContext.request.contextPath }/servlet"); //第三步:发送请求 req.send(null); } </script> <body> </body> </html> 其中xmlhttprequest.js如下: function getXMLHttpRequest(){ var xmlhttp; if (window.XMLHttpRequest) { xmlhttp = new XMLHttpRequest(); } else { xmlhttp = new ActiveXObject("Microsoft.XMLHTTP"); } return xmlhttp; } 然后,我们再写一个非常简单的Servlet /** * 本文作者:谷哥的小弟 * 博客地址:http://blog.csdn.net/lfdfhl */ package cn.com; import java.io.IOException; import java.io.PrintWriter; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class TestAJAXServlet extends HttpServlet{ @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { PrintWriter writer = resp.getWriter(); writer.print("Hello AJAX"); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { } } 运行后效果如下图所示:

优秀的个人博客,低调大师

后台(13)——Cookie

探索Android软键盘的疑难杂症 深入探讨Android异步精髓Handler 详解Android主流框架不可或缺的基石 站在源码的肩膀上全解Scroller工作机制 Android多分辨率适配框架(1)— 核心基础 Android多分辨率适配框架(2)— 原理剖析 Android多分辨率适配框架(3)— 使用指南 自定义View系列教程00–推翻自己和过往,重学自定义View 自定义View系列教程01–常用工具介绍 自定义View系列教程02–onMeasure源码详尽分析 自定义View系列教程03–onLayout源码详尽分析 自定义View系列教程04–Draw源码分析及其实践 自定义View系列教程05–示例分析 自定义View系列教程06–详解View的Touch事件处理 自定义View系列教程07–详解ViewGroup分发Touch事件 自定义View系列教程08–滑动冲突的产生及其处理 版权声明 本文原创作者:谷哥的小弟 作者博客地址:http://blog.csdn.net/lfdfhl 在之前学习HTTP时我们说:标准HTTP协议既有优点也有缺点,优点在于极大地减轻了服务器的压力,每一次请求不会造成不必要连接占用;缺点在于繁复地建立连接和断开连接,请求会传输大量重复的信息,所以随后出现了Keep-Alive、Cookie、HttpSession在某些程度上弥补了标准HTTP的缺点。从今天起,我们就开始学习Cookie和HttpSession 会话 在正式进入Cookie和HttpSession的学习之前,我们先来了解一个术语:会话。 简单地说:用户打开一个浏览器后点击网页中的多个超链接访问多个web资源,访问完毕后关闭浏览器。这个过程就称之为一个会话。在会话过程中会产生一些和用户操作密切相关的数据,所以客户端或者服务端需要为每个用户保存这些数据。例如:用户点击超链接通过一个Servlet购买了几件商品,客户端或者服务端应保存这些商品信息以便于用户点结帐Servlet时,结帐Servlet得到这些已购买的商品信息从而正确地结算。 那么客户端和服务端应该怎么样保存这些用户信息呢?通常地讲,有两种方式:Cookie和HttpSession Cookie Cookie属于客户端技术。服务端把每个用户的数据以cookie的形式返回到用户的浏览器的缓存中。当用户使用浏览器再去访问服务器里的web资源时,就会将这些Cookie携带给服务端 HttpSession Session属于服务器端技术。服务端为每个用户创建一个其独享的HttpSession对象,用户在访问服务器的web资源时可把不同用户的数据放在各自对应的session中 嗯哼,在了解了关于会话的基础知识之后,我们开始进入Cookie的学习 Cookie简介 Cookie是Servlet发送浏览器的少量信息,这些信息由浏览器保存,当浏览器访问Web资源时再将其发送回服务器。一个Cookie只能标识一种信息,它至少含有一个标识该信息的名称(NAME)和值(VALUE)并且名称和值都是String类型的;除此以外,它还有一些其它信息:比如注释、路径和域限定符、最大生存时间maxAge和版本号。Cookie的值可以唯一地标识客户端,因此Cookie常用于会话管理。 Servlet可通过httpServletResponse.addCookie()将字段添加到HTTP 响应头以便将Cookie发送到浏览器。浏览器支持每台Web服务器20个Cookie,合计300个,并且将每个Cookie的大小限定为4KB. 浏览器通过向HTTP请求头添加字段将Cookie返回给Servlet。服务端可用HttpServletRequest.getCookies()从请求中获取Cookie. 一个服务端可以给同一个WEB浏览器发送多个Cookie,此时,如果有两个Cookie的name一样那么后者会覆盖前者。同一个WEB浏览器也可以存储多个不同服务端提供的Cookie。请注意,发送到浏览器的Cookie在默认情况下它是一个会话级别的cookie(即Cookie被存储在浏览器的内存中)当用户退出浏览器之后即被删除。若希望浏览器将该Cookie存储在磁盘上,则需要设置Cookie的maxAge. Cookie入门示例 请看如下示例: /** * 本文作者:谷哥的小弟 * 博客地址:http://blog.csdn.net/lfdfhl */ package cn.com; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class CookieTest1 extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setCharacterEncoding("UTF-8"); response.setContentType("text/html;charset=UTF-8"); Cookie[] cookies = request.getCookies(); for(int i=0;cookies!=null&&i<cookies.length;i++){ Cookie cookie=cookies[i]; System.out.println(cookie.getName()+" "+cookie.getValue()); } if (cookies == null) { Cookie c1 = new Cookie("username", "tom"); Cookie c2 = new Cookie("password", "123456"); response.addCookie(c1); response.addCookie(c2); } } @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { } } 代码解析如下: 生成两个Cookie,请参见代码第26-27行 利用response.addCookie()将Cookie返回给客户端,请参见代码第28-29行 利用request.getCookies()获取到浏览器携带至服务端的所有Cookie,请参见代码第20-24行 输出结果: username tom password 123456 我们再去浏览器中看看这些Cookie值,在此以谷歌浏览器为例 嗯哼,看到了吧,在此展示了Cookie的相关信息。 Cookie应用详解 cookie使用示例 在实际开发中Cookie有许多实用的地方,在此我们实现一个功能:利用Cookie记录上次访问时间 请看如下示例: /** * 本文作者:谷哥的小弟 * 博客地址:http://blog.csdn.net/lfdfhl */ package cn.com; import java.io.IOException; import java.io.PrintWriter; import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.Date; import java.util.Locale; import javax.servlet.ServletException; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class CookieTest2 extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String LAST_ACCESS_TIME="time"; response.setCharacterEncoding("UTF-8"); response.setContentType("text/html;charset=UTF-8"); PrintWriter writer = response.getWriter(); Cookie[] cookies = request.getCookies(); for(int i=0;cookies!=null&&i<cookies.length;i++){ Cookie cookie=cookies[i]; if(LAST_ACCESS_TIME.equals(cookie.getName())){ String time = cookie.getValue(); writer.println("<h4>"+"上次访问时间:"+time+"</h4>"); System.out.println("time="+time); } } Calendar calendar = Calendar.getInstance(); int year = calendar.get(Calendar.YEAR); int month = calendar.get(Calendar.MONTH); int date = calendar.get(Calendar.DATE); int hour = calendar.get(Calendar.HOUR_OF_DAY); int minute = calendar.get(Calendar.MINUTE); int second = calendar.get(Calendar.SECOND); String time = year+"/"+(month+1)+"/"+date+" " +hour+":"+minute+":"+second; Cookie cookie = new Cookie(LAST_ACCESS_TIME, time); response.addCookie(cookie); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { } } 代码解析如下: 得到所有Cookie,请参见代码第27行 从所有Cookie中筛选出上次访问的时间,请参见代码第28-35行 利用Cookie将最新的访问时间返回给浏览器 运行后效果如下图所示: 我们再去浏览器中看看这些Cookie值,在此以谷歌浏览器为例 cookie的maxAge 请注意,”过期时间:浏览会话结束时”,也就是说当我们结束了本次通话(关闭浏览器)之后这些Cookie就被删除了。这是为什么呢?之前我们也说过了:Cookie在默认情况下是一个会话级别的cookie,它存在于浏览器的内存中,当用户退出浏览器之后即被删除。现在,我们希望在关闭浏览器之后再次打开浏览器时依然可以显示上次登录的时间,这就需要设置Cookie的maxAge将该Cookie存储在磁盘上。打开API我们可以看到方法cookie.setMaxAge(int expiry),默认情况下expiry的值为-1,即关闭浏览器后Cookie被删除。如果设置expiry的值为0则表示立即删除Cookie;如果设置expiry的值为正数则表示将Cookie保存到硬盘中且在expiry秒后失效。 cookie的path 请注意,”路径:/TestCookie01”,这个路径是不是觉得有点眼熟呢?哇哈,它就是我们通过request.getContextPath();获取到的应用上下文路径!所以,默认情况下通过cookie.getPath()获取到Cookie的路径正是应用的上下文路径,当然你也可以cookie.setPath()为Cookie设置path。这个path有啥用呢?我们打开官方文档看看是怎么说这个path的:在为Cookie设置path(即目录)后,Cookie对于该指定目录中的所有页面及该目录子目录中的所有页面都是可见的。哇哈,看到这里可能还是有点懵;嗯哼,我再换个方式说:如果浏览器访问的资源路径(即URI而非URL)如果是以Cookie的path开头的那么浏览器就会将该Cookie携带至服务端;否则不会携带该Cookie。举个例子:比如Cookie的path是/TestCookie01/hello;浏览器访问的是xxxx/TestCookie01/hello/haha/test.html那么浏览器就会将该Cookie携带至服务端;假如浏览器访问的是xxxx/TestCookie01/abcde/ppha/test.html那么浏览器就不会将该Cookie携带至服务端

优秀的个人博客,低调大师

后台(14)——HttpSession

探索Android软键盘的疑难杂症 深入探讨Android异步精髓Handler 详解Android主流框架不可或缺的基石 站在源码的肩膀上全解Scroller工作机制 Android多分辨率适配框架(1)— 核心基础 Android多分辨率适配框架(2)— 原理剖析 Android多分辨率适配框架(3)— 使用指南 自定义View系列教程00–推翻自己和过往,重学自定义View 自定义View系列教程01–常用工具介绍 自定义View系列教程02–onMeasure源码详尽分析 自定义View系列教程03–onLayout源码详尽分析 自定义View系列教程04–Draw源码分析及其实践 自定义View系列教程05–示例分析 自定义View系列教程06–详解View的Touch事件处理 自定义View系列教程07–详解ViewGroup分发Touch事件 自定义View系列教程08–滑动冲突的产生及其处理 版权声明 本文原创作者:谷哥的小弟 作者博客地址:http://blog.csdn.net/lfdfhl 在之前学习HTTP时我们说:标准HTTP协议既有优点也有缺点,优点在于极大地减轻了服务器的压力,每一次请求不会造成不必要连接占用;缺点在于繁复地建立连接和断开连接,请求会传输大量重复的信息,所以随后出现了Keep-Alive、Cookie、HttpSession在某些程度上弥补了标准HTTP的缺点。在上一篇博客我们学习了Cookie,在本篇博客中我们来一起学习HttpSession HttpSession简介 与客户端技术Cookie技术不同,HttpSession是服务端技术。在WEB开发中,服务器可为用户的每个浏览器创建一个会话对象即HttpSession。默认情况下,一个浏览器独占一个HttpSession对象。因此,服务器程序可将用户数据存入用户浏览器对应的独占的HttpSession中,当用户使用浏览器访问当前应用下的其它程序时,其它程序也可以从该HttpSession中取出该用户的数据。 HttpSession的特点 HttpSession对象由服务器创建 开发人员可调用request.getSession()得到该对象 在同一个会话下多个Servlet可共享数据 比如用户访问一个含有多个Servlet的网站,那么这些Servlet可共享一个HttpSession。比如,登录后把User存放到HttpSession后其他Servlet都可以使用该对象,并且在结算Servlet时用此对象判断用户是否登录。还记得之前的ServletContext么?对于存放在ServletContext中的数据,其它用户也有权限访问,但是存放在HttpSession中的数据其他应用是无法访问的。 Cookie只能存字符串但是HttpSession可以存对象 服务器重启时会将HttpSession中的数据保存到服务器硬盘,并在重启完成后将这些数据恢复到内存中。但是请注意这些数据要实现序列化否则无法保存和还原。 HttpSession常用方法 setAttribute( ) 向HttpSession中保存数据 getAttribute( ) 从HttpSession中获取数据 removeAttribute( ) 从HttpSession中移除数据 getId( ) 返回HttpSession的唯一标识符 setMaxInactiveInterval( ) 设置session的存活时间(单位是秒),默认为30分钟 invalidate() 销毁HttpSession HttpSession使用示例 请看如下代码: /** * 本文作者:谷哥的小弟 * 博客地址:http://blog.csdn.net/lfdfhl */ package cn.com; import java.io.IOException; import java.io.PrintWriter; import javax.servlet.ServletException; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; public class TestHttpSession extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String NAME="name"; String JSESSIONID="JSESSIONID"; response.setCharacterEncoding("UTF-8"); response.setContentType("text/html;charset=UTF-8"); PrintWriter writer = response.getWriter(); HttpSession httpSession = request.getSession(); String httpSessionID=httpSession.getId(); String name=(String) httpSession.getAttribute(NAME); Cookie[] cookies = request.getCookies(); for(int i=0;cookies!=null&&i<cookies.length;i++){ Cookie cookie=cookies[i]; if (JSESSIONID.equals(cookie.getName())) { writer.println("<h4>"+"JSESSIONID="+cookie.getValue()+"</h4>"); } } if(httpSessionID!=null){ writer.println("<h4>"+"httpSessionID="+httpSessionID+"</h4>"); } if(name==null){ writer.println("<h4>"+"还没有存放数据"+"</h4>"); httpSession.setAttribute(NAME, "zxx9527"); }else{ writer.println("<h4>"+"从HttpSession中获取到数据name="+name+"</h4>"); } } @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { } } 代码解析如下: 为当前应用创建HttpSession,请参见代码第25行 获取HttpSession的ID,请参见代码第26行 获取名字为JSESSIONID的Cookie的值,请参见代码第32行 将数据保存至HttpSession中,请参见代码第40行 从HttpSession中取出数据,请参见代码第27行 运行后如效果如下图所示: 浅析request.getSession( ) 在刚才的示例中我们只调用了一句request.getSession()就可以开始对HttpSession的操作,那么这句代码的内部到底做了哪些事情呢? 1、获取名称为JSESSIONID的Cookie的值 2、如果没有获取到该Cookie那么就创建一个新的HttpSession且为其分配一个唯一的SessionID并且立马为浏览器生成一个名字为JSESSIONID值为sessionID的Cookie 3、如果可以获取到该Cookie,则取出该cookie的值(即HttpSession对象的SessionID),再依据此SessionID从服务器中找到与其对应的HttpSession 应对浏览器禁用Cookie的情况 通过之前的讲解我们知道:HttpSession是依赖于Cookie的,如果用户禁用了Cookie此时也就谈不上什么HttpSession了。所以,我们需要方法应对该情况的发生。 解决方案:利用response.encodeURL( )方法对网站的所有URL进行重写 执行request.getSession( ) 对sendRedirect( )涉及到的url地址进行重写 对表单的action和超链接的url进行重写 在重写URL后新的URL中就会包含JSESSIONID的值,比如JSESSIONID=30578BE1BD6600C2908607EE381BBB9B,从而将JSESSIONID携带至服务端。

优秀的个人博客,低调大师

后台(08)——Tomcat

探索Android软键盘的疑难杂症 深入探讨Android异步精髓Handler 详解Android主流框架不可或缺的基石 站在源码的肩膀上全解Scroller工作机制 Android多分辨率适配框架(1)— 核心基础 Android多分辨率适配框架(2)— 原理剖析 Android多分辨率适配框架(3)— 使用指南 自定义View系列教程00–推翻自己和过往,重学自定义View 自定义View系列教程01–常用工具介绍 自定义View系列教程02–onMeasure源码详尽分析 自定义View系列教程03–onLayout源码详尽分析 自定义View系列教程04–Draw源码分析及其实践 自定义View系列教程05–示例分析 自定义View系列教程06–详解View的Touch事件处理 自定义View系列教程07–详解ViewGroup分发Touch事件 自定义View系列教程08–滑动冲突的产生及其处理 版权声明 本文原创作者:谷哥的小弟 作者博客地址:http://blog.csdn.net/lfdfhl 本文简介 开发中常用的WEB服务器有:WebLogic、WebSphereAS、Tomcat、JbossAS等等。在本阶段的学习中我使用的是Tomcat,故在此记录Tomcat的安装和项目部署过程。 Tomcat的安装与配置 第一步:下载Tomcat 在Tomcat官网下载Tomcat,我下载的是Tomcat7对应的apache-tomcat-7.0.52.zip压缩包。 第二步:解压Tomcat 解压apache-tomcat-7.0.52.zip 第三步:配置环境变量 在环境变量中配置以下变量: 在PATH中配置tomcat的路径,比如:D:\developKits\apache-tomcat-7.0.52\bin; 配置JAVA_HOME,比如:C:\Program Files\Java\jdk1.8.0_60 配置CATALINA_HOME,比如:D:\developKits\apache-tomcat-7.0.52 第四步:启动Tomcat 第一种方式: 在D:\developKits\apache-tomcat-7.0.52\bin中双击startup.bat 第二种方式: 在DOS下执行startup 启动成功后,如下图所示: 在浏览器中输入:http://localhost:8080/ ,如下图所示: Tomcat的端口占用问题 Tomcat默认的端口号是8080,但我们时常可发现:由于端口号被占用导致Tomcat启动失败,可以这么做来解决该问题: 在DOS中执行 netstat -ano | findstr “8080” 找到占用8080端口的进程的PID 在任务管理中的详细信息中找到该PID对应的应用并将其结束 除了该方式以外,还以修改Tomcat默认的端口号。请找到D:\developKits\apache-tomcat-7.0.52\conf\server.xml中的 <Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" /> 将此处的端口号修改为8081再重启Tomcat即可。 Eclipse集成Tomcat 1、在Eclipse中添加Tomcat 在Window—>Preferences—>Server—>Runtime Environments—>add添加刚才已经配置好的Tomcat 2、为项目添加index.jsp 在项目的WebContent目录下添加index.jsp文件,请参照下图: 为什么要在此添加index.jsp呢?我们来瞅瞅Tomcat的web.xml文件中的配置,请参照下图: 嗯哼,看到了吧,Tomcat把index.jsp作为了项目的首页! 3、部署项目至Tomcat 项目右键—->Run As—>Run On Server—>Tomcat7;此时,发现没有运行成功。请在Eclipse的Server视图中remove掉刚才的项目,请参照下图: 在remove操作后,请继续在Eclipse的Server视图中点击Tomcat右键执行Clean。在完成以上两步操作后,仍在Eclipse的Server视图点击Tomcat右键Open,设置Server Locations,请参照下图: 最后,请保存刚才的配置。然后再次部署项目,即可成功! 此时,我们在本地找到Tomcat的webapps文件夹,请参照下图: 嗯哼,刚才部署的项目已经保存到该文件夹下面了。 由于软件版本差异,所以在Eclipse集成Tomcat的过程中或许有些地方不太一样,这个无关紧要,也不用太纠结;毕竟主要流程是一致的。 至此,关于Tomcat就介绍完了,谢谢。

优秀的个人博客,低调大师

后台(06)——JDBC

探索Android软键盘的疑难杂症 深入探讨Android异步精髓Handler 详解Android主流框架不可或缺的基石 站在源码的肩膀上全解Scroller工作机制 Android多分辨率适配框架(1)— 核心基础 Android多分辨率适配框架(2)— 原理剖析 Android多分辨率适配框架(3)— 使用指南 自定义View系列教程00–推翻自己和过往,重学自定义View 自定义View系列教程01–常用工具介绍 自定义View系列教程02–onMeasure源码详尽分析 自定义View系列教程03–onLayout源码详尽分析 自定义View系列教程04–Draw源码分析及其实践 自定义View系列教程05–示例分析 自定义View系列教程06–详解View的Touch事件处理 自定义View系列教程07–详解ViewGroup分发Touch事件 自定义View系列教程08–滑动冲突的产生及其处理 版权声明 本文原创作者:谷哥的小弟 作者博客地址:http://blog.csdn.net/lfdfhl 本文简介 本文主要介绍JDBC的基础知识及其用法。 JDBC简介 JDBC(java database connectivity)是SUN公司提供的一套利用Java语言操作数据库的标准规范。 在JDBC中定义了许多接口而数据库厂商在其数据库驱动中实现了这些接口。关于JDBC与数据库驱动的关系,请参见下图: JDBC中定义的接口(规范),各数据库厂商(比如MySQL,Oracle,DB2等)在其数据库驱动中实现了这些接口(规范)。所以,JDBC屏蔽了Java与不同数据库打交道的细节问题;假如项目原本使用的是MySQL,在某段时间后换成了Oracle,那我们基本不用去修改原来项目中的SQL语句,只需要把JDBC中MySQL的数据库驱动切换成Oracle对应的数据库驱动即可。 JDBC的核心技术点: DriverManager:用于加载驱动 Connection: 用于创建与数据库的连接 Statement: 用于操作sql语句 ResultSet: 结果集或一张虚拟表 JDBC的操作步骤: 加载驱动 创建连接 创建Statement对象 执行sql语句且返回结果 处理结果 关闭资源 嗯哼,在了解这些之后我们来看一个简单的示例,代码如下: package cn.com; import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import org.junit.Test; /** * 原创作者:谷哥的小弟 * 博客地址:http://blog.csdn.net/lfdfhl */ public class UnitTest { @Test public void testJDBC1() { Connection connection = null; Statement statement = null; ResultSet resultSet = null; try { Class.forName("com.mysql.jdbc.Driver"); connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/db1", "root", "root"); statement = connection.createStatement(); String sql = "select * from student"; resultSet = statement.executeQuery(sql); while (resultSet.next()) { Student student = new Student(); int id = resultSet.getInt("studentid"); String name = resultSet.getString("studentname"); student.setStudentID(id); student.setStudentName(name); System.out.println(student); } } catch (Exception e) { e.printStackTrace(); } finally { if (resultSet != null) { try { resultSet.close(); } catch (SQLException e) { e.printStackTrace(); } resultSet = null; } if (statement != null) { try { statement.close(); } catch (SQLException e) { e.printStackTrace(); } statement = null; } if (connection != null) { try { connection.close(); } catch (SQLException e) { e.printStackTrace(); } connection = null; } } } } 在该示例中,利用JDBC从数据库查询数据。代码解析如下: 第一步: 加载MySQL数据库驱动,请参见代码第22行 第二步: 创建与数据的连接,请参见代码第23行 第三步: 创建用于执行SQL语句的Statement对象,请参见代码第24行 第四步: 利用Statement执行SQL语句且得到结果集,请参见代码第26行 第五步: 处理结果集,请参见代码第27-34行 第六步: 关闭资源,请参见代码第37-64行 详解JDBC常用类和接口 通过刚才的例子,我们对于JDBC的使用有了初步的了解和认知,现在此基础上详解JDBC常用的类和接口 java.sql.Drivermanager类 java.sql.Drivermanager类用于加载(注册)驱动和与数据库建立连接。 利用java.sql.Drivermanager类用于加载(注册)驱动 在此,我们采用的是: Class.forName(“com.mysql.jdbc.Driver”); 还有另外一种方式: DriverManager.registerDriver(new com.mysql.jdbc.Driver()); 该方式不但强烈依赖数据库的驱动jar而且会导致驱动被重复注册2次。故不建议使用该方式 利用java.sql.Drivermanager类与数据库建立连接 比如,该示例中: DriverManager.getConnection(“jdbc:mysql://localhost:3306/db1”,”root”, “root”); 其中jdbc表示协议,mysql表示子协议,localhost代表IP,3306表示端口号,db1表示要连接的数据库,第一个root表示用户名,第二个root表示密码。这些值是我示例中的配置,在项目中使用时请替换这些值。 java.sql.Connection接口 java.sql.Connection是一个接口,它用于与数据库交互;各数据库驱动中均实现了该接口。java.sql.Connection最常用的操作是创建用于执行SQL语句的Statement,即: connection.createStatement(); java.sql.Statement接口 java.sql.Statement接口用于操作SQL语句并返回相应的结果集;各数据库驱动中均实现了该接口。java.sql.Statement的常用方法如下: executeQuery(String sql) 该方法用于执行select语句并根据查询语句返回结果集 executeUpdate(String sql) 该方法用于执行insert、update、delete语句并返回受影响的行数 execute(String sql) 此方法可以执行任意SQL语句。当且仅当执行select语句且有返回结果时该方法返回true, 其他情况下该方法返回false java.sql.ResultSet接口 java.sql.ResultSet用于保存执行SQL语句之后得到的结果集;各数据库驱动中均实现了该接口。 ResultSet对外暴露一个游标,该游标默认指向结果集第一行之前。ResultSet常见方法释义如下: boolean next() 将游标从当前位置向下移动一行 boolean previous() 将光标移动到此ResultSet对象的上一行。 void afterLast() 将光标移动到末尾,正好位于最后一行之后。 void beforeFirst() 将光标移动到开头,正好位于第一行之前。 Object getObject(int columnIndex); 根据序号取值,索引从1开始 Object getObject(String ColomnName); 根据列名取值 int getInt(int colIndex) 以int形式获取ResultSet结果集当前行指定列号值 int getInt(String colLabel) 以int形式获取ResultSet结果集当前行指定列名值 float getFloat(int colIndex) 以float形式获取ResultSet结果集当前行指定列号值 float getFloat(String colLabel) 以float形式获取ResultSet结果集当前行指定列名值 String getString(int colIndex) 以String形式获取ResultSet结果集当前行指定列号值 String getString(String colLabel) 以String形式获取ResultSet结果集当前行指定列名值 Date getDate(int columnIndex); 以Date形式获取ResultSet结果集当前行指定列号值 Date getDate(String columnName); 以Date形式获取ResultSet结果集当前行指定列号值 void close() 关闭ResultSet对象 在从ResultSet中获取到数据库中数据后,我们就将这些数据封装到JavaBean中;在此请注意Java的数据类型与数据库的数据类型的对应关系: 利用preparedStatement防止SQL注入 preparedStatement(预编译对象)是Statement的子类,它具有如下特点: 预先编译SQL语句 性能优异 在SQL语句中使用占位符 在此利用preparedStatement优化刚才的示例,代码如下: package cn.com; import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import org.junit.Test; /** * 原创作者:谷哥的小弟 * 博客地址:http://blog.csdn.net/lfdfhl */ public class UnitTest { @Test public void testJDBC2() { Connection connection = null; PreparedStatement preparedStatement = null; ResultSet resultSet = null; try { Class.forName("com.mysql.jdbc.Driver"); connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/db1", "root", "root"); String sql = "select * from student where studentid=? and studentname=?"; preparedStatement = connection.prepareStatement(sql); preparedStatement.setInt(1, 4); preparedStatement.setString(2, "波少野结衣"); resultSet = preparedStatement.executeQuery(); while (resultSet.next()) { Student student = new Student(); int id = resultSet.getInt("studentid"); String name = resultSet.getString("studentname"); student.setStudentID(id); student.setStudentName(name); System.out.println(student); } } catch (Exception e) { e.printStackTrace(); } finally { if (resultSet != null) { try { resultSet.close(); } catch (SQLException e) { e.printStackTrace(); } resultSet = null; } if (preparedStatement != null) { try { preparedStatement.close(); } catch (SQLException e) { e.printStackTrace(); } preparedStatement = null; } if (connection != null) { try { connection.close(); } catch (SQLException e) { e.printStackTrace(); } connection = null; } } } } 至此,关于JDBC的基础知识就介绍完了,谢谢。

优秀的个人博客,低调大师

后台(34)——MyBatis概述

探索Android软键盘的疑难杂症 深入探讨Android异步精髓Handler 详解Android主流框架不可或缺的基石 站在源码的肩膀上全解Scroller工作机制 Android多分辨率适配框架(1)— 核心基础 Android多分辨率适配框架(2)— 原理剖析 Android多分辨率适配框架(3)— 使用指南 自定义View系列教程00–推翻自己和过往,重学自定义View 自定义View系列教程01–常用工具介绍 自定义View系列教程02–onMeasure源码详尽分析 自定义View系列教程03–onLayout源码详尽分析 自定义View系列教程04–Draw源码分析及其实践 自定义View系列教程05–示例分析 自定义View系列教程06–详解View的Touch事件处理 自定义View系列教程07–详解ViewGroup分发Touch事件 自定义View系列教程08–滑动冲突的产生及其处理 版权声明 本文原创作者:谷哥的小弟 作者博客地址:http://blog.csdn.net/lfdfhl MyBatis简介 MyBatis原本是apache的一个开源项目iBatis, 2010年该项目由apache software foundation 迁移到了google code并改名为MyBatis 。2013年11月MyBatis又迁移到Github。 The MyBatis SQL mapper framework makes it easier to use a relational database with object-oriented applications. MyBatis couples objects with stored procedures or SQL statements using a XML descriptor or annotations. Simplicity is the biggest advantage of the MyBatis data mapper over object relational mapping tools. MyBatis是一个优秀的持久层框架,它对JDBC操作数据库的过程进行了封装,使开发者只需要关注SQL本身,而不需要开发人员花费大量的精力去处理例如注册驱动、创建connection、创建statement、手动设置参数等JDBC操作。 Mybatis通过xml或注解的方式将要执行的各种statement配置起来,并通过java对象和statement中的sql进行映射生成最终执行的sql语句,最后由MyBatis框架执行sql并将结果映射成java对象并返回。 更多介绍,敬请参见MyBatis官方网站 MyBatis框架 1、SqlMapConfig.xml是MyBatis的全局配置文件,它配置了MyBatis的运行环境等信息。 2、mapper.xml是sql映射文件,它配置了操作数据库的sql语句。此文件需要在SqlMapConfig.xml中加载 3、通过MyBatis配置信息构造SqlSessionFactory(会话工厂) 4、由会话SqlSessionFactory(工厂)创建SqlSession(会话)操作数据库 5、MyBatis底层自定义Executor(执行器)接口用于执行数据库操作,该接口有两个实现:基本执行器和缓存执行器。 6、MappedStatement是MyBatis的底层封装对象,它封装了操作数据库的sql语句,输入参数,输出结果等信息。mapper.xml文件中每一个sql语句对应一个MappedStatement对象,sql语句的id即是MappedStatement的id。 7、MappedStatement对sql执行输入参数进行定义,包括HashMap、基本类型、pojo,Executor通过MappedStatement在执行sql前将输入的java对象映射至sql中 8、MappedStatement对sql执行输出结果进行定义,包括HashMap、基本类型、pojo,Executor通过MappedStatement在执行sql后将输出结果映射至java对象中 MyBatis开发环境 在利用MyBatis开发前,请移步MyBatis官网下载开发所有的jar包、开发指南,说明文档等资料。

优秀的个人博客,低调大师

后台(32)——内省Introspector

探索Android软键盘的疑难杂症 深入探讨Android异步精髓Handler 详解Android主流框架不可或缺的基石 站在源码的肩膀上全解Scroller工作机制 Android多分辨率适配框架(1)— 核心基础 Android多分辨率适配框架(2)— 原理剖析 Android多分辨率适配框架(3)— 使用指南 自定义View系列教程00–推翻自己和过往,重学自定义View 自定义View系列教程01–常用工具介绍 自定义View系列教程02–onMeasure源码详尽分析 自定义View系列教程03–onLayout源码详尽分析 自定义View系列教程04–Draw源码分析及其实践 自定义View系列教程05–示例分析 自定义View系列教程06–详解View的Touch事件处理 自定义View系列教程07–详解ViewGroup分发Touch事件 自定义View系列教程08–滑动冲突的产生及其处理 版权声明 本文原创作者:谷哥的小弟 作者博客地址:http://blog.csdn.net/lfdfhl 内省(Introspector)简介 内省Introspector是一种特殊的反射技术,常用于操作JavaBean。 字段和属性以及JavaBean 先来看我们经常写的一种类: /** * 本文作者:谷哥的小弟 * 博客地址:http://blog.csdn.net/lfdfhl */ package cn.com; public class Girl { private String name; private int age; private int bust; private int waist; private int hip; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public int getBust() { return bust; } public void setBust(int bust) { this.bust = bust; } public int getWaist() { return waist; } public void setWaist(int waist) { this.waist = waist; } public int getHip() { return hip; } public void setHip(int hip) { this.hip = hip; } } 在该类中,我们定义了几个字段并且为它们生成了对应的getXXX()和setXXX()。在很多时候我们喜欢把name,age,waist这些东西叫属性。其实,更加规范的的说法应该是:把有getXXX()或者setXXX()的变量叫做属性,把没有getXXX()或者setXXX()的变量称作字段。 很多时候,我们会看到这样的类:里面只有属性及其对应的get和set方法,这样的类也被称为JavaBean,比如此处的Girl类。 内省Introspector的使用方式 在此介绍与内省Introspector相关的操作 /** * 本文作者:谷哥的小弟 * 博客地址:http://blog.csdn.net/lfdfhl */ private static void testPropertyDescriptor1() throws IntrospectionException{ BeanInfo beanInfo = Introspector.getBeanInfo(Girl.class); PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors(); for (PropertyDescriptor propertyDescriptor : propertyDescriptors) { System.out.println(propertyDescriptor.getName()); } } 在此,我们利用属性描述器PropertyDescriptor获取Bean的属性,结果如下: age bust class hip name waist 嗯哼,发现了么?我们不但获取到了Gril类的五个属性还获取到了它的父类Object的属性class。 继续来看下面的例子: /** * 本文作者:谷哥的小弟 * 博客地址:http://blog.csdn.net/lfdfhl */ private static void testPropertyDescriptor2() throws Exception{ Girl girl = new Girl(); PropertyDescriptor propertyDescriptor = new PropertyDescriptor("age", Girl.class); Method setter = propertyDescriptor.getWriteMethod(); setter.invoke(girl, 18); Method getter = propertyDescriptor.getReadMethod(); Object age = getter.invoke(girl, null); System.out.println("age="+age); } 输出结果: age=18 代码解析如下: 得到与属性对应的属性描述器PropertyDescriptor,请参见代码第7行 得到该属性的set方法后为对象赋值,请参见代码第8—9行 得到该属性的get方法后取出属性值,请参见代码第10—11行 Beanutils BeanUtils是Apache提供的操作JavaBean的工具,它使用简单,操作方便。如有需要,可至其官方网站详细了解;在此不再赘述。

优秀的个人博客,低调大师

后台(33)——注解Annotation

探索Android软键盘的疑难杂症 深入探讨Android异步精髓Handler 详解Android主流框架不可或缺的基石 站在源码的肩膀上全解Scroller工作机制 Android多分辨率适配框架(1)— 核心基础 Android多分辨率适配框架(2)— 原理剖析 Android多分辨率适配框架(3)— 使用指南 自定义View系列教程00–推翻自己和过往,重学自定义View 自定义View系列教程01–常用工具介绍 自定义View系列教程02–onMeasure源码详尽分析 自定义View系列教程03–onLayout源码详尽分析 自定义View系列教程04–Draw源码分析及其实践 自定义View系列教程05–示例分析 自定义View系列教程06–详解View的Touch事件处理 自定义View系列教程07–详解ViewGroup分发Touch事件 自定义View系列教程08–滑动冲突的产生及其处理 版权声明 本文原创作者:谷哥的小弟 作者博客地址:http://blog.csdn.net/lfdfhl 注解Annotation 关于注解,请参见《 详解Android主流框架不可或缺的基石》,在此不再赘述

优秀的个人博客,低调大师

后台(31)——反射Reflection

探索Android软键盘的疑难杂症 深入探讨Android异步精髓Handler 详解Android主流框架不可或缺的基石 站在源码的肩膀上全解Scroller工作机制 Android多分辨率适配框架(1)— 核心基础 Android多分辨率适配框架(2)— 原理剖析 Android多分辨率适配框架(3)— 使用指南 自定义View系列教程00–推翻自己和过往,重学自定义View 自定义View系列教程01–常用工具介绍 自定义View系列教程02–onMeasure源码详尽分析 自定义View系列教程03–onLayout源码详尽分析 自定义View系列教程04–Draw源码分析及其实践 自定义View系列教程05–示例分析 自定义View系列教程06–详解View的Touch事件处理 自定义View系列教程07–详解ViewGroup分发Touch事件 自定义View系列教程08–滑动冲突的产生及其处理 版权声明 本文原创作者:谷哥的小弟 作者博客地址:http://blog.csdn.net/lfdfhl 关于该部分内容,敬请参见《详解Android主流框架不可或缺的基石》,不再赘述

优秀的个人博客,低调大师

后台(29)——动态代理

探索Android软键盘的疑难杂症 深入探讨Android异步精髓Handler 详解Android主流框架不可或缺的基石 站在源码的肩膀上全解Scroller工作机制 Android多分辨率适配框架(1)— 核心基础 Android多分辨率适配框架(2)— 原理剖析 Android多分辨率适配框架(3)— 使用指南 自定义View系列教程00–推翻自己和过往,重学自定义View 自定义View系列教程01–常用工具介绍 自定义View系列教程02–onMeasure源码详尽分析 自定义View系列教程03–onLayout源码详尽分析 自定义View系列教程04–Draw源码分析及其实践 自定义View系列教程05–示例分析 自定义View系列教程06–详解View的Touch事件处理 自定义View系列教程07–详解ViewGroup分发Touch事件 自定义View系列教程08–滑动冲突的产生及其处理 本文已经deprecated,关于动态代理,请参见从狡猾的房产中介来看动态代理 版权声明 本文原创作者:谷哥的小弟 作者博客地址:http://blog.csdn.net/lfdfhl 代理模式简介 代理模式(Proxy Pattern)是面向对象中一种非常常见的设计模式。其实,不单是在软件开发领域,在我们的日常生活中对于代理也时常可见。比如:房东要将自家的房租出售,于是到房地产中介公司找一个代理,由他来帮自己完成销售房屋,签订合同等等事宜。 在此,就以该生活场景为蓝本介绍Java的代理模式。 静态代理 房东通过一纸协议将自己的房子挂靠在房屋中介;嗯哼,我们来一起瞅瞅这个房东和中介公司共同达成的协议: /** * 本文作者:谷哥的小弟 * 博客地址:http://blog.csdn.net/lfdfhl */ package cn.com; public interface TradeProtocol { public void sellHouse(int money); } 嗯哼,这个协议很简单,房东委托中介公司售卖自己的房子。 既然是房东和房屋中介共同达成的协议,那么房东和中介都需要遵守该协议。 先来看房东: /** * 本文作者:谷哥的小弟 * 博客地址:http://blog.csdn.net/lfdfhl */ package cn.com; public class Owner implements TradeProtocol{ @Override public void sellHouse(int money) { System.out.println("我是房主,我的房子卖了"+money+"块钱"); } } 再来看中介: /** * 本文作者:谷哥的小弟 * 博客地址:http://blog.csdn.net/lfdfhl */ package cn.com; public class HouseAgent implements TradeProtocol { private TradeProtocol mHouseProtocol; public HouseAgent(TradeProtocol houseProtocol){ mHouseProtocol=houseProtocol; } @Override public void sellHouse(int money) { System.out.println("我是中介,业主的房子卖了"+money+"块钱"); mHouseProtocol.sellHouse(money); } } 请注意HouseAgent的实现 在HouseAgent的构造方法中传入TradeProtocol的对象比如Owner 在HouseAgent的sellHouse()中调用TradeProtocol的对象(比如Owner)的sellHouse() 所以,HouseAgent售卖(sellHouse())房子,实际上是房东售卖了房屋 测试如下: /** * 本文作者:谷哥的小弟 * 博客地址:http://blog.csdn.net/lfdfhl */ package cn.com; public class Test { public static void main(String[] args) { Owner owner=new Owner(); HouseAgent houseAgent=new HouseAgent(owner); houseAgent.sellHouse(10000*100); } } 测试结果: 我是中介,业主的房子卖了1000000块钱 我是房主, 我的房子卖了 1000000块钱 在该静态代理中,房东将房屋的售卖交给了中介代理,中介将房屋出售后又把钱一分不少地交给了房东。咦,这咋不对呢?现实中有这样大公无私,视金钱如粪土的房屋中介么?没有!绝对没有! 那么,中介是怎么赚钱的呢?我们继续往下看 动态代理 与之前一样,房东和中介之间存有一纸协议: /** * 本文作者:谷哥的小弟 * 博客地址:http://blog.csdn.net/lfdfhl */ package cn.com; public interface TradeProtocol { public void sellHouse(int money); } 房东遵守该协议: /** * 本文作者:谷哥的小弟 * 博客地址:http://blog.csdn.net/lfdfhl */ package cn.com; public class Owner implements TradeProtocol{ @Override public void sellHouse(int money) { System.out.println("我是房主,中介告诉我:房子卖了"+money+"块钱"); } } 现在,房东又去房屋中介公司挂靠自己的房子,到了那一看:原来熟悉的那个代理出去办事了,刚好不在。正准备走呢,中介公司的经理挺着啤酒肚走过来,笑嘻嘻地说:请问您是来办理业务的吗?来,请坐,我给你们引荐以为我们这里服务最好的一个房屋代理人员办理您的相关事宜 嗯哼,我们来看看这个代理是怎么卖掉房东的房子的: /** * 本文作者:谷哥的小弟 * 博客地址:http://blog.csdn.net/lfdfhl */ package cn.com; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; public class Test { public static void main(String[] args) { TradeProtocol owner=new Owner(); ClassLoader classLoader=owner.getClass().getClassLoader(); Class<?>[] interfaces = owner.getClass().getInterfaces(); TradeProtocol houseAgent=(TradeProtocol) Proxy.newProxyInstance(classLoader,interfaces, new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args)throws Throwable { Object object=null; System.out.println("我是中介,业主的房子我实际卖了"+args[0]+"块钱"); object=method.invoke(owner, new Object[]{(Integer)args[0]/2}); System.out.println("我是中介,这次交易我赚了不少钱呢"); return object; } }); houseAgent.sellHouse(10000*100); } } 在该实现中由系统自动地为我们创建了一个代理Proxy.newProxyInstance() 结果如下: 我是中介,业主的房子我实际卖了1000000块钱 我是房主,中介告诉我:房子卖了500000块钱 我是中介,这次交易我赚了不少钱呢 哇哈,中介把房子卖了100W却告诉房东只买了50W,自己狠狠地赚了一笔! 嗯哼,这个例子我们看完了,在此对照Test类分析动态代理的实现 动态代理与静态模式的原理是一样的,只是它没有具体的代理类而是通过Proxy在JVM运行时利用反射动态地生成了一个代理。 请注意Proxy.newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)方法的三个参数 ClassLoader:类加载器,传入和被代理类使用的相同的类加载器即可 Class<?>[] interface:代理类要实现的接口,传入和被代理类使用的相同的接口即可 InvocationHandler h:当通过代理对象调用一个方法的时候,该方法的调用会被转发至InvocationHandler接口的invoke()方法来执行具体的调用 请注意invoke(Object proxy, Method method, Object[] args)方法的三个参数 Object proxy:代理对象的引用 Method method:当前调用的方法 Object[] args:当前调用的方法的输入参数 由此可见:动态代理可方便地对委托类的方法进行某些处理,比如在方法实际调用前做一些过滤或者拦截操作,在方法调用后做一些善后处理等 后会有期 我们会在学习Spring框架时对Java动态代理机制做进一步的认识和理解

优秀的个人博客,低调大师

后台(27)——文件上传

探索Android软键盘的疑难杂症 深入探讨Android异步精髓Handler 详解Android主流框架不可或缺的基石 站在源码的肩膀上全解Scroller工作机制 Android多分辨率适配框架(1)— 核心基础 Android多分辨率适配框架(2)— 原理剖析 Android多分辨率适配框架(3)— 使用指南 自定义View系列教程00–推翻自己和过往,重学自定义View 自定义View系列教程01–常用工具介绍 自定义View系列教程02–onMeasure源码详尽分析 自定义View系列教程03–onLayout源码详尽分析 自定义View系列教程04–Draw源码分析及其实践 自定义View系列教程05–示例分析 自定义View系列教程06–详解View的Touch事件处理 自定义View系列教程07–详解ViewGroup分发Touch事件 自定义View系列教程08–滑动冲突的产生及其处理 版权声明 本文原创作者:谷哥的小弟 作者博客地址:http://blog.csdn.net/lfdfhl 文件上传概述 文件上传通常指的是用户在浏览器中通过表单form将文件上传至服务端 但是请注意该表单必须满足如下三个条件: form的method必须是post form的enctype必须是multipart/form-data form中利用input type=”file”提供上传的入口 Commons-fileupload简介 Commons-fileupload是Apache提供的一个用来处理表单文件上传的一个开源组件。该组件操作简单、API通俗易懂、性能优异。 使用Commons-fileupload需要导入两个jar包: commons-fileupload commons-io Commons-fileupload使用示例 我们先来准备一个表单form <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>文件上传</title> </head> <body> <form enctype="multipart/form-data" action="${pageContext.request.contextPath }/servlet" method="post" > 文件描述:<input type="text" name="description"/><br/><br/> 选择文件:<input type="file" name="photo"/><br/><br/> <input type="submit" value="上传"/><br/><br/> </form> </body> </html> 运行后效果如下图所示: 再来写一个处理文件上传的Servlet /** * 本文作者:谷哥的小弟 * 博客地址:http://blog.csdn.net/lfdfhl */ package cn.com; import java.io.File; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.text.SimpleDateFormat; import java.util.Date; import java.util.List; import java.util.UUID; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.commons.fileupload.FileItem; import org.apache.commons.fileupload.disk.DiskFileItemFactory; import org.apache.commons.fileupload.servlet.ServletFileUpload; import org.apache.commons.io.FilenameUtils; public class UploadServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { } @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 判断表单是否支持文件上传 boolean isMultipartContent = ServletFileUpload.isMultipartContent(request); if (isMultipartContent) { DiskFileItemFactory factory = new DiskFileItemFactory(); ServletFileUpload servletFileUpload = new ServletFileUpload(factory); servletFileUpload.setHeaderEncoding("UTF-8"); try { //得到并处理表单中的每个项 List<FileItem> fileItems = servletFileUpload.parseRequest(request); for (FileItem fileitem : fileItems) { if (fileitem.isFormField()) { processFormField(fileitem); } else { processUploadField(fileitem); } } } catch (Exception e) { e.printStackTrace(); } } } // 处理普通表单项 private void processFormField(FileItem fileitem) { try { String fieldName = fileitem.getFieldName(); String fieldValue = fileitem.getString("UTF-8"); System.out.println(fieldName + "=" + fieldValue); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } } //处理上传表单项 private void processUploadField(FileItem fileItem) { try { //创建存放上传文件的目录 String folderName="upload"; String folderPath = getServletContext().getRealPath("/WEB-INF/"+folderName); File folder = new File(folderPath); if (!folder.exists()) { folder.mkdirs(); } //生成不重复的文件名 String fileName = fileItem.getName(); if (fileName != null) { fileName = FilenameUtils.getName(fileName); } fileName = UUID.randomUUID() + "_" + fileName; //按照日期生成子文件名称 String dateDirectory = makeDateDirectory(folder); //存放被上传的文件 File file=new File(folder, dateDirectory + File.separator + fileName); fileItem.write(file); } catch (Exception e) { e.printStackTrace(); } } //依据日期生成子文件夹 private String makeDateDirectory(File folder) { SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd"); Date date=new Date(); String dateDirectory = simpleDateFormat.format(date); File file = new File(folder, dateDirectory); if (!file.exists()) { file.mkdirs(); } return dateDirectory; } } 嗯哼,鉴于该示例比较简单和代码里已有的注释,在此不再对代码作详解。 文件上传的注意事项 实际开发中,在处理文件上传时请注意以下问题: 将保存上传文件的目录放在用户直接访问不到的地方,比如WEB-INF 上传文件重名的处理,可用UUID+文件名的方式 将上传文件合理地存入子目录中,比如按照日期 限制上传文件的大小,比如3M

优秀的个人博客,低调大师

后台(26)——Filter(3)

探索Android软键盘的疑难杂症 深入探讨Android异步精髓Handler 详解Android主流框架不可或缺的基石 站在源码的肩膀上全解Scroller工作机制 Android多分辨率适配框架(1)— 核心基础 Android多分辨率适配框架(2)— 原理剖析 Android多分辨率适配框架(3)— 使用指南 自定义View系列教程00–推翻自己和过往,重学自定义View 自定义View系列教程01–常用工具介绍 自定义View系列教程02–onMeasure源码详尽分析 自定义View系列教程03–onLayout源码详尽分析 自定义View系列教程04–Draw源码分析及其实践 自定义View系列教程05–示例分析 自定义View系列教程06–详解View的Touch事件处理 自定义View系列教程07–详解ViewGroup分发Touch事件 自定义View系列教程08–滑动冲突的产生及其处理 版权声明 本文原创作者:谷哥的小弟 作者博客地址:http://blog.csdn.net/lfdfhl 在这篇博客中我们利用Filter一并解决POST和GET请求的中文乱码。 测试表单 <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>Welcome</title> </head> <body> <form action="${pageContext.request.contextPath}/loginServlet" method="post"> <br/> 用 户:<input type="text" name="username"/><br/><br/> 密 码:<input type="password" name="password"/><br/><br/> <input type="submit" value=" 登 录 " /><br/> </form> </body> </html> 该页面非常简单,在点击”登录”之后跳转到loginServlet 测试Servlet /** * 本文作者:谷哥的小弟 * 博客地址:http://blog.csdn.net/lfdfhl */ package cn.com; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class LoginServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doGetAndPost(req, resp); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doGetAndPost(req, resp); } private void doGetAndPost(HttpServletRequest req, HttpServletResponse resp){ String username=req.getParameter("username"); System.out.println("---> username="+username); } } 在该页面中利用getParameter( )测试是否乱码 利用Filter过滤乱码 嗯哼,终于轮到今天的主角上场了 /** * 本文作者:谷哥的小弟 * 博客地址:http://blog.csdn.net/lfdfhl */ package cn.com; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequestWrapper; import javax.servlet.http.HttpServletResponse; public class TestFilter implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { } @Override public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { HttpServletRequest request=(HttpServletRequest) req; HttpServletResponse response=(HttpServletResponse) res; request=new MyHttpServletRequest(request); chain.doFilter(request, response); } @Override public void destroy() { } } class MyHttpServletRequest extends HttpServletRequestWrapper{ HttpServletRequest request; boolean isFirst=true; public MyHttpServletRequest(HttpServletRequest request) { super(request); this.request=request; } @Override public String getParameter(String name) { Map<String, String[]> parameterMap=getParameterMap(); String[] values=parameterMap.get(name); String value=values[0]; return value; } @Override public String[] getParameterValues(String name) { Map<String, String[]> parameterMap=getParameterMap(); String[] values=parameterMap.get(name); return values; } @Override public Map<String, String[]> getParameterMap() { Map<String, String[]> parameterMap = request.getParameterMap(); if(isFirst){ Set<Entry<String, String[]>> entrySet = parameterMap.entrySet(); for(Entry<String, String[]> entry:entrySet){ String[] values = entry.getValue(); for(int i=0;i<values.length;i++){ String value=values[i]; try { value=new String(value.getBytes("iso8859-1"),"UTF-8"); values[i]=value; } catch (UnsupportedEncodingException e) { e.printStackTrace(); } } } isFirst=false; } return parameterMap; } } 在该类中通过继承HttpServletRequestWrapper实现自己的HttpServletRequest,即文中的MyHttpServletRequest。 覆写getParameter( )使其最终调用getParameterMap( ) 覆写getParameterValues( )使其最终调用getParameterMap( ) 在getParameterMap( )中将每个请求参数用UTF-8重新编码 配置文件 <?xml version="1.0" encoding="UTF-8"?> <web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"> <filter> <filter-name>TestFilter</filter-name> <filter-class>cn.com.TestFilter</filter-class> </filter> <filter-mapping> <filter-name>TestFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <servlet> <servlet-name>loginServlet</servlet-name> <servlet-class>cn.com.LoginServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>loginServlet</servlet-name> <url-pattern>/loginServlet</url-pattern> </servlet-mapping> <welcome-file-list> <welcome-file>index.jsp</welcome-file> </welcome-file-list> </web-app> 嗯哼,至此利用Filter同时解决了Post和Get请求时带来的中文乱码问题。

优秀的个人博客,低调大师

后台(25)——Filter(2)

探索Android软键盘的疑难杂症 深入探讨Android异步精髓Handler 详解Android主流框架不可或缺的基石 站在源码的肩膀上全解Scroller工作机制 Android多分辨率适配框架(1)— 核心基础 Android多分辨率适配框架(2)— 原理剖析 Android多分辨率适配框架(3)— 使用指南 自定义View系列教程00–推翻自己和过往,重学自定义View 自定义View系列教程01–常用工具介绍 自定义View系列教程02–onMeasure源码详尽分析 自定义View系列教程03–onLayout源码详尽分析 自定义View系列教程04–Draw源码分析及其实践 自定义View系列教程05–示例分析 自定义View系列教程06–详解View的Touch事件处理 自定义View系列教程07–详解ViewGroup分发Touch事件 自定义View系列教程08–滑动冲突的产生及其处理 版权声明 本文原创作者:谷哥的小弟 作者博客地址:http://blog.csdn.net/lfdfhl 自动登录 在本篇博客中我们将完成一个小例子:利用Servlet、Filter、C3P0、MD5、HttpSession、Cookie实现等技术实现自动登录。 请看,本次示例的项目结构 在此,详解项目中的核心代码。 /** * 本文作者:谷哥的小弟 * 博客地址:http://blog.csdn.net/lfdfhl */ package cn.com.servlet; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import cn.com.bean.User; import cn.com.service.UserService; import cn.com.service.impl.UserServiceImpl; import cn.com.util.MD5Utils; public class LoginServlet extends HttpServlet { private final String MESSAGE="message"; @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String username=request.getParameter("username"); String password=request.getParameter("password"); password=MD5Utils.md5(password); UserService userService=new UserServiceImpl(); User user=userService.findUser(username, password); if(user!=null){ String autologin=request.getParameter("autologin"); Cookie cookie=new Cookie("userinfo", user.getUsername()+"&"+user.getPassword()); cookie.setPath("/"); if(autologin==null){ cookie.setMaxAge(0); }else{ cookie.setMaxAge(60*60*24*3); } response.addCookie(cookie); request.getSession().setAttribute("user", user); request.getRequestDispatcher("/home.jsp").forward(request, response); }else{ request.setAttribute(MESSAGE, "用户名或者密码错误,请重新登录"); request.getRequestDispatcher("/login.jsp").forward(request, response); } } @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { } } 获取表单传递过来的参数,请参见代码第24-25行 从数据库查询用户,请参见代码第28行 生成Cookie并返回给客户端,请参见代码第30-38行 依据是否自动登录设置Cookie的MaxAge,请参见代码第33-37行 将用户User保存到Session中,请参见代码第39行 /** * 本文作者:谷哥的小弟 * 博客地址:http://blog.csdn.net/lfdfhl */ package cn.com.filter; import java.io.IOException; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import cn.com.bean.User; import cn.com.service.UserService; import cn.com.service.impl.UserServiceImpl; public class LoginFilter implements Filter{ @Override public void init(FilterConfig filterConfig) throws ServletException { } @Override public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { HttpServletRequest request=(HttpServletRequest) req; HttpServletResponse response=(HttpServletResponse) res; String path = request.getContextPath(); String uri = request.getRequestURI(); path = uri.substring(path.length()); if (!("/login.jsp".equals(path) || "/loginServlet".equals(path))) { User user=(User) request.getSession().getAttribute("user"); if(user==null){ String username=null; String password=null; Cookie[] cookies = request.getCookies(); for(int i=0;cookies!=null&&i<cookies.length;i++){ Cookie cookie=cookies[i]; String name=cookie.getName(); if("userinfo".equals(name)){ String value = cookie.getValue(); String[] strings = value.split("&"); username=strings[0]; password=strings[1]; break; } } UserService userService=new UserServiceImpl(); User u = userService.findUser(username, password); if(u!=null){ request.getSession().setAttribute("user", u); } } } chain.doFilter(request, response); } @Override public void destroy() { } } 如果访问的不是登录相关的资源那么就考虑是否自动登录,请参见代码第39-62行 如果用户没有登录过则开始自动登录,请参见代码第40-61行 自动登录成功后将User保存到Session中,请参见代码第59行 <?xml version="1.0" encoding="UTF-8"?> <web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"> <filter> <filter-name>loginFilter</filter-name> <filter-class>cn.com.filter.LoginFilter</filter-class> </filter> <filter-mapping> <filter-name>loginFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <servlet> <servlet-name>loginServlet</servlet-name> <servlet-class>cn.com.servlet.LoginServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>loginServlet</servlet-name> <url-pattern>/loginServlet</url-pattern> </servlet-mapping> <welcome-file-list> <welcome-file>index.jsp</welcome-file> </welcome-file-list> </web-app> 在web.xml中配置Servlet和Filter

优秀的个人博客,低调大师

后台(24)——Filter(1)

探索Android软键盘的疑难杂症 深入探讨Android异步精髓Handler 详解Android主流框架不可或缺的基石 站在源码的肩膀上全解Scroller工作机制 Android多分辨率适配框架(1)— 核心基础 Android多分辨率适配框架(2)— 原理剖析 Android多分辨率适配框架(3)— 使用指南 自定义View系列教程00–推翻自己和过往,重学自定义View 自定义View系列教程01–常用工具介绍 自定义View系列教程02–onMeasure源码详尽分析 自定义View系列教程03–onLayout源码详尽分析 自定义View系列教程04–Draw源码分析及其实践 自定义View系列教程05–示例分析 自定义View系列教程06–详解View的Touch事件处理 自定义View系列教程07–详解ViewGroup分发Touch事件 自定义View系列教程08–滑动冲突的产生及其处理 版权声明 本文原创作者:谷哥的小弟 作者博客地址:http://blog.csdn.net/lfdfhl Filter简介 JavaWeb中的过滤器Filter可以拦截所有访问Web资源(Servlet、JSP、HTML等等)的请求及其响应。Filter本身并不会产生请求和响应对象,它只提供过滤作用;比如:在Servlet被调用之前检查或者修改Request、Request Header、Request中的内容;在Servlet被调用之后检查或者修改Response、Response Header和Response中的内容。通过Filter我们可以实现URL级别的权限访问控制、过滤敏感词汇、压缩响应信息、验证、加密等一些非常实用的功能。 Filter入门示例 Filter的实现步骤如下: 1、创建一个类实现Filter接口 2、实现接口中方法,在doFilter( )中实现过滤 3、在web.xml文件中配置Filter 请看如下示例 先写一个Filter /** * 本文作者:谷哥的小弟 * 博客地址:http://blog.csdn.net/lfdfhl */ package cn.com; import java.io.IOException; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; public class FilterTest implements Filter{ public FilterTest(){ System.out.println("调用Filter构造方法"); } @Override public void init(FilterConfig filterConfig) throws ServletException { System.out.println("调用Filter的init()方法"); } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { System.out.println("doFilter()开始过滤"); chain.doFilter(request, response); System.out.println("doFilter()过滤结束"); } @Override public void destroy() { System.out.println("调用Filter的destroy()方法"); } } 再写一个Servlet /** * 本文作者:谷哥的小弟 * 博客地址:http://blog.csdn.net/lfdfhl */ package cn.com; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class HttpServletTest extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("调用HttpServlet的doGet()方法"); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("调用HttpServlet的doPost()方法"); } } 最后在web.xml中配置Filter和Servlet <?xml version="1.0" encoding="UTF-8"?> <web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"> <filter> <filter-name>TestFilter</filter-name> <filter-class>cn.com.FilterTest</filter-class> </filter> <filter-mapping> <filter-name>TestFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <servlet> <servlet-name>TestServlet</servlet-name> <servlet-class>cn.com.HttpServletTest</servlet-class> </servlet> <servlet-mapping> <servlet-name>TestServlet</servlet-name> <url-pattern>/servlet</url-pattern> </servlet-mapping> <welcome-file-list> <welcome-file>index.jsp</welcome-file> </welcome-file-list> </web-app> 在这里也可看出:Filter的配置方式和Servlet非常类似!不仅如此,Filter的生命周期也与Servlet基本一致: 服务器启动时创建Filter对象并调用init(),该方法只调用一次. 访问的资源路径与Filter的拦截路径匹配时执行Filter中的doFilter()执行过滤(拦截) 当服务器关闭时调用Filter的destroy() 运行后输出结果: 调用Filter构造方法 调用Filter的init()方法 doFilter()开始过滤 调用HttpServlet的doGet()方法 doFilter()过滤结束 嗯哼,看到了吧:浏览器发起的请求会首先进入到Filter中后才进入到Servlet里;当Servlet中的方法执行完成后再次回到Filter。 在该实例中,我们只使用了一个Filter。其实,我们也可以编写多个Filter实现不同的功能,这些Filter将形成一个链条即FilterChain;当浏览器发起请求时,请求会先通过这个链条再到达资源。请注意一个小问题:链条中Filter的调用顺序是由其在web.xml中对应的<filter-mapping>的先后顺序决定的。 FilterConfig 我们知道Servlet中有一个ServletConfig;其实,在Filter也存在一个类似于ServletConfig的东西,它就是FilterConfig。嗯哼,想起来没有?——我们刚才的示例FilterTest中的init( )方法参数是不是一个FilterConfig呢? FilterConfig的作用: 1.获取Filter的名称 2.获取初始化参数 3.获取ServletContext对象 FilterConfig的常用方法: getFilterName( ) getInitParameter(String name) getInitParameterNames( ) getServletContext() Filter配置详解 Filter基本配置介绍 <filter> <filter-name>filter名称</filter-name> <filter-class>filter类全名</filter-class> </filter> <filter-mapping> <filter-name>filter名称</filter-name> <url-pattern>映射路径</url-pattern> </filter-mapping> 关于<url-pattern>的配置 1、完全匹配 必须以”/”开始. 2、目录匹配 必须以”/”开始,以*结束. 3、扩展名匹配 以*.xxx结束. 关于<servlet-name>的配置 可在<filter-mapping>中利用<servlet-name>对servlet进行过滤配置。即用<servlet-name>设置当前Filter拦截哪一个servlet 关于<dispatcher>的配置 可在<filter-mapping>中利用<dispatcher>指明:Filter在资源以什么方式被Servlet容器调用时才进行拦截。<dispatcher>的取值有:REQUEST、FORWARD、ERROR、INCLUDE 可在<filter-mapping>中利用<dispatcher>设置Web资源以什么方式被Servlet容器调用时Filter才进行拦截。<dispatcher>的取值有:REQUEST、FORWARD、ERROR、INCLUDE 1、REQUEST 目标资源被访问或者系统重定向后指向该目标资源时Filter才拦截。这也是默认的值 2、FORWARD 目标资源通过RequestDispatcher.forward(request, response)被调用时Filter才拦截 3、ERROR 目标资源是在系统处理异常时被调用 4、INCLUDE 目标资源通过RequestDispatcher.include(request, response)被调用时Filter才拦截

优秀的个人博客,低调大师

后台(19)——事务Transaction

探索Android软键盘的疑难杂症 深入探讨Android异步精髓Handler 详解Android主流框架不可或缺的基石 站在源码的肩膀上全解Scroller工作机制 Android多分辨率适配框架(1)— 核心基础 Android多分辨率适配框架(2)— 原理剖析 Android多分辨率适配框架(3)— 使用指南 自定义View系列教程00–推翻自己和过往,重学自定义View 自定义View系列教程01–常用工具介绍 自定义View系列教程02–onMeasure源码详尽分析 自定义View系列教程03–onLayout源码详尽分析 自定义View系列教程04–Draw源码分析及其实践 自定义View系列教程05–示例分析 自定义View系列教程06–详解View的Touch事件处理 自定义View系列教程07–详解ViewGroup分发Touch事件 自定义View系列教程08–滑动冲突的产生及其处理 版权声明 本文原创作者:谷哥的小弟 作者博客地址:http://blog.csdn.net/lfdfhl 事务简介 事务(Transaction)是数据库操作中并发控制的基本单位。事务是一个操作序列,这个序列中的所有数据库操作要么都成功执行,要么都不执行,它是一个不可分割的工作单位。 在数据库中与事务相关的操作有: 开启事务:start transaction 提交事务:commit 回滚事务:rollback 事务的特性 原子性 事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。 一致性 事务必须使数据库从一个一致性状态变换到另外一个一致性状态。比如:两个账户相互转账,那么转账前和转账后的总金额不变。 隔离性 多个用户并发访问数据库时,数据库为每一个用户开启的事务不能被其他事务的操作数据所干扰;即多个并发事务之间要相互隔离。 持久性 事务一旦被提交,它对数据库中数据的改变就是永久性的,即使数据库发生故障也不应该对其有任何影响 事务的四大特性中原子性,一致性,持久性很好理解,在此不再赘述。 下面重点介绍一下事务的隔离级别。 在多个线程中各自开启事务操作数据库中数据时,数据库系统要负责隔离操作,以保证各个线程在获取数据时的准确性。假如,事务不考虑隔离性可能会引发如下问题: 赃读 在一个事务中读取了另一个事务未提交的数据。 不可重复读 在一个事务内读取表中的某些数据,多次读取结果不同;即一个事务读取到了另一个事务提交后的数据。比如:开启事务后利用SQL语句查询数据,得到结果;立即再次查询得到的结果却与刚才不一样。通俗地说,不可重复读就是:自己的事务还没有提交,又读到了其它事务中执行了upate并提交的数据。 虚读 是指在一个事务内读取到了别的事务中执行insert并提交的数据,导致前后读取不一致。 数据库通过设置事务的隔离级别防止以上情况的发生,常用的事务级别有如下几种: 1、READ UNCOMMITTED 将事务级别设置为该级别后,赃读、不可重复读、虚读都有可能发生。 2、READ COMMITTED 将事务级别设置为该级别后,可避免赃读;但是不可重复读、虚读都有可能发生 3、REPEATABLE READ 将事务级别设置为该级别后,避免赃读、不可重复读;但是虚读有可能发生。 4、SERIALIZABLE 将事务级别设置为该级别后,可避免赃读、不可重复读以及虚读。 关于数据库的事务级别,请注意: 事务级别越高那么数据操作越安全但是操作性能越低 在MySQL中可利用SELECT @@TX_ISOLATION查看当前的事务隔离级别 在MySQL中可利用SET TRANSACTION ISOLATION LEVEL XXXXX 更改事务隔离级别 应在开启事务之前设置隔离级别 JDBC中控制事务以及操作事务 利用connection.setTransactionIsolation(int level)设置隔离级别 利用connection.setAutoCommit(false)开启事务 利用connection.commit( )提交事务 利用connection.rollback( )回滚事务

资源下载

更多资源
腾讯云软件源

腾讯云软件源

为解决软件依赖安装时官方源访问速度慢的问题,腾讯云为一些软件搭建了缓存服务。您可以通过使用腾讯云软件源站来提升依赖包的安装速度。为了方便用户自由搭建服务架构,目前腾讯云软件源站支持公网访问和内网访问。

Nacos

Nacos

Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service 的首字母简称,一个易于构建 AI Agent 应用的动态服务发现、配置管理和AI智能体管理平台。Nacos 致力于帮助您发现、配置和管理微服务及AI智能体应用。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据、流量管理。Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。

Spring

Spring

Spring框架(Spring Framework)是由Rod Johnson于2002年提出的开源Java企业级应用框架,旨在通过使用JavaBean替代传统EJB实现方式降低企业级编程开发的复杂性。该框架基于简单性、可测试性和松耦合性设计理念,提供核心容器、应用上下文、数据访问集成等模块,支持整合Hibernate、Struts等第三方框架,其适用范围不仅限于服务器端开发,绝大多数Java应用均可从中受益。

WebStorm

WebStorm

WebStorm 是jetbrains公司旗下一款JavaScript 开发工具。目前已经被广大中国JS开发者誉为“Web前端开发神器”、“最强大的HTML5编辑器”、“最智能的JavaScript IDE”等。与IntelliJ IDEA同源,继承了IntelliJ IDEA强大的JS部分的功能。

用户登录
用户注册