Servlet:
1)servlet : servlet是一个特殊的java程序,需要在web服务器上运行,并接收和响应客户端的请求,遵循http协议.
2)Servlet;作用: 主要用于控制层.
3)Servlet;实现方式:
3.1实现接口servlet,复写所有的方法;
3.2在web.xml文件中配置servlet内容.
>>在web.xml文件中的配置,目的是通知Web服务器,Servlet的存在和访问路径
>>程序员写的每一个Servlet程序(普通类实现了Servlet接口)被Web服务器调用,叫反调,即main方法在Web服务中
实现servlet的三种方式:
1)类implementServlet接口,复写其中的方法
2)类extendsGenericServlet类
public class GenericServlet extends HttpServlet {
public void doGet(HttpServletRequestrequest, HttpServletResponse response)
throws ServletException, IOException {
//获取字符输出流对象
PrintWriter pw = response.getWriter();
//将数据输出到浏览器
pw.write("servlet first methodgenericServlet
");
}
public void doPost(HttpServletRequestrequest, HttpServletResponse response)
throws ServletException, IOException {
}
}
3)类extendsHttpServlet类
HttpServlet指能够处理HTTP请求的servlet,它在原有Servlet接口上添加了一些与HTTP协议处理方法,它比Servlet接口的功能更为强大。因此开发人员在编写Servlet时,通常应继承这个类,而避免直接去实现Servlet接口。
HttpServlet在实现Servlet接口时,覆写了service方法,该方法体内的代码会自动判断用户的请求方式,如为GET请求,则调用HttpServlet的doGet方法,如为Post请求,则调用doPost方法。因此,开发人员在编写Servlet时,通常只需要覆写doGet或doPost方法,而不要去覆写service方法。
由于客户端是通过URL地址访问web服务器中的资源,所以Servlet程序若想被外界访问,必须把servlet程序映射到一个URL地址上,这个工作在web.xml文件中使用
一个
例: <servlet>
<servlet-name>MyServletservlet-name>
<servlet-class>cn.web.servlet.MyServletservlet-class>
servlet>
<servlet-mapping>
<servlet-name>MyServletservlet-name>
<url-pattern>/MyServleturl-pattern>
servlet-mapping>
*3 Servlet生命周期和工作原理
1)工作原理
默认情况下,在用户第一次访问时
空参构造方法():1次
init()方法():1次
doGet()/doPost()每次请求都执行一次:n次
当Web应用重新部署/加载到Web服务器时,Web服务器移除原Web应用,此时调用destory()方法:1次.每次请求,都由同一个Servlet来服务,该Servlet是一个单例模式
2)工作原理
*1)应用能过URL访问某个Servlet,Web服务端根据URL中在web.xml文件中查询是有对应的
*2)Web服务器在Servlet实例缓存池中根据
*3)Web服务端,调用该Servlet中的doGet()或doPost()或service(),创建一个用于封装HTTP请求消息的HttpServletRequest对象和一个代表HTTP响应消息的HttpServletResponse对象,然后调用Servlet的service()方法并将请求和响应对象作为参数传递进去。这个实现对象与具体Web服务器有关,只需要基于接口编程
*4)Web服务器解析web.xml文件,通过反射方式创建该Servlet实例,初始化Servlet实例,然后将该Servlet实例,放入Servlet实例缓存池中,由Web服务器维护,以备下次重用,(这也是为什么Servlet是单例的原因)
5)执行doGet()或doPost()或service()方法中的业务操作,执行完后,返回Web服务器
6)当Web服务器收到返回请求后,将返回数据取出,形成html网页,发送到浏览器
7)当Web服务器决定销毁该Servlet时(重新部署Web应用或停止Web服务器或者设置servlt的存活时间),会在真正销毁前,调用该Servlet实例的destory()方法
*4 Servlet细节
1)Servlet的访问路径,只是一个以/杠开头的字符串而以,不能以扩展判段是一个什么资源,当静态资源和动态资源访问URL一至的情况下,动态优先
2)一个Servlet可以映射到多个URL路径上,至少一个.
访问URL的路径只有二种:
*.扩展名
/*,*表示通配符
3)多个Servlet访问,按照最像原则,有扩展名的最后考虑
4)程序员写的Servlet,是被Web服务器调用, 主要愿因是:Servlet是一个供其他Java程序(Servlet引擎)调用的Java类,它不能独立运行,它的运行完全由Servlet引擎来控制和调度
5)
但数字最小为0,如果写成负数,相当于不用
举例:
org.apache.catalina.servlets.InvokerServlet
用途:为web应用写一个InitServlet,这个servlet配置为启动时装载,为整个web应用创建必要的数据库表和数据。
6)当用户的URL访问路径在web.xml的所有servlet中不存在时,由这个url-pattern为/的缺省servlet来处理
其实Web服务器已有一个url-pattern为/的缺省Servlet,它叫DefaultServlet,像404页面/a.jpg/post.html,等都是由这个缺省的Servlet来处理的
7)Servlet中线程安全问题:
当多个客户端并发访问同一个Servlet时,web服务器会为每一个客户端的访问请求创建一个线程,并在这个线程上调用Servlet的service方法,因此service方法内如果访问了同一个资源的话,就有可能引发线程安全问题。
8)构成线程安全的三要素
>>单例
>>实例变量
>>对实例变量更新(关键)
5 ServletConfig对象
1)用Servlet读取在web.xml文件中,为该Servlet配置的参数,注意,针对的是该Servlet
2)Web服务器会创建实现ServletConfig接口的实现类传入,该接口主要用于读取配置文件中的数据.
在Servlet的配置文件中,可以使用一个或多个
当servlet配置了初始化参数后,web容器在创建servlet实例对象时,会自动将这些初始化参数封装到ServletConfig对象中,并在调用servlet的init方法时,将ServletConfig对象传递给servlet。进而,程序员通过ServletConfig对象就可以得到当前servlet的初始化参数信息。
3)常用的API:
getInitParameter(String参数):String返回值
getInitParameterNames():Enumeration枚举
<context-param>
<param-name>filePathparam-name>
<param-value>/file.propertiesparam-value>
context-param>
public classDemo5 extends HttpServlet {
//读取web.xml文件中的内容
public void init(ServletConfigservletConfig) throws ServletException {
//取得初始化参数
Enumeration
//迭代枚举
while(enums.hasMoreElements()){
//取得键
String key =enums.nextElement();
//取得值
String value =servletConfig.getInitParameter(key);
//显示
System.out.println(key +"<-->" + value);
}
System.out.println("Servlet实例名为:" + servletConfig.getServletName());
String email =servletConfig.getInitParameter("email");
String tel =servletConfig.getInitParameter("tel");
//判段
if(email!=null &&tel!=null){
System.out.println("邮箱:" + email);
System.out.println("电话:" + tel);
}else{
System.out.println("查无此信息");
}
}
}
*1 ServletContext对象
1)当某个Web应用部署到Web服务器中,Web服务器为该Web应用创建一唯一的ServletContext对象
2)Web应用和ServletContext对象一一对应
3)Web应用中的所有Servlet都有访问或共享该ServletContext对象中的信息
4)如果要在多个Servlet之间共享数据,此时可以使用ServletContext
5)常用的API
setAttribute(String,Object):void
getAttribute(String):Object
//获取文件的路径名
String path = this.getServletContext().getRealPath("/1231321.jpg");
//D:\apache-tomcat-6.0.33\webapps\WebTest\1231321.jpg
6)浏览器以GET方式发送请求,在服务端为转发的情况下,此时服务端无需通知浏览器,是服务端内部行为,所以依然是GET方式的请求,此时的request和response与先前的一样
7)可以通过ServletContext读取位于web应用中的所有资源文件
8)可以通过ServletContext读取位于web.xml文件中的初始化参数,该参数针对整个web应用,而不是针对某个servlet。
例: 通过ServletContex读取配置文件中的所有信息
public class MyContextServlet extends HttpServlet {
public void doGet(HttpServletRequestrequest, HttpServletResponse response)
throws ServletException, IOException {
//创建ServletContext对象
ServletContext context = this.getServletContext();
//获取web.xml文件中的参数filePath 对应的路径
String filePath = context.getInitParameter("filePath");
//获取输入流对象,该方法适用于整个web.xml文件中
InputStream inStream = context.getResourceAsStream(filePath);
//创建Properties对象
Properties p = new Properties();
//让Properties对象与文件关联
p.load(inStream);
//读取该文件中的键.并通过键查找对应的值.
String name = p.getProperty("name");
String password = p.getProperty("password");
String role = p.getProperty("role");
//设置属性,并存储到域对象中
context.setAttribute("name", name);
context.setAttribute("password",password);
context.setAttribute("role", role);
//获取RequestDispatcher(传发)对象,此处"/"表示当前的目录day06
RequestDispatcher disp = context.getRequestDispatcher("/MyContext");
//转发至MyContext(302转向)
disp.forward(request, response);
}
}
public class MyContext extends HttpServlet {
public void doGet(HttpServletRequestrequest, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html;charset=utf-8");
//创建ServletContext对象
ServletContext context = this.getServletContext();
//获取指定的属相键对应的value值
String name = (String)context.getAttribute("name");
String password =(String)context.getAttribute("password");
String role = (String) context.getAttribute("role");
response.getWriter().write(name+": "+password+" ,"+role);
}
}
*2 缓存应用
1)在默认情况下,浏览器访问某个静态资源时,如果浏览器的缓存有该资源,则找缓存;如果无该资源,则首次找服务端,之后将其缓存,以提高效率。
1 设置缓存的两种场景:
场景一:对于不经常变化的数据,在servlet中可以为其设置合理的缓存时间值,以避免浏览器频繁向服务器发送请求,提升服务器的性能。
场景二:如果要实现一种高级功能,即客户端请求动态web资源时,动态web资源发现发给客户端的数据更新了,就给客户端发送最新的数据,如果发现数据没有更新,则动态web资源就要客户端就去访问它自己缓存的数据。此种情况可以通过覆写动态web资源(即servlet)的getLastModify方法予以实现。
2)服务端和浏览器能过If-Modified-Since和Last-Modified二个头相互通信,来判段请求资源的新旧,这就是底层原理。
13 getLastModified方法由service方法调用,默认情况下,getLastModified方法返回一个负数,开发人员在编写servlet时,如果不覆盖getLastModified方法,则每次访问servlet时,service方法发现getLastModified方法返回负数,它就会调用doXXX方法向客户端返回最新的数据。此种情况下,服务器在向客户端返回doXXX方法返回的数据时,不会在数据上加Last-Modified头字段。
8 如果编写servlet时,覆盖了getLastModified方法,并返回某一个时间值,则客户端访问Servlet时,service方法首先会检查客户端是否通过If-Modified-Since头字段带一个时间值过来。如果没有的话,则service方法会调用doXXX方法向客户端返回最新的数据。在返回数据时,service方法还会调用getLastModified方法得到一个时间值,并以这个时间值在数据上加上一个Last-Modified头字段。(即通知客户端缓存数据)
13 客户端在访问servlet时,如果通过If-Modified-Since头字段带了一个时间值过来,则service方法在调用doXXX方法之前,它会先调用getLastModified方法,得到一个时间值,并与客户端带过来的时间值进行比较,如果比客户端的时间值要新,则service方法调用doXXX方法向客户端返回最新的数据。如果要旧,则service方法而不会调用doXXX方法向客户端返回数据,而是返回一个304的状态码给客户端,通知客户端在拿它缓存中的数据。
例1:设置资源文件的修改时间
public class LastModified extends HttpServlet {
protected longgetLastModified(HttpServletRequest req) {
ServletContext context = this.getServletContext();
//获取文件路径
String filepath = context.getRealPath("/temp.html");
//设置资源文件最后修改时间
return newFile(filepath).lastModified();
}
public void doGet(HttpServletRequestrequest, HttpServletResponse response)
throws ServletException, IOException {
ServletContext context = this.getServletContext();
//读取temp.html文件中的内容输到浏览器
InputStream in = context.getResourceAsStream("/temp.html");
ServletOutputStream out =response.getOutputStream();
byte[] buf = new byte[1024];
int len = 0;
while((len = in.read(buf))>0)
{
out.write(buf, 0, len);
}
}
}
*3 response的应用
1)用字节方式输出[数字],该数字需要查询ISO8859-1编码表,将查询后的结构取出,存放响应体中
2)用字节方式输出[字符串],则直接将该字符串存放响应体中
3)用字符方式输出[数字],则直接将该字数字存放响应体中,原样输出
4)以采用二种方式设置浏览器解析的编码方式时,后者起决定作用
>>response.setCharacterEncoding("GB2312");
>>response.setContentType("text/html;charset=UTF-8");
>>//重定向302请求的资源被修改了,需要重新访问新的地址
response.sendRedirect("/myday06/Demo1");/斜杠代表当前web工程/myday06
5)response.setContentType("text/html;charset=UTF-8");位置很重要,在默认情况下,response.getWriter()返回的PrintWriter对象,采用ISO8859-1的方式进行编码,即存入到PrintWriter对象中的一切数据都要采用ISO8859-1方式进行编码
6)字节 vs 字符
>>输出字符串(不论中文或英文),此时使用字符(response.getWriter())
>>输出多媒体数据(图片), 此时使用字节(response.getOutputSteram())
4 response的细节
1)转发:/表示day06(虚拟路径)
重定向:/表示webapps
表单提交的action属性:/表示webapps
2)可以采用字符或字节方式输出中文数据
3)response对象只能使用一种方式进行输出,要么字节,要么字符,不能同时使用
4)response对象不关闭流也没关系,由web服务器在适当的时候关闭;如果你想立即关闭,建议手工关闭
5)在重定向或转发的情况下,设置的请求头,后者起决定作用
案例1:图片下载: 中文图片名字的转换: URLEncoder.encode(file.getName(),"UTF-8")
public class Dowload extends HttpServlet {
public void doGet(HttpServletRequestrequest, HttpServletResponse response)
throws ServletException, IOException {
//获取ServletContext对象
ServletContext context = this.getServletContext();
//获取图片路径
String url = context.getRealPath("/123.jpg");
File file = new File(url);
response.setHeader("content-disposition","attachment;filename="+ URLEncoder.encode(file.getName(),"UTF-8"));
InputStream in = new FileInputStream(file);
OutputStream out = response.getOutputStream();
byte[] by = new byte[1024];
int len = 0;
while((len=in.read(by))>0){
out.write(by,0,len);
}
}
}
案例2:生成一个带验证码图片的表单:
public class CheckOutCode extends HttpServlet {
//随机生成校验码
public void doGet(HttpServletRequestrequest, HttpServletResponse response)
throws ServletException, IOException {
//设置禁止浏览器进行页面缓存
response.setHeader("expires","-1");
response.setHeader("cache","no-cache");
response.setHeader("pragram","no-cache");
//在内存中创建一个图片对象,并指定其x,y轴的坐标,并指定图片的类型
BufferedImage img = new BufferedImage(70,30,BufferedImage.TYPE_INT_RGB);
//获取画图的对象
Graphics g = img.getGraphics();
Random r = new Random();
for(int x=0;x<10;x++)
{
//随机生成一些数字
int x1 = r.nextInt(100);
int x2 = r.nextInt(100);
int y1 = r.nextInt(30);
int y2 = r.nextInt(30);
g.setColor(new Color(r.nextInt(256),r.nextInt(256),r.nextInt(256)) );
//在此图片上下文的坐标系中,使用当前颜色在点 (x1, y1)和(x2, y2) 之间画一条线。
g.drawLine(x1, y1, x2,y2);
}
g.setColor(Color.yellow);
//使用此图形上下文的当前字体和颜色绘制由指定 string给定的文本
g.drawString(getRandom(), 20, 20);
//图片输出流,写会浏览器
ImageIO.write(img,"JPG",response.getOutputStream());
}
//随机生成一个0-999的随机数字
public String getRandom()
{
return (int)Math.ceil((Math.random()*10000))+"";
}
}
例3: 设置静态资源的缓存时间:
public class Expires extends HttpServlet {
public void doGet(HttpServletRequestrequest, HttpServletResponse response)
throws ServletException, IOException {
//获取静态资源的uri路径
String uri = request.getRequestURI();
int end = uri.indexOf("/");
String path = uri.substring(end);
//创建ServletContext对象,加载静态资源
ServletContext context = this.getServletContext();
//获取输入流对象
InputStream input=context.getResourceAsStream(path);
BufferedReader bufr = new BufferedReader(new InputStreamReader(input));
String line = bufr.readLine();
if(line!=null && line.contains("html"))
{ //设置临时文件的存储时间
long time = System.currentTimeMillis()*1*24*60*60*1000;
response.setDateHeader("expires",time);
}
}
}
例4:设置请求头referer
public class RefererServlet extends HttpServlet {
public void doGet(HttpServletRequestrequest, HttpServletResponse response)
throws ServletException, IOException {
//获取请求头(地址栏,主要作用是防止用户在通过url访问时间接访问)
String referer =request.getHeader("referer");
if(referer!=null && referer.equals("http://localhost:8080/myday07/page.html"))
{//转发至success.html
this.getServletContext().getRequestDispatcher("/success.html").forward(request, response);
}else{
this.getServletContext().getRequestDispatcher("/failure.html").forward(request, response);
}
}
}
*1 request对象常用API
//创建每个浏览器对应的HttpSession
HttpSessionsession = request.getSession();
获取每个浏览器对应的HttpSession的编号
String id =session.getId();
String ip =request.getRemoteAddr();///获取每个浏览器的IP////127.0.0.1
String method =request.getMethod();//取得客户端提交请求的方式//get,post
取得客户端提交请求的URL和URI //http://localhost:8080/myday07/ResquestMethod
String url =request.getRequestURL().toString();
String uri = request.getRequestURI();//myday07/ResquestMethod
String path =request.getContextPath();//取得客户端访问的虚拟路径,即/day07
String query =request.getQueryString();//取得客户端传入的参数(查询字符串)
String username= request.getParameter("username");//取得客户端传入的值单个值
String [] likes= request.getParameterValues("like");//取得客户端传入的值多个值
//从HttpSession域对象中取得IP/ID/用户名
String id =(String) session.getAttribute("id");
String ip =(String) session.getAttribute("ip");
获得客户机请求头信息:
getHeader(stringname)方法:String
String encoding = request.getHeader("Accept-Encoding");
String agent = request.getHeader("user-agent");
获得客户机请求体信息(客户端提交的数据)
getParameterNames方法+BeanUtils框架
*2 基于MVC的request应用
request对象也能实现请求转发:请求转发指一个web资源收到客户端请求后,通知服务器去调用另外一个web资源进行处理。
请求转发的应用场景:MVC设计模式
request对象也提供了一个getRequestDispatcher方法,该方法返回一个RequestDispatcher对象,调用这个对象的forward方法可以实现请求转发。
request对象同时也是一个域对象,开发人员通过request对象在实现转发时,把数据通过request对象带给其它web资源处理。
setAttribute方法 getAttribute方法
//转发307
ServletContext context = this.getServletContext();
RequestDispatcher rd =context.getRequestDispatcher("/Demo1");
rd.forward(request, response);
1)POST请求
>>1个中文----3个%----按照UTF-8编码----------浏览器
>>1个中文----2个%----按照GB2312或GBK编码----浏览器
2)HTTP协议特点所决定在HTTP协议中只能明文传递英文和数字,其它字符都需要按照一定方式编码
3)request.getParameter("username"),默认情况下,采用ISO的方式取得username的值,必须在该代码[之前]使用request.setCharacterEncoding("UTF-8")方式,设置request采用UTF-8取得表单参数的值
4)编码和解码的方式要一致
5)request.setCharacterEncoding只能针对[请求体post]中的数据有效,对GET无效
6)GET请求
>>一定要对URL的字符进行编码,比如UTF-8
>>在接收方,一定要对URL字符进行手工解码,比如UTF-8
>>编码和解码双方要一致
doget() 请求:
dopost() 请求:
7)请求和转发的区别:
一个web资源(servlet实例对象)收到客户端请求后,通知[服务器]去调用另外一个web资源进行处理,称之为请求转发。
一个web资源(servlet实例对象)收到客户端请求后,通知[浏览器]去访问另外一个web资源进行处理,称之为请求重定向。
*3 基于request的转发
>>request请求对象
MVC是(模型-视图-控制器)的一种简称,主要用于一种架构设计模式,MVC是独立于任何语言
MVC三层结构的内部关系
MVC把应用程序的开发分为3个层面:视图层、控制层和模型层。其中视图层负责从用户获取数据和向用户展示数据,在这层中不负责对业务逻辑的处理和数据流程的控制。
而模型层负责处理业务逻辑和数据库的底层操作,其中视图层和模型层之间没有直接的联系。控制层主要负责处理视图层的交互,控制层从视图层接受请求,然后从模型层取出
对请求额处理结果,并把结果返回给视图层。在控制层中只负责数据的流向,并不涉及具体的业务逻辑处理。
从图中可以看出,Servlet在MVC开发模式中承担着重要的巨额收入。在MVC结构中,控制层就是依靠Servlet实现的,Servlet可以从流量拿起端接受请求,然后从模型层取出处理结果,并且把处理结果返回给浏览器端的用户。在整个结构中,Servlet负责数据流向控制的功能。
虽然现在利用很多开源框架都能很好地实现MVC的开发模式,且这些开源框架对MVC的实现都是非常出色的,但是在这些框架中处理数据控制流向的时候,采用的还是servlet。
request也具有转发功能
request.getRequestDispatcher().forward(request,response);
>>request域对象
因为该request对象有对应的存取方式
setAttribute()和getAttribute()
405错误:某个URL请求是GET/POST,但对应的Servlet没有提交doGet()/doPost()方式,
在转发情况下,request域对象,二个Servlet的数据共享
在重定向情况下,request域对象,二个Servlet的数据不共享,
>>什么时候用ServletContext域对象,什么时候用HttpServletRequest域对象
ServletContext:
(1)当需要保存的内容在重定向的情况下依然存在,比较适合使用ServletContext域对象
(2)当需要保存的内容时间长时,比较适合使用ServletContext域对象
(3)公共聊天室,就应该使用ServletContext进行保存
HttpServletRequest:
(1)当需要保存的内容在转发的情况下依然存在,[比较适合]使用HttpServletRequest域对象,但此时如果使用, ServletContext也是可以的
(2)当需要保存的内容只要在一次请求中可见,[比较适合]使用HttpServletRequest域对象
(3)查询某个新闻信息,就应该使用HttpServletRequest进行保存
#4 转发 vs 重定向
(1)URL地址拦
转发: 地址拦不变
重定向: 地址拦变化
(2)代码
转发:
>>this.getServletContext().getRequestDispatcher(html/jsp/servlet).foward(*,*);
>>request.getRequestDispatcher(html/jsp/servlet).foward(*,*);
重定向:
>>response.sendRedirect("html/jsp/servlet");
(3)同一个Web还是在不同的Web应用中
转发:只能在当前Web应用中导航
重定向:可以在当前Web应用中,或非当前Web应用中导航,也可以导航外网
转发: 能访问WEB-INF/目录的所有资源
重定向:不能访问WEB-INF/目录的所有资源
(4)/的含义
转发:/表示当前web应用名,虚拟路径,即/day07
重定向:/表示webapps
(5)内部或外部行为
转发:服务器内部行为
重定向:服务器与浏览器的外部行为
(6)request的数据共享能力
转发:reques共享数据
重定向:request不共享数据
5 include包含方法:
RequestDispatcher.include方法用于将RequestDispatcher对象封装的资源内容作为当前响应内容的一部分包含进
来,从实现可编程的服务器端包含功能。被包含的Servlet程序不能改变响应消息的状态码和响应头,如果它
里面存在这样的语句 这些语句的执行结果将被忽略。
1)一定在要IncludeServlet中设置response的输出编码方式,不能在只在所包含的Servlet中,因为作用域的问题
2)先输出的,决定输出编码方式,默认为ISO
3)如果在IncludeServlet中设置response的输出编码方式,被包含的Servlet无需设置编码方式
4)最后编码方式由IncludeServlet决定
COOKE入门
什么是cookie: Cookie是客户端技术,服务器把每个用户的数据以cookie的形式写给用户各自的浏览器。当用户使用浏览器再去访问服务器中的web资源时,就会带着各自的数据去。这样,web资源处理的就是用户各自的数据了。
1)cookie是存于浏览器缓存中的数据集合
2)能够解决多个web应用之间或同一个web应用多个动态资源之间的数据交互,例如:day08web应用和day09web应用之间交互数据
3)用户打开浏览器,在该浏览器中的一系列超链接操作后,再关闭该浏览器的过程,叫会话
4)浏览器和服务器交互的过程中,是通过响应头set-cookie和请求头cookik来完成整个会话交互数据的
2 cookie常用api
>>new Cookie("名","值")://构造函数
>>cookie.setMaxAge(秒钟);//设置cookie的存活时间
>>response.addCookie(cookie);//将cookie写入到浏览器中
>>request.getCookies()//获取浏览器所有的cookie,返回的是一个数组cookie[]
>>cookie.getName()//获取cookie的名字
>>cookie.getValue()//获取cookie的值
>>cookie.setPath("/day08-2")//设置cookie的路径
*3 cookie的细节
1)cookie的更新就是再向浏览器写入一个与原cookie名字一样的cookie,后者替换前者;如果cookie的名字不一样,则向浏览器添加新的cookie。
2)cookie产生于服务端,存于浏览器,可以人工删除
3)cookie的存与不存,可以由浏览器自已设置,服务端无法管理是否将cookie存于浏览器
4)一个cookie只能存放一种类型的信息,如果要存多个信息,得用多个cookie完成
5)一个浏览器能够存储多个网站的cookie信息,每个cookie的数量和大小受限,浏览器一般只允许存放300个Cookie,每个站点最多存放20个Cookie,每个Cookie的大小限制为4KB
6)如果不设置cookie的有效存活时间,默认cookie只在内存中有效,当程序结束后,内存中的cookie销毁,不会写入浏览器的缓存中,因此,需要为cookie设置一个有效的时间,当程序运行完毕后,会将该cookie写入浏览器的缓存中,即硬盘中保留。如果cookie的存活时间为0,就相当于浏览器将删除cookie删除或无效
7)在默认情况下,day08web应用的cookie只能被day08web应用的动态资源访问,如果要让其它的web资源访问,得设置cookie.setPath("/day08-2"),默认cookie的path为当前web应用。
8)cookie是以文本文件的方式存于浏览器缓存中,而且里面的内容明码.
例: cookie的使用
public class ClientCount extends HttpServlet {
public void doGet(HttpServletRequestrequest, HttpServletResponse response)
throws ServletException, IOException {
// 通知浏览器以UTF-8的方式解码
response.setContentType("text/html;charset=UTF-8");
// 获取浏览器所有的Cookie
Cookie[] cookie = request.getCookies();
// 获取printWrite流对象
PrintWriter pw = response.getWriter();
Cookie newCookie = null;
// 判断是否为空
if (cookie != null && cookie.length > 0) {
for (Cookie c : cookie) {
if (c.getName().contains("counter")) {
newCookie = c;
break;
}
}
if (newCookie != null) {
String value = newCookie.getValue();
int num = Integer.parseInt(value);
// 创建Cookie对象
Cookie cookies = new Cookie("counter", (num++) +"");
// 设置cookies的存活时间
cookies.setMaxAge(1 * 24 * 60 * 60);
// 将cookies写入浏览器中
response.addCookie(cookies);
pw.write("当前在线" + num +"人");
}
} else { // 创建Cookie对象
Cookie cookies = new Cookie("counter","1");
// 设置cookies的存活时间
cookies.setMaxAge(1 * 24 * 60 * 60);
// 将cookies写入浏览器中
response.addCookie(cookies);
pw.write("当前在线1人");
}
}
}
4 httpSession入门
1)session是服务端创建的一个为每个浏览器所[独享]的容器对象,存在服务端。
2)如果项目中需要多个动态资源找到同一个浏览器所需要的数据,例如:购物车
浏览器禁用cookie后的session处理
解决方案:URL重写
String url = response. encodeRedirectURL(java.lang.String url)
response. sendRedirect(url)
用于对sendRedirect方法后的url地址进行重写。
String url = response.encodeURL(url);
pw.write("继续购物");
用于对表单action和超链接的url地址进行重写
注:
session的失效(默认30分钟)
web.xml文件配置session失效时间
5 httpSession常用api
>>request.getSession()//获取所有的HttpSesseion对象
>>session.getId()
>>session.setAttribute(String,Object)
>>session.getAttribute(String):Object
*6 httpSession细节(上)
1)IE高版本浏览器,需要打开浏览器-[文件]-[新建会话],这样才能启动一个新会话,否则依然是原会话
2)context和session和request域对象的比较
>>在哪个域对象中放的数据,只能在哪个域对象中取
>>request只能在转发情况下,共享数据;重定向不共享数据
session在转发或重定向情况下,都共享数据;
context在转发或重定向情况下,都共享数据;
>>session创建的条件如下三个,缺一不可
(1)打开浏览器
(2)访问某一个Servlet
(3)request.getSession(),必须要用执行这个API
>>session销毁
>>当重新部署web应用
>>停此服务器
>>多个动态资源需要共享[同一个浏览器]的数据,可以使用HttpSession
一次HTTP请求中需要共享数据,但下一次请求无需共享上一次数据,可以使用HttpServletRequest
一个web应用中的所有动态资源都需要共享[所有浏览器]的数据时,可以使用ServletContext
>>购物车:HttpSession
公共聊天室:ServletContext
显示某条新闻的信息: HttpServletRequest
3)request.getSession()具有二层含义:
>>创建(无-有)
>>获取(有-有),关键是看代码流程
4)session底层是用cookie技术,通过响应头set-cookie和请求头cookie来维护浏览器与服务端的数据交互,以至于不会出错,完全由服务端负责维护,与浏览器无关
5)cookie和session的比较
>>cookie创建于服务端,存于浏览器;session创建于服务端,存于服务端
>>cookie是有文本文件存入相关的数据并且存入的数据如果是中文需要编码(BASE64),但session没有
>>cookie是使用set-cookie响应头和cookie请求头交换数据,session一样
6)session位于服务端,其大小受web服务器的限制,因此session主要存放web应用中核心的数据,而将非核心的数据放在cookie中。
*1 httpSession细节(下)
1)如果通过超链接点击购买图书,会有"referer"这个请求头;反之如果通过在地址栏直接输入URL购买图书,则无"referer"。
2)httpSession在维护浏览器和服务端的会话中,使用了一个特殊的cookie,它的名字叫JSESSIONID,其值就是httpSession.getId()
3)如果我们强行创建一个cookie,名叫JSESSIONID,其它是httpSession.getId(),那么服务端会认为是同一个用户
4)当浏览器禁用cookie时,可以使用URL重写来达到服务端和浏览器维持通信的能力
5)url = response.encodeURL(url);将url重写,即加上jsessionid这个参数,该方法适合于
超链接,或者表表单
6)url = response.encodeRedirectURL(url);
response.sendRedirect(url);
上述API针对只针对重定向
7)不论当浏览器禁用或不禁用cookie,服务端都可以使用URL重写技术来达到浏览器和服务端维护的方式,因为这二个API能够智能判段浏览器是否禁用cookie,从而采用对应的处理方式
*2 销毁httpSession的几种方法
1)重新部署web应用
2)停止web服务器,
3)httpSession一旦创建,就会在web服务器中,默认停留30个分钟,30分钟后,由web服务器销毁,何时销毁,由web服务器决定,类似GC(垃圾回收处理机制)。
*4)通过在web.xml文件中,配置session的有效时间,单位分钟,关闭浏览器,在默认情况下,没有销毁session。
*5)session.invalidate(),立即马上销毁session,将session中绑定的所有信息清空,例如:安全退出
例: <session-config>
<session-timeout>5session-timeout>
session-config>
*3 httpSession应用
1)判段是否是新/旧会话,session.isNew():true表示新会话
2)用户登录[v1版]
3)防止表单重复提交
>>表单需要动态产生一个唯一的随机值,该随机值需要在服务端保存一份,客户端隐蔽域的形成提交
>>服务端在比较的过程中,相同则表示提交成功;成功后,删除服务端的随机值
>>项目中多用于注册,登录等场景
4)一次性验证码
public class ServletCheckcode extends HttpServlet {
public void doGet(HttpServletRequestrequest, HttpServletResponse response)
throws ServletException, IOException {
//设置浏览器不缓存
response.setHeader("expires","-1");
response.setHeader("cache-control","no-cache");
response.setHeader("pragma","no-cache");
//获取验证码
String checkcode = this.getRandom();
//绑定到HttpSession中
request.getSession().setAttribute("checkcode",checkcode);
//在内存中创建一个图片
Buffered-image img = new BufferedImage(80, 30, BufferedImage.TYPE_INT_RGB);
//获取画笔
Graphics g = img.getGraphics();
g.drawString(checkcode, 30, 20);
//输出到浏览器
ImageIO.write(img,"JPG",response.getOutputStream());
}
//产生一个1000-9999的随机数
private String getRandom(){
Random r = new Random();
int num = r.nextInt(9000) + 1000;
return num + "";
}
}
什么是JavaBean
JavaBean是一个遵循特定写法的Java类,它通常具有如下特点:
这个Java类必须具有一个无参的构造函数
属性必须私有化。
私有化的属性必须通过public类型的方法暴露给其它程序,并且方法的命名也必须遵守一定的命名规范。
JavaBean的属性
4 JavaBean的属性可以是任意类型,并且一个JavaBean可以有多个属性。每个属性通常都需要具有相应的setter、 getter方法,setter方法称为属性修改器,getter方法称为属性访问器。
5 属性修改器必须以小写的set前缀开始,后跟属性名,且属性名的第一个字母要改为大写,例如,name属性的修改器名称为setName,password属性的修改器名称为setPassword。
6 属性访问器通常以小写的get前缀开始,后跟属性名,且属性名的第一个字母也要改为大写,例如,name属性的访问器名称为getName,password属性的访问器名称为getPassword。
7 一个JavaBean的某个属性也可以只有set方法或get方法,这样的属性通常也称之为只写、只读属性。
在JSP中使用JavaBean
4 JSP技术提供了三个关于JavaBean组件的动作元素,即JSP标签,它们分别为:
5
6
7
8
• 如果存在则直接返回该JavaBean对象的引用。
• 如果不存在则实例化一个新的JavaBean对象并将它以指定的名称存储到指定的域范围中。
常用语法:
scope="page|request|session|application"/> id属性用于指定JavaBean实例对象的引用名称和其存储在域范围中的名称。 class属性用于指定JavaBean的完整类名(即必须带有包名)。 scope属性用于指定JavaBean实例对象所存储的域范围,其取值只能是page、request、session和application等四个值中的一个,其默认值是page。 语法格式: { property="propertyName"value="{string | <%= expression %>}" | property="propertyName"[ param="parameterName" ] | property= "*" }/> name属性用于指定JavaBean对象的名称。 property属性用于指定JavaBean实例对象的属性名。 value属性用于指定JavaBean对象的某个属性的值,value的值可以是字符串,也可以是表达式。为字符串时,该值会自动转化为JavaBean属性相应的类型,如果value的值是一个表达式,那么该表达式的计算结果必须与所要设置的JavaBean属性的类型一致。 param属性用于将JavaBean实例对象的某个属性值设置为一个请求参数值,该属性值同样会自动转换成要设置的JavaBean属性的类型。 语法: name属性用于指定JavaBean实例对象的名称,其值应与 property属性用于指定JavaBean实例对象的属性名。 JSP开发模式 4 SUN公司推出JSP技术后,同时也推荐了两种web应用程序的开发模式,一种是JSP+JavaBean模式,一种是Servlet+JSP+JavaBean模式。 5 JSP+JavaBean模式适合开发业务逻辑不太复杂的web应用程序,这种模式下,JavaBean用于封装业务数据,JSP即负责处理用户请求,又显示数据。 6 Servlet+JSP+JavaBean(MVC)模式适合开发复杂的web应用,在这种模式下,servlet负责处理用户请求,jsp负责数据显示,javabean负责封装数据。 Servlet+JSP、JavaBean模式程序各个模块之间层次清晰,web开发推荐采用此种模式。 4 什么是JSP 1)为什么说,HTML是一个静态Web开发技术呢? 因为HTML没有代表客户端和服务端的一个对象 2)为什么说,Servlet是一个动态Web开发技术呢? 因为Servlet有代表客户端和服务端的一个对象,分别request和response对象, 3)SUN公司还定义了一种动态开发技术规则,叫JSP(Java Server Pages) 4)JSP主要解决页面需要动态从服务端获取的数据,例如:(从request/session/servletContext) 5)JSP的特点: >>与HTML具有类似的风格,原HTML怎么写,JSP就可以怎么写 >>为了取得动态的效果,可以在JSP页面中,使用JSP特有的语言规则嵌入Java代码 5 JSP+Servlet最佳配合体验 1)Servlet可以完成页面输出,但效果较差,不便于难护,比较适合做控制器 2)JSP比较适合做页面输出 *6 JSP工作原理 1)Web服务器是如何调用并执行一个jsp页面的? >>date.jsp文件,会被web服务器翻充成date_jsp.java源代码,本质就是一个servlet,也与servlet一样,有着类似的生命周期方法,即jsp本质上就是一个servlet,servlet能做的事情,jsp也能做到,jsp和servlet决不能替代,只是一个互相补充的关系 2)Jsp页面中的html排版标签是如何被发送到客户端的? 所有静态数据通过write()方法输出 3)Jsp页面中的java代码服务器是如何执行的? 所有动态数据通过print()方法输出 4)Web服务器在调用jsp时,会给jsp提供一些什么java对象? Web服务器在将date.jsp文件翻译成date_jsp.java源代码时,会自动创建[看得见]的8个内置对象,因此在jsp面页中可以直接使用这8个内置对象 5)工作原理 1)如果访问的是jsp,那么web服务器就会将请求的资源交由jsp引擎来处理 2)如果是第一次访问jsp,那么jsp引擎会将jsp翻译成servlet源码 3)jsp引擎将servlet源码交由servlet引擎来处理,参见< 4)如果第一次访问jsp,时间会相对较长,因为jsp引擎在做翻译成servlet工作 5)如果再次访问相同的jsp,如果内容没有改变,这时jsp引擎不做翻译,直接交由servlet引擎处理 6)如果再次访问的jsp,但内容更新了,这时jsp引擎又会将新的jsp做翻译 7 JSP语法 1)JSP模版元素 就是在JSP页面中的HTML标签,例如: 2)JSP表达式 语法:<%="需要输出的字符常量或变量"%>, 类似于response.getWriter().write("当前时间"); 输出表达式中不能含有分号 3)JSP脚本片断 语法:<% JSP脚本代码,以分号结束,即Java代码 %> 多个脚本片段之间完全可以访问,因为这些片段中的代码,最终都被翻译到_jspService()方法中,成为局部变量 4)JSP声明 语法:<%! Java代码,会被翻译成实例变量%> 5)JSP注释 语法: XML: Java: //单行 /* */多行 /** * 文档注释 * */ JS: //单行 /* */多行 HTML: JSP:<%-- xxxxxx --%> 如果你使用JSP特有的注释,JSP引擎不会将其翻译到Servlet源码中,除此之外,就要翻译到Servlet源码中 注释不允许嵌套 Day10: 1 JSP中指令 1)JSP指令有三类 >>page指令 >>include指令 >>taglib指令 2)JSP指令的作用:针对JSP引擎来设计,通知JSP引擎应该针对该JSP页面如何处理 *3)page指令的属性含义 格式一:<%@ 指令名 属性名1="属性值1" 属性名2="属性值2"%> 格式二:<%@ 指令名 属性名1="属性值1"%> <%@ 指令名 属性名2="属性值2"%> [ language="java" ] 指明JSP脚本〈%%>中的语言是java,默认也是java语言 *[ import="{package.class | package.*},..." ] 指明让JSP引擎导入的相关包,有些包不需要导,javax.servlet.*,javax.servlet.http.* [ session="true | false" ] 在servlet中,产生HttpSession必需要有如下代码:request.getSession(),没有的话,不会产生当我们访问的任何一个jsp页面时,在默认情况下,web服务器[一定]都会帮我们产生HttpSession 指明让JSP引擎产生HttpSession,true表示产生HttpSession,false表示不产生HttpSession,默认true [ buffer="none | 8kb | sizekb" ] 指明让JSP引擎创建缓存池的大小,默认有,且8kb [ autoFlush="true | false" ] 指明让JSP引擎自动监视缓存池的数据刷新情况, >>当缓存池满,自动刷新,将数据输出到浏览器 >>当缓存池未满,但整个jsp执行完毕,将数据输出到浏览器 如果设置为false,则不让JSP引擎自动刷新,只有人工通过API刷新,默认true. [ isThreadSafe="true | false" ] jsp本质就是一个servlet,jsp与servlet一样具有安全问题,如果需要让JSP引擎解决安全问题,则选中true;如果选中false,则自已解决安全问题,即加锁,默认true。[ info="text" ] 指明JSP引擎,该JSP页面的作者,版本,时间,等等相关信息 *[errorPage="relative_url" ] 出错后,转发的页面,路径可以是相对或绝对路径, *[isErrorPage="true | false" ] 出错面页,如果选用true表示该jsp为出错后转发的页面,JSP引擎自动产生exception对象,如果选用false,JSP引擎不会自动产生exception对象可以在web.xml文件中配置全局的错误出错处理页面,也可以在jsp文件中配置局部的错误出错处理页面,当二者冲突时,局部优先当全局异常中,出现代码和类型时,类型优先默认false,即不能使用exception内置对象 *[contentType="text/html ; charset=UTF-8" ] , [pageEncoding="UTF-8" ] 上述二者等价,但如果冲突,contentType优先 *[isELIgnored="true | false" ] false指明JSP引擎支持EL,true不支持EL,默认是支持的,为false 4)include指令的属性含义 当在一个jsp页面中包含N个子页面时,可以采用include指令,最终的编码方式由总的页面决定 对于子jsp页面,并没有翻译成servlet,都在翻译时,包含在总的jsp所翻译的servlet中,结构不清淅将include指令所包含的jsp称做"静态包含" *2 JSP中九大内置对象 request/response session/application config/out 注意:out对应的类型是JspWriter类型,它是比PrintWriter更高级的对象,相当于将PrintWriter的缓存作用out对象的缓存用 exception:只有当该JSP为错误处理页时,才能使用 3. PageContext对象作用 1)PageContext是一个域对象(setAttribute/getAttribute/removeAttribute),有且只有在当前jsp页面中有效也可以通过PageContext将信息绑定任意域对象中 (page/request/session/application),默认page 2)PageContext可以取得其它8个内置对象 3)且有转发和包含操作 4 JSP中四个域对象汇总 1)生命周期: 从生命周期的小到大:pageContext->request->session->application page域:是JSP特有的,当前页面中共资源 request域:一次请求中的所有servlet和jsp共享资源 session域:整个会话中的所有servlet和jsp共享资源 application域:整个web应用中的所有servlet和jsp共享资源 2)案例 page域:在当前页面中使用 request域:读取内容显示,错误消息显示 session域:购物车,用户登录,注册 application域:在线访问计数器 5 JSP内置[非JavaBean]标签 1)JSP标签是一种替代JSP脚本的技术 2)JSP标签可以达到美化页面,又且有业务逻辑流程控制的功能 3) 转发:<%pageContext.forward()%> 4) 包含:<%pageContext.include()%> <%@ include file%> >>有N个jsp,则翻译成N个servlet源码 >>总的jsp页面结构清晰 >>是一个动态包含的过程,执行时,临时加载,因此叫动态包含 flush表示表示是否先将include页面的信息输出,false表示不先输出,true表示先输出 5) 带参转发或包含?name=jack&pass=123 6)JSP错误有三类 >>JSP表面语言错误 >>翻译成Servlet源码有误 >>JSP运行过程中出错,逻辑错误为主 JSP内置[JavaBean]标签 什么是JavaBean JavaBean是一个遵循特定写法的Java类,它通常具有如下特点: 这个Java类必须具有一个无参的构造函数 属性必须私有化。 私有化的属性必须通过public类型的方法暴露给其它程序,并且方法的命名也必须遵守一定的命名规范。 1)JavaBean分二类; >>狭义JavaBean/实体/域对象/值对象(今天特指) 1)私有字段 2)公有的构造器 3)公有的set(存)/get(取)方法 >>广义JavaBean(模型) 普通类,含普通方法 2)JavaBean有作用? 存取程序员的数据, 3)JavaBean的命名规则 private String name; 存:setName() 取:getName(); JavaBean的属性 JavaBean的属性可以是任意类型,并且一个JavaBean可以有多个属性。每个属性通常都需要具有相应的setter、 getter方法,setter方法称为属性修改器,getter方法称为属性访问器。 属性修改器必须以小写的set前缀开始,后跟属性名,且属性名的第一个字母要改为大写,例如,name属性的修改器名称为setName,password属性的修改器名称为setPassword。 属性访问器通常以小写的get前缀开始,后跟属性名,且属性名的第一个字母也要改为大写,例如,name属性的访问器名称为getName,password属性的访问器名称为getPassword。 一个JavaBean的某个属性也可以只有set方法或get方法,这样的属性通常也称之为只写、只读属性 4)在JSP中如何使用JavaBean 1) >>首先在指定的域对象中[查询]是否有对应的JavaBean存在,如果有的话,直接取得该JavaBean; 如果没有的话,则创建该JavaBean,然后自动绑定到指定的域对象中,默认page域 >>标签体的内容,只有新会话首次访问时,才会执行,否则不执行 2) >>为JavaBean设置值 >>自动将String类型,转成8种基本类型 >>当参数名和字段名一致的情况下,可以使用下列代码 3) >>从JavaBean中取出值 例: <body> <jsp:useBeanid="User" class="cn.web.servlet.JSP.User"/> <jsp:setPropertyproperty="name"name="User" value="张三"/> <jsp:setPropertyproperty="id" name="User" value="12334"/> <jsp:setPropertyproperty="salary"name="User" value="4500"/> <jsp:setPropertyproperty="name"name="User" param="username"/> <jsp:setPropertyproperty="id" name="User" param="userid"/> <jsp:setPropertyproperty="salary"name="User" param="usersalary"/> <jsp:setPropertyproperty="*" name="User"/> 用户名:<jsp:getPropertyproperty="name"name="User"/> 编号:<jsp:getPropertyproperty="id" name="User"/> 薪水:<jsp:getPropertyproperty="salary"name="User"/> body> 在JSP中使用JavaBean JSP技术提供了三个关于JavaBean组件的动作元素,即JSP标签,它们分别为: 如果存在则直接返回该JavaBean对象的引用。 如果不存在则实例化一个新的JavaBean对象并将它以指定的名称存储到指定的域范围中。 常用语法: id属性用于指定JavaBean实例对象的引用名称和其存储在域范围中的名称。 class属性用于指定JavaBean的完整类名(即必须带有包名)。 scope属性用于指定JavaBean实例对象所存储的域范围,其取值只能是page、request、session和application等四个值中的一个,其默认值是page。 语法格式: { property="propertyName"value="{string | <%= expression %>}" | property="propertyName"[ param="parameterName" ] | property= "*" }/> name属性用于指定JavaBean对象的名称。 property属性用于指定JavaBean实例对象的属性名。 value属性用于指定JavaBean对象的某个属性的值,value的值可以是字符串,也可以是表达式。为字符串时,该值会自动转化为JavaBean属性相应的类型,如果value的值是一个表达式,那么该表达式的计算结果必须与所要设置的JavaBean属性的类型一致。 param属性用于将JavaBean实例对象的某个属性值设置为一个请求参数值,该属性值同样会自动转换成要设置的JavaBean属性的类型。 语法: name属性用于指定JavaBean实例对象的名称,其值应与 property属性用于指定JavaBean实例对象的属性名。 JSP开发模式 Jsp(v)+jsp(c)+javaBean Jsp(v)+Servlet(c)+javaBean SUN公司推出JSP技术后,同时也推荐了两种web应用程序的开发模式,一种是JSP+JavaBean模式,一种是Servlet+JSP+JavaBean模式。 JSP+JavaBean模式适合开发业务逻辑不太复杂的web应用程序,这种模式下,JavaBean用于封装业务数据,JSP即负责处理用户请求,又显示数据。 Servlet+JSP+JavaBean(MVC)模式适合开发复杂的web应用,在这种模式下,servlet负责处理用户请求,jsp负责数据显示,javabean负责封装数据。Servlet+JSP、JavaBean模式程序各个模块之间层次清晰,web开发推荐采用此种模式。 * Web开发中的二种模式 1 EL和JSTL简介和快速入门 1)Java Standard Taglib Language JSTL是一种SUN公司提供的外置标签,目的是替代原JSP中的脚本符号 JSTL和JSP内置标签是一个互补的关系 2)使用JSTL是补充JSP内置标签的功能不足 3)Expression Language表达式语言,主要是配合JSTL的使用 4)开发JSTL应用的步骤: >>导入jstl.jar和Standard.jar二个jar包,如果工具自带就不用导入 >>在需要使用jstl的jsp面页,通过<%taglib%>指令声明 <%@ tagliburi="http://java.sun.com/jsp/jstl/core" prefix="c" %> >>使用相关的jstl外置标签 数据类型: tinyint/smallint/mediumint/int/bigint-->1B/2B/3B/4B/8B float/double-->单精度/双精度浮点型 decimal-->不会产生精度丢失的单精度/双精度浮点型 date-->日期类型 time-->时间类型 datetime-->日期时间类型 year-->年类型 char-->定长字符串类型 varchar-->可变长字符串类型(65535字符) BLOB类别类型 tinyblob/blob/mediumblob/longblob-->255B/64K/16M/4G大小的图片/音乐等二进行数据(JDBC中讲) text类别类型 tinytext/text/mediumtext/longtext-->255B/64K/16M/4G大小的文本数据(JDBC中讲) 1 什么是数据库 1)SQL=Struture [Query] Language 结构化的查询语言 2)存放有一定规则的数据的容器 3)文件本身并没有一种机制去检测数据是否合法,因此需要一种新的技术去替代文件作数据的容器 4)文件内容如果过大,打开相对较慢,因此需要一种新的技术去替代文件作数据的容器 5)MySQL-软件- 2 各种常用数据库简介 1)Oracle&Unix/MySQL&Linux/SQLServer&Window(个人推荐,不能固定) 3 SQL[通用/专用]与数据库的关系 1)SQL是Java应用程序操作数据库的工具之一 2)如果某个SQL能够操不各种各样的数据库,这样的SQL叫做"通用SQL"(重点) 3)如果某个SQL只能够操一种数据库,这样的SQL叫做"专用SQL" *4 MySQL数据库的卸载,安装,设置,进入和退出 1)进入MySQL数据库:c:\>mysql -u root -p回车 entry password:****回车 2)退出MySQL数据库:mysql:>exit回车 8 修改、备份、恢复数据库中的数据 alter database 数据名 mysqldump -u root -p 需要备份的数据库名 > *.sql文件的路径(需要在window环境中运行,专用于mysql) source *.sql文件的路径(需要mysql环境中运行,专用于mysql) 9 MySQL支持的常用数据类型简介 1)日期时间(date/datetime/timestamp),其它date/datetime必须给定值,而timestamp可以不给定值,默认为当前时间 2)文本数据和多媒体数据(varchar/text/blob) 3)数值型(TINYINT/SMALLINT/MEDIUMINT/INT(默认是11个位数/BIGINT) 4)数值型默认有符号,INT型能存多大的值,不是看位数,而是看INT类型的最大有效值 *10 创建/查看/修改/删除表 create table 表名 desc 表名 11 MySQL解决插入中文乱码问题(WindowXP/7平台) set character_set_client=gbk;(window平台默认字符集) set character_set_results=gbk;(window平台默认字符集) *12 表的增删改操作 1)删除数据可以使用以下二种方式: >>delete from 表名 都能删除表中的所有记录,保留表结构 一行一行的删除 项目中数据量相对小时,推荐使用delete删除所有数据 可以按条件删除 >>truncate table 表名 都能删除表中的所有记录,保留表结构 复制原表结构->删除整个表->自动创建表结构 项目中数据量相当大时,推荐使用trancate删除所有数据 不可以按条件删除 >>drop table 表名 不光表数据删除,表结构也删除,不可以按条件删除 2)insert into 表名(字段) values(对应的字段值) 3)update table 表名 set 字段1=值1,字段2=值2 where 字段 = 条件 1. 创建一个数据库。 2. 显示数据库语句: 3. SHOW DATABASES 4. 显示数据库创建语句: 5. SHOW CREATE DATABASE db_name 6. 使用数据库 7. USE db_name 8. 数据库删除语句: 9. DROP DATABASE [IF EXISTS] db_name 10. 查看指11. 定的库下所有的表 Show tables; CHARACTER SET:指定数据库采用的字符集 COLLATE:指定数据库字符集的比较方式、规则,比如排序 创建一个名称为mydb1的数据库。 create databaseif not exists mydb1; 如果在创建数据库时,没有指明编码方式,默认和配置数据库时采用的编码方式一致 if not exists 表示先查询在数据库服务器中是否有数据库存在,如果没有,才创建;如果有,则直接使用 创建一个使用utf8字符集的mydb2数据库。 create databasemydb2 character set utf8; //准意,MySQL编码集中没有UTF-8,只有UTF8 //MySQL的SQL命令,大小写不敏感 创建一个使用utf8字符集,并带校对规则的mydb3数据库。 create databasemydb3 character set utf8 collate utf8_general_ci; 查看当前数据库服务器中的所有数据库 show databases; 查看前面创建的mydb2数据库的定义信息 show createdatabase mydb2; 删除前面创建的mydb1数据库 drop databaseif exists mydb1; 使用mydb2数据库 use mydb2; 查看服务器中的数据库,并把其中mydb3库的字符集修改为gbk。 alter databasemydb3 character set gbk; 备份mydb2库中的数据,并恢复。 use mydb2; create tableuser( name varchar(20) ); insert intouser(name) values('jack'); insert intouser(name) values('marry'); select * fromuser; 备份:c:\>mysqldump -u root -p mydb2 > d:\xx.sql回车 //mysqldump命令需要在window环境中运行,而不是mysql环境中运行 //mysqldump命令是mysql提供的专用sql命令 //mysqldump命令是将数据库中表数据备份到*.sql文件中 恢复:mysql>source d:\xx.sql回车 //source命令需要在mysql环境中运行,而不是window环境中 //source命令是mysql提供的专用sql命令 //source命令是将硬盘中的*.sql文件中的内容恢复到数据库中 创建一个user表,包含id/name/password/birthday drop table ifexists user; create tableuser( id int(5), name varchar(20), password varchar(6), salary float(6,2),//6表示整个数值的位数,2表示小数点后的位数,不包含小数点本身 birthday timestamp ); insert intouser(id,name,password,salary) values(1,'jack','123456',9999.99); 查询user表的结构desc user 创建employee表 create table ifnot exists employee( id int, name varchar(20), gender varchar(6), birthday timestamp, entry_date timestamp, job varchar(20), salary float, resume text ); 在上面员工表的基本上增加一个image列。 alter tableemployee add image blob; 修改job列,使其长度为60。 alter tableemployee modify job varchar(60); 删除gender列。 alter tableemployee drop gender; 表名改为staff。 rename tableemployee to staff; 修改表的字符集为utf8。 alter tablestaff character set utf8;如果在创建表时不指定,默认和数据库的编码方式一致 列名name修改为username。 alter tablestaff change column name usernamevarchar(40); 删除表 drop table ifexists user; 1.增加数据(使用 INSERT 语句向表中插入数据) 插入的数据应与字段的数据类型相同。 数据的大小应在列的规定范围内,例如:不能将一个长度为80的字符串加入到长度为40的列中。 在values中列出的数据位置必须与被加入的列的排列位置相对应。 字符和日期型数据应包含在单引号中。 插入空值,不指定或insert into table value(null) insert intoemployee values(1,'jack','male','engineer',5500,'16K','2012-7-14 15:15:15'); insert intoemployee(id,name,gender,job,salary,resume) values(2,'tom','male','engineer',5500,'16K'); insert intoemployee(name,id,gender,job,salary,resume) values('marry',3,'female','engineer',5500,'16K'); insert intoemployee(id,name,gender,job,salary,resume) values(4,'sisi','female','engineer',5500,NULL); insert intoemployee(id,name,gender,job,salary) values(5,'soso','female','engineer',5500); insert intoemployee(id,name,gender,job,salary) values(6,'xx','xxxxxx','engineer',5500); insert intoemployee(id,name,gender,job,salary) values(7,'杰克','男','工程师',5500); 2.使用 update语句修改表中数据。 SET子句指示要修改哪些列和要给予哪些值。 WHERE子句指定应更新哪些行。如没有WHERE子句,则更新所有的行。 将所有员工薪水修改为6000元。 update employeeset salary = 6000; update employeeset salary=6500,resume='16K' where name='xx'; 将姓名为’杰克’的员工薪水修改为7000元。 update employeeset salary=7000 where name='杰克'; 将’马利’的薪水在原有基础上增加1000元。 update employeeset salary=salary+1000 where name='马利'; 使用 delete语句删除表中数据。
如果不使用where子句,将删除表中所有数据。delete语句不能删除某一列的值(可使用update),使用delete语句仅删除记录,不删除表本身。如要删除表,使用drop table语句。同insert和update一样,从一个表中删除记录将引起其它表的参照完整性问题,在修改数据库数据时,头脑中应该始终不要忘记这个潜在的问题。删除表中数据也可使用TRUNCATE TABLE 语句,它和delete有所不同。
TRUNCATE(复制表结构->销毁表->重建表结构) DELETE(逐行删除记录) 删除表中名称为’xx’的记录。 delete fromemployee where name='xx'; 删除表中所有记录。 delete fromemployee; 使用truncate删除表中记录。 truncate tableemployee 查询表中所有学生的信息。 selectid,name,chinese,english,math from student; selectname,id,chinese,english,math from student; 简化 select * fromstudent; 查询表中所有学生的姓名和对应的英语成绩。 selectname,english from student; 基本select语句 Select 指定查询哪些列的数据。 column指定列名。 *号代表查询所有列。 From指定查询哪张表。 DISTINCT可选,指显示结果时,是否剔除重复数据 过滤表中重复数据。 select distinctchinese from student; 在所有学生分数上加10分特长分。 加分前:select name,chinese,math,english from student; 加分后:select name,chinese+10,math+10,english+10 from student; 统计每个学生的总分。 selectname,chinese+math+english from student; 使用别名表示学生分数。 select name as 姓名,chinese+math+english as 总分 from student; 查询姓名为’张小明’的学生成绩 select * fromstudent where name = '张小明'; 查询英语成绩大于90分的同学 selectname,english from student where english > 90; 查询总分大于200分的所有同学 select name as 姓名,chinese+math+english as 总分 from student wherechinese+math+english > 200; 查询英语分数在 80-90之间的同学。 selectname,english from student whereenglish>=80 and english <=90; 或 selectname,english from student where englishbetween 80(小) and 90(大); 查询数学分数为89或者90或者91的同学。 selectname,math from student where math=89or math=90 or math=91; 或 select name,math from student where mathin(89,90,91); 查询所有姓’李’的学生成绩。 select name from student where name like'李%';//模糊查询 查询所有名’李’的学生成绩。 select name from student where name like'%李'; 查询所有姓名中包含’李’的学生成绩。 select name from student where name like'%李%'; select name from student where name like'%%'; 等价于 select name from student; 查询所有姓’李’的学生成绩,但姓名必须是三个字 select name from student where name like'李__'; 查询数学分>80且语文分>80的同学。 selectname,math,chinese from student wheremath>80 and chinese>80; 使用order by 子句排序查询结果。 对数学成绩排序(降序)后输出。 selectname,math from student order by mathdesc; 或 selectname,math from student order by mathasc;//默认升序 对总分排序(降序)后输出。 select name as 姓名,chinese+math+english as 总分 from student order bychinese+math+english desc; 对姓’李’的学生总分排序(降序)输出。 select name as 姓名,chinese+math+english as 总分 from student where name like'李%' order bychinese+math+english desc; 统计一个班级共有多少学生? select count(*)as 学生总人数 from student; selectcount(math) as 学生总人数 from student; 统计数学成绩大于80的学生有多少个? select count(*)as 学生总人数 from student where math >80; 统计总分大于250的人数有多少? select count(*)as 学生总人数 from student wherechinese+math+english>250; 统计一个班级数学总成绩。 selectsum(math) as 数学总分 from student; 统计一个班级语文、英语、数学各科的总成绩。 selectsum(math) as 数学总分,sum(english) as 英语总分,sum(chinese) as 语文总分 from student; 统计一个班级语文、英语、数学的成绩总和。 selectsum(math+english+chinese) as 班级总分 from student; 统计一个班级语文成绩平均分。 selectsum(chinese)/count(chinese) as 语文成绩平均分 from student; 求一个班级数学平均分。 selectavg(math) as 数学平均分 from student; 求一个班级总分平均分。 select avg(math+english+chinese)as 班级总平均分 from student; 求班级最高分和最低分数学。 selectmax(math) as 最高数学分,min(math) as 最低数学分 from student; 对订单表中商品归类后,显示每一类商品的总价 select productas 商品,sum(price) as 总价 from orders group byproduct; 查询购买了几类商品,并且每类总价大于100的商品 select productas 商品,sum(price) as 总价 from orders group byproduct having sum(price) > 100; 查询购买了几类商品,并且每类总价大于100的商品,同时按照总价的降序排列 select productas 商品,sum(price) as 总价 from orders group byproduct having sum(price) > 100 order bysum(price) desc; 1 表的完整性 (1)实体完整性:每条记录有一个唯一标识符,通常用无任何业务含义的字段表示 (2)参照完整性:一张表的某个字段必须引用另一张表的某个字段值(外健),主要针对多张表 (3)域完整性:域即单元数据,域中的数值必须符合一定的规则 2 键的概念 (1)单一主键:只有唯一字段(推荐) (2)组合主键:由多个字段组合起来,形成唯一字段 (3)外键:针对多张表之间的关联 3 主键的特点 1)定义主键的语法:id int primary key 2)主键值不能为NULL 3)主键值不能重复 4)删除已存在的主键的语法:alter table person drop primary key 5)当删除已存在的主键后,可插入重复制,但不允许为NULL或者‘NULL’空串 6)主键可以由MySQL数据库系统自动产生一个唯一的值,语法如下:id int primary key auto_increment,MySQL特有 7)这个唯一的值,只在要表结构删除时清0,否则不会清0,继承累加,可能出现主键值不连续的情况,但一定唯一 8)项目中,主键不采用MySQL特有的auto_increment方式,而是采用UUID或自定义算法来产生唯一的主键,这样当多表中 的数据合起来时,主键重复的机会相当小 4 唯一约束的特点 1)定义唯一约束的语法:name varchar(20) unique 2)不能二次插入同一具体值 3)但可以插入NULL值,而不是’NULL‘串 5 非空约束特点 1)定义非空约束的语法:name varchar(20) unique notnull 2)不能插入NULL值,而不是’NULL‘串 3)以上约束的目的是确保数据的安全性,即能够存入到表中的数据,一定在符合安全约束的 6 外健特点 1)外键体现多表关联, 2)外键的值通常来自于另一张表的主键值 3)在对象世界中,有一对一单向和一对一双向 4)在关系世界中,只有一对一单向关联,原因在于删除操作 5)外键的语法:constraint pid_FK(给MySQL系统) foreign key(pid) references person(id) 测试MySQL特有的函数 使用MySQL特有函数举例: 1)到年底还有几少天[datediff] select datediff('2012-6-2','2012-12-31'); 2)截取字符串[substring] select substring('abcdef',1,5); 3)保留小数点后2位(四舍五入)[format] select format(2.333,1); 4)向下取整[floor] select floor(2.5621); 5)取随机值[rand] 0~1 select rand(); 6)取1-6之间的随机整数值[floor和rand] select floor(rand()*6)+1; 7)截取字符串长度和去空格 select trime(" jack "); 8)字符比较 select strcmp('a','w'); JDBC全称为:Java Data Base Connectivity(java数据库连接),它主要由接口组成。 组成JDBC的2个包:java.sql javax.sql 编写一个程序,这个程序从user表中读取数据,并打印在命令行窗口中。 一、搭建实验环境: 1、在mysql中创建一个库,并创建user表和插入表的数据。 2、新建一个Java工程,并导入数据驱动。 二、编写程序,在程序中加载数据库驱动 DriverManager. registerDriver(Driver driver) 三、建立连接(Connection) Connection conn =DriverManager.getConnection(url,user,pass); 四、创建用于向数据库发送SQL的Statement对象,并发送sql Statement st = conn.createStatement(); ResultSet rs = st.excuteQuery(sql); 五、从代表结果集的ResultSet中取出数据,打印到命令行窗口 六、断开与数据库的连接,并释放相关资源 4祥解JDBC连接数据库的各个步骤 1)JDBC开发的步骤 >>导入mysql驱动jar包(mysql-connector-java-5.1.7-bin.jar,每种数据库都会有自动的JDBC实现jar包) >>使用JDBC规则中的接口或类来操作所有关系型数据库 2)祥解 >>DriverManager(类)是管理一组具体数据库的驱动 >>Drvier(接口)需要被DriverManager管理的驱动,必须实现java.sql.Driver接口DriverManager会试图用URL和每个数据库驱动尝试进行连接 底层方法: boolean acceptsURL(String url):能够连接返回true;不能够连接返回false ,如果返回true的情况下,DriverManager再调用Connectionconnect(String url, Properties info)返回真正的连接对象 >>Connection(接口)表示与特定数据库连接的对象(会话) ,在完成业务操作后,该对象越早关闭越好,一定要关闭,先关闭轻量有资源Statement和Resulset,后关系重量级资源Connection >>Statement(接口)表示封装SQL命令对象,并执行SQL命令,取得返回值 >>ResultSet(接口)表示根据查询SQL命令得到的结果集,类似于一个List集合 初始情况下,位于第一行记录之前,需要通过next()方法将光标下移来取值 5 JDBC的六个固定步骤(以查询为例) >>向DriverManager注册数据库驱动 >>取得Connection重量级连接对象 >>创建封装SQL命令的对象Statement >>执行SQL命令,返回结果集ResultSet >>处理结果集ResultSet >>先轻后重,关闭结果集 Statement对象的executeUpdate方法,用于向数据库发送增、删、改的sql语句,executeUpdate执行完后,将会返回一个整数(即增删改语句导致了数据库几行数据发生了变化)。 Statement.executeQuery方法用于向数据库发送查询语句,executeQuery方法返回代表查询结果的ResultSet对象。 数据库分页: MySQL分页的实现: select * from table limit M,N M:记录开始索引位置 N:取多少条记录。 完成WEB页面的分页显示, 先获得需分页显示的记录总数,然后在web页面中显示页码。 根据页码,从数据库中查询相应的记录显示在web页面中。以上两项操作通常使用Page对象进行封装。 使用JDBC处理大数据 在实际开发中,程序需要把大文本或二进制数据保存到数据库。 基本概念:大数据也称之为LOB(Large Objects),LOB又分为: clob和blob clob用于存储大文本 blob用于存储二进制数据 对MySQL而言只有blob,而没有clob,mysql存储大文本采用的是text,text和blob分别又分为: TINYTEXT、TEXT、MEDIUMTEXT和LONGTEXT TINYBLOB、BLOB、MEDIUMBLOB和LONGBLOB MySQL中的Text类型 PreparedStatement.setCharacterStream(index, reader, length); //注意length长度须设置,并且设置为int型 reader = resultSet. getCharacterStream(i); reader = resultSet.getClob(i).getCharacterStream(); string s = resultSet.getString(i); 对于MySQL中的BLOB类型 文件的存储 PreparedStatement. setBinaryStream(i, inputStream, length); //字节字符流输出文件 InputStream in =resultSet.getBinaryStream(i); InputStream in =resultSet.getBlob(i).getBinaryStream(); 批处理: 1 批处理 1)当需要向表中插入很多SQL命令时,可以使用批处理 2)对于[相同结构]的SQL命令来说,可以使用PreparedStatement对象, 原因: >>只需一次性对SQL命令进行编译,以后凡是相同的SQL无需编译 >>只后的每次只是参数不同, 以上这种情部况,叫做预编译 3)对于[不相同结构的SQL命令来说,可以使用Statement对象, 原因: >>每条不同的SQL命令,都需要进行编译,再到数据库执行 4)项目中,相同结构的SQL命令,建议使用PreparedStatement;不相同结构的SQL命令,建议使用Statement;但不表示,不能使用其它的对象。 5)批对象常用的API >>stmt.addBatch(sqlA); >>int[] ints = stmt.executeBatch();//如果执行100次,成功的话,数组中包含100个1 >>stmt.clearBatch(); >>pstmt.addBatch(); >>int[] ints = pstmt.executeBatch(); >>pstmt.clearBatch(); 6)如果批处理的数据量较大,不要一次性执行多个SQL命令,而应该分次执行SQL命令 7)Statement对象:不具有预编译功能 PreparedStatement对象:具有预编译功能,可以使用占位符,可以防止SQL注入 2 获取主键值 1)需要使用外键关联别一张表的主键时,这时可以使用获取主键值 2)获取主键值专用于针对insert操作,因为insert操作才能产生主键值 3)常用的API: pstmt =conn.prepareStatement(sql,Statement.RETURN_GENERATED_KEYS); Statement.RETURN_GENERATED_KEYS表示在完成insert操作后,保留主键值,以备程序获取 rs = pstmt.getGeneratedKeys(); 获取插入记录的主键值 3 事务概述 1)事务指逻辑上的一组操作,组成这组操作的各个单元,要么全部成功,要么全部不成功。类似于&&操作 2)事务是每个数据库都具有的特点,只是强度不同而以。 3)MySQL常用事务控制命令 start transaction;//显示开始事务,其它每条SQL命令都有事务存在,只是以前我们使用隐式开始事务 commit;//提交事务,以前每条SQL命令执行结束后,数据库帮我们自动隐式提交事务, 在start transaction和commit这二个命令之间的SQL命令,都位于一个事务之间 rollback;//回滚事务,回滚到start transaction之前 4)事务可以让某些程序功能更加安全可靠 5)常用的JDBCAPI: conn.setAutoCommit(false);等价于start transaction; conn.commit();等价于commit; conn.rollback();等价于rollback; 注意:回滚后,也要提交事务 *6)要确定事务成功,就必须操作的是同一个connection对象,才能回滚成功,不能操作不同的connection对象,这一点非常重要 *4 事务的特性(ACID vs CURD) 原子性(A) >>事务中的各个组成部份是一个整体,不可再分,要么成功,要么失败。 一致性(C) >>以转帐为例,转帐前后,二者的余额总数不变 隔离性(I) >>一个用户操作表的某条记录时,应该加锁,不影响其它用户同时使用该张表的某条记录,类似于Java中的同步操作 持久性(D) >>当用户对表中的数据进行更新后,一旦确认后,就不应该非人为更新,应当永久保存 *5 事务的三个缺点 如果没有遵循“隔离性”原则,就会产生如下错误: 1)脏读 一个事务看到了另一个事务未提交的数据,强调[未提交],但数量不变 2)不可重复读 多次查询,结果不相同,强调[已提交],但数量不变 3)幻读/虚读 多次查询,结果不相同,强调[数量变化] 4)由轻到重:脏读->不可重复读->幻读/虚读 *6 事务的隔离级别 查看当前事务隔离级别 select @@tx_isolation; 修改当前事务隔离级别: set transaction isolation level read uncommitted; set transaction isolation level read committed; set transaction isolation level repeatable read; set transaction isolation level serializable; 启动事务: start transaction; 提交事务: commit; 回滚事务: rollback 1)事务的隔离级别能够解决[脏读/不可重复读/幻读] 2)事务的隔离级别有三类: >>readcommitted:不能解决任何事物 >>read committed:只能解决脏读 >>repeatable read:只能解决脏读/不可重复读,MySQL默认 >>serializable:能够解决脏读/不可重复读/幻读,但效率超低,因为在查询表时,都会将表锁住,以至于其它用户无法操作 3)在serializable隔离级别,二者都可以查询操作,但对方不能做非查询操作,即[insert/update/delete] 4)项目中,查询无需事务,但非查询操作得需要事务帮助,更安全 5)设置事务隔离级别的API: conn.setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE); *7 转帐 1)一定在确保Service和Dao层,用的都是同一个Connection对象 2)不要将Dao的Api引用到Serivce层,要做到解耦 3)使用ThreadLocale 连接池 DBCP----DATABASE CONNECTIONPOOL tomcat内置 C3P0---hibernate内置 使用的.jar commons-dbcp.jar:连接池的实现 commons-pool.jar: 连接池实现的依赖类 commons-collections.jar :连接池实现的集合类 DBCP: static{ InputStream in =JdbcUtil.class.getClassLoader(). getResourceAsStream("dbcp.properties"); Propertiesprop = new Properties(); prop.load(in); BasicDataSourceFactory factory = newBasicDataSourceFactory(); dataSource =factory.createDataSource(prop); } C3P0 数据源: 元数据- DataBaseMetaData: Api: Connection.getMetaData() DataBaseMetaData对象 getURL():返回一个String类对象,代表数据库的URL。 getUserName():返回连接当前数据库管理系统的用户名。 getDatabaseProductName():返回数据库的产品名称。 getDatabaseProductVersion():返回数据库的版本号。 getDriverName():返回驱动驱动程序的名称。 getDriverVersion():返回驱动程序的版本号。 isReadOnly():返回一个boolean值,指示数据库是否只允许读操作。 元数据- ParameterMetaData: PreparedStatement.getParameterMetaData() 获得代表PreparedStatement元数据的ParameterMetaData对象。 ParameterMetaData对象 getParameterCount() 获得指定占位符?参数的个数 元数据- ResultSetMetaData ResultSet.getMetaData() 获得代表ResultSet对象元数据的ResultSetMetaData对象。 ResultSetMetaData对象 getColumnCount() 返回resultset对象的列数 getColumnName(int column) 获得指定列的名称 getColumnTypeName(int column) 获得指定列的类型(Types类) 常用O-R Mapping映射工具 Hibernate(全自动框架) Ibatis(半自动框架/SQL) CommonsDbUtils(只是对JDBC简单封装) API介绍: org.apache.commons.dbutils.QueryRunner(类) org.apache.commons.dbutils.ResultSetHandler(接口) 工具类 org.apache.commons.dbutils.DbUtils。
QueryRunner类 需要一个 javax.sql.DataSource 来作参数的构造方法。 public Object query(Connection conn, Stringsql, Object[] params, ResultSetHandler rsh) throws SQLException:执行一个查询操作,在这个查询中,对象数组中的每个元素值被用来作为查询语句的置换参数。该方法会自行处理PreparedStatement 和 ResultSet 的创建和关闭。 public Object query(String sql, Object[]params, ResultSetHandler rsh) throws SQLException: 几乎与第一种方法一样;唯一的不同在于它不将数据库连接提供给方法,并且它是从提供给构造方法的数据源(DataSource) 或使用的setDataSource方法中重新获得 Connection。 public Object query(Connection conn, Stringsql, ResultSetHandler rsh) throws SQLException : 执行一个不需要置换参数的查询操作。 public int update(Connection conn, Stringsql, Object[] params) throws SQLException:用来执行一个更新(插入、更新或删除)操作。 public int update(Connection conn, Stringsql) throws SQLException:用来执行一个不需要置换参数的更新操作。 ResultSetHandler接口 BeanHandler:将结果集中的第一行数据封装到一个对应的JavaBean实例中。 BeanListHandler:将结果集中的每一行数据都封装到一个对应的JavaBean实例中,存放到List里。 ArrayHandler:把结果集中的第一行数据转成对象数组。 ArrayListHandler:把结果集中的每一行数据都转成一个对象数组,再存放到List中 MapHandler:将结果集中的第一行数据封装到一个Map里,key是列名,value就是对应的值。 MapListHandler:将结果集中的每一行数据都封装到一个Map里,然后再存放到List。 calarHandler:结果集中只有一行一列数据。 2012-7-22day17: 使用文档 JSPAPI <%=request.getrRemoteAdd() %> 标签: 自定义标签: 1.实现simpleTag 接口. 2)在WEB-INF/*.tld,目的是通知Web服务器自定义标签对应的处理类 3)在需要使用该自定义标签的JSP面页中,通过<%@taglib%>导入 <%@ page language="java" pageEncoding="UTF-8"%> <%@ taglib uri="http://java.sun.com/jsp/jstl/simple" prefix="simple"%> 客户端IP是: Jsp - Jstl: JSTL: javastandar taglib language: java标准标签语言 Out:标签: 例: <% request.setAttribute("role","管理员");%> <c:outvalue="${roles}"default="<ahref='/myday18/index.jsp'><fontcolor='red'>游客font>a>"escapeXml="false"/> Value : 表示输出的值. default : 表示指定如果value属性的值为null/””时所输出的默认值 escapeXml : 表示输出的内容,是否将>、<、&、'、" 等特殊字符进行HTML编码转换后再进行输出。默认值为true 例: <jsp:useBeanid="user" class="cn.web.User" scope="page"/> <c:set var="username"value="张三"scope="session"/> 用户名:<c:outvalue="${user.username}"/><br/> Value; 用于指定属性值 var :用于指定要设置的Web域属性的名称 Target : 用于指定要设置属性的对象,这个对象必须是JavaBean对象或java.util.Map对象 property : 用于指定当前要为对象设置的属性名称 例:<% request.setAttribute("name","哈哈哈");%> <c:removevar="name"/> var :表示需要删除的属性名 用户名:<c:outvalue="${name}"/> 输出为空白字符串 “”
例: <%try{ int a = 10/0; }catch(Exception e) { e.printStackTrace(); }%> <c:catchvar="exce"> <% int a = 10/0;%> c:catch> <c:outvalue="${exce.message}"/> Test : 表示决定是否处理标签体中的内容的条件表达式 var :用于指定将test属性的执行结果保存到某个Web域中的某个属性的名称
Var : 指定将当前迭代到的元素保存到page这个Web域中的属性名称 items : 将要迭代的集合对象 VarStatus : 指定将代表当前迭代状态信息的对象保存到page这个Web域中的属性名称 begin : 如果指定items属性,就从集合中的第begin个元素开始进行迭代,begin的索引值从0开始编号;如果没有指定items属性,就从begin指定的值开始迭代,直到end值时结束迭代. Step: 指定迭代的步长,即迭代因子的迭代增量(默认为1)
在JSP页面进行URL的相关操作时,经常要在URL地址后面附加一些参数。
http://localhost:808/servlet/MyServlet?name=“中国” 示例:
例: <body> <ahref='/day18/result.jsp?name=<%=URLEncoder.encode("中文","UTF-8")%>'> 传中文(脚本版) a> <hr/> <c:urlvalue="/result.jsp"var="myURL"> <c:paramname="name" value="中文"/> c:url> <ahref="${myURL}"> 传中文(标签版) a> body> Value ; 指定要构造的URL,/表示dayXX(web工程名) Var:指定将构造出的URL结果保存到Web域中的属性名称 Scope: 指定将构造出的URL结果保存到哪个Web域中
例:<c:redirecturl="/c_forEach.jsp"/> url : 指定要转发或重定向到的目标资源的URL地址
JSTL自定义标签: a.配置*.tld文件。 b.定义一个类继承SimpleTagSupport,重写doTag()方法。 c.通过jsp页面输出到浏览器。 d.
EL 全名为ExpressionLanguage EL表达式主要用于替换JSP页面中的脚本表达式,以从各种类型的web域中检索java对象、获取数据。(某个web域中的对象,访问javabean的属性、访问list集合、访问map集合、访问数组)
获取路径如: myday8(web应用工程名) ${pageContext.request.contextPath} <ahref="${pageContext.request.contextPath}/jstl/froeach.jsp"> 下载 a>
EL表达式语言中定义了11个隐含对象, 语法:${隐式对象名称} :获得对象的引用 pageContext 对应于JSP页面中的pageContext对象(注意:取的是pageContext对象。) pageScope 代表page域中用于保存属性的Map对象 requestScope 代表request域中用于保存属性的Map对象 sessionScope 代表session域中用于保存属性的Map对象 applicationScope 代表application域中用于保存属性的Map对象\ param 表示一个保存了所有请求参数的Map对象 paramValues 表示一个保存了所有请求参数的Map对象,它对于某个请求参数,返回的是一个string[],从0开始 ${paramValues.like[0]} header 表示一个保存了所有http请求头字段的Map对象 headerValues 同上,返回string[]数组。注意:如果头里面有“-”,例Accept-Encoding,则要headerValues[“Accept-Encoding”] cookie 表示一个保存了所有cookie的Map对象 initParam 表示一个保存了所有web应用初始化参数的map对象
测试headerValues时,如果头里面有“-”,例Accept-Encoding,则要headerValues[“Accept-Encoding”] 测试cookie时,例${cookie.key}取的是cookie对象,如访问cookie的名称和值,须${cookie.username.name}或${cookie.username.value} new Cookie(“username”,”jack”);
EL自定义函数开发: 步骤: 1.编写一个Java类的静态方法。 2.编写标签库描述符(tld)文件,在tld文件中描述自定义函数,在WEB-INF/目录下。 3.在JSP页面中导入和使用自定义函数。 4.编写完标签库描述文件后,需要将它放置到 5.TLD文件中的 6. < function>元素用于描述一个EL自定义函数,其中: *.TLD:文件配置
1)写一个普通类,无需扩展类或现实接口 publicstatic String filter(String message) {}
2)在WEB-INF/目录下写一个*.tld文件,目的是通知Web服务器有一个自定义函数,在部署时通知
(处理类中函数的签名,引用类型需要使用全路径,基本类型不需要全路径名)
3)在JSP中引用函数 <%@ pagelanguage="java" pageEncoding="UTF-8"%> <%@ tagliburi="http://java.sun.com/jsp/jstl/el" prefix="el" %>
${el:filterString("点点")}
JSP-国际化
i18n:internationalization
创建资源包和资源文件:
1.中文、英文环境相对应的资源文件名则为:
“myproperites_zh_CN.properties”
“myproperites_en_US.properties”
2.实现方式:
ResourceBundle类提供了一个静态方法getBundle,该方法用于装载资源文件,并创建ResourceBundle实例:
Locale currentLocale = Locale.getDefault();
ResourceBundle myResources =
ResourceBundle.getBundle(basename, currentLocale);
basename为资源包基名(且必须为完整路径)。
如果与该locale对象匹配的资源包子类找不到。一般情况下,则选用默认资源文件予以显示。
加载资源文件后,程序就可以调用ResourceBundle实例对象的 getString方法获取指定的资源信息名称所对应的值。
Stringvalue = myResources.getString(“key");
3.国际化标签:
<%@taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt"%>
key=“itcast.hello”>(资源文件key)
value=“itcast.name”/>(资源文件占位符)
设置POST请求消息的字符集编码
动态数据的国际化:
使用的包java.util包和java.text包
Locale 类
Locale 实例对象代表一个特定的地理,政治、文化区域。
DateFormat类可以将一个日期/时间对象格式化为表示某个国家地区的日期/时间字符串,项目中不提倡用Date类型,可以使用Calendar类,它还定义了一些用于描述日期/时间的显示模式的 int型的常量,包括FULL, LONG, MEDIUM, DEFAULT, SHORT,实例化DateFormat对象时,可以使用这些常量,控制日期/时间的显示长度
实例化DateFormat类方式
getDateInstance(intstyle, Locale aLocale):以指定的日期显示模式和本地信息来获得DateFormat实例对象,该实例对象不处理时间值部分。
getTimeInstance(int style, Locale aLocale):以指定的时间显示模式和本地信息来获得DateFormat实例对象,该实例对象不处理日期值部分。
getDateTimeInstance(int dateStyle, int timeStyle, Locale aLocale):以单独指定的日期显示模式、时间显示模式和本地信息来获得DateFormat实例对象。
2.DateFormat对象的方法:
format:将日期/时间对象格式化为符合某个本地环境习惯的字符串。
parse:将符合某个本地环境习惯的日期/时间字符串解析为日期/时间对象
注意:parse和format完全相反,一个是把date时间转化为相应地区和国家的显示样式,一个是把相应地区的时间日期转化成date对象,该方法在使用时,解析的时间或日期要符合指定的国家、地区格式,否则会抛异常
国际化标签:
中国:
美国:
NumberFormat类
NumberFormat可以将一个数值格式化为符合某个国家地区习惯的数值字符串,也可以将符合某个国家地区习惯的数值字符串解析为对应的数值
方法:
getIntegerInstance(Localelocale):以参数locale对象所标识的本地信息来获得处理整数的NumberFormat实例对象
getCurrencyInstance(Localelocale):以参数locale对象所标识的本地信息来获得处理货币的NumberFormat实例对象
getPercentInstance(Localelocale):以参数locale对象所标识的本地信息来获得处理百分比数值的NumberFormat实例对象
国际化标签:
<body>
将字符串转成整型: <fmt:formatNumbertype="number" value="3.555" pattern=".0"/>
<hr/>
将字符串转成货币: <fmt:formatNumbertype="currency"value="3.555"/>
<hr/>
将字符串转成百分比:<fmt:formatNumbertype="percent"value="3.555"/>
<hr/>
body>
文件上传概述:
如何在web页面中添加上传输入项?
标签用于在web页面中添加文件上传输入项,设置文件上传输入项时须注意:
1、必须要设置input输入项的name属性,否则浏览器将不会发送上传文件的数据。
2、必须把form的enctype属值设为multipart/form-data.设置该值后,浏览器在上传文件时,将把文件数据附带在http请求消息体中,并使用特定的格式对上传的文件进行描述,以方便接收方对上传数据进行解析和处理。
实现步骤
1、创建DiskFileItemFactory对象,设置缓冲区大小和临时文件目录
2、使用DiskFileItemFactory 对象创建ServletFileUpload对象,并设置上传文件的大小限制。
3、调用ServletFileUpload.parseRequest方法解析request对象,得到一个保存了所有上传内容FileItem的List对象。
4、对list进行迭代,每迭代一个FileItem对象,调用其isFormField方法判断是否是上传文件
为普通表单字段,则调用getFieldName、getString方法得到字段名和字段值
为上传文件,则调用getName、 getInputStream方法得到数据输入流,从而读取上传数据。
需要使用的类和方法:
1.DiskFileItemFactory是创建 FileItem 对象的工厂,这个工厂类常用方法:
public voidsetSizeThreshold(int sizeThreshold) 设置内存缓冲区的大小,默认值为10K。当上传文件大于缓冲区大小时, fileupload组件将使用临时文件缓存上传文件。
public void setRepository(java.io.File repository) 指定临时文件目录,默认值为System.getProperty("java.io.tmpdir").
2.ServletFileUpload 负责处理上传的文件数据,并将表单中每个输入项封装成一个 FileItem 对象中。常用方法有:
booleanisMultipartContent(HttpServletRequest request)判断上传表单是否为multipart/form-data类型
List parseRequest(HttpServletRequest request)解析request对象,并把表单中的每一个输入项包装成一个fileItem 对象,并返回一个保存了所有FileItem的list集合。
setHeaderEncoding(java.lang.String encoding)
设置编码格式,解决上传中文名文件的问题
3.中文文件名乱码问题
文件名中文乱码问题,可调用ServletFileUpload的setHeaderEncoding方法,或者设置request的setCharacterEncoding属性
4.临时文件的删除问题
由于文件大小超出DiskFileItemFactory.setSizeThreshold方法设置的内存缓冲区的大小时,commons-fileupload组件将使用临时文件保存上传数据,因此在程序结束时,务必调用FileItem.delete方法删除临时文件。
delete方法的调用必须位于流关闭之后,否则会出现文件占用,而导致删除失败的情况。
Filter: 过滤器
1)Filter是位于服务器中,用于拦截客户端和Web资源之间的访问
2)Filter可以客户端访问的Web资源作预先处理,如果符合要求,则将请求交给Web资源;如果不符合要求,则不将请求交给Web资源,类似于一个判段角色
3)将Web请求返回时,也会经过Filter,此时可以通过Filter对该请求做后备处理
Filter开发分为二个步骤:
1.编写java类实现Filter接口,并实现其doFilter方法。
2.在 web.xml 文件中使用
行注册,并设置它所能拦截的资源。
Filter链:
1.在一个web应用中,可以开发编写多个Filter,这些Filter组合起来称之为一个Filter链。
2.web服务器根据Filter在web.xml文件中的注册顺序,决定先调用哪个Filter,当第一个Filter的doFilter方法被调用时,web服务器会创建一个代表Filter链 FilterChain对象传递给该方法。在doFilter方法中,开发人员如果调用了FilterChain对象的doFilter方法,则web服务器会检查FilterChain对象中是否还有filter,如果有,则调用第2个filter,如果没有,则调用目标资源。
Filter生命周期
1)Filter是一个单例的,每个用户访问的都是同一个Filter过滤器实例
2)防止线程安全问题,线程安全有三要素
>>单例
>>实例变量
>>对实例变量的更新操作
如果以上三个问题[同时]满足,就要用"加锁"来解决
3)部署Web应用时,执行空参构造方法(),再执行初始化方法
第一,二,N次请求,执行doFilter()方法,
当重新部署web应用时,由web服务器先销毁原Filter实例,在销毁[之前]执行destory()方法,然后立即创建新的Filter实例。
4)注意:过滤器,是不能够直接访问的
FilterConfig接口
1.在配置filter时,可以使用
String getFilterName():得到filter的名称。
String getInitParameter(String name): 返回在部署描述中指定名称的初始化参数的值。如果不存在返回null.
Enumeration getInitParameterNames():返回过滤器的所有初始化参数的名字的枚举集合。
public ServletContext getServletContext():返回Servlet上下文对象的引用。
Filter常见应用
1. 禁止浏览器缓存所有动态页面的过滤器:
response.setDateHeader("Expires",-1); Expires数据头:值为GMT时间值,为-1指浏览器不要缓存页面
response.setHeader("Cache-Control","no-cache");
response.setHeader("Pragma","no-cache");
Cache-Control响应头有两个常用值:
no-cache指浏览器不要缓存当前页面。
max-age:xxx指浏览器缓存页面xxx秒
Filter的部署—映射Filter
1.
2.一个Filter拦截的资源可通过两种方式来指定:Servlet名称和资源访问的请求路径
3.
明过的过滤器的名字
4.
5.
6.
1 映射Filter的细节
1)在默认情况下,Filter只过滤用户直接请求的Web资源,例如在地栏直接访问,重定向,超链接。
除此之外,不会被过滤,例如:转发
Filter映射有四个种方式
>>REQUEST:默认(地栏直接访问,重定向,超链接)
>>FORWARD:专用于Web资源之间的转发
>>INCLUDE:专用于Web资源之间的包含
>>ERROR:专用于Web资源的[出错]情况,通常不单独使有,要配REQUEST/FORWARD/INCLUDE使用
如果没有出错,ERROR不会执行,项目中可以使用单独的ERROR过滤器
注意:ERROR过滤方式,需要适合在web.xml文件中的全局错误声明
3)一旦在
装饰设计模式
1)当一个类的某个或某些方法不能满足应用的需求,此时可以继承该类,重写对应的方法,前提是该类非final类型
2)装饰设计模式主要运用在对某个不满足应用需求的类的方法而产生的
3)开发步骤:
>>普通类继承需要被装饰的类,例如:BufferedReader,哪个类的方法不满足需求,该类就叫做需要被装饰的类
>>用实例变量的方式记住需要被装饰的类
>>通过构造方式为实例变量覆值
>>争对不满足需求的方法重写
>>对于满足需求的方法,直接调用需要被装饰的类的相关方法[可省]
4 总结Servlet和Filter
1)Servlet和Filter是一个互补的技术
2)Servlet比较适合做核心的业务控制
Filter比较适合做预先处理后后备处理
3)Servlet和Filter在细粒度分层结构中,位于控制层,可以调用业务层
Servlet监听器
监听的事件源分别为 ServletContext,HttpSession 和 ServletRequest 这三个域对象
监听器划分为三种类型。
监听三个域对象创建和销毁的事件监听器
监听域对象中属性的增加和删除的事件监听器
监听绑定到HttpSession域中的某个对象的状态的事件监听器。
监听ServletContext域对象创建和销毁
1.ServletContextListener接口用于监听 ServletContext 对象的创建和销毁事件。
2.当 ServletContext 对象被创建时,激发contextInitialized(ServletContextEvent sce)方法
当 ServletContext 对象被销毁时,激发contextDestroyed(ServletContextEvent sce)方法。
3.和编写其它事件监听器一样,编写servlet监听器也需要实现一个特定的接口,并针对相应动作覆盖接口中的相应方法。
4.和其它事件监听器略有不同的是,servlet监听器的注册不是直接注册在事件源上,而是由web容器负责注册,开发人员只需在web.xml文件中使用
5.一个 web.xml 文件中可以配置多个Servlet 事件监听器,web 服务器按照它们在 web.xml 文件中的注册顺序来加载和注册这些 Serlvet 事件监听器。
ServletContext事件源
创建初始化销毁:ServletContextListener
属性变化:ServletContextAttributeListener
何时产生:部署Web应用时
何时销毁:重新部署Web应用时,或Web服务器停止再重新启动
3)ServletRequest事件源
创建初始化销毁:ServletRequestListener
属性变化:ServletRequestAttributeListener
何时产生:同一个客户端第一次请求会产生一个ServletRequest,该客户端随后多次请求,都是同一个ServletRequest对象别一个客户端访问,会产生不同的ServletRequest对象
何时数据销毁:每次请求的数据不能被第二次请求共享,只能在当次请求中取得数据,这也是为什么重定向情况下,request域中的数据不能共享的原因多次请求request域中的数据不共享
4)HttpSession事件源
创建销毁:HttpSessionListener
属性变化:HttpSessionAttributeListener
何时产生:新会话,首次访问jsp;在原会话中,多次请求,不会创建新HttpSession
当关闭浏览器后,不会销毁原HttpSession
何时销毁:
>>在web.xml文件中,配置HttpSession的有效生命时间,当有效生命时间完成后,Web服务器会依次删除位于Web服务器中的HttpSession,但是删除的时间不精确
>>自已定义一个HttpSession扫描器,来较精确的控制HttpSession的销毁时间
>>当session.invalidate()方法执行时,会被执行attributeRemoved()和sessionDestroyed()
>>当session.removeAttribute()方法执行时,会被执行attributeRemoved()
Session 绑定的事件监听器
保存在 Session 域中的对象可以有多种状态:绑定到 Session 中;从 Session 域中解除绑定;随 Session 对象持久化到一个存储设备中[钝化];随 Session 对象从一个存储设备中恢复[激活]
Servlet 规范中定义了两个特殊的监听器接口来帮助 JavaBean 对象了解自己在 Session 域中的这些状态:HttpSessionBindingListener接口和HttpSessionActivationListener接口 ,实现这两个接口的类不需要 web.xml 文件中进行注册,因为事件源和监听器都是JavaBean自身
实现了HttpSessionBindingListener接口的JavaBean 对象可以感知自己被绑定到 Session 中和从 Session 中删除的事件
当对象被绑定到HttpSession 对象中时,web 服务器调用该对象的 void valueBound(HttpSessionBindingEventevent) 方法
当对象从HttpSession 对象中解除绑定时,web 服务器调用该对象的 voidvalueUnbound(HttpSessionBindingEvent event)方法
HttpSessionActivationListener接口
实现了HttpSessionActivationListener接口的JavaBean 对象可以感知自己被激活和钝化的事件
当绑定到HttpSession 对象中的对象将要随 HttpSession 对象被钝化之前,web 服务器调用如下方法sessionWillPassivate(HttpSessionBindingEventevent) 方法
当绑定到HttpSession 对象中的对象将要随 HttpSession 对象被活化之后,web 服务器调用该对象的void sessionDidActive(HttpSessionBindingEvent event)方法
案例1:手动销毁session
public class MyservletHttpSession implements HttpSessionListener, ServletContextListener{
private List
private Timer timer= new Timer();
public voidsessionCreated(HttpSessionEvent arg0) {
System.out.println("session的创建");
//获取每个用户的session域对象
HttpSession session = arg0.getSession();
//获取每一个回话的id
String id = session.getId();
//查看session的创建时间
System.out.println(id+","+newDate(session.getCreationTime()));
synchronized (MyservletHttpSession.class) {
//将用户的session加入集合中
sessionlist.add(session);
}
}
public voidsessionDestroyed(HttpSessionEvent arg0) {
System.out.println("session的销毁");
HttpSession session = arg0.getSession();
//获取每一个回话的id
String id = session.getId();
//查看session的销毁时间
System.out.println(id+","+new Date().toLocaleString());
}
public voidcontextInitialized(ServletContextEvent arg0) {
//初始化定时器
timer.schedule(new MyTimer(sessionlist),5000,1000);
}
public voidcontextDestroyed(ServletContextEvent arg0) {
}
}
//定时器类(作用:每个1分钟在集合中查找session一次,并判断session的创建时间与当前时间只差是否大于1分钟,大于1分钟,销毁session)
class MyTimer extends TimerTask
{
private List
public MyTimer(List
this.sessionlist =sessionlist;
}
public void run() {
if(sessionlist.size()>0)
{
synchronized (MyservletHttpSession.class) {
//迭代取出集合中的每一个HttpSession
Iterator
while(it.hasNext())
{
HttpSession session = it.next();
//获取session的最后一次的访问时间
long end = session.getLastAccessedTime();
//获取当前时间
long currnet = System.currentTimeMillis();
//判断session的存储时间,如果大于1分钟,即销毁该session
long result = currnet-end;
if(result>5000)
{
it.remove();
//销毁session
session.invalidate();
}
}
}
}
}
}
案例2: ServletContextListener
//创建表
publicclass SystemDao {
public void createTable() throws SQLException
{
String sql = "create table if not exists systemdao(idint primary key auto_increment,name varchar(20) not null)";
QueryRunner runner = new QueryRunner(C3P0Util.getDataSource());
runner.update(sql);
}
public void updateTable() throws SQLException
{
String sql = "update systemdao set name ='你好' where id=1";
QueryRunner runner = new QueryRunner(C3P0Util.getDataSource());
runner.update(sql);
}
public void dropTable() throws SQLException
{
String sql = "drop table if exists systemdao";
QueryRunner runner = new QueryRunner(C3P0Util.getDataSource());
runner.update(sql);
}
public void insertTable() throws SQLException
{
String sql = "insert into systemdao(name) value('呵呵')";
QueryRunner runner = new QueryRunner(C3P0Util.getDataSource());
runner.update(sql);
}
}
测试类: SystemListener
public class SystemListener implements ServletContextListener {
private Timer timer = new Timer();
public void contextInitialized(ServletContextEventarg0) {
//启动定时器
timer.schedule(new TimerTask(){
public void run() {
DateFormat dataformat = DateFormat.getDateTimeInstance(DateFormat.FULL,DateFormat.DEFAULT,Locale.CHINA);
String time = dataformat.format(new Date());
System.out.println(time);
}
}, 5000, 1000);
//创建表进行数据的初始化
SystemDao dao = new SystemDao();
try {
dao.createTable();
dao.insertTable();
dao.updateTable();
} catch (SQLException e) {
e.printStackTrace();
}
}
public void contextDestroyed(ServletContextEventarg0) {
SystemDao dao = new SystemDao();
try {
//删除表
dao.dropTable();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
Base64编码
BASE64Encoderencoder = new BASE64Encoder();
加密:
encoder.encode(username.getBytes());
encoder.encode(password.getBytes());
Base64解码
BASE64Decoderdecoder = new BASE64Decoder();
解密:
new String(decoder.decodeBuffer(username))
new String(decoder.decodeBuffer(password))
案例1: 加密和解密
//BASE64加密和解密过程
public class Demo1 {
public static void main(String[] args) throws IOException {
String username = "bbb";
String password = "123456";
//加密
BASE64Encoder encoder = new BASE64Encoder();
String usrename64 =encoder.encode(username.getBytes());
String password64 =encoder.encode(password.getBytes());
System.out.println(usrename64);
System.out.println(password64);
//解密
BASE64Decoder decoder = new BASE64Decoder();
byte[] name = decoder.decodeBuffer(usrename64);
byte[] pass = decoder.decodeBuffer(password64);
System.out.println(new String(name));
System.out.println(new String(pass));
}
}
案例2:使用cookie存储用户信息
public class CookieBASE64Servletextends HttpServlet {
public void doGet(HttpServletRequestrequest, HttpServletResponse response)
throws ServletException, IOException {
/* Stringusername = "李四";
//加密,cookie中所存储的中文只能是以base64进行加密和解密
BASE64Encoder encoder = newBASE64Encoder();
username =encoder.encode(username.getBytes());
//创建cookie
Cookie cookie = new Cookie("username",username);
//设置cookie的存活时间
cookie.setMaxAge(1*60*60);
//将cookie写入浏览器中
response.addCookie(cookie);
*/
//取出cookie
Cookie[] cookies = request.getCookies();
Cookie newCookie = null;
for(Cookie cookie : cookies)
{
if(cookie.getName().contains("username"))
{
newCookie = cookie;
}
}
if(newCookie!=null)
{ //获取cookie的名字
String name = newCookie.getName();
String value = newCookie.getValue();
//解密
BASE64Decoder decoder = new BASE64Decoder();
byte[] buf = decoder.decodeBuffer(value);
System.out.println(name +":"+new String(buf));
}
}
}
案例3:使用文件存储用户信息
public class Demo2 {
//将用户信息加密后写入文件中
public static void main(String[] args) throws Exception {
File file = new File("e:/base64.text");
//FileOutputStream有自动创建文件的功能
FileOutputStream out= new FileOutputStream(file);
PrintWriter pw = new PrintWriter(out);
pw.write("username=哈哈\r\n");
pw.write("password=123456");
pw.flush();
pw.close();
}
}
JavaMail开发
1.邮件服务器:要在Internet上提供电子邮件功能,必须有专门的电子邮件服务器
2. 电子邮箱: 电子邮箱(E-mail地址)的获得需要在邮件服务器上进行申请 ,确切地说,电子邮箱其实就是用户在邮件服务器上申请的一个帐户。
邮件传输协议和邮件服务器类型
1.SMTP协议(SimpleMail Transfer Protocol): 通常我们也把处理用户smtp请求(邮件发送请求)的邮件服务器称之为SMTP服务器。(25端口)
2.POP3协议POP3(PostOffice Protocol 3): 通常我们也把处理用户pop3请求(邮件接收请求)的邮件服务器称之为POP3服务器。(110端口)
SMTP协议
WindowXP平台下,进入cmd命令状态:
[email protected]向[email protected]手工发送邮件步骤:
相同域:
telnet 127.0.0.1 25 (SMTP服务器固定端口号)<回车>
ehlo 用户名/可任意 <回车\r\n>
auth login <回车>经过base64编码后的用户名和密码
YWFh 经过Base64加密后的用户名aaa<回车>
MTIzNDU2 经过Base64加密后的密码123456<回车>
mail from:
rcpt to:
data <回车>
from:[email protected] <回车>
to:[email protected] <回车>
subject:test <回车>
Hello! How are you doing! <回车> 发送的内容
. <回车>点号代表邮件内容的结束
quit <回车> 退出
POP3协议
[email protected]手工接收邮件
相同域
telnet 127.0.0.1 110 (POP3服务器固定端口号)<回车>
user bbb <回车>
pass 123456 <回车>
stat <回车> 返回邮箱的统计信息(字节数)
list 2 <回车> 返回某一封邮件的统计信息
retr 1 <回车>
quit <回车>
JavaMail常用API
1)MimeMessagemessage = new MimeMessage(Session.getDefaultInstance(new Properties()));
Session表示发送到邮件服务器的相关属性<后面继续讲>
2)message.setFrom(new InternetAddress("[email protected]"));
3)message.setRecipient(RecipientType.TO,newInternetAddress("[email protected]"));
4)message.setSubject("bbs");
5)message.setContent("邮件内容","text/html;charset=ISO8859-1");
6)message.writeTo(new FileOutputStream("d:/demo1.eml"));
7)DataHandler dh= new DataHandler(newFileDataSource("src/cn/itcast/web/config/photo.jpg"));
读取附件或图片
8)图片:image.setContentID("imageAID");设置编号
9)附件:append.setFileName(dh.getName());设置附件的名字
10)MimeMultipartmm = new MimeMultipart();
11)mm.addBodyPart(text);
12)mm.setSubType("related");只适合于文本+图片,不限文本和图片的数量
mm.setSubType("mixed");只适合到文本+附件,不限文本和附件的数量
13)message.setContent(mm);一定要将关系设置到邮件中,否则邮件就是空邮件
5 创建简单邮件
>>直接在message对象中设置内容,即MimeMessage.setContent()
6 创建复杂邮件
>>一封邮件有多少个部分组成,就有多少个BodyPart
>>不管是文本前,还是图片,最终加入到MimeMultipart关系中的顺序一定是:[文本先,图片后],关系名字叫"related"
>>图片设置编号,附件设置名字
>>不管是文本前,还是附件前,最终加入到MimeMultipart关系中的顺序[任意],关系名字叫"mixed"
案例1:
//JAVAMail创建邮件
public class MessageMail {
public static void main(String[] args) throws Exception{
//创建一个邮件
MimeMessage message = new MimeMessage(Session.getInstance(new Properties()));
//设置mail: from
message.setFrom(new InternetAddress("[email protected]"));
//设置mail: to
message.setRecipient(RecipientType.TO,new InternetAddress("[email protected]"));
//设置发送发送方式cc,bcc
message.setSubject("cc");
//设置发送的内容
message.setContent("创建一个邮件","text/html;charset=UTF-8");
//将内存中的message写入硬盘中
message.writeTo(new FileOutputStream(new File("e:/mail.eml")));
}
}
案例2.
//创建复杂邮件(文本+图片)
public class Demo2 {
public static void main(String[] args)throws Exception {
MimeMessage message = new MimeMessage(Session.getDefaultInstance(new Properties()));
message.setFrom(new InternetAddress("[email protected]"));
message.setRecipient(RecipientType.TO,new InternetAddress("[email protected]"));
message.setSubject("text+img");
//文本
MimeBodyPart text = new MimeBodyPart();
text.setContent("
this is aphoto
"
//图片A
MimeBodyPart imageA = new MimeBodyPart();
//采用类去加载图片
DataHandler dh = new DataHandler(new FileDataSource("src/cn/itcast/web/config/photo.jpg"));
//设置图片加载器为DataHandler
imageA.setDataHandler(dh);
//设置图片的编码,该编号是整个邮件中的唯一标识符
imageA.setContentID("imageAID");
//图片B
MimeBodyPart imageB = new MimeBodyPart();
//采用类去加载图片
dh = new DataHandler(new FileDataSource("src/cn/itcast/web/config/zgl.jpg"));
//设置图片加载器为DataHandler
imageB.setDataHandler(dh);
//设置图片的编码,该编号是整个邮件中的唯一标识符
imageB.setContentID("imageBID");
//设置文本和图片的关系,MimeMultipart相关于他们的关系,类似于容器
MimeMultipart mm = new MimeMultipart();
//将文本和图片加入到关系中
mm.addBodyPart(text);//文本
mm.addBodyPart(imageA);//图片A
mm.addBodyPart(imageB);//图片B
mm.setSubType("related");
//设置邮件的内容
message.setContent(mm);
//将邮件输出到硬盘
message.writeTo(new FileOutputStream("d:/demo2.eml"));
}
}
RFC822文档规定了如何编写一封简单邮件
邮件头和邮件体,两者使用空行分隔
邮件头
from字段
to字段
subject字段
cc、bcc字段
邮件体
邮件内容
创建邮件—— MIME协议
1.JavaMail常用API
//发送邮件采用的协议(小写smtp)
props.put("mail.transport.protocol","smtp");
//发送到哪台邮件服务器的IP地址
props.put("mail.host","192.168.10.176");
>>Sessionsession = Session.getDefaultInstance(props);
>>Transport transport =session.getTransport();
>>transport.connect("[email protected]","123456");
>>transport.send(messsage);
>>transport.close();
发送邮件步骤:
1,创建propertiesProperties p = new Properties()
2.设置发送邮件的协议:
p.put("mail.transport.protocol", "smtp");
p.put("mail.host", "127.0.0.1");
3.创建session对象: Session session =Session.getInstance(p)
4.建立连接:
Transport transport = session.getTransport();
transport.connect("[email protected]","123456");
5.发送邮件 : 创建邮件
5.1 :创建邮件 : MimeMessage message = new MimeMessage(session)
5.2:设置邮件的 from , to ,主题,内容(设置编码方式)
message.setFrom(new InternetAddress("[email protected]"));
message.setRecipient(RecipientType.TO,new InternetAddress(email));
message.setSubject("欢迎光临");
message.setContent("注册成功码:","text/html;charset=UTF-8");
6.发送: transport.send(message);
7.关闭资源: transport.close();
例:
public class MailUtil {
public static void send(String username, String password,String email)throws Exception { //创建properties
Properties p = new Properties();
//设置发送邮件的协议
p.put("mail.transport.protocol","smtp");
p.put("mail.host", "127.0.0.1");
//创建session
Session session = Session.getInstance(p);
//建立连接
Transport transport = session.getTransport();
transport.connect("[email protected]","123456");
//发送邮件
Message message = cteateMessage(session,username, password,email);
transport.send(message);
transport.close();
}
public static MessagecteateMessage(Session session,String username, String password, String email)throws Exception, MessagingException
{ //创建邮件
MimeMessage message = new MimeMessage(session);
message.setFrom(new InternetAddress("[email protected]"));//158409270@163.com
message.setRecipient(RecipientType.TO,new InternetAddress(email));
message.setSubject("欢迎光临");
message.setContent("注册成功
用户名:"+username+"
密码:"+password+"
邮箱:"+email,"text/html;charset=UTF-8");
return message;
}
}
例: MD5: 加密和解密
public class Md5Util {
private static String[] hex = { "0", "1", "2","3", "4", "5", "6", "7",
"8", "9", "A", "B","C", "D", "E", "F" };
public static String encoder(Stringpassword) throws Exception {
//创建MD5加密
MessageDigest md5 =MessageDigest.getInstance("md5");
// 将原码进行加密
byte[] byteArray =md5.digest(password.getBytes());
// 将字节转换为十六进制数
String passwordMD5 =byteArrayToString(byteArray);
return passwordMD5;
}
// 将byte[]数组转换为Stirng类型
private static String byteArrayToString(byte[] byteArray) {
StringBuffer sb = new StringBuffer();
for (byte b : byteArray) {
sb.append(byteToHexChar(b));
}
return sb.toString();
}
private static Object byteToHexChar(byte b) {
int n = b;
if (n < 0)
n = n + 256;
return hex[n / 16] + hex[n % 16];
}
}
泛型自定义,注解,代理
1. 泛型(Generic):
注意:泛型是提供给javac编译器使用的,它用于限定集合的输入类型,让编译器在源代码级别上,即挡住向集合中插入非法数据。但编译器编译完带有泛形的java程序后,生成的class文件中将不再带有泛形信息,以此使程序运行效率不受到影响,这个过程称之为“擦除”。
泛形的基本术语,以ArrayList
ArrayList
ArrayList
整个称为ArrayList
整个BaseDao
例:自定义泛型
//父类(不能含有具体类型)
public class BaseDao
//即BaseDao
//也可以叫做“参数据类型”,用ParameterizedType接口来表示,它是
//Type接口中子接口
private String tableName;
private Class clazz;
public BaseDao() {
//取得BaseDao类的字节码对象,即Class
Class baseDaoClass = this.getClass();
//取得子类型的参数据类型
Type type =baseDaoClass.getGenericSuperclass();
//将父接口强转成子接口,即只表示"参数据类型(BaseDao
ParameterizedType pt = (ParameterizedType)type;
//取得"参数据类型(BaseDao
Type[] types = pt.getActualTypeArguments();
//取得第一个实际类型,即Type类型
type = types[0];
//class也是一种数据类型
this.clazz = (Class) type;
//取得表名
int index = this.clazz.getName().lastIndexOf(".");
this.tableName =this.clazz.getName().substring(index+1).toLowerCase();
}
//根据编号查询内容
public T findById(int id)throws SQLException{
T t = null;
QueryRunner runner = new QueryRunner(JdbcUtil.getDataSource());
String sql = "select * from " +tableName + " where id=?";
t = (T)runner.query(sql,id,new BeanHandler(clazz));
return t;
}
}
继承类:
public class TopicDao extends BaseDao
}
public class TypeDao extends BaseDao
}
测试类:
public static void main(String[] args) throws Exception {
//子类
TypeDao typeDao = new TypeDao();
Type type = typeDao.findById(1);
System.out.println(type.getId()+":"+type.getTitle());
System.out.println("-------------------------------");
TopicDao topicDao = new TopicDao();
Topic topic = topicDao.findById(1);
System.out.println(topic.getId()+":"+topic.getTitle());
}
2,JDK5中内置注解:
>>@Override: 限定重写父类方法, 该注解只能用于方法
>>@Deprecated: 用于表示某个程序元素(类, 方法等)已过时
>>@SuppressWarnings: 抑制编译器警告.
元注解:被注解修饰的注解的注解称为原注解
1.元 Annotation指修饰Annotation的Annotation。JDK中定义了如下元Annotation:
@Retention:只能用于修饰一个 Annotation 定义, 用于指定该 Annotation 可以保留的域, @Rentention包含一个 RetentionPolicy 类型的成员变量, 通过这个变量指定域。
RetentionPolicy.CLASS:编译器将把注解记录在 class 文件中. 当运行 Java 程序时, JVM 不会保留注解. 这是默认值
RetentionPolicy.RUNTIME:编译器将把注释记录在 class 文件中. 当运行 Java 程序时, JVM 会保留注解. 程序可以通过反射获取该注释
RetentionPolicy.SOURCE:编译器直接丢弃这种策略的注释
@Target:指定注解用于修饰类的哪个成员. @Target 包含了一个名为 value,类型为ElementType(JDK6)的成员变量。
@Documented:用于指定被该元 Annotation 修饰的 Annotation 类将被 javadoc 工具提取成文档。
@Inherited:被它修饰的 Annotation 将具有继承性.如果某个类使用了被 @Inherited 修饰的Annotation, 则其子类将自动具有该注解。
例:自定义注解
//自定义注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface DbInfo {
String driver() default "com.mysql.jdbc.Driver";
String url() default "jdbc:mysql://127.0.0.1:3306/bbs";
String username() default "root";
String password() default "root";
}
测试类:
public class Demo3 {
@DbInfo
public Connection getConnection()throws Exception{
//取得Demo3的字节码对象Class
Class clazz = Demo3.class;
//取得getConnection()方法
Method method = clazz.getMethod("getConnection",null);
System.out.println(method.getName());
//取得DbInfo注解,注解也是一个类型,即DbInf.class是可以的
DbInfo dbInfo = method.getAnnotation(DbInfo.class);
//取得注解中的属性
String driver = dbInfo.driver();
String url = dbInfo.url();
String username = dbInfo.username();
String password = dbInfo.password();
//再通过DriverManager取得连接
Class.forName(driver);
Connection conn =DriverManager.getConnection(url,username,password);
return conn;
}
public static void main(String[] args) throws Exception {
Demo3 demo = new Demo3();
Connection conn = demo.getConnection();
if(conn!=null){
System.out.println("取得连接");
conn.close();
}
}
}
3 自定义注解和反射注解
1)注释有三种生命周期
>>SOURCE:有且只有在源码级别可见,(不推荐使用)
>>CLASS: 有且只有在源码和字节码级别可见,(默认),但在运行时,不可见
>>RUNTIME:在整个类的运行期周,都可见。(即源码,字节码,运行时)
2)通过反射注解在运行级别获取该注解中的属性值,以替换配置文件
3)元注解:修饰其它注解的注解,注意:元注解也可以被其它注解修饰
>>@Retention(RetentionPolicy.RUNTIME)
>>@Target(ElementType.METHOD):表示该注解可以运在哪些地方,默认情况下,该注解可以出在在任意地方
代理(proxy):
代理模式的作用
为其他对象提供一种代理以控制对目标对象的访问。某些情况下客户不想或不能直接引用另一个对象,而代理对象可在客户端和目标对象间起到中介作用
1.静态代理:
优点
不需要修改目标对象就实现了功能的增加
缺点
真实角色必须是事先已经存在的,并将其作为代理对象的内部属性。如果事先并不知道真实角色则无法使用
一个真实角色必须对应一个代理角色,如果大量使用会导致类的急剧膨胀
2.动态代理: java.lang.reflect
InvocationHandler接口
Proxy类
InvocationHandler接口仅定义了一个方法
public objectinvoke(Object obj,Method method, Object[] args)
obj一般是指代理类
method是被代理的方法
args为该方法的参数数组
这个抽象方法在代理类中动态实现
Proxy类即为动态代理类,主要方法包括
Object newProxyInstance(ClassLoader loader, Class[ ] interfaces,InvocationHandler h)
返回代理类的一个实例
loader是类装载器
interfaces是真实类所拥有的全部接口的数组
h - 指派方法调用的调用处理程序
静态代理和动态代理的比较
1,静态代理类确实存在,动态代理类在运行期动态生成
2,一个真实角色必须对应一个静态代理角色,而动态代理大大减少了代理类的数量
3,动态代理类不会作实质性的工作,在生成它的实例时必须提供一个handler,由它接管实际的工作(会自动执行handler的invoke方法)
如何代理
调用任务业务方法,都会被代理类拦截,从而调用invoke()方法
动态代理的步骤:
NO1:写一个普通类
NO2:写一个实例变量,记录代理谁
NO3:使用构造方法为上述实例变量设置值
NO4:写一个普通方法,返回接口,该方法的返回值就是动态代理对象,该动态代理对象也实现了这个接口,该方法内部使用Proxy.newProxyInstance()来动态创建代理对象
例:1
//实现类:过滤器
public class EncodingFilter implements Filter {
public void destroy() {
}
public void doFilter(ServletRequestreq, ServletResponse res,FilterChain chain) throws IOException, ServletException {
//目标对象
HttpServletRequest request =(HttpServletRequest) req;
HttpServletResponse response =(HttpServletResponse) res;
//创建代理类
MyRequest myRequest = new MyRequest(request);
//动态产生代理对象
HttpServletRequest httpServletRequestProxy =myRequest.getProxy();
//放行请求时,传入代理对象
chain.doFilter(httpServletRequestProxy,response);
}
public void init(FilterConfigfilterConfig) throws ServletException {
}
}
自定义代理类:
class MyRequest{//NO1
private HttpServletRequestrequest; //NO2
public MyRequest(HttpServletRequest request) {//NO3
this.request =request;}
public HttpServletRequest getProxy() {//NO4(核心)
return (HttpServletRequest)Proxy.newProxyInstance(
MyRequest.class.getClassLoader(),
request.getClass().getInterfaces(),
newInvocationHandler(){
public Object invoke(Object proxy, Methodmethod,Object[] args)throws Throwable {
//如果调用是的getParameter方法
if(method.getName().equals("getParameter")){
//取得请求的类型
String methodType = request.getMethod();
//如果是POST
if(methodType.equals("POST")){
//设置request的解码方式为UTF-8
request.setCharacterEncoding("UTF-8");
//取得表单参数
String username = (String) args[0];
//正确编码
String value = request.getParameter(username);
//将值返回
return value;
//如果是GET
}elseif(methodType.equals("GET")){
//取得表单参数
String username = (String) args[0];
//乱码
String value = request.getParameter(username);
//还原
byte[] buf = value.getBytes("ISO8859-1");
//正码
value = new String(buf,"UTF-8");
return value;
}
}else{
return method.invoke(request,args);
}
return null;
}
});
}
}
javaScript增强:
1.window : alert(‘信息’) :消息框 open( ) : 打开一个新窗口
2.Form表单对象:
访问表单的方式:
document.forms[n]
document.表单名字
3.定义函数的三种方式
函数定义方式一
function add(num1,num2){
returnnum1 + num2;
}
document.write("100+200的值为:" + add(100,200) + "
");
函数定义方式二
varadd = new Function("num1","num2","returnnum1+num2;");
document.write("300+400的值为:" + add(300,400) + "
");
函数定义方式三
varmul = function(num1,num2){
returnnum1 * num2;
}
window.alert("100*2的值为:" + mul(100,2));
4.常用API
>>getElementById(),其中id的值,在整个html页面中必须唯一,大小写不敏感,只能在document对象中使用
>>getElmenetsByName(),其中name的属性name的值,返回一个数组
>>getElemnetsByTagName()
>>hasChildNodes();innerHTML取得的是文本值,即是一个字符串,不是元素
>>firstChild取得是是元素,不是字符串
>>nodeName_nodeType_nodeValue:其中nodeValue是可读可写的属性,其它二个属性只可读
>>replaceChild()
>>getAttrbuteNode():返回属性节点,即是一个对象
>>getAttribute():返回属性值,即是一个字符串
>>setAttribute():创建/更新属性的值,更新要确保name一致
>>removeAttribute()
>>innerHTML:不是DOM的标准,即就是可能在某些浏览器上运行不支持,但各主要的浏览器均支持
>>createTextNode():是DOM的标准
>>appendChild()
>>insertBefore()
>>removeChild(),删除子元素时,一定要从直接父元素中删除,不能自删
>>parentNode
>>childNodes:将空白字符也会当作一个子元素,不同的浏览器会有差别
>>showModelessDialog()打开窗口后,[允许]操作该窗口的其它地方,叫非模式
>>showModalDialog()打开窗口后,[不允许]操作该窗口的其它地方,叫模式
6)dom的api同样适用于xml文件,但有些在html文件中的非标准的api在xml文件中不有执行,例如
options和innerHTML。
例2:
<metahttp-equiv="content-type"content="text/html; charset=UTF-8">
head>
<body>
<inputtype="button" value="生效"style="font-size:111px"/>
<inputtype="button" value="恢复"/>
<scripttype="text/javascript">
//获取标签元素的第一个元素
var inputElement =window.document.getElementsByTagName("input")[0];
//给该元素绑定时间监听
inputElement.onclick = function()
{
//方式一:
//设置属性值
inputElement.disabled= "disabled";
inputElement.value="失效";
//方式二:
inputElement.setAttribute("disabled","disabled");
inputElement.setAttribute("value","失效");
};
//获取标签元素的第-个元素
var input2Element =window.document.getElementsByTagName("input")[1];
input2Element.οnclick= function()
{ inputElement.removeAttribute("disabled");
inputElement.setAttribute("value","生效");
};