JSP笔记
Tomcatserver
port:
port就是指的某一个程序网络入口,Tomcat的初始化port为:8080;
port的个数:256*256=65536个;
一般常见协议的缺省port为:
http 80
smtp 25
pop3 110
ftp 23
https 443
port占用查看命令:dos中执行netstat –ano命令
conf目录下的server.xml配置文件里的<Connectorport="8080"protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443"/>
标签用于配置Tomcatserver的port
环境变量配置:
新增一个JAVA_HOME环境变量配置为JDK的根文件夹。由于Tomcatserver是java写的全部也须要JVM来执行
文件共享:<Context />标签中的reloadable=”true”属性,设置为true
表示仅仅要文件被改动。就又一次公布并载入(不建议使用)(context.xml配置文件里)
第一种:
tomcatserver会自己主动扫描webapps这个文件夹。所以我们要共享的web应用文件夹。就能够放在此文件夹下,也就是说Tomcatserverwebapp文件夹中的web应用,外界能够直接訪问。不须要配置
另外一种:
也能够改动conf文件夹下的server.xml配置文件。在Host标签中加入<Contextpath="/hello" docBase="E:/Demo/Test" />标签。hello为E:/Demo/Test映射;也就是说hello是一个虚拟的web应用文件夹地址,而E:/Demo/Test是我们的实际web应用文件夹地址,假设path=””;就表示是缺省的;
第三种:
也能够在\conf\Catalina\localhost\文件夹以下 创建一个随意文件名称的.xml文件,在里面配置<ContextdocBase="E:/Demo/Test" />标签。那么这个web的虚拟文件夹就是这个文件的名称。实际的文件夹地址就是docBase所配置的地址。但实际的文件夹地址不能是\webapps这个文件夹中的,此方法的优点是配置好了不须要重新启动;假设这个.xml文件名称为ROOT.xml就表示缺省默认的;这种关系称为映射关系
Tomcat的文件夹层次结构:
bin文件夹 存放的是启动和关闭Tomcat的脚步文件
conf 存放Tomcatserver的各种配置文件
lib 存放Tomcatserver的支持jar包
logs 存放Tomcatserver的日志文件
temp 存放Tomcat执行时产生的暂时文件
webapps web应用所在文件夹,即能够供外界訪问的web资源的存放文件夹
work Tomcat的工作文件夹
Tomcatserver的应用管理:(适用于Tomcat7.0,Tomcat6.0角色名用的是manager)
改动\conf文件里的tomcat-users.xml位置文件。 在<tomcat-users> </tomcat-users>标签中加上<rolerolename="manager-gui"/><user username="admin"password="admin"
roles="manager-gui"/>
manager-gui是角色 然后在设置账号和password
然后在Tomcat首页打开ManagerApp输入账号password进入应用管理页面。Start 開始。Stop 停止;Reload 重装;Undeploy 卸载
当我们打开www.mybaidu.com时,执行E:\practice\JSP\practice1\Test\Demo\1.html文件
第一步:
设置Demoproject下WEB-INF中的web.xml配置文件;将1.html设置为缺省页。<welcome-file-list>
<welcome-file>1.html</welcome-file>
</welcome-file-list>
第二步:
配置\conf中server.xml文件;在后面加入<Contextpath="" docBase="E:\practice\JSP\practice1\Test\Demo" />;将path设置为””表示缺省
第三步:
设置port为80port(仅仅限定HTTP协议);配置\conf中server.xml文件;<Connectorport="8080" protocol="HTTP/1.1"connectionTimeout="20000" redirectPort="8443" />;
port=”80”
新建一个主机
在conf目录下的server.xml配置文件里。加入一个<Hostname=”主机名” appBase=”webproject位置”>
<Contextpath=”映射路径” docBase=”訪问路径” /> </Host>
Tomcat体系结构
Tomcatserver启动时会启动一个Server服务,Server服务启动时会启动多个连接器(Connector);
浏览器发送一个HTTP协议然后找到Server服务中与HTTP协议相应的连接器(Connector)。然后通过连接器(Connector)找到引擎(Engine)。然后再找主机。主机再找WEB应用。最后找到WEB资源
配置https连接器
webproject(Project)
注意:每次创建一个新的project都需公布到Tomcatserver。改动代码后需又一次部署(Redeploy)一下
war包
jar –vcf 新的文件名称.war
要打包的目录;用此命令将webproject打包为war包
当war包的文件。放入Tomcat的webapps目录中时,会自己主动解压
文件夹层次结构
Myeclipse 2013 project文件夹结构
project文件夹 |
存放java文件的文件夹 |
存放于java文件相应的class文件 |
存放Web应用文件夹 |
存放jar包 |
Web应用的配置文件 |
索引页 |
Tomcatproject物理结构
web.xml 配置文件
web.xml仅仅能放在WEB-INF这个目录中;
设置项目的缺省首页(不能直接将servlet映射为首页,假设首页必需要经过serlvet转发,那么能够在index.jsp文件里转发至serlvet,再通过此servlet转发至首页)
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
在web应用中注冊com.scxh.Test这个serlvet。并取名为Test
<servlet>
<servlet-name>Test</servlet-name>
<servlet-class>com.scxh.Test</servlet-class>
</servlet>
当出现此链接到此project后,出现.com的后缀就交给Test这个类处理。
<servlet-mapping>
<servlet-name>Test</servlet-name>
<url-pattern>*.com</url-pattern>//不能在前面加字符了,如:<url-pattern>aa/*.com</url-pattern>;这样也是不同意的
</servlet-mapping>
这个Test能够映射成多个URL(伪静态)这就是servlet的映射,如:
<servlet-mapping>
<servlet-name>Test</servlet-name>
<url-pattern>/*</url-pattern>//表示‘/‘后面无论跟什么都訪问这个类(aa/*;这样的方式也能够)
</servlet-mapping>
一般Servlet在配置映射时,不要将url-pattern标签配置为”/”;由于’/’通配符表示全部缺省的(找不到的资源)都訪问此Servlet;在Conf/web.xml文件里。系统向全部的Servlet都配置了此’/’。当我们訪问全部静态资源(如直接訪问webproject中的1.html)都会被系统配的’/’
所捕获到,都是訪问的系统自带的Servlet对象;然后再通过这个对象去找你web应用中是否有这个静态资源。有就直接返回,没有就抛404异常(假设我们再设置url-pattern标签配置为’/’就会覆盖系统的设置,当我们再訪问有些静态资源时就訪问不到)
修饰符的优先级(谁最符合就匹配谁。’*’号越在前面,优先级越低)
HTTP协议
请求头(client与server交互)
client连上server后,向server请求某个web资源,称之为client向server发生了一个HTTP请求,一个完整的HTTP请求包含:一个请求行、若干请求头、及实体内容
演示样例:
GET/books/java.html HTTP/1.1
请求行(请求行用于描写叙述client的请求方式、请求的资源名称、及使用的HTTP协议版本号)
请求方式:GET(缺省,特点:显示提交,在URL地址后附带的參数是有限的,其数据容量通常不能超过1K;
提交数据显示在url栏中”?”后面);
POST(一般用于表单的提交,则能够再请求的实体内容中向发武器发送数据,传送的数据量无限制。隐式提交);
Accept:*/*
Accept-Language:en-ue
Connection:Keep-Alive
多个请求头(用于描写叙述client请求的主机
Host:localhost
,及client的一些环境信息等)
Referer:thp://localhost/links.asp
User-Agent:Mozilla/4.0
Accept-Encoding:gzip,deflate
Hello world!
实体内容(通常是表单所提交的内容)
请求头的描写叙述:
Accept:
告诉server,客户机所支持的数据类型,如:text/html,image/*;
就是说支持text的html,支持全部的image;
如为:*/*;表示什么都支持
Accept-Charset:ISO-8859-1
告诉server,客户机所採用的编码格式
Accept-Encoding:gzip,compress
告诉server,客户机所支持的压缩格式
Accept-Language: en-us,zh-cn 告诉server,客户机的语言环境
Host:localhost:8080
客户机要訪问的主机
If-Modified-Since:Tue,11 jul 2000 18:23:51 GMT
告诉server,资源的缓存时间;
我们上一次浏览此网页的时间,发送给server,server用于比对原站点的更新时间,假设原站点在此时间内更新过数据,就又一次返回数据,如没有更新就直接用客户机中缓存内的所缓存的网页
Referer:www.baidu.com 告诉server,他是从哪个资源来訪问server的;
这里表示这是从百度訪问的该资源(能够用于防盗链)
User-Agent:Mozilla/4.0(compatible;MSIE 5.5;Windows NT 5.0)
告诉server,客户机的软件环境(系统,及浏览器版本号等)
Cookie:
客户机通过这个能够向server带数据
Connection:close/Keep-Alive
告诉server,当这个请求完成后,是保持连接还是关闭连接; close是关闭连接, Keep-Alive是保持连接
Date:Tue,11 jul 2000 18:23:51 GMT
告诉server,客户机当前的时间
UA-CPU:X86
告诉server。windows的执行平台,如64位,32位等
If-None-Match:W/* 182-1303717494234
响应头(server与client交互)
一个http对应代表server向client回送的数据它包含:一个状态行、若干消息头、以及实体内容
演示样例:
HTTP/1.1 200 OK
状态行(状态行用于描写叙述server对请求的处理结果)
Server:Microsoft-IIS/5.0
Date:Tue,11 jul 2000 18:23:51 GMT
多个消息头(消息头用于描写叙述server的基本信息,
Content-Length:2291
以及数据的描写叙述,server通过这写数据的描写叙述信息,
Content-Type:text/html
能够通知client怎样处理等一会它(server)回送的数据。)
Cache-control:private
<html>
<body></body>
实体内容(通常是网页代码)
</html>
响应头的描写叙述
HTTP/1.1 200 OK
描写叙述返回数据的协议版本、状态码、原因叙述
状态码用于表示server对请求的处理结果,它是一个三位的十进制数.对应状态码分为5类,如:
100~199 表示成功接收请求,要求client继续提交下一次请求才干完毕整个处理过程
200~299 表示成功接收请求并已完毕整个处理过程, 经常使用200
300~399 为完毕请求,客户需进一步细化请求.比如,请求的资源已经移动到一个新的地址,经常使用302、307、304(307和304表示server的资源位做改动。能够用客户机上的缓存,302表示去訪问Location:
这个头所返回的地址)
400~499 client的请求有误,经常使用404(404表示server没有这个资源;403表示server拒绝你訪问这个资源,请检查是否有权限訪问)
500~599 server端出现错误, 经常使用500,(如同一个response对象同一时候使用字符流和字节流时server就会返回此异常,由于它们是相互排斥的)
Location:www.baidu.com
(请求重定向)这个头配合302状态码使用,用于告诉client应该转到的地址
Server:Apache-Coyote/1.1
server通过这个头,告诉clientserver的类型
Content-Encoding:gzip,compress
server通过这个头,告诉client数据的压缩格式
Content-Length:
server通过这个头,告诉client压缩后数据的大小
Content-Type:text/html; charset=UTF-8 server通过这个头。告诉client数据的类型(如要查询要返回的文件类型的具体请查询conf/web.xml)
Last-Modified:Tue,11 jul 2000 18:23:51 GMT
告诉client,当前资源最后公布时间
Refresh:1;[url=’http://www.baidu.com’]
告诉浏览器隔多长时间刷新一次[假设在分号后面加上一个地址,表示隔多久时间跳转到这个站点]
Content-Disposition:attachment;filename=文件名称
告诉浏览器通过这个头,告诉浏览器下面载方式打开
Transfer-Encoding:chunked
告诉浏览器文件的传送格式
Set-Cookie:ss=Q0=5Lb_nQ;path=/search
Etag:W/”7777-1242234904000”
缓存相关的头
Expires:-1
告诉浏览器,这个资源的缓存时间。-1或0表示不缓存;
假设要缓存,设置为要缓存到的时间
也是控制浏览器要不要缓存数据(浏览器的内核不同。所以响应头比較多)假设要缓存, |
Cache-Control:no-cache
Pragma:no-cache
Connection:close/Keep-Alive
告诉浏览器请求完成后是否断开连接
Date:Tue,11 jul 2000 18:23:51 GMT
告诉浏览器。server当前的时间
断点下载
请求头
Range头指示server仅仅传输一部分Web资源.这个头能够用来实现断点续传功能,Range字段能够通过三种格式设置要传输的字节范围:
Range: bytes=1000-2000
表示请求server发送这个资源中1000字节到2000字节之间的内容
Range: bytes=1000-
表示请求server发送这个资源中1000字节以后的全部内容
Range: bytes=1000
表示请求server发送这个资源中最后1000字节的内容
响应头
Accept-Ranges: 这个头告诉浏览器这个资源是否支持Ranges(断点下载) 支持返回:bytes 不支持返回:none
Content-Ranges:1000-3000/5000指定了返回的Web资源的字节范围。
这里表示返回的数据是1000-3000字节之间的内容。这个资源总共同拥有5000个字节
JSP中的路径(java中的路径相对于src文件夹)
訪问资源:
訪问时,假设从client訪问server的资源: /project名/要訪问的资源路径;
一般在前端超链接时,后台从定向时使用
訪问时,假设直接在server端直接訪问资源: /要訪问的资源路径; 一般在server端转发时使用;
server訪问文件:
在web开发中假设要获取到用到一个文件的位置。仅仅实用绝对路径,比方创建文件流、文件对象、等。
通过 类载入器的 getClassLoader().getResource("相对路径").getPath();返回此文件的绝对路径;或者用getClassLoader().getResourceAsStream("相对路径”) 直接将文件载入到内存中。并返回一个字节输入流对象。
这里的相对路径,相对的是WEB-INF/classes
文件夹。
假设一个要在一个jar包中訪问这个jar包中的文件,那么仅仅能将此文件载入到内存中,通过字节输入流对象来获取此文件的数据。由于文件在jar包中,不能获取到绝对路径。
Servlet开发
Servlet生命周期:
l Servlet一般仅仅会在第一次訪问时创建,然后一直停留在内存中,直到server停止才会被释放(或者这个web应用被删除时)。每产生一次请求都会创建一个request和response对象但它们的生命周期非常短,当请求和响应完毕后就销毁。但Servlet对象仅仅会创建一个
l server启动时创建servlet,需加上<load-on-startup>标签(一般不会这样用的。载入框架文件时就是这样用的)
l servlet中尽量使用局部变量。假设要用static全局变量。并要对static变量进行改动的话。就要考虑线程安全问题了,解决线程安全需用线程同步代码块,同步代码块中的代码不能太多,不然会影响性能
<servlet>
<servlet-name>ServletDemo1</servlet-name>
<servlet-class>cn.webapp.ServletDemo1</servlet-class>
<!--增加了下面的标记,此servlet就会在server启动时创建-->
<load-on-startup>1</load-on-startup>//假设在web.xml配置文件里在当前Servlet注冊标签中加上了当前标签。就会在server启动时创建当前的Servlet对象,并调用Servlet对象的init方法;中间这个1表示假设有多个Servlet对象要在server启动时载入时,这个值将控制Servlet的载入顺序;越小优先级越高
</servlet>
l Servlet创建的时候会调用init方法结束时会调destroy方法
新建一个Servlet类它必须继承HttpServlet类或GenericServlet类
继承GenericServlet类需重写service(ServletRequestarg0, ServletResponse arg1)这种方法
继承HttpServlet类需重写doGet或doPost方法是(改动模板路径:MyEclipse\plugins\com.genuitec.eclipse.wizards_11.0.0.me201303311935.jar中templates\Servlet.java文件里改动doGet和doPost)
地址的使用方法(最好都在前面加上/)
当给浏览器用时,是相对于当前的主机(要在最前面加上项目名称)
当给server用时,
是相对于当前的web应用
线程安全
//线程安全
/*SingleThreadModel接口是一个空的接口;假设实现了此接口就表示此对象时一个线程安全的对象;
当一个用户訪问当前的Servlet后,下一个用户訪问当前的Servlet时发现Servlet实用户,
就会创建出一个新的Servlet对象,这也是在一个server中创建两个Servlet的方法
(此方法不推荐使用;太消耗资源,由于Servlet创建后仅仅有关闭server才会销毁)
*/
public
class ThreadSafe extends HttpServlet
implements SingleThreadModel{
public
int i = 0;
public
void doGet(HttpServletRequest request, HttpServletResponseresponse)
throws ServletException, IOException {
response.setContentType("text/html; charser=utf-8");
response.setCharacterEncoding("utf-8");
request.setCharacterEncoding("utf-8");
i++;
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
// TODO Auto-generatedcatch block
e.printStackTrace();
}
System.out.println(i);
}
public
void doPost(HttpServletRequest request, HttpServletResponseresponse)
throws ServletException, IOException {
doGet(request, response);
}
}
Servlet中的六大对象
ServletConfig(servlet配置对象及servlet对象域)
用于封装Servlet的配置信息
能够在project下的web.xml配置文件里,注冊Servlet标签中加上:<init-param>标签;
<servlet>
<servlet-name>ConfigDemo</servlet-name>
<servlet-class>cn.java.ConfigDemo</servlet-class>
<init-param><!—此标签的作用域仅仅能在cn.java.ConfigDemo这个servlet中-->
<param-name>name1</param-name> <!--
这是相当于通过"name1"能够映射到"张三" -->
<param-value>张三</param-value>
</init-param>
<init-param>
<param-name>name2</param-name> <!--
这是相当于通过"name2"能够映射到"李四" -->
<param-value>李四</param-value>
</init-param>
</servlet>
获取方法
import java.io.IOException;
import
java.util.Enumeration;
import
javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
//获取ServletConfig对象中的值(此值须要在web.xml中注冊此servlet标签中加)
public
class Test extends HttpServlet {
//privateServletConfig
config;//方式一所需定义的全局变量
public
void doGet(HttpServletRequest request, HttpServletResponseresponse)
throws ServletException, IOException {
response.setContentType("text/html; charser=utf-8");
response.setCharacterEncoding("utf-8");
request.setCharacterEncoding("utf-8");
//第一种方式获取值
//输出config对象中name1和name2中所映射的值
// System.out.println(config.getInitParameter("name1"));
// System.out.println(config.getInitParameter("name2"));
/*
//另外一种方式获取(用另外一种方式时不能重写init(ServletConfig
config)方法)
//先通过this.getServletConfig()获取到config对象,再通过getInitParameter("name1")获取到name1和name2中所映射的值
System.out.println(this.getServletConfig().getInitParameter("name1"));
System.out.println(this.getServletConfig().getInitParameter("name2"));
*/
/*
//第三种方式,使用迭代(获取所有的config值)
ServletConfigconfig1 = this.getServletConfig();//获取config对象
Enumeration en =config1.getInitParameterNames();//获取config1对象中全部的name。并返回一个列表
while(en.hasMoreElements()){//用这个列表的迭代器进行遍历
String key = (String) en.nextElement();
String value =this.getServletConfig().getInitParameter(key);
System.out.println(value);
}
*/
//第四种直接获取
System.out.println(this.getInitParameter("name1"));
}
public
void doPost(HttpServletRequest request, HttpServletResponseresponse)
throws ServletException, IOException {
doGet(request, response);
}
/*
//第一种方式:重写HttpServet父类GenericServlet中的init(ServletConfig
config)方法
//此方法会在server启动时调用,并将config对象传入进来
@Override
public void init(ServletConfig
config)throws ServletException {//获取config对象
// TODO Auto-generated method stub
this.config=config;
}
*/
}
servletContext(代表一个web应用(域))此对象的生命周期为:当web应用公布时创建,卸载web应用时销毁。假设在servlet中要对servletContext域对象中的key值进行更改的话,需考虑线程安全问题
ServletContext.removeAttribute(Stringkey); 删除servletContext对象域中键值的映射关系,不然此键值会一直存活到server关闭。会浪费资源
WEB容器在启动时,它会为每一个WEB应用程序都创建一个相应的ServletContext对象。它代表”当前web应用”。
SerletConfig对象中维护了ServletContext对象的引用,开发者在编写servlet时,能够通过servletConfig.getServletContext方法获得ServletContext对象
因为一个WEB应用中的全部Servlet共享同一个ServletContext对象,所以多个Servlet能够通过servletContext对象实现数据共享(ServletContext对象通常也被称之为context域(域就是一个范围,Context域是一个应用范围的域)对象)
n 获取web应用的參数(配置数据库)
<context-param><!—这是配置整个web应用初始化參数的标签;一个web应用中能够有多个初始化參数的标签-->
<param-name>name</param-name>
<param-value>haoren</param-value>
</context-param>
Stringsex = this.getServletContext().getInitParameter("name");//取出context-param标签中的值。也能够用this.getServletContext().getInitParameterNames();方法获取所有的key;然后再迭代取出value(在ServletConfig对象中有讲过取出value的方式)
System.out.print(sex);
n 数据共享
ServeltDemo1:
this.getServletContext().setAttribute("address",
"chengdu");//存入servletContext对象中
ServeltDemo2:
String address =(String) this.getServletContext().getAttribute("address");//取出servletContext对象中的值
n 利用ServletContext对象读取源文件
读取properties文件
//第一种方式
ServletContext servletContext = this.getServletContext();//获取到servletContext对象
InputStream inStream = servletContext.getResourceAsStream("/config.properties");//把/config.properties资源作为一个输入流返回(这里"/"
路径指的是webproject的根文件夹)
//假设此处的路径写的是相对路径,相对的是Tomcat的bin文件夹,由于Tomcat的是在此文件夹中启动的
Properties properties = new Properties();//创建一个properties对象(properties对象中底层是用map集合维护的)
properties.load(inStream);//properties对象中提供了load方法,从流中获取properties文件的值
String name = properties.getProperty("name");//获取properties对象中key是name的value
String url = properties.getProperty("url");//获取properties对象中key是url的value
System.out.println(name+" "+url);
//另外一种方式(通过硬盘上的绝对路径訪问config.properties文件),此方法能够获取到文件的名称
ServletContext servletContext = this.getServletContext();//获取到servletContext对象
String path = servletContext.getRealPath("/config.properties");//会返回这个路径在硬盘上的绝对路径;这里会返回
//E:\practice\JSP\practice2\Tomcat7.0\webapps\servletContext\config.properties这个路径
String fileName = path.substring(path.lastIndexOf("\\")+1);//这样能够获取到文件的名称;在下载时须要用到
System.out.println("文件名为:"+fileName);//打印这个文件名
FileInputStream in = new FileInputStream(path);//有了绝对路径后就能够创建
文件字节流了
Properties properties = new Properties();//创建一个properties对象(properties对象中底层是用map集合维护的)
properties.load(in);//properties对象中提供了load方法,从流中获取properties文件的值
in.close();//关闭流
String name = properties.getProperty("name");//获取properties对象中key是name的value
String url = properties.getProperty("url");//获取properties对象中key是url的value
System.out.println(name+" "+url);
//第三种方式(通过一个普通类载入config.properties文件的方式)
//假设在一个普通类中用ServletContext对象来获取;就是前台应用耦合了后台应用;所以我们应该避免这个使用方法
//在一个普通类中读取config.properties文件;
public
class Test4 {
public
static void print()
throws IOException {
/*
//此方法获取的输入流(in)不能实时更新(由于是类载入器载入器仅仅载入一次到内存中)
InputStream in = Test4.class.getClassLoader().getResourceAsStream("../../config.properties");
//这里相对的是Tomcat中webapps\servletContext\WEB-INF\classes这个目录(假设用类载入器载入文件,这个文件不能太大)
*/
//此方法获取的输入流(in)能够实现实时更新(此方法是得到绝对路径后在创建输出流)
URL url1 = Test4.class.getClassLoader().getResource("../../config.properties");//获取到这个文件的URL
String path = url1.getPath();//通过url获取到这个config.properties文件的绝对路径
InputStream in = new FileInputStream(path);//通过这个绝对路径来创建输入流
Properties properties = new Properties();//创建一个Properties对象
properties.load(in);//properties对象中提供了load方法,从流中获取properties文件的值
in.close();//关闭流
String name = properties.getProperty("name");//获取properties对象中key是name的value
String url = properties.getProperty("url");//获取properties对象中key是url的value
System.out.println("Test4:"+name+" "+url);
}
public
static void main(String[] args) {
try {
Test4.print();
} catch (IOException e) {
// TODO Auto-generatedcatch block
e.printStackTrace();
}
}
}
request(请求头对象)用于获取client传入的数据
获取表单中的值:
//获取表单提交的数据
public
class Test extends HttpServlet {
public
void doGet(HttpServletRequest request, HttpServletResponseresponse)//request:请求;response:响应
throws ServletException, IOException{
response.setContentType("text/html; charser=utf-8");//设置浏览器的编码格式
response.setCharacterEncoding("utf-8");//设置响应回传数据的编码格式
request.setCharacterEncoding("utf-8");//设置请求数据的编码格式
//获取表单的參数,除多选框外,其他表单的取值方式都一样
String username = request.getParameter("username");
String password = request.getParameter("password");
String sex = request.getParameter("sex");
String country = request.getParameter("country");
String [] enjoy = request.getParameterValues("enjoy");//多选框用getParameterValues(String arg0)这种方法,并会返回一个String
[]的数组
String remark = request.getParameter("remark");
PrintWriter out = response.getWriter();//获取到响应对象的输出流
out.print("你的用户名为:"+username+";你的密码为:"+password+";你的性别为:"+sex+";你的国籍为:"+country+";备注为:"+remark);
out.println(";你的爱好为:");
for(int i=0; i<enjoy.length; i++){
out.print(enjoy[i]+" ");
}
}
public
void doPost(HttpServletRequest request, HttpServletResponseresponse)
throws ServletException, IOException {
doGet(request, response);
}
}
//解决提交表单方式为get时出现的乱码问题
public
class getMessyCodeextends HttpServlet {
public
void doGet(HttpServletRequest request, HttpServletResponseresponse)
throws ServletException, IOException{
response.setContentType("text/html; charser=utf-8");
response.setCharacterEncoding("utf-8");
request.setCharacterEncoding("utf-8");
String text = request.getParameter("text");//获取提交表单的參数
String temp = new String(text.getBytes("ISO8859-1"),"UTF-8");//将ISO8859-1的參数,转换为字节数组后,再传入一个新的字符串,并又一次指定编码格式
response.getWriter().print("你提交的为:"+temp);//将数据传送给响应对象的输出流
}
public
void doPost(HttpServletRequest request, HttpServletResponseresponse)
throws ServletException, IOException{
doGet(request, response);
}
}
通过request对象实现转发:
/*转发
请求重定向与转发的差别
请求重定向:
是向client发回一个网址,让浏览器又一次去訪问(向server发出了两次请求;而且网址会变。
重定向完毕后。再刷新,刷新的是重定向后的网页)
转发:
是通过server直接转发到某一个网址(向server仅仅发出一次请求。网址不会变。
当转发完毕后,再刷新,因为网址没变,刷新的还是没转发前的资源,并会再次转发(提交数据时要注意));
总结:所以一般不用请求重定向,一般用转发完毕,由于请求重定向会向server发出两次请求。转发时要避免因刷新而导致数据反复提交的问题;
*/
//通过request对象实现转发,
public
class Test1 extends HttpServlet {
public
void doGet(HttpServletRequest request, HttpServletResponseresponse)
throws ServletException,IOException {
response.setContentType("text/html; charser=utf-8");
response.setCharacterEncoding("utf-8");
request.setCharacterEncoding("utf-8");
//获取表单中的值
String text = request.getParameter("text");
//向request对象域中存入值
request.setAttribute("text", text);
//假设用转发时不能关闭response对象的流,不然调用forward方法时会抛出异常(但能够设置response响应对象的头,而且转发后还是有效的)
//
PrintWriter out = response.getWriter();//Error
//out.print(“asdf”); //Error
//out.close();//Error
//转发到Test12.jsp,并将request, response对象提交过去(最好在跳转完毕之后return)
request.getRequestDispatcher("/Test12.jsp").forward(request, response);
return;
}
public
void doPost(HttpServletRequest request, HttpServletResponseresponse)
throws ServletException, IOException {
doGet(request, response);
}
}
通过request对象获取请求头的请求资源和全部请求头的值
public
class Test2 extends HttpServlet {
public
void doGet(HttpServletRequest request, HttpServletResponseresponse)
throws ServletException, IOException {
response.setContentType("text/html; charser=utf-8");
response.setCharacterEncoding("utf-8");
request.setCharacterEncoding("utf-8");
//获取请求方式(结果为:GET或POST等)
System.out.println(request.getMethod());
//当后缀名为/com/*时,都会訪问到此servlet(我输入的是:http://localhost:8080/request/com/1.html
)
//获取请求资源的URI(结果为:/request/com/1.html )
System.out.println(request.getRequestURI());
//获取请求资源的URL(结果为:http://localhost:8080/request/com/1.html
)
System.out.println(request.getRequestURL());
//URL代表的是互联网上面的资源地址
//URI代表的是随意一个资源的地址(能够理解为某个server上的地址)
//通过request请求头对象,获取一个头的值(这里获取的是client所支持的压缩格式)
System.out.println(request.getHeader("Accept-Encoding"));
System.out.println("获取全部的头");
//通过request请求对象得到全部头的名称
Enumeration<String> headerNames =request.getHeaderNames();
while(headerNames.hasMoreElements()){//推断此枚举是否还有下一个值
String headerName = headerNames.nextElement();//返回它的下一个值
System.out.println(headerName+":"+request.getHeader(headerName));
}
//request.getHeaders("");//当一个头设置了两个值,就用此方法获取
}
public
void doPost(HttpServletRequest request, HttpServletResponseresponse)
throws ServletException, IOException {
doGet(request, response);
}
}
通过request对象防止盗链
//防止盗链
public
class Test5 extends HttpServlet {
public
void doGet(HttpServletRequest request, HttpServletResponseresponse)
throws ServletException, IOException {
response.setContentType("text/html; charser=utf-8");
response.setCharacterEncoding("utf-8");
request.setCharacterEncoding("utf-8");
//防止反复提交,此处是验证提交的次数
System.out.println("提交的次数");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
// TODO Auto-generatedcatch block
e.printStackTrace();
}
//获取来訪者的url
String path = request.getHeader("referer");
//获取本server的主机名
String server = "http://"+request.getServerName();
//推断path的前端是否是server(也就是推断主机名是否同样)
if(path != null && path.startsWith(server)){
//将path和server转入request对象域
request.setAttribute("path", path);
request.setAttribute("server", server);
//转发至Test52.jsp
request.getRequestDispatcher("/Test52.jsp").forward(request, response);
}else{
//不转发
response.getWriter().print("不是本网页的连接");
}
}
public
void doPost(HttpServletRequest request, HttpServletResponseresponse)
throws ServletException, IOException {
doGet(request, response);
}
}
比較经常使用的方法:
request.getRequestURL();
获取完整的URL
request.getRequestURI();
仅仅获取请求资源的地址
request.getQueryString();
获取在地址栏中’?
’号后面的全部字符串(所以它仅仅针对get的提交方式有效)
request.getRemoteAddr();
获取客户机的IP地址
request.getRemoteHost();
获取客户机的完整主机名(可是客户机的IP要在DNS上注冊后才干显示主机名,不然还是显示IP地址)
request.getRemotePort();
获取浏览器所使用的port号(当浏览器关闭后再又一次打开时port号会随机变化)
request.getLocalAddr();
获取WEBserver的IP地址
request.getLocalName();
获取WEB服务的主机名
request.getServerName();
获取WEBserver的主机名
request.getServerPort();
货物WEBserver的port
request.getMethod();
获取浏览器的请求方式
request.getHeader(headerName);
依据对应的响应头名字来获取响应的值
request.getHeaders(headerName);
假设同一个响应头有多个值,就用此方法获取,并返回一个Enumeration
request.getContextPath();
得到当前的project名:如:/dy19
request.getServletContext().getContextPath();
得到当前的project名:如:/dy19
request.getServletPath();
得到web配置文件<url-pattern>/servlet/UploadServlet</url-pattern>此标签中的值
URL与URI的差别
www.sina.com/news/1.html 这就是一个URL
news/1.html
这就是一个URI
URL代表的是互联网上面的资源地址
URI代表的是随意一个资源的地址(能够理解为某个server上的地址)
通过request对象获取client所提交的数据(通过get或post等,提交的数据)(在上面request对象的第一个知识点中已做过笔记)
//通过request对象获取client所提交的数据(通过get或post等,提交的数据)(先要检查用户提交的数据中是否有str这个key)
public
class Test3 extends HttpServlet {
public
void doGet(HttpServletRequest request, HttpServletResponseresponse)
throws ServletException, IOException {
response.setContentType("text/html; charser=utf-8");
response.setCharacterEncoding("utf-8");
request.setCharacterEncoding("utf-8");
System.out.println("-------------------直接通过key获取提交的value--------------------------------");
/*
//client用get或post方式提交的数据,都能够用下面方式获取
//如我们在訪问时在后面加上数据如:http://localhost:8080/request/servlet/Test3?
name=abc&password=123
//或者用表单提交都能获取到
String value = request.getParameter("name");//获取指定名称的值
if(value!=null && !value.trim().equals("")){//推断是否是没有输入或输入的所有为空格(检查提交的数据合格再使用)
System.out.println("name="+value);//value.trim()方法会删除字符串两边的空格,假设字符串所有都是空格,那么返回的字符串相等于new
Stirng("");
}
System.out.println("-------------------先获取到全部请求数据key。再通过迭代取出请求的值--------------------------------");
//获取全部的数据的key,再通过key遍历全部的值
Enumeration<String> names =request.getParameterNames();
while(names.hasMoreElements()){//推断此枚举是否还有下一个key
String name = names.nextElement();//获取下一个key
String value1 = request.getParameter(name);//通过key遍历全部的value
System.out.println(name+"="+value1);
}
System.out.println("--------------------获取同一个key有两个value的方法-------------------------------");
//假设提交的数据key有多个同样如:http://localhost:8080/request/servlet/Test3?str=abc&str=123
//这里的str有两个值,就用request.getParameterValues("str");来获取
String[] values = request.getParameterValues("str");//返回str中的多个值
for(int i=0; values!=null && i<values.length;
i++){//遍历这些值(先要检查用户提交的数据中是否有str这个key)
System.out.println(values[i]);
}
*/
System.out.println("----------------------将请求的数据以一个map集合返回-----------------------------");
//将提交的数据返回到一个Map集合中提交方式为:http://localhost:8080/request/servlet/Test3?
name=abc&name=111&password=123
Map<String,String[]> map =request.getParameterMap();
//遍历输出这个map集合
//获取map中全部的key,以Set集合的方式返回
Set<String> set = map.keySet();
//获取set集合的迭代器
Iterator<String> it = set.iterator();
//遍历这个迭代器
while(it.hasNext()){//推断迭代器是否还有下一个key值
String key = it.next();//获取下一个key
//输出key
System.out.print(key);
//通过key返回所相应的value;返回一个数组,由于同一个key可能有两个以上value
String [] values = map.get(key);
//遍历输入这个values
for(String s:values){
System.out.print(":"+s);
}
//换行
System.out.println();
}
}
public
void doPost(HttpServletRequest request, HttpServletResponseresponse)
throws ServletException, IOException {
doGet(request, response);
}
}
通过request.getRequestDispatcher("").include(request,response);方法实现多个页面动态包括(也就是将多个页面的数据同一时候传给前台)
public
class Test6 extends HttpServlet {
public
void doGet(HttpServletRequest request, HttpServletResponseresponse)
throws ServletException, IOException {
response.setContentType("text/html; charser=utf-8");
response.setCharacterEncoding("utf-8");
request.setCharacterEncoding("utf-8");
//在response对象的返回数据的尾部加上了/Test61.jsp中的内容
request.getRequestDispatcher("/Test61.jsp").include(request, response);
//在response对象的返回数据的尾部加入了我是servlet<br/>
response.getWriter().print("我是servlet<br/>");
//在response对象的返回数据的尾部加入了/Test62.jsp中的内容
request.getRequestDispatcher("/Test62.jsp").include(request, response);
/*
所以输出的结果为:
This is my Test61.JSP page.
我是servlet
This is my Test62.JSP page.
*/
}
public
void doPost(HttpServletRequest request, HttpServletResponseresponse)
throws ServletException, IOException {
doGet(request, response);
}
}
response(响应头对象)用于向client返回数据
依据response对象向浏览器发送数据
//通过response对象的字节流返回数据
response.setHeader("Content-type","text/thml;charset=UTF-8");
//设置头的值,这是是设置Content-type告诉浏览器回送的数据。为html格式,它编码格式为UTF-8
response.setContentType("text/html;charser=utf-8");//设置ContentType(返回数据类型)响应头的数据类型为html格式。编码格式为utf-8
//也相当于是告诉浏览器以什么样的编码格式打开这段数据
response.setCharacterEncoding("utf-8");//设置response对象的编码格式(response对象里面也有对数据进行转码,所以也须要编码格式)
request.setCharacterEncoding("utf-8");//设置request对象的编码格式(request对象里面也有对数据进行转码。所以也须要编码格式)
PrintWriter out = response.getWriter();//获取到字符输出流
out.write("中国");//向字符流中写入数据
out.flush();//移除流中的缓存
out.close();//关闭流
//通过response对象的字符流返回数据
public
class Test2 extends HttpServlet {
public
void doGet(HttpServletRequest request, HttpServletResponseresponse)
throws ServletException, IOException {
response.setContentType("text/html; charser=utf-8");//设置ContentType(返回数据类型)响应头的数据类型为html格式,编码格式为utf-8
//也相当于是告诉浏览器以什么样的编码格式打开这段数据
response.setCharacterEncoding("utf-8");//设置response对象的编码格式(response对象里面也有对数据进行转码,所以也须要编码格式)
request.setCharacterEncoding("utf-8");//设置request对象的编码格式(request对象里面也有对数据进行转码,所以也须要编码格式)
String data = "中国";
PrintWriter out = response.getWriter();//获取到字符输出流
out.write(data);//向字符流中写入数据
out.flush();//移除流中的缓存
out.close();//关闭流
}
public
void doPost(HttpServletRequest request, HttpServletResponseresponse)
throws ServletException, IOException {
doGet(request, response);
}
}
提示:
字符流和字节流是相互排斥的,假设同一个response对象,用了字符流就不能用字节流了,最好都用字节流(从response或request对象中获取的流不须要关闭。由于当servlet对象在销毁时会自己主动检測这些流是否调用了close方法,然后关闭,可是假设是我们自己new的流必须关闭它,不然会浪费资源)
从浏览器中下载web应用中的资源
String path = this.getServletContext().getRealPath("/image/美女.zip");//获取这个文件的绝对路径
String fileName = path.substring(path.lastIndexOf(File.separator)+1);//获取到这个文件的名字
fileName = URLEncoder.encode(fileName,
"UTF-8");//假设为中文名字须要进行转码
response.setHeader("Content-Disposition",
"attachment;filename="+fileName);//设置响应头Content-Disposition的值为attachment表示下面载方式打开,后面跟文件名称字
FileInputStream in = new FileInputStream(path);//创建一个字节输入流,并指向这个图片
byte[] buffer =
new byte[1024];//创建一个缓冲字节数组
int len=0;//定义一个缓冲大小的变量
OutputStream out = response.getOutputStream();//获取response的字节输出流
while((len=in.read(buffer)) > 0){//推断从输入流中获取的字节数,假设小于或等于0表示没有读取到字节
out.write(buffer,0,len);//将buffer中的缓存写入到 response的字节输出流中
}
//关闭流
in.close();
out.flush();
out.close();
请求重定向
/*请求重定向
请求重定向与转发的差别
请求重定向:
是向client发回一个网址,让浏览器又一次去訪问(向server发出了两次请求,而且网址会变)
转发:
是通过server直接转发到某一个网址(向server仅仅发出一次请求,网址不会变)
总结:所以一般不用请求重定向,一般用转发完毕,由于请求重定向会向server发出两次请求。
*/
public
class Test7 extends HttpServlet {
public
void doGet(HttpServletRequest request, HttpServletResponseresponse)
throws ServletException, IOException {
response.setContentType("text/html; charser=utf-8");
response.setCharacterEncoding("utf-8");
request.setCharacterEncoding("utf-8");
/*
response.setStatus(302);//设置response对象中状态行的状态码为302。表示请求重定向
response.setHeader("Location","/response/welcome.jsp");//Location此头为请求重定向头,重定向到welcomem.jsp文件
*/
//此方法也能够直接重定向到welcomem.jsp文件
response.sendRedirect("/response/welcome.jsp");
}
public
void doPost(HttpServletRequest request, HttpServletResponseresponse)
throws ServletException, IOException {
doGet(request, response);
}
}
设置浏览器定时重复刷新页面或定时重定向
public class
Test5 extends HttpServlet {
public
void doGet(HttpServletRequest request, HttpServletResponseresponse)
throws ServletException, IOException {
response.setContentType("text/html; charser=utf-8");
response.setCharacterEncoding("utf-8");
request.setCharacterEncoding("utf-8");
/*
//不停的刷新
response.setHeader("refresh","3");//表示每隔3秒钟就訪问一次此servlet
//产生一个随机数并通过字符流返回
PrintWriter out = response.getWriter();
out.write(new Random().nextInt()+"");
*/
/*
//3秒钟以后能够重定向到其它的网页
response.setHeader("refresh", "3;url=‘http://www.baidu.com‘");//设置refresh(刷新头,设置其值,表示3秒钟后跳转至百度)
PrintWriter out = response.getWriter();
out.write("本网页将在3秒钟后重定向到百度;假设未跳转,情点击<a
href=‘http://www.baidu.com‘>百度</a>");
*/
//最有用的方式。用jsp文件控制页面跳转
//通过meta表示设置jsp文件response对象的响应头,隔3秒跳转至/response/welcome.jsp网页
String str = "<meta http-equiv=‘refresh‘ content=‘3;url=/response/welcome.jsp‘>" +
"本网页将在3秒钟后跳转;假设未跳转,情点击<a
href=‘/response/welcome.jsp‘>超链接</a>";
//将数据存入ServletContext应用域对象中
this.getServletContext().setAttribute("str", str);
//跳转至MyJsp网页;但先要在webproject下建一个MyJsp.jsp文件,并在body标签中用java代码获取ServletContext应用域对象str中的value如:
//<%//获取servletContext应用域对象中的值String
str =(String)application.getAttribute("str");
//通过流向当前的位置输出数据 out.write(str);%>
this.getServletContext().getRequestDispatcher("/MyJsp.jsp").forward(request, response);
}
public
void doPost(HttpServletRequest request, HttpServletResponseresponse)
throws ServletException, IOException {
doGet(request, response);
}
}
请求重定向时,通过url方式将数据提交给Test41.jsp
Servlet中的代码
//请求重定向时,通过url方式将数据提交给Test41.jsp
public
class Test4 extends HttpServlet {
public
void doGet(HttpServletRequest request, HttpServletResponseresponse)
throws ServletException, IOException {
response.setContentType("text/html; charser=utf-8");
response.setCharacterEncoding("utf-8");
request.setCharacterEncoding("utf-8");
/*
//第一种方式:
//数据
String str = new String("中国");
str = new String(str.getBytes("utf-8"),"ISO-8859-1");
//请求重定向,并将数据传过去
response.sendRedirect("/request/Test41.jsp?str="+str);
*/
/*
//另外一种方式:
String value = new String("四川");//数据
String value1 = URLEncoder.encode(value,
"UTF-8");//加码
response.sendRedirect("/request/Test41.jsp?value="+value1); //假设直接在url后面跟中文数据,须要对中文进行转码
*/
//加码与解码
String temp = new String("中文数据");
//加码(就是将temp这个UTF-8编码的字符串转换为ISO-8859-1;当某些时候进行中文传输数据时,须要进行加码与解码)
String temp1 = URLEncoder.encode(temp,
"UTF-8");
//也就是说加码过后,将temp的值的存储格式变为了默认的ISO-8859-1编码格式
System.out.println(temp1);//打印为:%E4%B8%AD%E6%96%87%E6%95%B0%E6%8D%AE
//解码(将加码过的字符串(ISO-8859-1),转换为相应的编码格式)
String temp2 = URLDecoder.decode(temp1,
"UTF-8");
System.out.println(temp2);//打印为:中文数据
}
public
void doPost(HttpServletRequest request, HttpServletResponseresponse)
throws ServletException, IOException {
doGet(request, response);
}
}
Jsp中的代码
<body>
<!--第一种方式 :< %=new String(request.getParameter("str").getBytes("ISO-8859-1"),"UTF-8") %> -->
<!-- 另外一种方式的解码与第一种一样(仅仅限于servlet传值给jsp时)
--><%=new String(request.getParameter("value").getBytes("ISO-8859-1"),
"UTF-8") %>
</body>
通过改动server配置文件。来设置server默认的编码格式(但此方法一般不用)
在apache-tomcat-7.0.6\conf\servlet.xml文件里的<Connector port="8080"protocol="HTTP/1.1" connectionTimeout="20000"redirectPort="8443"
/>标签末尾加入一个属性: URIEncoding=”UTF-8”;
或者在末尾加入一个useBodyEncodingForURI=”true”。这个属性是设置URL地址的默认编码格式
控制浏览器对数据的缓存
public
class Test6 extends HttpServlet {
public
void doGet(HttpServletRequest request, HttpServletResponseresponse)
throws ServletException, IOException {
response.setContentType("text/html; charser=utf-8");
response.setCharacterEncoding("utf-8");
request.setCharacterEncoding("utf-8");
/*
//设置浏览器不要缓存(为了使很多其它的浏览器兼容,所以设置的头比較多)
response.setHeader("expires","-1");
response.setHeader("cache-Control","no-cache");
response.setHeader("pragma","no-cache");
*/
//设置浏览器缓存数据
//设置数据缓存的时间为当前时间加上1小时
response.setDateHeader("expires", System.currentTimeMillis()+(1000*3600));//1000毫秒*3600
==一小时
String data = new String("aaaaaaaaa");//数据
response.getWriter().write(data);//data的数据会被保存1小时
}
public
void doPost(HttpServletRequest request, HttpServletResponseresponse)
throws ServletException, IOException {
doGet(request, response);
}
}
将数据压缩后才传给client
public
class Test8 extends HttpServlet{
public
void doGet(HttpServletRequest request, HttpServletResponseresponse)//request为请求;response为响应头
throws ServletException, IOException {
response.setContentType("text/html; charser=utf-8");
String str = new String("要压缩的数据");
//创建一个字节缓存输出流
ByteArrayOutputStream bout = newByteArrayOutputStream();
//把输出流作为參数,创建一个gzip压缩对象,将压缩后的数据放到缓存流中
GZIPOutputStream gout = new GZIPOutputStream(bout);
//将要压缩数据的字节数组传入压缩对象的write方法中进行压缩
gout.write(str.getBytes());
//关闭压缩
gout.close();
//从缓存输出流中获取压缩后的byte数组数据
byte[] gzip = bout.toByteArray();
//关闭缓存输出流
bout.close();
//设置Content-Encoding(server返回数据的压缩格式)响应头的返回值
response.setHeader("Content-Encoding",
"gzip");
//设置Content-Length(server返回数据压缩后的长度)响应头的返回值
response.setHeader("Content-Length", String.valueOf(gzip.length));
//设置response的传出数据
response.getOutputStream().write(gzip);
//传送过去后浏览器会自己主动解压显示
}
public
void doPost(HttpServletRequest request, HttpServletResponseresponse)
throws ServletException, IOException {
doGet(request, response);
}
}
cookie(缓存数据)
把数据缓存在client的硬盘上,一个Cookie仅仅能表示一种信息,它至少含有一个键值
一个WEB网站能够给一个WEB浏览器发送多个Cookie,一个浏览器也能够存储多个WEB网站提供的Cookie
能够调用Cookie对象的setMaxAge()方法设置此Cookie的存储时间即生命周期(单位秒),设置为0表示删除该Cookie;假设不设置保存时间。此Cookie仅仅会保存在用户的内存中,当浏览器关闭释放内存,此Cookie会被删除,删除cookie时,path必须一致,否则不会被删除
浏览器一般仅仅同意存放300个Cookie,每一个网站最多存放20个Cookie,每一个Cookie的限制大小为4KB
输出用户訪问此servlet的次数
public
class Test1 extends HttpServlet {
public
void doGet(HttpServletRequest request, HttpServletResponseresponse)
throws ServletException, IOException {
response.setContentType("text/html; charser=utf-8");
response.setCharacterEncoding("utf-8");
request.setCharacterEncoding("utf-8");
//获得本web应用(/cookie)存储在client的cookie
Cookie [] cookies = request.getCookies();
//标记訪问的次数
int time = 1;
//推断本web应用是否在client存有cookie
if(cookies!=null && cookies.length>0){
//遍历这些cookie
for(int i=0; i<cookies.length; i++){
//推断是否是numberOfTimes(次数)这个key
if("numberOfTimes".equals(cookies[i].getName())){
//获得numberOfTimes这个key的值(上次訪问的次数)
String value = cookies[i].getValue();
//并删除当前的Cookie
cookies[i].setMaxAge(0);//将存储时间设置为0表示删除此cookie
//将获取的值转换为int类型
int temp = Integer.valueOf(value);
//上次訪问的次数加上1,并赋值给time标记
time = ++temp;
}
}
}
//创建一个新的cookie并把numberOfTimes的值设为本次的次数
Cookie cookie = new Cookie("numberOfTimes",time+"");
//设置cookie的保留时间,单位为秒
cookie.setMaxAge(3600);
//设置标识(表示当前cookie是/cookieWEBproject的)
cookie.setPath("/cookie");
//向浏览器返回此cookie(保存在客户机的硬盘上)
response.addCookie(cookie);
//打印用户訪问的次数
response.getWriter().print("你是第"+time+"次訪问本servlet");
}
public
void doPost(HttpServletRequest request, HttpServletResponseresponse)
throws ServletException, IOException {
doGet(request, response);
}
}
session(会话对象)
把数据缓存在server端
在WEB开发中,server能够为每一个用户浏览器创建一个会话对象(session对象),也就是说一个浏览器独占一个session对象(默认情况下).因此。在须要保存用户数据时,server能够把用户数据写到用户浏览器独占的session对象中,当用户用浏览器訪问其它程序时,其它程序能够从用户的session中取出该用户的数据,为用户服务
Session对象是由server创建,我们能够调用request对象的getSession方法得到用户的session对象
Session与cookie的差别:
Cookie是把用户的数据写在用户本地硬盘上
Session是把用户的数据写到server为每一个用户单独分配的session对象中
当session对象超过30分钟(默认)无訪问,就销毁此session对象;调用session对象的getLastAccessedTime()方法返回此session对象最后的訪问时间(时间戳)
Session对象的生命周期设置
Session也是依附于cookie的,当server新建了一个session对象,就会生成一个id,然后通过cookie将此id返回到client上id的key为”JSESSIONID”,然后当用户下次来訪问此web应用时是带着session对象的id来的,通过此id我们就能够找到与之相应的session对象 ; 但此cookie对象没有设置保存时间。所以并没有写到客户机的硬盘上,而是在客户机的内存中,当用户关闭浏览器时,此cookie会被自己主动释放,再打开浏览器就訪问不到原有的session对象了;
为了解决此问题我们须要重写一个key为”JSESSIONID”。并设置了保存时间(一般与在web.xml配置文件里配置了的时间一致)的cookie 覆盖掉原有的cookie。session的id能够通过session对象的getId()方法获得; 作用域:此session对象仅仅在本project中有效
在WEBproject的配置文件web.xml文件里加入一个标签:
<session-config>
<session-timeout>10</session-timeout>
</session-config>
表示session对象在10分钟无人使用的情况下,将被自己主动销毁
也能够直接调用session对象的invalidate();方法,马上销毁此用户的session对象
通过一个servlet向session对象中存值,一个servlet输出session对象中的值的例题:
Jsp中的代码:
<body>
<a
href="servlet/Test1">存</a><br/>
<a
href="servlet/Test2">取</a>
</body>
存值的servlet的代码:
//用户訪问此Test1
servlet时。向此用户的session对象中存入一个值,当用户訪问Test2
servlet时取出用户的session对象所存入的值
//也就是说。当我们第一此获取用户的session对象时,server假设没有给此用户创建session对象,那么就给用户新建一个
//session对象的生命周期是:server第一次获取用户的session对象
到
此session对象30分钟(默认)不用时就自己主动释放
public
class Test1 extends HttpServlet {
public
void doGet(HttpServletRequest request, HttpServletResponseresponse)
throws ServletException, IOException {
response.setContentType("text/html; charser=utf-8");
response.setCharacterEncoding("utf-8");
request.setCharacterEncoding("utf-8");
//获取在server中获取用户的session对象,假设有就返回,没有就创建一个新的session对象再返回
HttpSession session = request.getSession();
//向此用户的session对象总存入值
String str = "hello";
session.setAttribute("data", str);
//打印此值
response.getWriter().print("存入了:"+str);
/*
Session也是依附于cookie的。当server新建了一个session对象,就会生成一个id,
然后通过cookie将此id返回到client上id的key为”JSESSIONID”,然后当用户下次来
訪问此web应用时是带着session对象的id来的,通过此id我们就能够找到与之相应的session对象
;
但此cookie对象没有设置保存时间。所以并没有写到客户机的硬盘上,而是在客户机的内存中,
当用户关闭浏览器时,此cookie会被自己主动释放,再打开浏览器就訪问不到原有的session对象了;
为了解决此问题我们须要重写一个key为”JSESSIONID”,并设置了保存时间(一般与在web.xml配置文件里配置了的时间一致)
的cookie
覆盖掉原有的cookie;session的id能够通过session对象的getId()方法获得;
*/
//以下就是覆盖原有cookie的代码
//获取到session对象的id
String sessionId = session.getId();
//创建一个新的cookie对象(并设置一个键值。key必须为JSESSIONID才干覆盖原有的cookie。value为当前session对象的id)
Cookie cookie = new Cookie("JSESSIONID",sessionId);
//设置cookie的存活时间(一般和web.xml配置的session对象的存活时间一致,缺省为30分钟)
cookie.setMaxAge(1800);//存活时间为30分钟。不调用此方法设置值,此cookie将不会被写入客户机的硬盘上
//设置此cookie的网站
cookie.setPath("/session");
//返回给客户机
response.addCookie(cookie);
}
public
void doPost(HttpServletRequest request, HttpServletResponseresponse)
throws ServletException, IOException {
doGet(request, response);
}
}
取值的servlet的代码:
//用户訪问取出Test1
servlet存在session对象中的值
public
class Test2 extends HttpServlet {
public
void doGet(HttpServletRequest request, HttpServletResponseresponse)
throws ServletException, IOException {
response.setContentType("text/html; charser=utf-8");
response.setCharacterEncoding("utf-8");
request.setCharacterEncoding("utf-8");
//取出此用户的session对象
HttpSession session = request.getSession(false);//传入一个false,表示此方法仅仅获取session对象,并不创建session对象(当没有session对象时)
//HttpSessionsession = request.getSession();//此方法尽管也能够,但假设此用户没有session对象时。此处并不须要创建一个session对象,所以最好用上面这样的方法,(能够添加效率)
//再取出此session对象中的data的值
String str = "no";
//推断是否获取到了session对象
if(session !=
null){
str = (String)session.getAttribute("data");
}
//打印此值
response.getWriter().print("取出了:"+str);
}
public
void doPost(HttpServletRequest request, HttpServletResponseresponse)
throws ServletException, IOException {
doGet(request, response);
}
}
当用户禁用cookie后怎么解决传入session对象的id问题(通过url想servlet带入session对象的id,但用户关闭浏览器后将失去session对象。无法避免(上面用cookie能够避免))
上面的servlet中的代码不变 jsp中代码为:
<body>
<%
//当訪问首页时就创建此session对象
request.getSession();
//encodeURL()是本应用级别的,encodeRedirectURL()是跨应用的
//此方法会在servlet/Test1后面自己主动加上session对象的id(重要:假设在servlet重定向跳转时,这里重写url的方法是:response.encodeRedirectURL("servlet/Test1")
Stringurl1 = response.encodeURL("servlet/Test1");
//这里是重写在jsp中跳转页面的url
所以用response.encodeURL("servlet/Test1"))
//此方法会在servlet/Test2后面自己主动加上session对象的id
Stringurl2 = response.encodeURL("servlet/Test2");
%>
<a
href="<%=url1
%>">存</a><br/>
<a
href="<%=url2
%>">取</a>
</body>
还有在做登录时须要用到session对象,当用户登录成功后就把user对象存入session中,当注销时就须要调用session对象的invalidate();方法,马上销毁此session对象。也可用session.removeAttribute("user");值删除指定的映射关系(推荐)
base64编码
假设一个字符串用dase64编码, 会将这个字符串的二进制代码的3个字节转换为4个字节,在最高位补0的形式;
如 :
10100110 11110010 00010100
dase64编码后:
00101001 00101111
00001000 00010100
这样每一个字节的第值就在0-63之间了,再经过dase64的码表转换过来后就是仅仅有包含了键盘上的全部字符
在java中获得dase64编码对象
sun.misc.BASE64Encoder base64Encoder =
new sun.misc.BASE64Encoder();//获得dase64编码对象
String str = base64Encoder.encode(byte[] arg0);//传入一个byte[]数组,将将这个数组用dase64编码,并返回一个新的字符串
在java 中获得dase64解码对象
sun.misc.BASE64Decoderbase64Decoder = new BASE64Decoder();//创建解码对象
byte[] buff =base64Decoder.decodeBuffer(str);//将要解码字符串传入,返回一个byte数组
String token = new String(buff);//通过这个byte数组创建一个字符串
一般在MyEclipse不能用,需用下面方法进行设置
仅仅须要在projectbuild path中的JRE System Library中的Accessible,再又一次加入库JRE System Library 的Accessible,Rule Pattarn为**。又一次编译后就一切正常了(例如以下图)
JSP开发
jsp是SUN公司定义的一种用于开发动态web页面的计算。和html不同的是。jsp同意在页面中编写java代码及在页面中获取request、response等对象实现与浏览器交互,所以jsp也是一种web资源开发技术
jsp文件在訪问时server会将其转换为servlet的java文件(转换后文件的位置Tomcat7.0\work\Catalina\localhost\project名\org\apache\jsp;
一、jsp中的代码转换后,会被放到此java文件里的_jspService(
)方法中
二、由于第一次訪问一个jsp文件时,会将此jsp翻译成servlet,所以
jsp文件在第一次訪问时会非常慢。第二次訪问时引擎(就是将jsp转换为servlet的程序)发现jsp文件没有改变就不在翻译,直接訪问此servlet所以速度快
三、而且JSP引擎在调用_jspService( )方法时,会传入9个隐式对象(以下JSP隐式对象中讲)
JSP模版元素
JSP中的模板元素定义了网页的基本骨架,即定义了页面的结构和外观
JSP表达式
JSP表达式用于将程序数据输出到client 语法<%=变量或表达式 %> 也就是直接在此位置输出数据
server会将此语法直接转换为 out.print(变量或表达式);
JSP脚本片断
就是在jsp文件里嵌套<% java代码 %>这就是脚本片断, 能够出如今jsp中的任何位置,脚本片断中仅仅能出现java代码;
Jsp中能够出现多个脚本片断,在两个脚本片断之间能够嵌套文本、HTML标签及其它jsp元素
多个脚本片断中的代码能够相互訪问。单个脚本片断中的java语句能够是不完整的,可是多个脚本片断组合后的结果必须是完整的java语句,比如:
<%
for(inti=0; i<5; i++){
%>
<p>hello world!</p>
<%
}
%>
这表示把<p>hello world!</p>输出5遍
JSP声明
假设在用下面方式定义脚本片断,表示把此脚本片断中的代码放到_jspService( )方法的外面去。比如:
<%!
Publicstatic void fun(){
System.out.println(“helloworld!”);
}
%>
,假设不将此java代码放到_jspService( )方法的外面去,相当于在_jspService()方法的内部定义了一个fun()方法,这样java语法是不同意的;这样我们就也能够在JSP中定义一个成员变量或静态域等功能了
JSP凝视
在JSP中的凝视方法是<%--被凝视的内容 --%>
此方法凝视是。当server将jsp文件转换为servlet时,会丢弃此凝视,并不会向用户返回此凝视的代码;假设用<!- - 凝视的内容- -> 此方法凝视,server会将此代码发送给客户,会添加网络数据;用此<!---->种凝视,被凝视的javaBean标签也会被servlet翻译,所以在jsp中尽量注意
JSP模板更改
改动模板路径:MyEclipse\plugins\com.genuitec.eclipse.wizards_11.0.0.me201303311935.jar中templates\jsp\Jsp.vtl文件里改动
JSP指令
Jsp指令是为了JSP引擎(就是将jsp转换为servlet的程序)而设计的,它们并非直接生产不论什么可见输出,而仅仅告诉引擎怎样将jsp页面翻译成servlet,指令仅仅是用于传给引擎,并不会发送给客户机,在jsp2.0中共定义了三个指令:
page指令
语法<%@ 指令 属性名=”值” %>
演示样例:
<%@ page contentType=”text/html;charset=gb2312” %>
表示告诉引擎 此文件的格式及编码
多个属性能够写在一个指令中(用空格分隔),也能够写在多个指令中
演示样例:
<%@page contentType=”text/html; charset=gb2312” %>
<%@page import=”java.Util.Date” %>
和
<%@ pagecontentType=”text/html; charset=gb2312” import=”java.Util.Date” %>
是一样的效果
page指令和存放在jsp中的任何位置,但最好放在JSP页面的起始位置
page指令的属性
language=”java” 表示在JSP脚本片断中的代码是java代码
extends=”package.class” 表示此jsp翻译成servlet后继承谁(一般不改)
import=”java.util.Date,java.sql.* ” 表示在此jsp中的脚本片断中的java代码须要导入的包,多个包能够用”,”隔开,也能够写多个page指令导入
jsp中下面包会自己主动导入
java.lang.*;
javax.servlet.*;
javax.servlet.jsp.*;
javax.servlet.http.*;
session=”true| false” 是否获取此用户的session对象(默认值为true, 获取)。假设设为不自己主动获取, 我们也能够手动通过request对象来获取session对象
buffer=”none| 20kb” 设置out隐式对象的缓存。缺省默觉得8KB的缓存,none为不缓存
autoFlush=”true| false” 设置out隐式对象的缓存满后是否自己主动刷新,一般不改(默觉得true, 自己主动刷新)
isThreadSafe=”true| false” 设置此jsp的线程是否是安全的(默觉得true, 是线程安全的),假设设置为false 那么此jsp翻译成servlet后会实现SingleThreadModel接口以确保线程安全(SingleThreadModel接口上面已讲过);
info=”text” 能够通过此属性带信息
errorPage=”relative_url” 指定当此jsp页面出现异常后。转向哪个页面,此路径必须使用相对路径,假设以”/”开头表示相对于当前应用的程序的根文件夹,
否则相对于当前的页面位置
此属性仅仅是设置此jsp异常时跳转的页面,也能够再web.xml文件里配置全局的异常跳转页面,如:
<error-page>
<exception-type>java.lang.ArithmeticException</exception-type>
<location>/error.jsp</location>
</error-page>
表示当前web应用中全部的serlvet及jsp文件抛出java.lang.ArithmeticException异常后将跳转至/error.jsp这个jsp文件
<error-page>
<error-code>404</error-code>
<location>/error.jsp</location>
</error-page>
表示当前web应用抛出404(找不到资源)异常后,就跳转至/error.jsp这个jsp文件
假设在一个jsp文件里设置了errorPage的属性,那么在web.xml文件里设置的异常跳转。将不正确此jsp页面起作用
isErrorPage=”true | false” 设置当前页面是否为错误处理页面(默认值为false,不是错误处理页面)。假设将此属性设为true,那么此jsp会接收一个异常对象(exception)
contentType=”text/html;charset=ISO-8859-1” 告诉告诉浏览器打开此文件的格式及编码 (也就是设置response的Content-Type头属性的值)
pageEncoding=”characterSet |ISO-8859-1” 也是设置编码格式的
isELIgnored="true | false" 是否忽略EL表达式,默觉得false(不忽略);
include指令
语法<%@ includefile=” /Test1/head.jsp” %>
用此指令包括
属于静态包括
静态包括,在将jsp转换为servlet时就将要包括的jsp文件整合成一个servlet。在返回给客户机。所以静态包括仅仅有一个serlvet,效率高
用request.getRequestDispatcher("/Test1/head.jsp").include(request,response);方法包括就是动态包括
动态包括,就是包括者须要訪问被包括者时,再去訪问被包括者的servlet,所以包括了多少个jsp文件就会多出多少个servlet,效率低
注意事项:
被包括者不能有外部的框架(框架代码) ,由于包括时会将被包括者文件里的全部字符都包括到当中
taglib指令
taglib指令用于在JSP页面中导入标签库,语法:<%@ taglib url=” http://java.sun.com/jsp/jstl/core” prifix=”c”%>
JSP隐式对象
JSP引擎在调用_jspService()方法时,会传入9个隐式对象(注意cookie不是一个隐式对象)
request 请求对象(已讲)
response 响应对象(已讲)
session 回话对象(已讲)
application servletContext对象(已讲)
config servletConfig对象(已讲)
page 当前的servlet(this)对象,因接收时转换成了Object对象,所以用时要强转为org.apache.jasper.runtime.HttpJspBase(已讲)
exception 错误对象。当仅仅有page指令的isErrorPage属性设置为”true”(表示此页面是错误处理页面)时才会传入此对象(此对象继承了Exception对象, 已讲)
out
out 隐式对象用于向客户发送字符数据
out对象通过pageContext对象的getOut方法获得,其作用和使用方法与response.getWriter()方法很相似。
此out隐式对象的类型为JspWriter。JspWriter相当于一种带缓存功能的PringtWriter,设置JSP页面的page指令的buffer属性能够调整此out对象的缓存大小,也能够关闭缓存
仅仅有向此out对象中写入了内容。且满足下面不论什么一个条件, out对象才去调用response.getWriter()方法,并通过该方法返回的PrintWriter输出流对象将out对象的缓冲区的内容正真写入到Servlet引擎提供的缓冲区域中。
1、设置page指令的buffer属性的值为:none,关闭out对象的缓存
2、out对象的缓存区已满
3、jsp页面结束
注意事项:
此out对象不要和response.getWriter流对象一起使用 ,不然会出现输出顺序不一致的异常
pageContext
1、 pageContext对象是JSP技术中最中意的一个对象,它代表JSP页面的执行环境
2、 pageContext对象封装了其它8大隐式对象的引用
3、 pageContext对象它自身就是一个域对象,能够用来存储数据
4、 pageContext对象还封装了web开发中经常涉及到的一些经常使用的操作, 如包括和转发到其它资源, 检索其它域对象中的属性等
5、 生命周期: 当jsp文件运行完毕后,此域就被销毁了
获取其他隐式对象的方法
pageContext.getEcception(); 获取exception隐式对象
pageContext.getPage(); 获取page隐式对象(this)
pageContext.getRequest(); 获取request(请求)隐式对象
pageContext.getResponse(); 获取response(响应)隐式对象
pageContext.getServletConfig(); 获取servletConfig隐式对象
pageContext.getServletContext(); 获取servletContext隐式对象
pageContext.getSession(); 获取session隐式对象
pageContext.getOut(); 获取out隐式对象
pageContext对象中域方法
pageContext.setAttribute(Stringkey,String value); 向pageContext域中存入值,注意pageContext对象的生命周期是:当前的jsp文件运行完毕后就消失了,所以要注意pageContext的使用范围
pageContext.getAttribute(Stringkey); 获取pageContext对象域中的映射值
pageContext.removeAttribute(Stringkey); 删除pageContext对象中的映射关系
pageContext对象中訪问其他三个域的方法
pageContext对象中有代表各个域的常量(int
类型)
pageContext.APPLICATION_SCOPE 表示application (servletContext)对象
pageContext.SESSION_SCOPE 表示session对象
pageContext.REQUEST_SCOPE 表示request对象
pageContext.PAGE_SCOPE 表示当前的pageContext对象
pageContext对象中有获取其它域中键值的方法
pageContext.getAttribute(Stringkey, pageContext.SESSION_SCOPE); 表示获取session对象中的映射关系pageContext.SESSION_SCOPE这个常量代表session对象
pageContext.setAttribute(Stringkey, String value, pageContext.SESSION_SCOPE); 表示设置session对象中的映射关系
pageContext.removeAttribute(Stringkey, pageContext.SESSION_SCOPE); 表示删除session对象中的映射关系
pageContext.findAttribute方法
pageContext.findAttribute(String key);
此方法是遍历获取全部域中的键值关系(顺序: pageContext-> request -> session ->
application(servletContext))假设在request对象中找到了此映射关系的值,那么将不会在向下继续寻找,如四个域中都没找到此键值关系,就返回一个“”字符串,并非“null”
pageContext对象中定义了forward(转发)方法和include(动态包括)方法分别用来简化和取代request.getRequestDispatcher(“url”).forward(request, response)方法;參数是要转发的url(加”/”表示当前的web应用根文件夹)。但这两个方法。会将除了pageContext这个域对象以外的全部对象转发或包括过去
servlet中的四大域对象有:
pageContext 当jsp文件运行完毕后,此域就被销毁了
request 当request对象销毁是,此域就被销毁了
session session对象域的存活时间是能够调整的。默认情况下30分钟无操作自己主动销毁
servletContext 当前web应用中的全部资源都能够訪问此域,此对象的存活时间为, 当server启动就创建, 当server关闭时销毁(一般存储在此域中的键值在不用时都要用.removeAttribute(String key)方法来删除此键值,不然会浪费资源)
JSP标签
JSP标签页称为JSP Action(JSP动作)元素,它用于在jsp页面中提供业务逻辑功能,避免在JSP页面中直接编写java代码造成jsp页面难以维护
jsp中有例如以下三个经常使用的标签:
<jsp:include>标签
语法:<jsp:include page=”/url”><jsp:include>(动态包括和pageContext.include(“/url”)是一样的)会将除了pageContext这个域对象以外的全部对象转发或包括过去
<jsp:forward>标签
语法:<jsp:forwardpage=”/url”></jsp:forward>(转发和pageContext.forward(“url”)是一样的)会将除了pageContext这个域对象以外的全部对象转发或包括过去
<jsp:param>标签(配合前面两个标签一起使用)
语法:
<jsp:paramname=”key” value=”value”>
使用方法:
<jsp:forwardpage=”/servlet”>
<jsp:param name=”key” value=”value”/>
<jsp:paramname=”key1” value=”value1” />//可传入多个键值
</jsp:forward>
这样我们就能够在/servlet中用request. getParameter("key");方法来获取key的值,
也就是说能够通过<jsp:param>标签传值给转发或包括的jsp或serlvet
JSP映射与serlvet的映射
servlet的映射(前面已讲过)
在web应用中注冊com.scxh.Test这个serlvet,并取名为Test
<servlet>
<servlet-name>Test</servlet-name>
<servlet-class>com.scxh.Test</servlet-class>//不加“/”相对于classes文件夹,加上相对于当前的web文件夹,此处不用加
<!--增加了下面的标记。此servlet就会在server启动时创建-->
<load-on-startup>1</load-on-startup>//假设在web.xml配置文件里在当前Servlet注冊标签中加上了当前标签,就会在server启动时创建当前的Servlet对象,并调用Servlet对象的init方法;
</servlet>
serlvet的映射
<servlet-mapping>
<servlet-name>Test</servlet-name>
<url-pattern>*.com</url-pattern>//不能在前面加字符了。如:<url-pattern>aa/*.com</url-pattern>;这样也是不同意的
</servlet-mapping>
JSP的映射
在web应用中注冊这个head.jsp文件并取名为head
<servlet>
<servlet-name>head</servlet-name>
<jsp-file>/Test1/head.jsp</jsp-file>//不加“/”相对于classes文件夹,加上相对于当前的web文件夹,此处需加上
<!--增加了下面的标记,此servlet就会在server启动时创建-->
<load-on-startup>1</load-on-startup>//假设在web.xml配置文件里在当前JSP注冊标签中加上了当前标签,就会在server启动时创建当前的JSP所相应的Servlet对象,并调用Servlet对象的init方法;
</servlet>
映射这个jsp
<servlet-mapping>
<servlet-name>head</servlet-name>
<url-pattern>/head.htlm</url-pattern>//这样我们就能够再浏览器值直接project名/head.htlm
訪问head.jsp文件了
</servlet-mapping>
JavaBean
JavaBean一般用于封装数据的实体。JavaBean是一个遵循特定写法的java类, 它通常具有例如以下特点:
1. 这个java类必须具有一个无參的构造函数
2. 属性必须私有化
3. 私有化的属性必须有public类型的set和get方法(set方法称为对属性的改动器,get方法称为属性的訪问)
4. 必须放在一个java文件里而且此类必须为public类型的
JSP中提供了三个标签用于操作javaBean
<jsp:useBean>
演示样例:
JSP中的代码:
<jsp:useBean
id="ren"
class="cn.Demo1"
scope="page"></jsp:useBean>
<%--假设这个scope属性不缺省。默认值为“page“--%>
<%=ren.getName()
%>
转换为servlet后的代码
cn.Demo1ren= null;
//创建一个cn.Demo1这个类的引用变量,取名为
//到pageContext域对象中去取ren这个映射关系
ren =(cn.Demo1)_jspx_page_context.getAttribute("ren", PageContext.PAGE_SCOPE);
//推断是否取到。没有取到就在内存中创建个新的对象
if (ren == null){
//创建一个新的对象
ren = new
cn.Demo1();
//并把此对象存入pageContext域中,key值为引用的变量名
_jspx_page_context.setAttribute("ren",ren, PageContext.PAGE_SCOPE);
}
也就是说当我们在<jsp:useBean>标签中把id设置”ren”
并把class设置为一个类(带有包名的完整名称)时,那么系统会自己主动创建这个类名为ren的引用变量,
假设scope属性的值为page,表示到pageContext域中去获取key为ren的Bean对象,假设没有获取到就创建一个这个类的新对象,并存入scope域对象中key的值就是id的值(“ren”);scope的值能够使四大域中的不论什么一个;
注意事项:
<jsp:useBean id="ren"class="cn.Demo1" scope="session">
嵌套的代码
</jsp:useBean>
在<jsp:useBean >
标签中嵌套的代码仅仅有当这个标签创建一个新对象时,此代码才会运行;假设在域中获取到了此对象(此标签就不创建新对象),中间嵌套的代码将不会运行
<jsp:setProperty>
通过Bean对象的属性名来给此属性赋值(配合<jsp:useBean>标签来用)
<%--<jsp:setProperty>标签的使用方法(配合<jsp:useBean>标签来用) --%>
<%-- 通过Bean为"ren"对象中的"gender"属性赋值为”女“
--%>
<jsp:setProperty
name="ren"
property="gender"value="女"/>
<%=ren.getGender()
%><%-- 打印的值为:”女“ --%>
<%="<br/>1-----------------------------------------------------------------<br/>"
%>
<%-- 能够用字符串为8大基本类型赋值(会自己主动将字符串转换为向相应的类)
--%>
<jsp:setProperty
name="ren"
property="id"value="13"/><%--这里的id属性为int类型。赋值字符串会自己主动转换
--%>
<%=ren.getId()
%><%-- 打印的值为:”13“ --%>
<%="<br/>2-----------------------------------------------------------------<br/>"
%>
<%-- 通过请求參数为ren对象的password赋值
--%>
<%-- 提交的方式为:http://localhost:8080/javaBean/Test1/Demo1.jsp?
password=abc123 --%>
<jsp:setProperty
name="ren"
property="password"param="password"/>
<%=ren.getPassword()
%><%-- 打印的值为:”abc123“ --%>
<%--param="password"
这句话会被翻译成 request.getParameter("password");
也就是所从我们提交的数据中去找password这能够key的值,找到了就赋值,没有这个key就不赋值--%>
<%="<br/>3-----------------------------------------------------------------<br/>"
%>
<%-- 通过请求參数为ren对象的date赋值(date为Date日期对象)
--%>
<%-- 提交的方式为:http://localhost:8080/javaBean/Test1/Demo1.jsp?date=2012-5-19 --%>
<%-- Date date = newSimpleDateFormat("yyyy-MM-dd").parse(request.getParameter("date"));--%>
<%-- <jsp:setPropertyname="ren" property="date" value="<%=date%>"/>--%>
<%=ren.getDate()
%><%--打印的值为:Sat May 19 00:00:00 CST 2012 --%>
<%--从以上value="<%=date %>代码出能够看出,value的值能够引用java代码所返回的值
--%>
<%="<br/>4-----------------------------------------------------------------<br/>"
%>
<%-- 通过请求參数自己主动为ren对象相应的属性赋值 --%>
<%-- 提交的方式为:http://localhost:8080/javaBean/Test1/Demo1.jsp?name=lishi&gender=nan
注意此处用中文会出现get方式提交乱码问题(前面已讲过怎么解决)--%>
<jsp:setProperty
name="ren"property="*"
/><%--还能够写为<jsp:setProperty
name="ren"property="name"
/>表示从请求中找到name这个key,并赋值给ren这个对象-->
<%=ren.getName()
%><%--打印的值为:lishi --%>
<%=ren.getGender()
%><%--打印的值为:nan --%>
<%-- 假设property="*"的话,那么表示依据名称自己主动匹配请求參数的key给ren对象中和此key一样的属性赋值--%>
<jsp:getProperty>
此标签用于读取JavaBean对象的属性,也就是调用属性的get方法,然后将读取属性的值转换成字符串后插入响应的正文中,假设某个属性的值为null,那么输出的内容为字符串”null”
语法:
<jsp:getProperty
name="ren"
property="name"/>
<%--表示直接输出ren这个Bean对象的name属性,假设此属性的值为null,那么将输出"null"字符串
--%>
JSP设计模式
JSP+JavaBean模式
此模式适合开发逻辑不太复杂的web应用程序,这样的模式下,JavaBean用于封装业务数据,JSP即负责处理用户请求,又显示数据
Servlet(controller)+JSP(view)+JavaBean(model)称为MVC模式
此模式适合开发复杂的web应用,在这样的模式下,serlvet复杂处理用户请求,JSP负责显示数据,JavaBean负责封装数据,採用此MVC模式各个模块之间层次清晰,web开发推荐採用此模式
此模式分为三层:web层、业务逻辑层(service)、数据訪问层(dao)
Web层分
处理用户请求
Service(业务逻辑层)
处理dao层返回的数据,及封装这些数据
Dao(数据訪问层)
訪问数据库
包名规范
cn.itcast.domain 存放javaBean的类
cn.itcast.dao 存放Dao层的类
cn.itcast.dao.impl 存放Dao层的接口
cn.itcast.servce 存放Service层的类
cn.itcast.servce.impl 存放servlce层的接口
cn.itcast.web.serlvet名 存放处理用户请求的servlet+JSP
cn.itcast.web.listener
cn.itcast.web.filter
cn.itcast.utils 存放工具类的包
Junit.test 存放測试的程序
在MVC模式下的jsp文件必须放在WEB-INF/jsp文件夹中防止client直接訪问
MVC模式开发的流程图:
EL表达式
EL表达式语法:${date }假设在jsp中用此语句,那么表示到每一个域中去找date的映射关系,找到了就返回此date的value,假设没有找到就返回一个“”为空的字符串。
此表达式会翻译成servlet的代码:pageContext.findAttribute(date);
此方法是遍历获取全部域中的键值关系(顺序: pageContext-> request -> session ->
application(servletContext))假设在request对象中找到了此映射关系的值,那么将不会在向下继续寻找。如四个域中都没找到此键值关系,就返回一个“”字符串,并非“null”(上面已讲过)(能够从指定域中获取指定的映射关系)
假设存入的是一个key键所相应的是一个Bean对象,EL表达式语法为:${date.name}; 那么当取出date对象后会去调用name属性的get方法,并返回其值
在WEB开发中,假设JS中要用到EL表达式。在JSP引入JS文件时,能够把js文件命名为.jsp文件就能够了。这样里面el就能执行,也就是server能够执行这个文件了。
无非页面引用的时候引用jsp就能够了。
如:<script src="myjs.jsp"type="text/javascript></script>;但取值范围仅仅能取到session和application域中的值
通过EL表达式从指定域中获取数据
<%="<br/>-------------------从指定域中取出数据---------------------<br/>"%>
<%
pageContext.setAttribute("name",
"page");
request.setAttribute("name",
"request");
session.setAttribute("name",
"session");
application.setAttribute("name",
"application");
%>
${pageScope.name }
${requestScope.name }
${sessionScope.name }
${applicationScope.name}
通过EL表达式获取请求的数据
<%-- 通过EL表达式,获取请求的数据
--%>
<body>
<%="<br/>-------------------获取请求的数据---------------------<br/>"%>
<%-- 如訪问的url为:http://localhost:8080/javaBean/Test1/Demo4.jsp?name=zhangshan--%>
${param.name }
<%--以上代码就相当于:request.getParameter("name")--%>
<%="<br/>-------------------获取复选框中请求的数据---------------------<br/>"%>
${paramValues.name[0] }
<%--以上代码就相当于:
String[] str = request.getParameterValues("name");
System.out.println(str[0]);
获取的是复选框中第下标为0的值
--%>
通过EL表达式获取集合中的数据
list集合
<%--
用EL表达式取出list集合中的数据 --%>
<%
//向list集合中存入Demo1的对象
List<Demo1> list = new ArrayList<Demo1>();
list.add(new Demo1("aaa"));
//构造方法会为此对象的name属性赋值
list.add(new Demo1("bbb"));
list.add(new Demo1("ccc"));
request.setAttribute("list", list);
%>
${list[0].name}<%--表示取出域中list的集合对象后。在取出这个集合的第0个对象再调用这个对象的getName方法
--%>
map集合
<%--
用EL表达式取出map集合中的数据 --%>
<%
//向map集合中以键值对的方式存入Demo1对象
Map<String, Demo1> map = new HashMap<String, Demo1>();
map.put("d1",
new Demo1("d111111"));
//构造方法会为此对象的name属性赋值
map.put("d2",
new Demo1("d222222"));
map.put("d3",
new Demo1("d333333"));
map.put("d4",
new Demo1("d444444"));
request.setAttribute("map", map);
%>
${map.d2.name}<%--表示取出域中取出map的集合对象后再通过d2这个key找出其value(Demo1对象),再调用name属性的get方法
--%>
输出当前工程的名称,如:/project
<%--输出当前所在的project名称 --%>
${pageContext.request.contextPath}<%--得到pageContext对象后再通过request对象的contextPath属性获取本project的名称
--%>
<%--在跳转页面时须要用到:如<a
href="${pageContext.request.contextPath }/Test1/Demo1.jsp">跳转</a>
这样我们就能够直接跳转至本project中的其它页面了--%>
JSTL标签
EL表达式能够通过JSTL标签迭代集合等功能
JSTL是SUN公司开发的一套标签库,使用JSTL能够在页面中实现一些简单的逻辑,从而替换页面中的脚本代码
在JSP中使用JSTL标签需完毕下面两个步骤
1、 导入jstl.jar和standerd.jar这两个JSTL的jar包
2、 在JSP页面使用<%@ tagliburl=” http://java.sun.com/jsp/jstl/core” prifix=”c” %> 元素导入标签库
url的地址是standerd.jar包中的/META-INF/c.tld文件里的<uri>http://java.sun.com/jsp/jstl/core</uri>标签中的地址
经常使用的JSTL标签
<c:foreach var=”” items=””>(迭代标签)
<%="<br/>-----------------------通过EL表达式配合JSTL来迭代list集合-------------------------------<br/>"
%>
<%
//向list集合中存入Demo1的对象
List<Demo1> list = newArrayList<Demo1>();
list.add(new Demo1("aaa"));//构造方法会为此对象的name属性赋值
list.add(new Demo1("bbb"));
list.add(new Demo1("ccc"));
request.setAttribute("list", list);
%>
<c:forEach
var="demo1"items="${list }">
${demo1.name }
</c:forEach>
<%--items="${list }" 通过EL表达式中取出这个集合,并将遍历的出的对象放入demo1变量中,
然后在通过EL表达式调用li这个对象的getName方法
--%>
<%="<br/>-----------------------通过EL表达式配合JSTL来迭代map集合-------------------------------<br/>"
%>
<%
//向map集合中以键值对的方式存入Demo1对象
Map<String, Demo1> map = new HashMap<String,Demo1>();
map.put("d1",
new Demo1("d111111"));//构造方法会为此对象的name属性赋值
map.put("d2",
new Demo1("d222222"));
map.put("d3",
new Demo1("d333333"));
map.put("d4",
new Demo1("d444444"));
request.setAttribute("map", map);
%>
<c:forEach
var="entry"items="${map }">
${entry.key } : ${entry.value.name }
</c:forEach>
<%--
//上面entry变量得到的就相当于以下it得到的
Set<Map.Entry<String,Demo1>> set = map.entrySet();
Iterator<Map.Entry<String,Demo1>> it = set.iterator();
//上面EL表达式所输出的就相当于以下out对象所输出的
while(it.hasNext()){
Map.Entry<String, Demo1>entry = it.next();
out.print(entry.getKey()+":"+entry.getValue().getName());
}
也就是说当map集合对象被遍历出来的对象就是一个Map.Entry对象
${entry.key} : ${entry.value.name }就是在在调用Map.Entry的getKey方法
及调用 Map.Entry的getValue方法再通过其返回对象,调用其对象的getName方法
--%>
<c:if test=””>(測试标签,推断)
<%="<br />-----------------------測试(推断)标签的使用-------------------------------<br
/>" %>
<%
//pageContext.setAttribute("user", "张三");
%>
<c:if
test="${user!=null }">
pageContext域中的user值为:${user }
</c:if>
<c:if
test="${user==null }"><!—EL表达式也能够用${empty
user};表示假设user为null的话就返回ture,emptykeyword还能够检測集合如:${empty
map},意思是当map集合等于null,或者map的isEmpty()方法返回为true(也就是map中没有映射关系时)
,那么此EL表达式就返回false -->
pageContext域中没有值
</c:if>
<%--当在四个域中取到user的值(映射关系)后就输出第一句话,否则输出第二句话。也就是说当test的值为true时此标签中的内容才会被运行
--%>
自己定义标签
传统标签
自己定义标签主要用于移除JSP页面中的java代码
生命周期:当jsp运行到此标签时,创建此标签类的对象,然后此标签类对象会在关闭server时销毁
要使用自己定义标签移除jsp页面中的java代码,仅仅须要完毕下面两个步骤:
1、编写一个实现Tag接口的java类(或者继承Tag的实现类TagSupport,把页面java代码移到这个java类中(标签处理器);
2、编写标签库描写叙述符(tld)文件,在tld文件里把标签处理器类描写叙述成一个标签
Tag接口中的方法
doEndTag();
当读取到标签结束符时,会调用此方法
doStartTag();
当读取到标签開始符时,会调用此方法
getParent();
得到此标签的父节点
release();
释放执行当前标签所占的资源
setPageContext(
PageContext pc);
设置当前标签的pageConotext对象(此标签会由运行引擎调用,并传入当前jsp标签中的pageContext对象)
setParent(
Tag t);
演示样例:(用标签在jsp页面获取IP地址)
标签类
//定义一个标签类,必须实现Tag接口,或者继承Tag接口的TagSupport实现类
/*
//第一种方法:实现Tag接口(介绍了此接口中方法的调用顺序,
也能够查看与调用此标签的JSP文件所相应的servlet中的代码)
public
class Test1 implements Tag{
private PageContext
pageContext = null;
private Tag
tag = null;
//当读取完当前标签開始符时,会调用此方法
@Override
public
int doStartTag() throws JspException {
// TODO Auto-generatedmethod stub
HttpServletRequest request = (HttpServletRequest)pageContext.getRequest();
String ip = request.getRemoteAddr();
JspWriter out = pageContext.getOut();
try {
out.write(ip);
} catch (IOException e) {
// TODO Auto-generatedcatch block
throw
new RuntimeException(e);//此异常不会再调用时不会抛给调用者,会直接抛给JVM
}
return 0;
}
//当读取完当前标签结束符时会调用此方法
@Override
public
int doEndTag() throws JspException {
// TODO Auto-generatedmethod stub
return 0;
}
//设置当前标签的pageContext对象(系统调用,此方法会最先被调用)
@Override
public
void setPageContext(PageContext pageContext) {
// TODO Auto-generatedmethod stub
this.pageContext = pageContext;
}
//设置当前标签的父节点(系统调用,当调用完setPageContext方法后就会调用此方法,假设没有父节点会传入一个null)
@Override
public
void setParent(Tag tag) {
// TODO Auto-generatedmethod stub
this.tag = tag;
}
//得到此标签的父节点
@Override
public Tag getParent() {
// TODO Auto-generatedmethod stub
return
this.tag;
}
//关闭标签所占用的资源(系统调用,此方法一般用于释放标签工作时所生成的资源,此方法会时最后被调用,比doEndTag()方法还要后调用)
@Override
public
void release() {
// TODO Auto-generatedmethod stub
}
}
*/
//另外一种方法:继承Tag接口的TagSupport实现类(推荐,由于继承此方法不须要重写其它的方法)
public
class Test1 extends TagSupport{
//当读取到标签開始符时,会调用此方法
@Override
public
int doStartTag() throws JspException {
// TODO Auto-generatedmethod stub
//通过pageContext对象来获取request对象
HttpServletRequest request = (HttpServletRequest)this.pageContext.getRequest();
//通过request对象来获取到客户机的IP地址
String ip = request.getRemoteAddr();
//通过pageContext对象获取到out缓存输出对象
JspWriter out = this.pageContext.getOut();
try {
//向缓存对象中加入此ip
out.write(ip);
} catch (IOException e) {
// TODO Auto-generatedcatch block
throw
new RuntimeException(e);
}
return
super.doStartTag();
}
}
在tld文件里注冊标签类(一般放在WEB-INF文件夹下。由于当jsp文件用tld文件的uri名导入标签库时,jsp会默认到WEB-INF文件夹下去找此文件)
<taglib
xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2eehttp://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd"
version="2.0">
<!-- 标签的描写叙述 -->
<description>A tag libraryexercising SimpleTag handlers.</description>
<!-- 版本号 -->
<tlib-version>1.0</tlib-version>
<!-- 名称(能够不改动) -->
<short-name>scxh</short-name>
<!-- 将此文件里标签绑定到一个uri上(映射名称)
-->
<uri>http://www.scxh.cn</uri>
<!-- 注冊标签 -->
<tag>
<!-- 此标签类的映射名称 -->
<name>printIP</name>
<!-- 标签类的完整名称 -->
<tag-class>cn.scxh.Test1</tag-class>
<!-- 标签内容为空 -->
<body-content>empty</body-content>
</tag>
</taglib>
在JSP中调用此标签
<%@
page language="java"import="java.util.*"
pageEncoding="UTF-8"%>
<%--导入标签,并为此标签取一个代号 --%>
<%--第一种方式:直接导入标签uri --%>
<%--@taglib uri="http://www.scxh.cn"prefix="test1"--%>
<%--另外一种方式:直接导入标签文件的路径 --%>
<%--@taglib uri="WEB-INF/test1.tld"prefix="test1"--%>
<%--第三种方式:导入在web.xml文件里配置的变量名(推荐)
--%>
<%@taglib
uri="testtab"
prefix="test1"%>
<!DOCTYPE
HTML PUBLIC
"-//W3C//DTDHTML 4.01 Transitional//EN">
<html>
<head>
<title>My JSP ‘Test1.jsp‘starting page</title>
</head>
<body>
你的IP地址为:<test1:printIP/>
</body>
</html>
使用第三种方式导入标签需在xml文件里设置
<jsp-config>
<taglib>
//取一个映射名称(uri)
<taglib-uri>testtab</taglib-uri>
//设置tld文件的路径(必须是路径,不能是tld文件的uri)
<taglib-location>/WEB-INF/test1.tld</taglib-location>
</taglib>
</jsp-config>
自己定义标签的扩张功能
Tag接口中的常量
EVAL_BODY_INCLUDE 表示运行标签体中的内容(doStartTag()方法返回值引用)
SKIP_BODY 表示不运行标签体中内容(doStartTag()方法返回值引用)
EVAL_PAGE 表示继续运行余下的JSP内容(doEndTag()方法返回值引用)
SKIP_PAGE 表示不运行余下的JSP内容(doEndTag()方法返回值引用)
控制Jsp页面某一部分内容是否运行
调用doStartTag()(此方法会在读取完标签開始符时调用)方法.假设此方法的返回值为Tag.SKIP_BODY表示不运行标签中的内容;假设返回的是Tag. EVAL_BODY_INCLUDE表示运行此标签中的内容
演示样例:
标签类
//控制标签体的内容是否输出
public
class Test2 extends TagSupport{
@Override
public
int doStartTag() throws JspException {
// TODO Auto-generatedmethod stub
return Tag.SKIP_BODY;//表示不输出标签中的内容
//return Tag.EVAL_BODY_INCLUDE;//表示输出标签中的内容
}
}
在tld文件里注冊标签类
<?xml
version="1.0"encoding="UTF-8"
?>
<taglib
xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2eehttp://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd"
version="2.0">
<!-- 此文件一般放在WEB-INF文件夹下,由于当jsp文件用tld文件的uri名导入标签库时,jsp会默认到WEB-INF文件夹下去找此文件
-->
<!-- 标签的描写叙述 -->
<description>A tag libraryexercising SimpleTag handlers.</description>
<!-- 版本号 -->
<tlib-version>1.0</tlib-version>
<!-- 名称(能够不改动) -->
<short-name>scxh</short-name>
<!-- 将此文件里标签绑定到一个uri上(映射名称)
-->
<uri>http://www.scxh.cn</uri>
<!-- 注冊标签 -->
<tag>
<!-- 此标签类的映射名称 -->
<name>print</name>
<!--
标签类的完整名称 -->
<tag-class>cn.scxh.Test2</tag-class>
<!--
标签内容为JSP。此值需大写-->
<body-content>JSP</body-content>
</tag>
</taglib>
JSP中调用此标签
<%@
page language="java"import="java.util.*"
pageEncoding="UTF-8"%>
<%@taglib
uri="labelTag"prefix="label"
%>
<!DOCTYPE
HTMLPUBLIC
"-//W3C//DTDHTML 4.01 Transitional//EN">
<html>
<head>
<title>My JSP ‘Test2.jsp‘starting page</title>
</head>
<body>
<%-- 控制标签中的内容是否被运行 --%>
<label:print>
<p>控制此内容是否被输出</p>
</label:print>
</body>
</html>
控制整个jsp页面是否运行
调用doEndTag()(此方法会在读取完标签结束符时调用)方法.假设此方法的返回值为Tag.SKIP_PAGE表示不运行此标签后面的JSP代码;假设返回的是Tag. EVAL_PAGE表示运行此标签后面的JSP代码
演示样例:
标签类
//控制此标签后的JSP是否还运行
public
class Test3 extends TagSupport{
//当读取完当前标签结束符时会调用此方法
@Override
public
int
doEndTag()throws JspException {
//returnTag.SKIP_PAGE;//表示不运行此标签后面的JSP代码
return Tag.
EVAL_PAGE;//表示运行此标签后面的JSP代码
}
}
在tld文件里注冊标签类
<?xml
version="1.0"encoding="UTF-8"
?>
<taglib
xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2eehttp://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd"
version="2.0">
<!-- 此文件一般放在WEB-INF文件夹下。由于当jsp文件用tld文件的uri名导入标签库时,jsp会默认到WEB-INF文件夹下去找此文件
-->
<!-- 标签的描写叙述 -->
<description>A tag libraryexercising SimpleTag handlers.</description>
<!-- 版本号 -->
<tlib-version>1.0</tlib-version>
<!-- 名称(能够不改动) -->
<short-name>scxh</short-name>
<!-- 将此文件里标签绑定到一个uri上(映射名称)
-->
<uri>http://www.scxh.cn</uri>
<!-- 注冊标签 -->
<tag>
<!-- 此标签类的映射名称 -->
<name>execution</name>
<!--
标签类的完整名称 -->
<tag-class>cn.scxh.Test3</tag-class>
<!--
标签内容为空 -->
<body-content>empty</body-content>
</tag>
</taglib>
JSP中调用此标签
<%@
page language="java"import="java.util.*"
pageEncoding="UTF-8"%>
<%@taglib
uri="labelTag"prefix="label"%>
<!DOCTYPE
HTMLPUBLIC
"-//W3C//DTDHTML 4.01 Transitional//EN">
<html>
<head>
<title>My JSP ‘Test3.jsp‘starting page</title>
</head>
<body>
<label:execution/>
This ismy JSP page.
<br>
<p>測试label:execution标签后面的JSP是否运行</p>
</body>
</html>
控制标签内容是否反复运行
此方法须要用到TagSupport父类中的public int doAfterBody();方法,此方法会在读取完当前内容后调用,在运行doEndTag()方法前被调用,假设doAfterBody();方法返回的是IterationTag. EVAL_BODY_AGAIN的话,表示此方法中的内容继续被输出。假设返回的是IterationTag.SKIP_BODY,表示此方法中的内容不被输出;但一定要让doStartTag()方法的返回值为Tag.EVAL_BODY_INCLUDE;表示运行标签中的内容
演示样例:
标签类
//控制标签中内容反复输出5此
public
class Test4 extends TagSupport{
private
int i = 5;
//一定要让此方法的返回值为Tag.EVAL_BODY_INCLUDE;表示运行标签中的内容
@Override
public
int doStartTag() throws JspException {
// TODO Auto-generatedmethod stub
return Tag.EVAL_BODY_INCLUDE;
}
@Override
public
int doAfterBody() throws JspException {
i--;
if(i>0){
return IterationTag.EVAL_BODY_AGAIN;//返回此常量表示继续输出标签中的内容
}else{
return IterationTag.SKIP_BODY;//返回此常量表示不输出标签中的内容
}
}
}
在tld文件里注冊标签类
<!-- 注冊标签 -->
<tag>
<!-- 此标签类的映射名称 -->
<name>print5</name>
<!-- 标签类的完整名称 -->
<tag-class>cn.scxh.Test4</tag-class>
<!-- 标签内容为空 -->
<body-content>JSP</body-content>
</tag>
JSP中调用此标签
<%@
page language="java"import="java.util.*"
pageEncoding="UTF-8"%>
<%@taglib
uri="labelTag"prefix="label"%>
<!DOCTYPE
HTMLPUBLIC
"-//W3C//DTDHTML 4.01 Transitional//EN">
<html>
<head>
<title>My JSP ‘Test4.jsp‘starting page</title>
</head>
<%--让标签中的内容输出5次 --%>
<body>
<label:print5>
<p>此处会被输出5次</p>
</label:print5>
</body>
</html>
改动标签内容后再输出
标签类须要继承BodyTagSupport类,并重写doStartTag();方法,让其返回值为BodyTag.EVAL_BODY_BUFFERED;让此标签的内容封装成一个对象,系统再通过调用此标签对象的setBodyContent(BodyContent b)方法将标签内容对象传入; 然后我们再重写doEndTag();方法。getBodyContent()方法通过获取到内容对象,再对此对象改动后通过out缓存输出到jsp
演示样例:
标签类
//将标签中的字母转换为大写
public
class Test5 extends BodyTagSupport{
@Override
public
int doStartTag() throws JspException {
// TODO Auto-generatedmethod stub
return BodyTag.EVAL_BODY_BUFFERED;//让此标签的内容封装成一个对象,系统再通过调用此标签对象的
//setBodyContent(BodyContent b)方法将标签内容对象传入;
}
@Override
public
int doEndTag() throws JspException {
BodyContent content = this.getBodyContent();//通过此方法得到内容对象
String str = content.getString();//将内容对象以字符串返回
str = str.toUpperCase();//将字符串中的全部字母都变为大写
try {
pageContext.getOut().write(str);//将转换过后的字符串输出到JSP
} catch (IOException e) {
//
TODO Auto-generatedcatch block
e.printStackTrace();
}
return
super.doEndTag();
}
}
在tld文件里注冊标签类
<!-- 注冊标签 -->
<tag>
<!-- 此标签类的映射名称 -->
<name>uppercase</name>
<!-- 标签类的完整名称 -->
<tag-class>cn.scxh.Test5</tag-class>
<!-- 标签内容为空 -->
<body-content>JSP</body-content>
</tag>
JSP中调用此标签
<%@
pagelanguage="java"
import="java.util.*"pageEncoding="UTF-8"%>
<%@taglib
uri="labelTag"prefix="label"%>
<!DOCTYPE
HTMLPUBLIC
"-//W3C//DTDHTML 4.01 Transitional//EN">
<html>
<head>
<title>My JSP ‘Test5.jsp‘starting page</title>
</head>
<%--将标签中的字母转换大写 --%>
<body>
<label:uppercase>
<p>此处的字母会被转换为大写:abcdef</p>
</label:uppercase>
</body>
</html>
简单标签
简单标签须要实现SimpleTag接口或继承它的实现类SimpleTagSupport,通过这个接口或这个它的实现类就能够完毕上面复杂标签的全部功能
生命周期:当jsp运行到此标签时,创建此标签类的对象,当整个jsp(servlet)运行完毕后这个标签类对象就被释放了
SimpleTagSupport类的方法(按运行顺过排序);当读到一个标签后会调用标签类的这些方法
setJspContext(
JspContext pc);
此方法由JSP的serlvet最先调用(能够看着是系统调用),将pageContext对象传入;
setParent(JspTag parent);
系统调用,会将此标签的父节点当做一个对象传入,假设没有父节点,就传入null
setJspBody(
JspFragment jspBody);
此方法会由JSP的servlet调用,并将标签的内容封装成对象后传入。
doTag();
此方法会在上面标签运行完毕后才被运行(由程序猿写操作语句)
getJspBody();
得到标签的内容对象(由程序猿调用)
getJspContext(); 得到调用此标签的pageContext对象(由程序猿调用)
getParent();
得到当前标签的父节点标签(由程序猿调用)
案例:
控制标签体中的内容是否被运行
java标签类中的内容
//控制标签体中的内容是否被运行
public
class Test1 extends
SimpleTagSupport{
//此方法会在系统调用完父类的全部set*方法后,才调用此方法
@Override
public
void
doTag()throws JspException, IOException {
//假设要输出标签中的内容就使用下面方法。假设不输出标签内容就什么也不做
//得到标签中的内容对象
JspFragment body = this.getJspBody();
//得到JSP的pageContext对象
JspContext pageContext = this.getJspContext();
//将内容对象输入到JSP的out对象中
body.invoke(pageContext.getOut());//也能够写为body.invoke(null);此内容将默认输出到pageContext.getOut()对象中
}
}
tld文件里注冊标签的内容
<?
xml
version="1.0"encoding="UTF-8"
?>
<taglib
xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2eehttp://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd"
version="2.0">
<!-- 此文件一般放在WEB-INF文件夹下,由于当jsp文件用tld文件的uri名导入标签库时,jsp会默认到WEB-INF文件夹下去找此文件
-->
<!-- 标签的描写叙述 -->
<description>A tag libraryexercising SimpleTag handlers.</description>
<!-- 版本号 -->
<tlib-version>1.0</tlib-version>
<!-- 名称(能够不改动) -->
<short-name>scxh</short-name>
<!-- 将此文件里标签绑定到一个uri上(映射名称)
-->
<uri>http://www.scxh.cn</uri>
<!-- 注冊标签 -->
<tag>
<!-- 此标签类的映射名称 -->
<name>print</name>
<!--
标签类的完整名称 -->
<tag-class>com.scxh.Test1</tag-class>
<!--
表示标签内容不能为脚本代码(java代码)。还能够有JSP表示能够java代码。empty表示此标签没有标签内容
-->
<body-content>scriptless</body-content>
</tag>
</taglib>
JSP文件里的内容
<%@ pagelanguage="java"
import="java.util.*"pageEncoding="UTF-8"%>
<%@taglib
uri="labelSimpleTag"prefix="label"
%>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>
<!DOCTYPE
HTMLPUBLIC
"-//W3C//DTDHTML 4.01 Transitional//EN">
<html>
<head>
<base
href="<%=basePath%>">
<title>My JSP ‘index.jsp‘starting page</title>
<meta
http-equiv="pragma"content="no-cache">
<meta
http-equiv="cache-control"content="no-cache">
<meta
http-equiv="expires"content="0">
<meta
http-equiv="keywords"content="keyword1,keyword2,keyword3">
<meta
http-equiv="description"content="This is my page">
<!--
<link rel="stylesheet"type="text/css"
href="styles.css">
-->
</head>
<%--控制标签体中的内容是否被运行 --%>
<body>
<label:print>
<p>控制此内容是否被运行</p>
</label:print>
</body>
</html>
控制标签体中的内容反复运行5次
仅仅需将上面java标签类中
//将内容对象输入到JSP的out对象中
body.invoke(pageContext.getOut());
的此代码用for循环运行5次就可以
将标签体中的字母变为大写(改动标签体的内容)
仅仅须要将上面java标签类中doTag()方法中的代码改为
//将标签体内容中的字母变为大写
@Override
public
void doTag() throws JspException, IOException {
//得到标签中的内容对象
JspFragment body = this.getJspBody();
//建一个缓存流对象
StringWriter buffer = new StringWriter();
//将内容对象当做一个字符串输出到一个缓冲对象中
body.invoke(buffer);
//取出缓存流对象中的内容
String content = buffer.toString();
//关闭缓冲流
buf.flush();
buf.close();
//再将内容中的小写转为大写
String content1 = content.toUpperCase();
//再将此内容放入到out对象中
this.getJspContext().getOut().write(content1);
}
控制此标签后的JSP是否被运行
仅仅须要将上面java标签类中doTag()方法中的代码改为
//控制标签后的JSP是否继续被运行
@Override
public
void doTag() throws JspException, IOException {
//仅仅须要在此方法中抛出此异常,那么此标签后面的JSP将不会被运行
throw
new SkipPageException();
}
标签的属性
假设想要自己定义标签具有属性,须要在标签处理器中编写每一个属性相应成员变量及它的的set方法和在tld文件里描写叙述标签的属性;系统当读取到标签属性时会自己主动调用此标签类与之相应的成员变量名的set方法将属性的值传入;
演示样例:
java标签类中的代码
//让标签中的内容输出属性count值全部指定的次数
public
class Test2 extends SimpleTagSupport{
private
int count = 0;
//当读取到与标签中count对象时会调用此方法(按名称相应,通过javaBean实现)
public
void setCount(int count) {
this.count = count;
}
@Override
public
void doTag() throws JspException, IOException {
// TODO Auto-generatedmethod stub
//将内容输出conut这么多次
for(int i=0; i<count; i++){
this.getJspBody().invoke(null);
}
}
}
tld文件里的内容
<!-- 注冊标签 -->
<tag>
<!-- 此标签类的映射名称 -->
<name>printCount</name>
<!-- 标签类的完整名称 -->
<tag-class>com.scxh.Test2</tag-class>
<!-- 表示标签内容不能为脚本代码(java代码),还能够有JSP表示能够java代码,empty表示此标签没有标签内容
-->
<body-content>scriptless</body-content>
<!-- 表示为此标签声明一个属性 -->
<attribute>
<!-- 指定属性的名称 -->
<name>count</name>
<!-- 表示此属性是否是必须的 -->
<required>true</required>
<!-- 表示此属性的值能否够是一个表达式(能否够是LE表达式等)
-->
<rtexprvalue>true</rtexprvalue>
<!-- 还能够指定属性值的类型(必须是java的完整类名)。不写此标签表示接收一个Object类型
-->
<!-- <type>java.lang.Integer</type> -->
</attribute>
</tag>
JSP文件里的内容
<%@ pagelanguage="java"
import="java.util.*"pageEncoding="UTF-8"%>
<%@taglib
uri="labelSimpleTag"prefix="label"%>
<!DOCTYPE
HTMLPUBLIC
"-//W3C//DTDHTML 4.01 Transitional//EN">
<html>
<head>
<title>My JSP ‘Test2.jsp‘starting page</title>
</head>
<%--让标签中的内容输出属性count值全部指定的次数 --%>
<body>
<label:printCount
count="5">
<p>标签中的内容</p>
</label:printCount>
</body>
</html>
用自己定义标签遍历全部的集合及全部的数组
java标签类中的代码
public
class ForEach extends SimpleTagSupport{
//全部单列集合的父接口
private
Collection items = null;
//用于存储在域对象中的key
private String
var = null;
//将传入的对象都转换为一个单列集合
public
void setItems(Object items) throws Exception{
if(items==null){//假设传入的是null
return;
}else
if(items instanceof Collection){//假设传入的对象时一个单例集合
this.items = (Collection)items;//就将这个对象强转换Collection(全部单列集合的父接口)对象
}else
if(items instanceof Map){//假设传入的是一个map集合
Map map = (Map)items;//就将其强转换Map(全部双列集合的父接口)对象
this.items = map.entrySet();//将其转换为一个entrySet的单例集合中
}else
if(items.getClass().isArray()){//假设传入的是一个数组
List list = new
LinkedList();//就将其装入一个list集合中
int len = Array.getLength(items);//通过Array类反射出此数组对象的长度
for(int i=0; i<len; i++){//遍历数组,将值存入list集中
list.add(Array.get(items,i));//通过Array类反射出items数组对象下标为i的值
}
this.items = list;//将list集合赋给成员变量
}else{
throw
new Exception("你传入的不是一个集合也不是一个数组");
}
}
public
void setVar(String var) {//接收用于存储在域中的key
this.var = var;
}
@Override
public
void doTag() throws JspException, IOException {
if(this.items==null){
return;
}
//获取单列集合的迭代器
Iterator it =
items.iterator();
//迭代这个集合
while(it.hasNext()){
Objectvalue = it.next();
this.getJspContext().setAttribute(var, value);//迭代出来的值向pageContext域中存
this.getJspBody().invoke(null);//运行此标签的内容,(让EL表达式从域中去取key为“var”的值)
}
}
}
tld文件里的内容
<?xml
version="1.0"encoding="UTF-8"
?
>
<taglib
xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2eehttp://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd"
version="2.0">
<!-- 此文件一般放在WEB-INF文件夹下。由于当jsp文件用tld文件的uri名导入标签库时,jsp会默认到WEB-INF文件夹下去找此文件
-->
<!--
标签的描写叙述 -->
<description>A tag libraryexercising SimpleTag handlers.</description>
<!--
版本号 -->
<tlib-version>1.0</tlib-version>
<!--
名称(能够不改动) -->
<short-name>scxh</short-name>
<!--
将此文件里标签绑定到一个uri上(映射名称) -->
<uri>http://www.scxh.com.cn</uri>
<!-- 注冊标签 -->
<tag>
<!--
此标签类的映射名称 -->
<name>forEach</name>
<!--
标签类的完整名称 -->
<tag-class>com.foreach.ForEach</tag-class>
<!--
表示标签内容不能为脚本代码(java代码)。还能够有JSP表示能够java代码,empty表示此标签没有标签内容
-->
<body-content>scriptless</body-content>
<!--
表示为此标签声明一个属性 -->
<attribute>
<!--
指定属性的名称 -->
<name>var</name>
<!--
表示此属性是否是必须的 -->
<required>true</required>
<!--
表示此属性的值能否够是一个表达式(能否够是LE表达式等) -->
<rtexprvalue>true</rtexprvalue>
<!--
表示此属性值得类型 -->
<type>java.lang.String</type>
</attribute>
<!--
表示为此标签声明一个属性 -->
<attribute>
<!--
指定属性的名称 -->
<name>items</name>
<!--
表示此属性是否是必须的 -->
<required>true</required>
<!--
表示此属性的值能否够是一个表达式(能否够是LE表达式等) -->
<rtexprvalue>true</rtexprvalue>
</attribute>
</tag>
</taglib>
JSP文件里的内容
<%@
page language="java"import="java.util.*"
pageEncoding="UTF-8"%>
<%@taglib
uri="http://www.scxh.com.cn"prefix="c"%>
<!DOCTYPE
HTMLPUBLIC
"-//W3C//DTDHTML 4.01 Transitional//EN">
<html>
<head>
<title>My JSP ‘Test1.jsp‘starting page</title>
</head>
<!-- 重写JSTL标签中的forEach标签。(遍历数组、集合)
-->
<body>
<%
List list = new
ArrayList();
list.add("list1");
list.add("list2");
list.add("list2");
list.add("list4");
list.add("list5");
pageContext.setAttribute("list", list);
%>
<%
Map map = new
HashMap();
map.put("1",
"map1");
map.put("2",
"map2");
map.put("3",
"map3");
map.put("4",
"map4");
map.put("5",
"map5");
pageContext.setAttribute("map", map);
%>
<%
int[] nums =
new int[]{1,2,3,4,5,6};
pageContext.setAttribute("nums", nums);
%>
<%
boolean[] bools =
new boolean[]{true,false,true};
pageContext.setAttribute("bools", bools);
%>
<br/>------------------------遍历list集合---------------------------------<br/>
<c:forEach
var="str"items="${list }"
>
${str }
</c:forEach>
<br/>------------------------遍历map集合---------------------------------<br/>
<c:forEach
var="entry"items="${map }">
key: ${entry.key } ,value: ${entry.value }
</c:forEach>
<br/>------------------------遍历int数组---------------------------------<br/>
<c:forEach
var="i"items="${nums }">
${i }
</c:forEach>
<br/>------------------------遍历boolean数组---------------------------------<br/>
<c:forEach
var="bool"items="${bools }">
${bool }
</c:forEach>
</body>
</html>
属性值的类型说明(属性的值必须用””引起来,EL表达式和<%= %>也一样)
假设在标签属性中传入一个字符串”5.5”,而在标签类中与此属性名相应成员变量是一个float类型的话,系统会自己主动将字符串转换为Float类型,但仅仅会自己主动转换八大基本类型;
假设标签类中的成员变量是一个对象,那么标签属性在传入值时必需要是一个对象,如:标签类中的属性是一个Date对象,引用变量名为date;那么标签属性的值必须是date=”<%=new Date()%>”;或者用EL表达式传入一个Date对象,不然系统不能自己主动转换
JSTL标签
EL表达式能够通过JSTL标签。迭代集合等功能
JSTL是SUN公司开发的一套标签库,使用JSTL能够在页面中实现一些简单的逻辑,从而替换页面中的脚本代码
在JSP中使用JSTL标签需完毕下面两个步骤
1、 导入jstl.jar和standerd.jar这两个JSTL的jar包
2、 在JSP页面使用<%@taglib url=” http://java.sun.com/jsp/jstl/core” prifix=”c” %> 元素导入标签库
url的地址是standerd.jar包中的/META-INF/c.tld文件里的<uri>http://java.sun.com/jsp/jstl/core </uri>标签中的地址
核心标签
<%@page
import="java.net.URLEncoder"%>
<%@page
import="cn.scxh.Person"%>
<%@
page language="java"import="java.util.*"
pageEncoding="UTF-8"%>
<%--导入JSTL包--%>
<%@taglib
uri="http://java.sun.com/jsp/jstl/core"prefix="c"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>
<!DOCTYPE
HTMLPUBLIC
"-//W3C//DTDHTML 4.01 Transitional//EN">
<html>
<head>
<base
href="<%=basePath%>">
<title>My JSP ‘index.jsp‘starting page</title>
<meta
http-equiv="pragma"content="no-cache">
<meta
http-equiv="cache-control"content="no-cache">
<meta
http-equiv="expires"content="0">
<meta
http-equiv="keywords"content="keyword1,keyword2,keyword3">
<meta
http-equiv="description"content="This is my page">
<!--
<link rel="stylesheet"type="text/css"
href="styles.css">
-->
</head>
<body>
<br/>----------------------------out标签的使用--------------------------------<br/>
<%-- out标签用于向浏览器输出内容 --%>
<%-- value的值为null时。输出default的值
--%>
<c:out
value="${aaaaa }"
default="aaa"></c:out>
<%-- escapeXml="true"表示将输出的内容转义后输出 --%>
<c:out
value="此标签会原样输出<a></a>"
escapeXml="true"></c:out>
<br/>----------------------------set标签的使用--------------------------------<br/>
<%--set标签用于把某一个对象存在指定的域范围,
或者设置Web域中的Map类型的属性对象或者javaBean类型的的属性对象的属性
--%>
<%-- 给data这个key赋值为:“xxx”。并存入pageContext对象中--%>
<c:set
var="data"
value="xxx"scope="page"></c:set>
${data }<%--输出此date,查看data的值是否是xxx
--%>
<%--将值存入map集合中 --%>
<%
Map map = new
HashMap();
pageContext.setAttribute("map", map);
%>
<%-- 设置key
设置value
设置要存入的map集合 --%>
<c:set
property="m1"
value="map1"target="${map }"></c:set>
${map.m1 }<%--输出map中m1中的值測试是否存入
--%>
<%--将值赋值给相应的javaBean对象 --%>
<%
Person person = new Person();
pageContext.setAttribute("person", person);
%>
<%--设置javaBean的属性 设置要此属性赋的值 设置要赋值的javaBean对象
--%>
<c:set
property="name"
value="张三"
target="${person }"></c:set>
${person.name }<%--输出此对象的name,測试是否被赋值 --%>
<br/>----------------------------catch标签的使用--------------------------------<br/>
<%--用于捕获异常对象,并存入pageContext对象中 --%>
<c:catch
var="exception"><%--var是设置此异常对象存入pageContext中的key
--%>
<%
int i = 1/0;
%>
</c:catch>
${exception.message }<%--输出此错误对象的异常描写叙述,測试pageContext对象中是否有此对象
--%>
<br/>----------------------------if标签的使用--------------------------------<br/>
<%-- 设置存储在域中的key
推断的表达式
设置此表达式的值的存储域--%>
<c:if
var="aaa"
test="${user==null }"scope="page">
你还没登陆
</c:if>
${aaa }<%--输出表达式的结果 --%>
<br/>----------------------------choose标签的使用--------------------------------<br/>
<%--和if else一样 --%>
<c:choose>
<c:when
test="${1+1==2 }">
你算对了
</c:when>
<c:otherwise>
你算错了
</c:otherwise>
</c:choose>
<br/>----------------------------foreach标签的使用--------------------------------<br/>
<%--遍历数组或集合 --%>
<%
List list = new
ArrayList();
list.add("aaa");
list.add("bbb");
list.add("ccc");
list.add("ddd");
pageContext.setAttribute("list", list);
%>
<%--设置存储在域中的key
设置要遍历的集合或数组--%>
<c:forEach
var="str"
items="${list }">
${str }
</c:forEach><br/>
<%--还能够当做for循环使用 --%>
<%--设置存储在域中的key
设置開始的数
设置结束的数 设置循环的步长--%>
<c:forEach
var="num"
begin="1"
end="9"
step="1">
${num }
</c:forEach>
<br/>
<%-- 用forEach实现表格间色显示 --%>
<%
List list1 = new
ArrayList();
list1.add("aaa");
list1.add("bbb");
list1.add("ccc");
list1.add("ddd");
list1.add("ddd");
list1.add("ddd");
pageContext.setAttribute("list1", list1);
%>
<style
type="text/css">
.odd{
background-color: #E6B489}
.even{background-color:
#EF6756}
tr:hover{background-color:
#D7DBE5}
</style>
<table
border="1"
width="20%"><%--varStatus="n"这个"n"用于记住当前遍历的次数(n.count)
--%>
<c:forEach
var="str"items="${list1 }"
varStatus="status">
<tr
class="${status.count%2==0?
‘even‘ : ‘odd‘ }">
<td>${str }</td>
</tr>
</c:forEach>
</table>
<br/>----------------------------url标签的使用--------------------------------<br/>
<%--又一次构建URL的地址,在URL后面加上session的id。而且此url前面没有加project名,重构过后会自己主动加上
--%>
<c:url
var="ur"
value="/Test1.jsp"><%--重构过后的超链接:/jstl/Test1.jsp;jsessionid=CC2C99E7E3C1B3255B647C67502096EA?name=%e4%b8%ad%e5%9b%bd--%>
<%--假设在url标签内嵌套此标签,表示在url后面加入传递參数:?name=zhangsan
--%>
<c:param
name="name"value="中国"></c:param><%--假设value的值是中文。此标签会自己主动调用
URLEncoder.encode(temp,"UTF-8");进行加码--%>
</c:url>
<a
href="${ur }">此超链接的url后面跟有session对象的id</a>
<br/>----------------------------forTokens标签的使用--------------------------------<br/>
<%-- 用于切割字符串 --%>
<%
String str = "aaa,bbb,cccc,ddd,eeee";
pageContext.setAttribute("str", str);
%>
<%--存储每次切割字符到域的key
要被切割的字符串
以什么切割--%>
<c:forTokens
var="ss"
items="${str }"
delims=",">
${ss }<br/>
</c:forTokens>
<br/>----------------------------remove标签的使用--------------------------------<br/>
<%
pageContext.setAttribute("page1",
"page1");
%>
${page1 }
<c:remove
var="page1"
scope="page"/><%--移除page域中的映射关系
--%>
${page1 }
</body>
</html>
<br/>--------------------import标签和jsp:include标签一样使用(相同是动态包括)--- ----------------<br/>
<br/>--------------------redirect标签是请求重定向--------------------------<br/>
软件国际化(i18n:internationalization)
软件开发时。要使它能同一时候因对世界不同地区和国家的訪问,并针对不同地区和国家的訪问,提供对应的、符合来訪者阅读习惯的页面或数据,
此运行文件JDK_1.7_X64\jdk\ben\native2ascii.exe用于将中文转换成unicode编码
国际化所具备的特征:
对程序中国固定的文本元素或错误提示信息。状态信息等须要依据来訪者的地区和国家选择不同语言的文本为之服务
对程序动态产生的数据,如(日期,货币等)软件应用能依据当前所在的国家或地址的文化习惯进行显示
资源包(功能名称国际化)
对于软件中的菜单条、导航条、错误提示信息,状态信息等这些固定的文本信息,能够把它们写在一个properties文件里,并依据不同的国家编写不同的properties文件,这一组properties文件称之为一个资源包
资源文件的名称规范
基名_语言_国家 如:mag_zh_CN mag就表示:基名 , zh表示:汉语。CN表示:中国; i18n_en_US i18n表示:基名。en表示:英语。US表示:美国
Locale类是一个区域信息类,里面有非常多代表国家和语言的常量
假设一个文件名称就为mag,那么这个文件就是一个默认的文件。当没有找到语言包时就找此文件
在javaAPI中提供了一个ResourceBundle类用于描写叙述一个资源包,而且ResourceBundle类提供了相应的方法getBundle,这种方法能够依据来訪者的国家地区自己主动获取与之相应的资源文件予以显示
演示样例:(当浏览器请求的是zh_CN。就将i18n_zh_CN.properties文件返回,假设浏览器请求的是en_US就将i18n_en_US.properties文件返回)
<%@
page language="java"import="java.util.*"
pageEncoding="UTF-8"%>
<!DOCTYPE
HTMLPUBLIC
"-//W3C//DTDHTML 4.01 Transitional//EN">
<html>
<head>
<title>登录</title>
</head>
<%
Locale locale = request.getLocale();//获取浏览器的语言(此类中有非常多代表国家和语言的常量)
ResourceBundle rb = ResourceBundle.getBundle("lang.i18n",locale);//通过语言来获取相应的基名为i18n的文件
%>
<body>
<p><%=rb.getString("i18n.Test1.title")
%></p><%--获取此文件里“i18n.Test1.title”这个key
的值,以下其它的都一样 --%>
<form
action="#">
<%=rb.getString("i18n.Test1.username")
%><input
type="text"/><br/>
<%=rb.getString("i18n.Test1.password")
%><input
type="text"/><br/>
<input
type="submit"value="<%=rb.getString("i18n.Test1.submit")
%>"/>
</form>
</body>
</html>
数字国际化
int i = 1000000;
NumberFormat number = NumberFormat.getCurrencyInstance();
String str = number.format(i);
System.out.println(str);//打印为:¥ 1,000,000.00
number = NumberFormat.getCurrencyInstance(Locale.US);
str = number.format(i);
System.out.println(str);//打印为:$1,000,000.00
float f = 0.5f;
number = NumberFormat.getPercentInstance();
str = number.format(f);
System.out.println(str);//打印为:50%
过滤器(Filter)
WEB开发者通过Filter(过滤器)技术,对webserver管理的全部WEB资源,如:JSP,Servlet。静态图片文件或静态html文件等进行拦截,从而实现一些特殊的功能。
比如实现URL主要的权限訪问控制、过滤敏感词汇、压缩响应信息等一些高级功能。假设一个资源被两个过滤器同一时候过滤,先后顺序看在web.xml配置文件里<filter-mapping>标签的先后顺序
ServletAPI中提供了一个Filter接口,假设一个类实现了Filter接口,那么这个类就是一个过滤器类。通过这个Filter。开发者能够实现用户在訪问某个目标资源之前,对訪问的请求和响应进行拦截
过滤器的声明周期:在servlet创建时创建,并永久驻留在内存中,在servlet销毁时销毁。当过滤器创建时会调用init(FilterConfig arg0);方法。销毁时会调用destroy();方法
演示样例:
通过过滤器让用户訪问的全部资源的编码格式为UTF-8
过滤器中的代码
public
class FilterDemo1 implements Filter{
private String
code = null;
//当实用于訪问/*(此web下资源时)就会訪问此过滤器
@Override
public
void doFilter(ServletRequest request, ServletResponseresponse,
FilterChain arg2) throws IOException,ServletException {
System.out.println("过滤器被运行了");
//将code设置到到request和response及浏览器的打开模式中
request.setCharacterEncoding(code);
response.setCharacterEncoding(code);
response.setContentType("text/html;charset="+code);
//表示转发到用户所訪问的资源上面去
arg2.doFilter(request,response);
}
//此方法会在过滤器创建时调用
@Override
public
void init(FilterConfig arg0)
throws ServletException {
// TODO Auto-generatedmethod stub
//获取xml文件<init-param>标签中code的value
String code = arg0.getInitParameter("code");
if(code==null)//假设没有配置,就使用UTF-8默认编码
code="UTF-8";
this.code = code;
}
//此方法在过滤器销毁时调用
@Override
public
void destroy() {
// TODO Auto-generatedmethod stub
}
}
servlet中代码
public
void doGet(HttpServletRequest request, HttpServletResponseresponse)
throws ServletException, IOException {
//接收JSP传入的值
String username = request.getParameter("username");
//打印这个值,查看是否是乱码
response.getWriter().write(username);
}
public
void doPost(HttpServletRequest request, HttpServletResponseresponse)
throws ServletException, IOException {
doGet(request, response);
}
web.xml文件里注冊过滤器
<!-- 注冊过滤器 -->
<filter>
<!—给过滤器取一个名字-->
<filter-name>FilterDemo1</filter-name>
<!—过滤器类的完整名称-->
<filter-class>cn.scxh.FilterDemo1</filter-class>
<!-- 设置init方法接收的參数 -->
<init-param>
<param-name>code</param-name>
<param-value>UTF-8</param-value>
</init-param>
</filter>
<!-- 当訪问此web应用中全部的资源时,都用过滤器拦截 -->
<filter-mapping>
<!—当訪问”/*”以下的资源时。都用FilterDemo1这个过滤器拦截-->
<filter-name>FilterDemo1</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
JSP中代码
<body>
<!—将text中的中文字符传入Test1这个servlet-->
<form
action="${pageContext.request.contextPath }/servlet/Test1"
method="post">
<input
type="text"name="username"/>
<input
type="submit"/>
</form>
</body>
过滤器默认仅仅拦截用户提交的请求。假设为server转发,那么默认将不会拦截(但能够手动设置以下的标签进行拦截)
<filter-mapping>
<!—当訪问”/*”以下的资源时。都用FilterDemo1这个过滤器拦截-->
<filter-name>FilterDemo1</filter-name>
<url-pattern>/*</url-pattern>
<dispatcher>REQUEST</dispatcher><!—缺省值是request,表示仅仅对用户提交的请求拦截,假设写了其它值。此缺省值将不在生效。但能够手动像这样再定义一次就也可了-->
<dispatcher>FORWARD</dispatcher><!—表示能够拦截转发的请求-->
<dispatcher>INCLUDE</dispatcher><!—表示能够拦截动态包括的请求-->
<dispatcher>ERROR</dispatcher><!—表示能够拦截错误时跳转页面时拦截(在web.xml文件里配置错误页面这样的)-->
</filter-mapping>
监听器(8个监听4个事件)
在serlvet中定义了多种类型的监听器,它们用于监听的事件源分别为serlvetContext,HttpSession和ServletRequest这三个对象
servlet规范针对这三个域对象(ServletContext。session,request)上的操作,又把这多种类型的监听器划分为三种类型:
监听三个域对象创建和销毁的事件监听器
监听域对象中的属性的添加和删除的事件监听器
监听绑定到HttpSession域中的某个对象的状态的时间监听器
每一个域对象的监听器中传入的ServletContextEvent类型的參数都能够得到当前的域对象。比方session的监听器能够通过传入的參数获取到当前的session域对象。
监听域对象属性(映射关系)的事件监听器还能够通过传入參数的getName和getValue方法获取当前操作属性(映射关系)的key和value
servletContext监听器
servletContext对象的创建,当web应用被公布时创建。当卸载web应用时销毁。并会被实现了ServletContextListener接口的监听器所监听到
//监听servletContext域对象创建和销毁的监听器
public
class MyServletContextListener implements ServletContextListener{
//当servletContext域对象销毁时此方法会被运行
@Override
public
void contextDestroyed(ServletContextEvent event) {
// TODO Auto-generated method stub
System.out.println("servletContext域对象被销毁了");
}
//当servletContext域对象创建时此方法会被运行
@Override
public
void contextInitialized(ServletContextEvent arg0) {
// TODO Auto-generatedmethod stub
System.out.println("servletContext域对象被创建了");
}
}
//监听servletContext域对象的属性的监听器(当对sevletContext对象域中的映射关系进行增,删,改时。都会被实现了ServletContextAttributeListener接口的监听器所监听到)
public
class MyServletContextAttributeListener implementsServletContextAttributeListener{
//当servletContext域对象中加入了新的映射关系就调用此方法
@Override
public
void attributeAdded(ServletContextAttributeEvent event) {
// TODO Auto-generatedmethod stub
System.out.println("servletContext域中添加了此映射关系:"+event.getName()+"="+event.getValue());
}
//当servletContext域对象中删除了映射关系就调用此方法
@Override
public
void attributeRemoved(ServletContextAttributeEvent event) {
// TODO Auto-generatedmethod stub
System.out.println("servletContext域中删除了此映射关系:"+event.getName()+"="+event.getValue());
}
//当servletContext域对象中改动了映射关系的值。就调用此方法
@Override
public
void attributeReplaced(ServletContextAttributeEvent event) {
// TODO Auto-generatedmethod stub
System.out.println("servletContext域中改动了此映射关系:"+event.getName()+"="+event.getValue());
}
}
<!-- 注冊监听器 -->
<listener>
<listener-class>cn.listener.MyServletContextListener</listener-class>
</listener>
<listener>
<listener-class>cn.listener.MyServletContextAttributeListener</listener-class>
</listener>
session域对象和erquest对象的监听器和上面的方法都一样。只是须要实现的接口不同
监听session对象的创建和销毁须要实现HttpSessionListener接口,监听此域中属性的监听器须要实现HttpSessionAttribteListener接口
session对象的创建,当第一次调用request.getSession()方法时创建session对象;
session对象的销毁,手动调用session对象的invalidate()方法和超时(session对象默认不操作情况下存活30分钟)session对象会被销毁,并会被实现了HttpSessionListener接口的监听器所监听到。
当对session对象域中的映射关系进行 增。删。改时,都会被实现了HttpSessionAttribteListener接口的监听器所监听到
监听request对象的创建和销毁须要实现ServletRequestListener接口,监听此域中属性的监听器须要实现ServletRequestAttributeListener接口
request对象的创建和销毁,当用户每发送一次请求时,request对象就被创建一次,当server返回了response对象后此request对象将被销毁,并会被实现了ServletRequestListener接口的监听器所监听到;
当对request对象域中的映射关系进行 增,删。改 时,都会被实现了ServletRequestAttributeListener接口的监听器所监听到
监听绑定到HttpSession域中的某个对象的状态的时间监听器
感知型监听器,谁实现了这个些接口,就能够获取在session域对象中的状态,这两种监听器不须要注冊
HttpSessionActivationListener实现了此接口就能够感知自己何时随着HttpSession对象钝化和活化(钝化和活化的意思是当在server中暂停了当前的web应用。那么此web应用中的全部域和域中的对象就钝化了,继续当前web应用,那么此web应用中的全部域和域中的对象就活化了)
sessionDidActivate(HttpSessionEvent event);当此对象和session域对象一起活化了时就会调用此方法
sessionWillPassivate(HttpSessionEvent event) ;当此对象和session域对象一起钝化了时就会调用此方法
HttpSessionBindingListener实现了此接口就能够感知自己何时被加入到了session对象,和被session对象删除了
valueBound(HttpSessionBindingEventevent);当自己被加入到了session域中时就会调用此方法
valueUnbound(HttpSessionBindingEventevent);当自己被session域对象移除时就会调用此方法
JDBC大数据(MySQL)
大数据分页
就是指定每次查询的条数
select* from emp limit m,n;
limitkeyword写在查询语句的最后面
m表示 从第几行開始显示(0開始算)
n表示 一次显示多少行
大数据存储
在实际开发中。一般不须要把大文本或二进制数据保存到数据库中(会减少查询效率)
大数据也称为LOB,LOB又分为clob和blob
clob用于 存储大文本(字符文本)
blob用于存储二进制数据(图片,声音,二进制文件等)
在MySQL中的clob是Text;在MySQL中的Text和blob的类型又分为:在MySQL中LOB的类型最大的存储大小为4G,所以一般要存储的文件最大不能超过4G
Text:(存储文本文件)
TINYTEXT、TEXT、MEDIUMTEXT和LONGTEXT
blob:(存储二进制文件)
TINYBLOB、BLOB、MEDIUMBLOB和LONGBLOB
以下说明了每种类型的最大存储大小
列类型 |
存储需求 |
CHAR(M) |
M个字节,0 <= M <= 255 |
VARCHAR(M) |
L+1个字节,当中L <= M 且0 <= M <= 65535(參见以下的凝视) |
BINARY(M) |
M个字节。0 <= M <= 255 |
VARBINARY(M) |
L+1个字节,当中L <= M 且0 <= M <= 255 |
TINYBLOB, TINYTEXT |
L+1个字节。当中L < 28 为256B |
BLOB, TEXT |
L+2个字节,当中L < 216 为64KB |
MEDIUMBLOB, MEDIUMTEXT |
L+3个字节,当中L < 224 为16M |
LONGBLOB, LONGTEXT |
L+4个字节,当中L < 232 为4G |
ENUM(‘value1‘,‘value2‘,...) |
1或2个字节,取决于枚举值的个数(最多65,535个值) |
SET(‘value1‘,‘value2‘,...) |
1、2、3、4或者8个字节。取决于set成员的数目(最多64个成员) |
演示样例1:(向数据库中存储一个.txt的文本文件)
MySQL代码
create table t_temp1(
idint primary key,
data TEXT
)
java代码
//向数据库中存取文本文件
public
class Test1 {
//设置数据库配置文件的位置
static{
JDBCUtil3.setPropertiesUrl("db.properties");
}
public
static void main(String[] args) {
//把一个文本文件存储数据库中
fun1();
//把这个文本文件取出,并存放到指定的文件夹
fun2();
}
private
static void fun1() {
//获取数据库连接
Connection cn = JDBCUtil3.getNewConnection();
//推断连接是否为null
if(cn ==
null)
return;
PreparedStatement ps = null;
//传入要存入的文本文件
File file = new File("J:"+File.separator+"1.txt");
//推断这个文件是否存储在
if(!file.exists()){
throw
new RuntimeException("没有文件能够存入到数据库中");
}
//创建一个字符流
Reader reader = null;
try {
//设置sql语句
ps = cn.prepareStatement("insert into t_temp1(id, data) values(?
,?)");
ps.setInt(1, 1);
//通过文件创建一个输入文件字符流
reader = new FileReader(file);
//将这个流设置为sql的第二个參数
ps.setCharacterStream(2, reader,file.length());
//运行这个sql语句
ps.executeUpdate();
} catch (SQLException e) {
//
TODO Auto-generatedcatch block
e.printStackTrace();
} catch (FileNotFoundException e) {
//
TODO Auto-generatedcatch block
e.printStackTrace();
}
//关闭数据库
JDBCUtil3.close(null, ps, cn);
}
//取出数据中的这个文本文件
private
static void fun2() {
//获取数据库连接
Connection cn = JDBCUtil3.getNewConnection();
//推断连接是否为null
if(cn ==
null)
return;
//创建一个数据库对象
PreparedStatement ps = null;
//创建一个结果集
ResultSet rs = null;
//创建一个输入字符流
Reader reader = null;
//创建一个输出字符流
Writer writer = null;
try {
//或的数据库对象,传入一个sql语句
ps = cn.prepareStatement("select data from t_temp1");
//运行这个sql语句,获得一个结果集
rs = ps.executeQuery();
//遍历这个结果集
while(rs.next()){
//以字符输入流的方式取出data字段中的字符
reader = rs.getCharacterStream(1);
//创建一个输出字符流,并指定要输出到的文件
writer = new
FileWriter("J:"+File.separator+"2.txt",true);
//把字符输入流的字符,放入到字符输出流中(就是从数据库中取出数据,放入某个文件里)
//创建一个字符缓存
char[] c =
new char[1024];
int len = -1;
//把输入流中取出字符放入字符缓存中,然后输出流就从字符缓存中去取出字符
while((len = reader.read(c))>0){
writer.write(c, 0, len);
}
//刷新一下,好让输出流将字符全然输出到文件里
writer.flush();
}
} catch (SQLException e) {
//
TODO Auto-generatedcatch block
e.printStackTrace();
} catch (IOException e) {
//
TODO Auto-generatedcatch block
e.printStackTrace();
}finally{
try {
//关闭结果集、字符输入流、字符输出流
rs.close();
reader.close();
writer.close();
} catch (IOException e) {
//
TODO Auto-generated catch block
e.printStackTrace();
} catch (SQLException e) {
//
TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
演示样例2:(向数据库中存储一张.jpg图片的二进制文件)
MySQL代码
createtable t_temp2(
idint primary key,
dataMEDIUMBLOB
)
java代码
//向数据库中存取二进制文件(图片)
public
class Test2 {
//设置数据库配置文件的位置
static{
JDBCUtil3.setPropertiesUrl("db.properties");
}
public
static void main(String[] args) {
//把一个二进制文件存储数据库中
//fun1();
//把这个二进制文件取出,并存放到指定的文件夹
fun2();
}
//把一个二进制文件存储数据库中
private
static void
fun1() {
//获取数据库连接
Connection cn = JDBCUtil3.getNewConnection();
PreparedStatement ps = null;
//创建一个输入字节流
InputStream in = null;
try {
//通过sql语句获取到数据库对象
ps = cn.prepareStatement("insert into t_temp2(id,data) values(?,?
)");
//设置id
ps.setInt(1, 1);
//创建一个文件输入字节流
in = new FileInputStream("J:"+File.separator+"1.jpg");
//将这个流设置为sql的第二个參数。in.available()获取流中的字节大小
ps.setBinaryStream(2, in,in.available());
//运行这个sql语句
ps.executeUpdate();
} catch (SQLException e) {
//
TODO Auto-generatedcatch block
e.printStackTrace();
} catch (FileNotFoundException e) {
//
TODO Auto-generatedcatch block
e.printStackTrace();
} catch (IOException e) {
//
TODO Auto-generatedcatch block
e.printStackTrace();
}finally{
try {
//关闭文件输入字节流
in.close();
} catch (IOException e) {
//
TODO Auto-generated catch block
e.printStackTrace();
}
}
}
//取出数据中的这个二进制文件
private
static void fun2() {
//获取数据库连接
Connection cn = JDBCUtil3.getNewConnection();
PreparedStatement ps = null;
InputStream in = null;
OutputStream out = null;
ResultSet rs = null;
try {
//通过sql语句获取数据库对象
ps = cn.prepareStatement("select data from t_temp2");
//运行sql语句。并返回一个结果集
rs = ps.executeQuery();
if(rs.next()){
//以字节输入流的方式获取结果集中数据
in = rs.getBinaryStream("data");
//创建一个文件字节输出流。并指定要输出的路径
out = new FileOutputStream("J:"+File.separator+"2.jpg");
//创建一个字节缓存
byte[] b =
new byte[1024];
int len = -1;
//让字节输入流的输出缓存到字节缓存区。然后字节输出流就从缓存中取出放入字节输出流中
while((len = in.read(b))>0){
out.write(b,0,len);
}
}
//关闭流
in.close();
//刷新输出流,好让流中的数据所有写入到文件里
out.flush();
out.close();
} catch (SQLException e) {
//
TODO Auto-generatedcatch block
e.printStackTrace();
} catch (FileNotFoundException e) {
//
TODO Auto-generatedcatch block
e.printStackTrace();
} catch (IOException e) {
//
TODO Auto-generatedcatch block
e.printStackTrace();
}finally{
//关闭数据库
JDBCUtil3.close(rs, ps, cn);
}
}
}
批处理
当须要向数据库发送一批SQL语句运行时,应避免向数据库一条条的发送运行,而应採用JDBC的批处理机制,提升运行效率
第一种方式:(操作方式不一致,用此方式。
如:即有增也有删的多条sql语句)
Statement类 此Statement对象中有一个list集合,将传入的sql缓存起来(通过Connection数据库连接对象的 createStatement()方法获取此对象)
Statement.addBatch(
sql ); 将sql语句缓存至Statement的list集合中
Statement.executeBatch(); 依次运行Statement类中list中缓存的全部sql语句
Statement.clearBathc(); 清除list集合中的全部sql语句
另外一种方式:(操作方式一致,用此方式。
如:仅仅有向数据库中添加的多条sql语句)
Statement的子类PreparedStatement类
能够依据?向sql设置值
演示样例1:(操作方式不一致,用此方式。
如:即有增也有删的多条sql语句)
MySQL代码
createtable temp1(
id int primary key,
data varchar(100)
)
java代码
//批处理(让sql语句一次提交)
public
class Test1 {
//设置数据库配置文件的位置
static{
JDBCUtil3.setPropertiesUrl("db.properties");
}
public
static void main(String[] args) {
//向temp1表中插入两条数据,并删除第一条
fun1();
}
//向temp1表中插入两条数据,并删除第一条
private
static void fun1() {
//准备好三条sql语句
String sql1 =
"insert into temp1(id, data) values(1, ‘aa‘)";
String sql2 =
"insert into temp1(id, data) values(2, ‘bb‘)";
String sql3 =
"delete from temp1 where id=1";
Connectioncn = JDBCUtil3.getNewConnection();
Statement s = null;
try {
//通过cn获取到Statement对象
s = cn.createStatement();
//将sql缓存到Statement对象中的list缓存集合中
s.addBatch(sql1);
s.addBatch(sql2);
s.addBatch(sql3);
//按顺序依次运行list缓存集合中的全部sql语句,并返回的是每条sql影响了的行数
int数组
int[] count = s.executeBatch();
//输出每条sql影响了的行数
for(int i : count){
System.out.println(i);
}
//关闭Statement对象
s.close();
} catch (SQLException e) {
//
TODO Auto-generatedcatch block
e.printStackTrace();
}finally{
JDBCUtil3.close(null,
null, cn);
}
}
}
演示样例2:(操作方式一致,用此方式。如:仅仅有向数据库中添加的多条sql语句)
MySQL代码
createtable temp1(
idint primary key,
namevarchar(20)
)
java代码
//批处理(让sql语句一次提交)
//操作方式一致。用此方式。
如:仅仅有向数据库中添加的多条sql语句
public
class Test2 {
//设置数据库配置文件的位置
static{
JDBCUtil3.setPropertiesUrl("db.properties");
}
public
static void main(String[] args) {
//向数据库中加入1000001条记录
fun1();
}
//向数据库中加入1000001条记录
/*
Oracle仅仅须要8秒
MySQL须要4个小时左右
*/
private
static void fun1() {
//记住当前的时间
long date = System.currentTimeMillis();
Connection cn = null;
PreparedStatement ps = null;
try {
//获取连接
cn = JDBCUtil3.getNewConnection();
//获取数据库对象
ps = cn.prepareStatement("insert into temp1(id, name) values(?, ?
)");
//遍历1000001
for(int i=1;i<=1000001; i++){
//以i变量设置id
ps.setInt(1, i);
//设置name
ps.setString(2,
"aaaaa"+i);
//将sql语句存入ps中的list集合缓存中
ps.addBatch();
//假设缓存有2000条sql语句就运行(由于内存不够)
if(i%2000==0){
//运行list集合中的全部sql语句
ps.executeBatch();
//当运行完毕后清除list中的全部sql语句
ps.clearBatch();
}
}
//运行后面多出来的1条sql,并清除它
ps.executeBatch();
ps.clearBatch();
} catch (SQLException e) {
//
TODO Auto-generatedcatch block
e.printStackTrace();
}finally{
//关闭数据库
JDBCUtil3.close(null, ps, cn);
//输出此次执行共消耗了多少秒
System.out.println("共用:"+(System.currentTimeMillis()-date)/1000+"秒");
}
}
}
当insert插入数据时获取此条记录的主键的值
当此表中的id字段为自增长时。我们在插入一条数据时能够用下面方式获取到插入数据的主键的值
ps = cn.prepareStatement("insert intotemp1(name) values( ?)");
ps.executeUpdate();
//获取到此记录的主键的值,并以结果集返回,仅仅指针对insert语句有效
ResultSetrs = ps.getGeneratedKeys();
System.out.println(rs.getInt(1));//输出此条记录的主键的值
JDBC调用存储过程
接口CallableStatement; 它的父接口为PreparedStatement
获取此接口需调用Connection连接对象的prepareCall(String
str);方法来获取;
str參数的规范:“{call
存储过程名(?
,?)}”;
用Connection对象的setString(1,”abc”);//来给占位符”?”赋值,对于要输出的參数用registerOutParameter(2,Types.VARCHAR2);来设置,第二个參数用于指定输出參数的类型。Types类中有与数据库数据类型匹配的常量
演示样例:
存储过程:
delimiter$$//分界的意思。$$符号是敲回车或遇到”;”号不运行,直到再读取到两个$$时运行
CREATEPROCEDURE dempSp(IN
inputParam VARCHAR(100), INOUT inOutParam
VARCHAR(1000))
begin
select concat(‘welcome-----’,
inputParam) into inOutParam;
//将“welcome-----”字符串,连接到传入參数inputParam的前面,并赋值给inOutParam。返回
end$$
delimiter;
java代码调用存储过程
//获取数据库连接
cn= JDBCUtil3.getNewConnection();
//获取存储过程对象
CallableStatementcs = cn.prepareCall(“{call demoSp(?, ?)}”);
//给第一个參数设置。假设此參数不用返回的话。就直接传入要传入的參数就可以
cs.setString(1,”abc”);
//因第二个參数要返回,所以要指定參数的类型,Types类中有与数据库类型相应的常量
cs.registerOutParameter(2,Types.VARCHAR);
//运行存储过程
cs.execute();
//获取第二个有返回值的參数
Stringvalue = cs.getString(2);
事务
事务指逻辑上的一组操作,组成这组操作的各个单元,要么所有成功,要么所有不成功
在java中事务是默认自己主动提交的,当我们仅仅想一条sql语句时,事务直接就提交了
事务的特性
原子性(Atomicity)
原子性是指事务是一个不可切割的工作单位,事务中的操作要么都发生,要么都不发生
一致性(Consistency)
事务必须使数据库从一个一执性状态变换到另外一个一致性状态 (比方转账。A转账给B,转账前和转账后A和B的钱的总和必须一致)
隔离性(Isolation)
事务的隔离性是多个用户并发訪问数据库时,数据库为每个用于开启的事务。不能被其它事务的操作数据所干扰,多个并发事务之间要互相隔离
持久性(Durability)
持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来即使数据库发生问题也不应该对其有不论什么影响
隔离性(必须用在事务之中)
多个线程开启各自事务操作数据库中数据时,数据库系统要负责隔离操作,以保证各个线程在获取数据时的准确性。不然会引发脏读、不可反复读、幻读
脏读:
脏读是指一个事务读到了还有一个事务中未提交的数据
不可反复读:
针对一条记录,每次读取记录前后不一致
虚读:
同一张表,读取记录前后的记录数不一致
隔离级别的分类(级别越高,消耗性能就越高)
READUNCOMMITTED: 此级别
脏读、不可反复读、虚读,都有可能发生(级别最低)
READCOMMITTED: 此级别 能避免发生脏读,但不可反复读、虚读。有可能发生
REPEATABLEREAD: 此级别 能避免发生脏读、不可反复读,但虚读,有可能发生(默认级别,一般也是用此级别,所以一般不用设置隔离级别)
SERIALIZABLE: 此级别 能避免脏读、不可反复读、虚读。但消耗性能最高
MySQL中控制事务隔离级别的语句:
[email protected]@tx_isolation; //查看当前的事务隔离级别
settransaction isolation level 要设置的级别(四种之中的一个); //设置隔离级别,必须在开启事务之前进行设置级别。否则无效
java中控制事务隔离级别的语句:
Connection连接对象的.setTransactionIsolation(Connection.TRANSACTION_READ_UNCOMMITTED); 设置事务的隔离级别,但 必须在开开启手动提交事务(以下这句)之前设置,否则无效。隔离的四大级别在Connection连接类中有与之相应的常量
Connection连接对象的.setAutoCommit(false); 设置数据库不自己主动提交事务
总结:隔离性。就是事务没提交前。所读取到的数据不能有
脏读、不可反复读、虚读 等异常发生。也就是所当我的事务还没提交前,查询出别人对数据更新后的数据,那么这是不正确的,所以就须要更改级别来控制。还没提交事务前读取的数据必须保持一致
数据库中处理事务的keyword是:
start transaction 开启手动提交事务
rollback 回滚事务
commit 提交事务
java中处理事务的方法是:
Connection连接对象的.setAutoCommit(false); 设置数据库不自己主动提交事务
PreparedStatement数据库对象的.commit(); 提交事务
PreparedStatement数据库对象的.rollback(); 回滚事务
sp = Connection连接对象的.setSavepoint(); 设置回滚点
PreparedStatement数据库对象的.rollback(sp ); 回滚事务到回滚点
演示样例1:
//当id为1的用户给id为2的用户转账,设置统一提交事务
Connection cn = null;
PreparedStatement ps = null;
try{
//获取连接
cn = JDBC.getConnection();
//设置事务不自己主动提交
cn.setAutoCommit(false);
//获取数据库操作对象
ps = cn.prepareStatement("update account set money=money-100 where id=1");
//运行sql语句
ps.executeUpdate();
//获取数据库操作对象
ps = cn.prepareStatement("update account set money=money+100 where id=2");
//运行sql语句
ps.executeUpdate();
}catch(Exception e){
e.printStackTrace();
try {
//当上面语句运行异常时回滚事务
cn.rollback();
} catch (SQLException e1) {
// TODO Auto-generatedcatch block
e1.printStackTrace();
}
}finally{
try {
//假设没有异常就直接提交
cn.commit();
} catch (SQLException e) {
// TODO Auto-generatedcatch block
e.printStackTrace();
}finally{
//关闭数据库连接
JDBC.close(null, ps, cn);
}
}
演示样例2:设置回滚点
//id1给id2转账100,id2又给id3转账100;当id2给id3转账时,发生异常,id1给id2转账照常运行
Connection cn = null;
PreparedStatement ps = null;
Savepoint sp = null;
try{
//获取连接
cn = JDBC.getConnection();
//设置事务不自己主动提交
cn.setAutoCommit(false);
//获取数据库操作对象
ps = cn.prepareStatement("update account set money=money-100 where id=1");
//运行sql语句
ps.executeUpdate();
//获取数据库操作对象
ps = cn.prepareStatement("update account set money=money+100 where id=2");
//运行sql语句
ps.executeUpdate();
sp = cn.setSavepoint();//设置回滚点
//获取数据库操作对象
ps = cn.prepareStatement("update account set money=money-100 where id=2");
//运行sql语句
ps.executeUpdate();
//int i =1/0;//当此处出错时,回滚点上面的sql照常运行
//获取数据库操作对象
ps = cn.prepareStatement("update account set money=money+100 where id=3");
//运行sql语句
ps.executeUpdate();
}catch(Exception e){
e.printStackTrace();
try {
//当上面语句运行异常时回滚事务
cn.rollback(sp);
} catch (SQLException e1) {
// TODO Auto-generatedcatch block
e1.printStackTrace();
}
}finally{
try {
//假设没有异常就直接提交
cn.commit();
} catch (SQLException e) {
// TODO Auto-generatedcatch block
e.printStackTrace();
}finally{
//关闭数据库连接
JDBC.close(null, ps, cn);
}
}
连接池
就是先创建多个数据库的连接存储在一个容器中。当要用时直接取出。用完后不用关闭,直接再放回到容器中
Tomcat自带的连接池
2.打开apache-tomcat-6.0.29\conf文件夹中的content.xml文件.
在此文件里配置Resource
Xml代码
1 <?
xml version=‘1.0‘encoding=‘utf-8‘?>
2 <Context>
3 <!-- Default set of monitored resources-->
4 <WatchedResource>WEB-INF/web.xml</WatchedResource>
5
6 <!--数据源-->
7 <Resource
8 name="jdbc/DBSource" <!--数据源名称,格式通常为jdbc/xxx名称-->
9 type="javax.sql.DataSource" <!--数据源类型-->
10 username="scott" <!--连接数据库用户名-->
11 password="tiger" <!--连接数据库密码-->
12 maxIdle="2" <!--最大空暇数,数据库连接的最大空暇时间。超过空暇时间,数据库连接将被标记为不可用,然后被释放。设为0表示无限制。-->
13 maxWait="5000" <!--最大的等待时间,单位毫秒。
假设超过此时间将接到异常。设为-1表示无限制-->
14 url="jdbc:oracle:thin:@localhost:1521:orcl" <!--数据库的连接-->
15 -- driverClassName="oracle.jdbc.driver.OracleDriver" <!—驱动类-->
16 maxActive="10" <!--连接池的最大数据库连接数。设为0表示无限制-->
17 />
18 </Context>
3.在Web项目中的web.xml里面须要引用数据源:
Xml代码
19 <!-- 引用数据源; -->
20 <resource-ref>
21 <description>Oracle dataSource</description>
22 <res-ref-name>jdbc/DBSource</res-ref-name> <!--数据源类型-->
23 <res-type>javax.sql.DataSource</res-type>
24 <res-auth>Container</res-auth>
25 <res-sharing-scope>Shareable</res-sharing-scope>
26 </resource-ref>
4.在java代码中,写一个方法调用一下.
比方:在一个DataSourceDemo
Java代码
27 package pack.java.datasource.demo;
28
29 import java.sql.Connection;
30 import java.sql.SQLException;
31 import javax.naming.InitialContext;
32 import javax.naming.NamingException;
33 /**
34 * 数据源实例;
35 * @authorzhouhaitao
36 *
37 */
38 public classDataSourceDemo {
39 /**
40 * 依据datasourceName获取数据源;
41 * @paramdsName
42 * @return
43 */
44 public Connection getConnection(){ // String dsName
45 Connectionconnection =
null;
46 try {
47 //初始化,获取上下文对象;
48 InitialContext context =
new InitialContext();
49
50 //依据datasourceName获取dataSource;
51 javax.sql.DataSource dataSource =(javax.sql.DataSource) context.lookup("java:comp/env"); //+dsName
52
53 try {
54 //从数据源中获取连接;
55 connection =dataSource.getConnection();
56 } catch (SQLException e) {
57 //TODO Auto-generated catch block
58 e.printStackTrace();
59 }
60 } catch (NamingException e) {
61 // TODOAuto-generated catch block
62 e.printStackTrace();
63 }
64 return connection;
65 }
66 }
自己定义连接池
其它链接池到E:\practice\MySpace\javautil工作空间中去參考
/*
此类是一个数据库连接池。初始化连接池数量能够再配置文件里设置
此类是一个线程安全的,当线程池中没有链接时,休眠该线程,当连接返回时再唤醒休眠的线程
*/
public
class ConnectionPool {
//数据库的username
private
static String user =
null;
//数据库的password
private
static String password =
null;
//数据库的连接地址
private
static String url =
null;
//数据的的驱动地址
private
static String driverClassName =
null;
//最大存活的连接数量
private
static int maxActiveConnectionNumber = 0;
//存储连接的List集合
private
static List<Connection> pool =
new LinkedList<Connection>();
//标识为,标志pool集合中是否还有连接
private
static boolean bool =
true;
//初始化数据库连接,数量
static{
//获取文件的输入流
InputStream in = ConnectionPool.class.getClassLoader().getResourceAsStream("db.properties");
//创建一个Properties文件
Properties properties = new Properties();
try {
//从输入流中获取Properties文件的内容
properties.load(in);
//获取Properties文件里的各种信息
user = properties.getProperty("user");
password = properties.getProperty("password");
url = properties.getProperty("url");
maxActiveConnectionNumber = Integer.parseInt(properties.getProperty("maxActiveConnectionNumber"));
driverClassName = properties.getProperty("driverClassName");
//载入驱动
Class.forName(driverClassName);
//创建连接。并存储到pool集合中
for(int i=0; i<maxActiveConnectionNumber; i++){
Connection cn = null;
cn = DriverManager.getConnection(url,
user, password);
System.out.println(cn);
pool.add(cn);
}
}catch(IOException e){
e.printStackTrace();
} catch (ClassNotFoundException e) {
// TODO Auto-generatedcatch block
e.printStackTrace();
} catch (SQLException e) {
// TODO Auto-generatedcatch block
e.printStackTrace();
}
}
//此方法因为获取、pool集合中的连接
//此方法是一个线程安全的。当线程池中没有链接时,休眠该线程,当连接返回时再唤醒休眠的线程
public
static Connection getConnection(){
//连接
Connection cn = null;
//加锁
ThreadLock.getLock().lock();
//获取状态对象
Condition c = ThreadLock.getCondition();
try{
//推断pool集合中是否有连接
while(!bool){
//假设没有就休眠当前的线程
c.await();//此休眠语句必须放在加了锁的域中
}
//取出pool集合中的一个连接,并在pool集合中移除它
cn = pool.remove(0);
//推断集合中是否还有连接
if(pool.size() <= 0){
//假设没有就将标志位设置为false
bool =
false;
}
} catch (InterruptedException e) {
// TODO Auto-generatedcatch block
e.printStackTrace();
}finally{
//解锁
ThreadLock.getLock().unlock();
}
return cn;
}
//此方法用于返回连接,并存储到pool集合中
public
static void release(Connection cn){
//加锁
ThreadLock.getLock().lock();
try{
//向pool集合中加入此连接
pool.add(cn);
//并把标识位设为true,表示pool中已有连接
bool =
true;
//唤醒状态对象中的某一个线程
ThreadLock.getCondition().signal();//此唤醒语句必须放在加了锁的域中
}finally{
//解锁
ThreadLock.getLock().unlock();
}
}
}
//此类主要用于提供锁和线程状态对象
class ThreadLock{
private
static Lock lock =
new ReentrantLock();
private
static Condition c =
lock.newCondition();
//返回一个锁对象
public
static Lock getLock(){
return
lock;
}
//返回一个线程状态对象
public
static Condition getCondition(){
return
c;
}
}
DBCP链接池(推荐使用)
DBCP是Apache软件基金组织下的开源链接池实现。用此连接池需导入Commons-dbcp.jar包(连接此的实现)和Commons-pool.jar包(连接池的依赖库)
dbcpconfig.properties文件的配置
#连接设置
#驱动位置
driverClassName=com.mysql.jdbc.Driver
#连接
url=jdbc:mysql://localhost:3306/jdbc
#账户password
username=root
password=
#<!--初始化连接 -->
initialSize=10
#最大连接数量
maxActive=50
#<!--最大空暇连接 -->
maxIdle=20
#<!--最小空暇连接 -->
minIdle=5
#<!--超时等待时间以毫秒为单位 6000毫秒/1000等于60秒 -->
maxWait=60000
#JDBC驱动建立连接时附带的连接属性属性的格式必须为这样:[属性名=property;]
#注意:"user"与 "password" 两个属性会被明白地传递,因此这里不须要包括他们。
connectionProperties=useUnicode=true;characterEncoding=utf8
#指定由连接池所创建的连接的事务自己主动提交(auto-commit)状态。
defaultAutoCommit=true
#driverdefault 指定由连接池所创建的连接的仅仅读(read-only)状态。
#假设没有设置该值,则“setReadOnly”方法将不被调用。(某些驱动并不支持仅仅读模式,如:Informix)。(一般不配)
defaultReadOnly=
#driverdefault 指定由连接池所创建的连接的事务级别(TransactionIsolation)。
#可用值为下列之中的一个:(详情可见javadoc。
)NONE,READ_UNCOMMITTED, READ_COMMITTED, REPEATABLE_READ, SERIALIZABLE
defaultTransactionIsolation=READ_UNCOMMITTED
java代码,用户获取连接和返回连接的工具类
/*
用DBCP连接池获取连接的工具类
*/
public
class DBCPUtils {
//定义一个数据源
private
static DataSource ds =
null;
static{
//将dbcpconfig.properties配置文件载入进来
InputStream in = DBCPUtils.class.getClassLoader().getResourceAsStream("DBCPConnectionPool/dbcpconfig.properties");
Properties pro = new Properties();
try {
pro.load(in);
//通过将配置文件传入下面类中。从而获得数据源对象
ds = BasicDataSourceFactory.createDataSource(pro);
} catch (Exception e) {
// TODO Auto-generatedcatch block
throw
new RuntimeException(e);
}
}
//获取连接
public
static Connection getConnection(){
try {
//通过数据源将连接池中的连接取出
return
ds.getConnection();
} catch (SQLException e) {
// TODO Auto-generatedcatch block
throw
new RuntimeException(e);
}
}
//关闭连接。用DBCP连接池,Connection的方法已被改写。所以调用Connection对象的close()方法时是将连接放回连接池中
public
static void close(Connection conn,Statement ps,ResultSet rs){
try {
if(rs!=null){
rs.close();
}
} catch (SQLException e) {
// TODO Auto-generatedcatch block
e.printStackTrace();
}finally{
try{
if(ps!=null){
ps.close();
}
} catch (SQLException e) {
//
TODO Auto-generatedcatch block
e.printStackTrace();
}finally{
try{
if(conn!=null){
conn.close();
}
} catch (SQLException e) {
//
TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
C3P0链接池(推荐使用)
用此连接池需导入c3p0-0.9.1.2.jar包c3p0-0.9.1.2-jdk1.3.jar及c3p0-oracle-thin-extras-0.9.1.2.jar包(此包是Orclase连接时须要的。但一般都导入)
此工具设置配置參数可有在对ComboPooledDataSource象中设置,也能够在当前src或web应用的WEB-INF/classes文件夹下建立一个c3p0-config.xml
(推荐)配置文件进行配置,也能够建立一个c3p0.properties映射文件进行配置,
c3p0的配置文件说明:
演示样例:
c3p0-config.xml文件的配置
<?xml
version="1.0"encoding="UTF-8"?>
<c3p0-config>
<default-config>
<property
name="driverClass">oracle.jdbc.OracleDriver</property>
<property
name="jdbcUrl">jdbc:oracle:thin:@127.0.0.1:1521:orcl</property>
<property
name="user">scott</property>
<property
name="password">tiger</property>
<property
name="acquireIncrement">50</property>
<property
name="initialPoolSize">100</property>
<property
name="minPoolSize">50</property>
<property
name="maxPoolSize">1000</property>
</default-config>
<!-- 当创建 ComboPooledDataSource数据源对象时,传入的參数是“mysql”那么调用的就是以下named-config标签中的配置,
假设没有传入不论什么參数就调用上面default-config默认配置中的配置-->
<named-config
name="mysql">
<!-- 驱动位置 -->
<property
name="driverClass">com.mysql.jdbc.Driver</property>
<!-- 设置连接 -->
<property
name="jdbcUrl">jdbc:mysql://127.0.0.1:3306/db_mydatabase_20140509</property>
<!-- 设置用户 -->
<property
name="user">root</property>
<property
name="password">123</property>
<!-- 设置初始化连接数量 -->
<property
name="initialPoolSize">10</property>
<!-- 设置最大的最大空暇时间 -->
<property
name="maxIdleTime">30</property>
<!-- 设置连接的最大数量 -->
<property
name="maxPoolSize">100</property>
<!-- 设置连接的最小数量 -->
<property
name="minPoolSize">10</property>
<property
name="maxStatements">200</property>
</named-config>
</c3p0-config>
java代码,用户获取连接和返回连接的工具类
/*
通过C3P0获取连接的工具类
*/
public
class C3P0Utils {
//假设创建数据源是传入的是“mysql”,那么配置文件调用的是名为“mysql”的named-config标签下的配置,
//假设不写调用的default-config(默认)的配置
private
static DataSource ds =
new ComboPooledDataSource("mysql");
public
static Connection getConnection(){
try {
return
ds.getConnection();
} catch (SQLException e) {
//
TODO Auto-generatedcatch block
throw
new RuntimeException(e);
}
}
//关闭连接。用C3P0连接池,Connection的方法已被改写,所以调用Connection对象的close()方法时是将连接放回连接池中
public
static void close(Connection conn,Statement ps,ResultSet rs){
try {
if(rs!=null){
rs.close();
}
} catch (SQLException e) {
//
TODO Auto-generatedcatch block
e.printStackTrace();
}finally{
try{
if(ps!=null){
ps.close();
}
} catch (SQLException e) {
//
TODO Auto-generated catch block
e.printStackTrace();
}finally{
try{
if(conn!=null){
conn.close();
}
} catch (SQLException e) {
//
TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
元信息
通过元信息。我们能够获取到数据库、结果集、占位符的全部信息
元信息有下面几种:其接口的方法。查看JDK_API
数据库元信息(DatabaseMetaData接口) 通过Connection(连接对象)的getMetaData()方法获取此数据库元信息对象
;通过数据库元信息对象,能够获取到数据库的名字、连接、驱动名,版本号、连接的username、是不是仅仅读的,等全部信息
占位符(?)元信息(ParameterMetaData接口)
通过PreparedStatement (数据库操作对象)的getParameterMetaData();方法获取到此占位符的元信息对象;获取此对象前必须设置PreparedStatement(数据库操作对象)的sql语句。通过此占位符元信息能够获取到sql语句中的”?”号占位符的个数。
getParameterCount(); 获取到”?”号占位符的个数
结果集元信息(ResultSetMetaData接口) 通过ResultSet(结果集对象)的getMetaData();方法获取到此结果集元信息对象;通过此对象能够获取到结果的列数、列名、列的类型(返回为int类型与Types中的常量相匹配)等;
演示样例:
//传入你的ResultSet
public
static void printRS(ResultSet rs)
throws SQLException
{
//检索此 ResultSet
对象的列的编号、类型和属性。
ResultSetMetaData rsmd = rs.getMetaData();
//得到当前的列数
int colCount = rsmd.getColumnCount();
while(rs.next()) {
//while控制行数
for(int i = 1; i <= colCount; i++) {//for循环控制列数
if(i > 1) {
System.out.print(",");
}
//得到当前列的列名
String name = rsmd.getColumnName(i);
//得到当前列的类型,此处得到的是int类型,与Types中的常量相相应
int type = rsmd.getColumnType(i);
//得到当前列的值
String value = rs.getString(i);
System.out.print(name"=" value+”:”+type);
}
System.out.println();
}
}
DBUtils(数据库操作工具)的使用
须要导入commons-dbutils-1.4.jar包,此包中的org.apache.commons.dbutils.QueryRunner类对数库进行增、删、改、查、批处理。
其它请查看文档
要创建此类对象。假设用的是Oracle数据库,此构造方须要传入一个true參数 ; 创建此类对象时,能够传入一个数据源(javax.sql. DataSource)对象,假设传入数据源,那么在调用增、删、改、查、批处理,方法时就不须要传入Connection(数据库连接对象了),此org.apache.commons.dbutils.QueryRunner对象会自己主动到数据源中去获取连接(Connection);否则在调用增、删、改、查、批处理,方法时就须要传入一个数据库连接对象(Connection);
BDUtils的简单操作
增、删、改
//通过DBUtils工具对数据进行增删改查(CRUD)
/*表
create table t_person(
id int(3) primary key auto_increment,
name varchar(7),
age int(3),
gender varchar(2)
);
*/
public
class DBCRUD {
/*
要创建此org.apache.commons.dbutils.QueryRunner类对象,假设用的是Oracle数据库。此构造方须要传入一个true參数
;
创建此类对象时。能够传入一个数据源(javax.sql.DataSource)对象,假设传入数据源。
那么在调用增、删、改、查、批处理,方法时就不须要传入Connection(数据库连接对象了),
此org.apache.commons.dbutils.QueryRunner对象会自己主动到数据源中去获取连接(Connection);
否则在调用增、删、改、查、批处理,方法时就须要传入一个数据库连接对象(Connection);
*/
//这里直接传入一个数据源对象
private
static QueryRunner qr =
new QueryRunner(JDBCUtils.getDataSource());
//对DBUtils工具的简单操作
public
static void main(String[] args)
throws SQLException, IOException {
System.out.println("--------------------------------增、删、改---------------------------------");
//由于在创建qr对象时,传入了数据源。所以这里不须要才传入Connection(连接对象)了,
//qr会自己主动去调用数据源的getConnection方法,获取数据库连接对象
//增(假设数据库的字段为日期类型,而且这里的參数传入的是“2014-06-10”这样格式的字符串,DBUtils会自己主动将字符串转换为日期类型并存入数据库)
qr.update("insert intot_person(name,age,gender) values(?,?,?)",
"张三", 20,"男");
//其它的删、改、操作都和上面一样,仅仅只是sql语句不一样,和參数不一样
System.out.println("--------------------------------批处理---------------------------------");
//批处理(向数据库插入10条记录)
//先准备10个參数的二维数组
Object[][] obj = new Object[10][];
for(int i=0; i<10; i++){
obj[i] = new Object[]{"name"+(i+1), i+1,
"男"};
}
qr.batch("insert intot_person(name,age,gender) values(?,?,?)", obj);
System.out.println("--------------------------------存入一个文本文件---------------------------------");
//存入一个文本文件
//须要用到Clob接口和SerialClob类(不推荐使用。由于存入时要将文件载入到内存中,大文件有可能导致内存溢出)
//表
//create tablet1(
// id int(3) primary key auto_increment,
// text longtext
//);
//创建一个文件对象
File file = new File("src/cn/com/1.txt");
//通过这个文件对象创建一个文件字符输入流
Reader r = new FileReader(file);
//创一个缓存的字符数组,长度就是文件的长度
char[] c =
new char[(int)file.length()];
//将字符输入流的字符,输入到缓存字符数组中
r.read(c);
r.close();
//创建一个SerialClob对象(文本)
Clob clob = new SerialClob(c);
qr.update("insert intot1(text) values(?)", clob);
System.out.println("--------------------------------存入一个二进制文件---------------------------------");
//存入一个二进制文件
//须要用到Blob接口和SerialBlob类(不推荐使用。由于存入时要将文件载入到内存中,大文件有可能导致内存溢出)
//表
// create table t2(
// id int(3) primary keyauto_increment,
// binarySystem
longblob
// );
//创建一个字节输入流对象
InputStream in = new FileInputStream("src/cn/com/1.jpg");
//创一个缓存的字节数组,长度就是文件的长度(通过字节流的available()方法也能够获取到文件的字节大小)
byte[] b =
new byte[in.available()];
//将字节输入流的数据,输入到缓存字节数组中
in.read(b);
in.close();
//创建一个SerialBlob(二进制)对象
Blob bolb = new SerialBlob(b);
qr.update("insert intot2(binarySystem) values(?
)", bolb);
}
}
查
表
createtable t_person(
id int(3) primary key auto_increment,
name varchar(7) not null,
age int(3) not null,
gender varchar(2) not null
)auto_increment=1001;
Bean对象
public
class T_person {
private
int id = 0;
private String
name = null;
private
int age = 0;
private String
gender = null;
public
int getId() {
return
id;
}
public
void setId(int id) {
this.id = id;
}
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 String getGender() {
return
gender;
}
public
void setGender(String gender) {
this.gender = gender;
}
@Override
public String toString() {
return
"t_person[id=" + id +
",name=" + name +
",age=" + age
+", gender=" +
gender + "]";
}
}
查询操作
//"--------------------------------查(结果处理器)---------------------------------"
/*表
create table t_person(
id int(3) primary key auto_increment,
name varchar(7) not null,
age int(3) not null,
gender
varchar(2) not null
)auto_increment=1001;
*/
@Test//BeanHandler将查询的一条结果封装到一个Bean对象中。仅仅适合查询一条结果
public
void test1() throws SQLException{
T_person a = qr.query("select * from t_person where id=?",
new BeanHandler<T_person>(T_person.class), 1001);//最后一个參数是一个可变參数。依次输入占位符的值
System.out.println("asfd"+a);
}
@Test//BeanListHandler将查询的多条结果封装到一个Bean对象的List集合中,适合查询多条结果
public
void test2() throws SQLException{
List<T_person> list = qr.query("select * from t_person",
new BeanListHandler<T_person>(T_person.class));
for(T_person a:list)
System.out.println(a);
}
@Test//ArrayHandler:把结果集中的第一行数据转成对象数组。仅仅适合结果集有一条记录的情况
public
void test3() throws SQLException{
//该数组中每一个元素就是记录的每列的值
Object objs[] = qr.query("select * from t_person where id=?",
new ArrayHandler(),1001);//最后一个參数是一个可变參数。依次输入占位符的值
if(objs!=null)
for(Object o:objs)
System.out.println(o);
}
@Test//ArrayListHandler:把结果集中的每一行数据都转成一个数组,再存放到List中。
public
void test4() throws SQLException{
//该数组中每一个元素就是记录的每列的值
List<Object[]> list = qr.query("select * from t_person",
new ArrayListHandler());
for(Object[] objs:list){
System.out.println("-----------------");
for(Object o:objs)
System.out.println(o);
}
}
@Test//ColumnListHandler:将结果集中某一列的数据存放到List中(仅仅能是一列,能够传入字段名,也能够传入数字)
public
void test5() throws SQLException{
List<Object> list = qr.query("select * from t_person",
new ColumnListHandler("name"));
for(Object o:list)
System.out.println(o);
}
@Test//KeyedHandler(name):将结果集中的每一行数据都封装到一个Map<列名,列值>里,再把这些map再存到一个map里,其“id”列的值为指定的key。
public
void test6() throws SQLException{
Map<Object, Map<String,Object>>bmap=
qr.query("select * from t_person",
new KeyedHandler("id"));
for(Map.Entry<Object, Map<String,Object>> bme:bmap.entrySet()){
Map<String,Object> lmap =bme.getValue();
System.out.println("-----------------");
System.out.println("key:"+bme.getKey());
for(Map.Entry<String,Object>lme:lmap.entrySet()){
System.out.println(lme.getKey()+"="+lme.getValue());
}
/*每次循环输出的结果:
key:1001
-----------------
id=1001
age=20
name=张三
gender=男
*/
}
}
@Test//MapHandler:将结果集中的第一行数据封装到一个Map里,key是列名,value就是相应的值。
public
void test7() throws SQLException{
Map<String,Object> map= qr.query("select * from t_person where id=?",
new MapHandler(), 1001);
for(Map.Entry<String, Object> me:map.entrySet()){
System.out.println(me.getKey()+"="+me.getValue());
}
/*
输出结果:
id=1001
age=20
name=张三
gender=男
*/
}
@Test//MapListHandler:将结果集中的每一行数据都封装到一个Map里。然后再存放到List集合中
public
void test8() throws SQLException{
List<Map<String,Object>> list= qr.query("select * from t_person",
new MapListHandler());
for(Map<String,Object> map:list){
System.out.println("-----------------");
for(Map.Entry<String, Object>me:map.entrySet()){
System.out.println(me.getKey()+"="+me.getValue());
}
}
}
@Test//ScalarHandler
适合取一条一列的记录。
比方记录总数
public
void test9() throws SQLException{
//返回的是一个Long包装类对象(整型数字)
传入的1表示结果集中第1行第1列的值,假设没有返回值,返回的结果就为null。假设不是数字类型就会抛异常
Object obj = qr.query("select count(*) from t_person",
new ScalarHandler(1));
//将Long类型的整型数字通过intValue()转换成一个int类型
int num = ((Long)obj).intValue();
System.out.println(num);
}
DBUtils中的事务控制
假设要控制事务,创建org.apache.commons.dbutils.QueryRunner对象时就不能传入源数据对象。在调用QueryRunner方法时。传入Connection连接对象,在外面进行设置连接对象的事务提交,conn.setAutoCommit(false);表示系统不自己主动提交事务。需我们手动提交事务
案例:A向B转账100元,假设不控制事务。那么当A转出100元钱后,发生了异常,那么B的账户还没加上转出去的钱。那么就会发生数据异常
表:
create table account(
id int(3) primary key auto_increment,
name varchar(7),
money float(10,3)
);
java代码
Dao层(操作数据库)
//DAO层不能牵扯不论什么的业务逻辑
public
class AccountDaoImpl {
//创建一个数据库操作对象
QueryRunner
qr = new QueryRunner();
//通过名称找出用户。并返回一个Bean对象
public Account findAccount(String name){
try {
return
qr.query(TransactionUtil.getConntcion(),"select * from account wherename=?",
new BeanHandler<Account>(Account.class), name);
} catch (SQLException e) {
//
TODO Auto-generated catch block
throw
new RuntimeException(e);
}
}
//通过Bean对象,更新这个对象在数据库中的Money
public
int update(Account account){
try {
return
qr.update(TransactionUtil.getConntcion(),"update account set money=? wherename=?",account.getMoney(),account.getName());
} catch (SQLException e) {
//
TODO Auto-generated catch block
throw
new RuntimeException(e);
}
}
}
service层(逻辑处理)
public
class AccountServiceImpl {
//创建一个Dao层的对象
AccountDaoImpl
dao = new AccountDaoImpl();
//此方法用于转账。传入转出者名称和转入者名称,及转出的钱
public
boolean transfer(String sourceAccountName,StringtargetAccountName,float money){
//设置标识位。表示转账是否成功
boolean bool =
true;
//设置事务为手动提交
TransactionUtil.startTransaction();
//查找出转出者和转入者的对象
Account sourceAccount =
dao.findAccount(sourceAccountName);
Account targetAccount =
dao.findAccount(targetAccountName);
//设置转出对象的money(减去要转出的钱)
sourceAccount.setMoney(sourceAccount.getMoney()-money);
//设置转入对象的money(加上要转入的钱)
targetAccount.setMoney(targetAccount.getMoney()+money);
try{
//通过dao对象,对转出对象进行更新操作(在数据库中减去要转出的钱,但没提交事务)
dao.update(sourceAccount);
//int i = 1/0;//模拟异常,如此处发生异常。转出对象的money不会被转出
//通过dao对象。对转入对象进行更新操作(在数据库中加上要转入的钱,但没提交事务)
dao.update(targetAccount);
}catch(Exception e){
//假设发生异常。就回滚事务,并设置标识位为false
TransactionUtil.rollback();
bool = false;
e.printStackTrace();
}finally{
//必须运行事务提交
TransactionUtil.commit();
//将连接返回到连接池中
TransactionUtil.relase();
}
return bool;
}
}
service工具类
//把得到连接及事务有关的方法写到此类中(此类是获取Connection对象的工具类)
public
class TransactionUtil {
//创建一个数据源
private
static DataSource ds =
null;
//创建一个线程范围内的变量
private
static ThreadLocal<Connection> tl =
newThreadLocal<Connection>();
static{
//读取配置文件。并将dbcpconfig.properties文件里的信息写入到p这个properties文件对象中
InputStream in = TransactionUtil.class.getClassLoader().getResourceAsStream("cn/service/impl1/dbcpconfig.properties");
Properties p = new Properties();
try {
p.load(in);
//通过DBCP连接池。将p配置文件里的信息创建一个数据源对象
ds = BasicDataSourceFactory.createDataSource(p);
} catch (IOException e) {
//
TODO Auto-generated catch block
e.printStackTrace();
} catch (Exception e) {
//
TODO Auto-generated catch block
e.printStackTrace();
}
}
//获取数据源对象
public
static DataSource getDataSource(){
return
ds;
}
//获取连接
public
static Connection getConntcion(){
//获取当前线程中的Connection对象
Connection conn = tl.get();
//假设当前线程没有Connection对象就获取一个
if(conn ==
null){
try {
//通过数据源从线程中获取一个Connection对象
conn = ds.getConnection();
//将获取到的Connection对象保存到
tl.set(conn);
} catch (SQLException e) {
throw
new RuntimeException(e);
}
}
//返回此连接
return conn;
}
//设置事务为手动提交
public
static Connection startTransaction(){
//获取到当前线程的Connection对象
Connection conn = TransactionUtil.getConntcion();
try {
//设置这个Connection连接对象的事务为手动提交
conn.setAutoCommit(false);
return conn;
} catch (SQLException e) {
//
TODO Auto-generated catch block
throw
new RuntimeException();
}
}
//回滚当前线程的Connection连接对象的事务
public
static void rollback(){
//获取到当前线程的连接对象
Connection conn = TransactionUtil.getConntcion();
try {
//回滚当前线程的连接对象的事务
conn.rollback();
} catch (SQLException e) {
//
TODO Auto-generated catch block
e.printStackTrace();
}
}
//提交当前线程的Connection连接对象的事务
public
static void commit(){
//获取到当前线程的连接对象
Connection conn = TransactionUtil.getConntcion();
try{
//提交当前线程的Connection连接对象的事务
conn.commit();
}catch(SQLException e){
e.printStackTrace();
}
}
//关闭连接
public
static void relase(){
//获取到当前线程的Connection对象
Connection conn = tl.get();
//假设获取到了
if(conn!=null){
try {
//就把连接放回到连接池中
conn.close();
//在当前线程中删除此Connection连接对象
tl.remove();
} catch (SQLException e) {
//
TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
測试
//是transaction_control的改进
public
class Client {
//转账
public
static void main(String[] args) {
AccountServiceImpl asi = newAccountServiceImpl();
//张三向李四转100元
asi.transfer("张三",
"李四", 100);
}
}
关系的转换
在java中一对多、多对多及一对一的关系非常好提现,然而在数据库中的关系和在java中完全不同,所以在关系转换时一定要注意其细节
一对多关系:
多对多关系:
代码演示样例在:E:\practice\JSP\practice2\数据库关系;中查看
文件的上传与下载
文件的上传与下载有两个工具能够进行操作smartupload.jar工具和commons-fileupload-1.2.2.jar及依赖包commons-io-2.0.1.jar
文件上传的前提:
1、 form表单的method属性值必须是post
2、 必须设置from表单enctype的值为”multipart/form-data”;(此值决定了post请求方式,请求正文的数据类型)
注意当设置了此属性时在serlvet中用request.getParameter(" ");已经无效,由于是以二进制文件提交的。必须用工具中的request对象的getParameter("");方法来获取
3、 form中提供input表单的type必须是“file”。而且还必须为此表单取一个名字
smartupload.jar工具操作
须要导入smartupload.jar包
jsp代码:
<body>
<%--仅仅能通过http://127.0.0.1:8080/upload/index.jsp
訪问。不然IP地址不对,必须在此处加上enctype="multipart/form-data"表示以二进制方式提交--%>
<form
action="${pageContext.servletContext.contextPath}/servlet/UploadServlet"
method="post"
enctype="multipart/form-data">
username:<input
type="text"
name= "username"><br/>
<input
type="file"name="file"/><br/>
<input
type="submit"value="上传"/>
</form>
</body>
servlet代码:
//訪问时仅仅能通过http://127.0.0.1:8080/upload/servlet/UploadServlet不然IP地址不对
public
void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html; charser=utf-8");
response.setCharacterEncoding("utf-8");
request.setCharacterEncoding("utf-8");
//创建一个上传对象
SmartUpload su = new SmartUpload();
//初始化上传对象
su.initialize(this.getServletConfig(), request, response);
try {
//将文件载入到内存中
su.upload();
//创建一个文件名工具类,将客户机的IP地址传入进去
FileNameUtils fn = new FileNameUtils(request.getRemoteAddr());
//fn.getIPTimeRandom()获取一个通过IP和当前时间生成的一个字符串
String fileName = fn.getIPTimeRandom();
//获取到内存中二进制文件对象
File file = su.getFiles().getFile(0);
//将此文件另存为,传入一个文件夹加文件名称;file.getFileExt()获取到此文件的后缀名
file.saveAs("/file/"+fileName+"."+file.getFileExt());
//假设要获取username这个文本中的值,须要用工具中的SmartUpload对象的request对象来获取,此处是获取到username文本框中的值
String username = su.getRequest().getParameter("username");
//打印此用户上传文件名及后缀名上传成功
response.getWriter().write(username+"上传"+fileName+file.getFileExt()+"成功。");
} catch (SmartUploadException e) {
// TODO Auto-generatedcatch block
e.printStackTrace();
}
}
java工具代码(通过IP生成一个IP+当前时间+随机数的一个字符串)
public
class FileNameUtils {
private String
IP = null;
public FileNameUtils(String IP){
this.IP = IP;
}
//通过IP和时间和随机数返回一个字符串
public String getIPTimeRandom(){
//将IP的.去掉
String[] nums =
IP.split("\\.");
StringBuffer sb = new StringBuffer();
for(int i = 0; i<nums.length; i++){
//不够三位的在前面补0
sb.append(addZero(nums[i], 3));
}
//添加一个时间字符串
sb.append(getTimeStamp());
//添加一个9以内的随机数
sb.append(new Random().nextInt(9));
//返回此字符串
return sb.toString();
}
//依据当前的时间返回一个字符串
public String getTimeStamp(){
//创建一个格式对象
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmssSSS");
//创建一个新的时间,并以sdf格式返回一个字符串
return sdf.format(new Date());
}
//将一个字符串,假设此字符串的长度不够len这么长。就在前面补‘0’
private String addZero(String str,int len) {
if(IP==null){
return
null;
}
StringBuffersb = new StringBuffer();
sb.append(str);
//推断sb中的长度是否小于len
while(sb.length()<len){
//是的话就在前面加‘0’
sb.insert(0,
‘0‘);
}
//并返回此字符串
return sb.toString();
}
}
commons-fileupload-1.2.2.jar工具操作
需导入commons-fileupload-1.2.2.jar及依赖包commons-io-2.0.1.jar(支持中文名)
上传演示样例1(直接上传):
jsp代码:
<body>
<!-- 设置提交方式为post,提交的格式为二进制方式 -->
<form
action="${pageContext.servletContext.contextPath}/servlet/UploadServlet2"method="post"
enctype="multipart/form-data">
username:<input
type="text"
name="name"><br/><!--
第一个文本域 -->
<input
type="file"name="file1"><br/><!--
第二个是文件上传域 -->
<input
type="file"name="file2"><br/><!--
第三个是文件上传域 -->
<input
type="submit"value="提交">
</form>
</body>
Servlet代码:
public
voiddoGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html; charser=utf-8");
response.setCharacterEncoding("utf-8");
request.setCharacterEncoding("utf-8");
//推断enctype的值是否是:"multipart/form-data";是返回true。不是返回false
if(!ServletFileUpload.isMultipartContent(request)){
response.getWriter().write("你的提交方式不是:enctype=\"multipart/form-data\"");
return;
}
//创建工厂,设置环境
DiskFileItemFactory factory = newDiskFileItemFactory();
//通过工厂创建一个上传对象
ServletFileUpload upload = newServletFileUpload(factory);
try {
//获取到request对象中全部的域对象(此处的域表示一个Input标签,但不包含提交标签)
List<FileItem> fileItems = upload.parseRequest(request);
//遍历这些域对象
Iterator<FileItem> it =fileItems.iterator();
while(it.hasNext()){
//获取到当前的域对象
FileItem item = it.next();
//推断item这个域是否是表单字段(文本框)
if(item.isFormField()){
//得到input标签中的name属性的名称(这里是name)
String name = item.getFieldName();
//得到input标签中name属性的值,并设置其编码格式
String value = item.getString("utf-8");
//打印这两个值
System.out.println(name+":"+value);
}else{//也能够推断其是否是一个文件类型:item.isInMemory()
//否则就是一个文件类型
//以输入流的方式获取此域中的内容
InputStream in = item.getInputStream();
//获取此文件的名称(这个名称有可能在前面加上了‘\‘)
String fileName = item.getName();
//切掉此文件名称中的’\‘
fileName =fileName.substring(fileName.lastIndexOf("\\")+1);
//获取到file文件夹的绝对路径(用于存储上传的文件)
String path = this.getServletContext().getRealPath("/file");
//创建一个字节输出流,路径设置为当前的文件夹+文件名
OutputStream out = newFileOutputStream(path+"/"+fileName);
//创建一个字节缓存
byte[] b =
new byte[1024];
int len = -1;
//将字节输入流中的数据写入到字节输出流中
while((len=in.read(b))>0){
out.write(b,0, len);
}
//刷新和关闭流
out.flush();
in.close();
out.close();
}
}
} catch (FileUploadException e) {
// TODO Auto-generatedcatch block
e.printStackTrace();
}
}
文件上传中要注意的9个问题
1、 怎样保证server的安全
把保存上传文件的文件夹放到WEB-INF文件夹中。以防止别人上传了一个带有<%Runtime.getRuntime().exe(“shutdown –t 0”);%>代码的JSP文件,要是别人訪问这个文件,将会导致server关机;此处的红色部分是运行cmd窗体中运行的命令。
2、中文乱码问题
2.1普通字段的中文请求參数
Stringvalue = FileItem.getString("UTF-8");
2.2上传的文件名称是中文
解决的方法:request.setCharacterEncoding("UTF-8");
3、重名文件被覆盖的问题
System.currentMillions()+"_"+a.txt(乐观)
UUID+"_"+a.txt:保证文件名称唯一
4、分文件夹存储上传的文件
方式一:当前日期建立一个目录,当前上传的文件都放到此目录中。
方式二:利用文件名称的hash码打散文件夹来存储。
int hashCode = fileName.hashCode();
1001 1010 1101 0010 1101 1100 1101 1010
hashCode&0xf; 0000 0000 0000 0000 0000 0000 0000 1111&
--------------------------------------------------------------
00000000 0000 0000 0000 0000 0000 1010 取hashCode的后4位
0000~1111:整数0~15共16个
10011010 1101 0010 1101 1100 1101 1010
(hashCode&0xf0) 0000 0000 0000 0000 0000 0000 11110000 &
--------------------------------------------------------------
00000000 0000 0000 0000 0000 1101 0000 >>4
--------------------------------------------------------------
00000000 0000 0000 0000 0000 0000 1101
0000~1111:整数0~15共16个
在原有的文件夹下再更具hash码来创建两个二级文件夹
因为存储时是通过文件名称的hash值来确定文件存储的位置的,所以也须要将其文件名称存储到数据库中。以便获取时好通过文件名称来获取到此文件的存储文件夹。及文件名称
表的设计格式:
ID(PK) 老文件名称 新文件名称 user_id
1 1.txt 64f84973-a52b-4b96-af1d-97bd1a03157f_1.txt 1001
当我们取出此新文件名称后。我们就能够通过其hash值来获取其存储的一级文件夹和二级文件夹的名字了;
5、限制用户上传的文件类型
通过推断文件的扩展名来限制是不可取的。
通过推断其Mime类型才靠谱。FileItem.getContentType();
6、怎样限制用户上传文件的大小
6.1单个文件限制大小。
超出了大小友好提示
抓异常进行提示:org.apache.commons.fileupload.FileUploadBase.FileSizeLimitExceededException
6.2总文件限制大小。超出了大小友好提示
抓异常进行提示:org.apache.commons.fileupload.FileUploadBase.SizeLimitExceededException
7、暂时文件的问题
commons-fileupload组件不会删除超出缓存的暂时文件。
FileItem.delete()方法删除暂时文件。
但一定要在关闭流之后。
8、多个文件上传时,没有上传内容的问题
if(fileName==null||"".equals(fileName.trim())){
continue;
}
9、上传进度检測
给ServletFileUpload注冊一个进度监听器就可以,把上传进度传递给页面去显示
//pBytesRead:当前以读取到的字节数
//pContentLength:文件的长度
//pItems:第几项
public void update(longpBytesRead, long pContentLength,
int pItems){
System.out.println("已读取:"+pBytesRead+",文件大小:"+pContentLength+",第几项:"+pItems);
}
上传演示样例2(攻克了上面9个注意事项后):
JSP代码:
<body>
<!-- 必须设置提交方式为post。提交的格式为二进制方式 -->
<form
action="${pageContext.servletContext.contextPath}/servlet/UploadServlet1"method="post"
enctype="multipart/form-data">
username:<input
type="text"
name="name"><br/><!--
第一个文本域 -->
<input
type="file"name="file1"><br/><!--
第二个是文件上传域 -->
<input
type="file"name="file2"><br/><!--
第三个是文件上传域 -->
<input
type="submit"value="提交">
</form>
</body>
Servlet代码:
public
class UploadServlet1 extends HttpServlet {
public
void doGet(HttpServletRequest request, HttpServletResponseresponse)
throws ServletException, IOException {
response.setContentType("text/html; charser=utf-8");
response.setCharacterEncoding("utf-8");
request.setCharacterEncoding("utf-8");
//推断上传的方式是否是enctype="multipart/form-data"
二进制方式提交的
if(!ServletFileUpload.isMultipartContent(request)){
response.getWriter().write("你提交的方式不是enctype=\"multipart/form-data\"");
return;
}
//创建工厂,设置环境
DiskFileItemFactory factory = newDiskFileItemFactory();
//获取到暂时文件"temp"文件夹的绝对路径
String tempPath = this.getServletContext().getRealPath("/WEB-INF/tempFile");
//创建一个文件作为暂时文件的存放位置
File tempFile = new File(tempPath);
//设置暂时文件的存放位置,系统默认有10KB的缓存位置。当文件操作10KB,那么将把缓存的文件存入设置的暂时文件夹中
factory.setRepository(tempFile);
//通过工厂创建一个上传对象
ServletFileUploadupload = new ServletFileUpload(factory);
//为此上传对象创建一个监听器
upload.setProgressListener(new ProgressListener(){
//当上传时会调用此方法
@Override//第一个參数为:当前读取到的字节数;第二个參数为:全部文件总共的字节数;
第三个參数是:当前的文件时第几项(第几个表单域)
public
void update(long pBytesRead,
long pContentLength,
int pItems) {
System.out.println("已读取:"+pBytesRead+"字节,全部文件大小:"+pContentLength+"字节,第几项:"+pItems);
//获取到以读取的百分比
double d = (double)pBytesRead/(double)pContentLength;
//设置一个格式,以四舍五入的方式保留三位小数的格式
String format =
".###";
//通过这个格式,创建一个格式对象
DecimalFormat df = newDecimalFormat(format);
//将这个double类型的d仅仅保留3位小数
String str = df.format(d);
//再将其转换为double类型
d = Double.parseDouble(str);
//再乘100,那么就是他的百分比了
System.out.println("已读取:"+d*100+"%");
}
});
try{
upload.setFileSizeMax(1024*1024*2);//设置上传单个文件的大小,单位是资字节(这里表示2M)
upload.setSizeMax(1024*1024*3);//设置上传的全部文件的总大小,单位是字节(这里表示3M)
//获取到request对象中全部的表单域对象
List<FileItem> fileItems = upload.parseRequest(request);
//通过迭代器遍历这些表单域对象
Iterator<FileItem> it =fileItems.iterator();
//记录文件的个数
int fileNumber = 0;
while(it.hasNext()){
FileItem fileItem = it.next();
//推断当前的表单域对象是否是一个普通文本
if(fileItem.isFormField()){
//假设是普通的文本域,就直接打印出提交的值
String name =fileItem.getFieldName();
String value = fileItem.getString("UTF-8");
System.out.println(name+":"+value);
}else{//也能够推断其是否是一个文件类型:item.isInMemory()
//否则就是一个文件类型
fileNumber++;
//推断是否没有提交文件
if(fileItem.getName()==null ||
"".equals(fileItem.getName())){
System.out.println("第"+fileNumber+"个文件为NULL");
continue;
}
//得到这个文件的类型(一般值为text/plain表示txt文件。image/JPEG
表示图片文件)
String fileType =fileItem.getContentType();
//推断这个类型是不是以“image”开头。以此来推断此文件是否是一张图片
if(!fileType.startsWith("image")){
response.getWriter().write("第"+fileNumber+"个文件不是图片,上传失败!
<br/>");
//并继续上传下一张图片
continue;
}
//获取此文件的名称(这个名称有可能在前面加上了‘\‘)
String fileName = fileItem.getName();
//切掉文件名称中的‘\’
fileName =fileName.substring(fileName.lastIndexOf("\\")+1);
//以流的方式获取此文件里的内容
InputStream in =fileItem.getInputStream();
//设置存储的位置,得到存储位置的绝对路径
String fileUploadPath = this.getServletContext().getRealPath("/WEB-INF/files");
//产生一个唯一的字符串名字
fileName = UUID.randomUUID()+"_"+fileName;
//通过文件名称字,的hash值来设置此文件的存储位置,并获取到一个新的存储路径
String newFileUploadPath =makeStorePath(fileUploadPath, fileName);
//通过存储文件夹的绝对路径创建一个输出流
OutputStreamout = new FileOutputStream(newFileUploadPath+"\\"+fileName);
//创建一个字节缓存
byte[] b =
new byte[1024];
int len = -1;
//将输入流中的数据输出到输出流中
while((len = in.read(b))>0){
out.write(b, 0, len);
}
//将输出流中的数据刷出到文件里
out.flush();
//关闭流
out.close();
in.close();
//一定要在关闭流后清除temp暂时文件夹的这个暂时文件。不然此暂时文件会一直存储在硬盘上
fileItem.delete();
response.getWriter().write("第"+fileNumber+"图片文件上传成功!
<br/>");
}
}
//当单个文件超出设置的大小时,捕获此异常
}catch(org.apache.commons.fileupload.FileUploadBase.FileSizeLimitExceededExceptione){
response.getWriter().write("上传失败。单个文件不能超过2M<br/>");
//当总文件超出设置的大小时,捕获此异常
}catch(org.apache.commons.fileupload.FileUploadBase.SizeLimitExceededExceptione){
response.getWriter().write("上传失败,总文件大小不能超过3M<br/>");
}catch (FileUploadException e) {
// TODO Auto-generatedcatch block
e.printStackTrace();
} finally{
//3秒钟后跳转到显示图片页面
response.setHeader("refresh",
"3;url=‘"+request.getContextPath()+"/servlet/ShowFile‘");
}
}
public
void doPost(HttpServletRequest request, HttpServletResponseresponse)
throws ServletException, IOException {
doGet(request, response);
}
//通过文件名称字,的hash值来设置此文件的存储位置
private String makeStorePath(String fileUploadPath, StringfileName) {
//获取到此文件名称的hash值
int hashValue = fileName.hashCode();
//通过hash值算出第一级文件夹是什么
int dir1 = hashValue&0xf;
//通过hash值算出第二级文件夹时什么
int dir2 = (hashValue&0xf0) >> 4;
//将这两个文件夹拼凑到文件夹中
String newFileUploadPath = fileUploadPath+"\\"+dir1+"\\"+dir2;
File file = new File(newFileUploadPath);
//推断此文件夹是否在硬盘是真实存在
if(!file.exists()){
//假设存在就创建此文件夹(连子文件夹一起创建)
file.mkdirs();
}
//返回此路径
return newFileUploadPath;
}
}
下载演示样例:
获取服务中/WEB-INF/files 文件夹中的全部文件名称的servlet
//显示WEB-INF/files文件夹中的全部文件,并存入到一个map集合中(key存的是加了UUID的名字。value存的是用户提交过来的原名字)
public
class ShowFile extends HttpServlet {
public
void doGet(HttpServletRequest request, HttpServletResponseresponse)
throws ServletException, IOException {
response.setContentType("text/html; charser=utf-8");
response.setCharacterEncoding("utf-8");
request.setCharacterEncoding("utf-8");
//获取到files文件夹的绝对路径
String sourceFileName = this.getServletContext().getRealPath("/WEB-INF/files");
//通过这个绝对路径来创建一个File对象
FilesourceFile = new File(sourceFileName);
//创建一个map集合(key存的是加了UUID的名字。value存的是用户提交过来的原名字)
Map<String,String> map = newHashMap<String,String>();
//将这个map集合存储到session域对象中
request.getSession().setAttribute("fileNames", map);
//将全部的文件名称都存储到map中
filenamebasename(sourceFile, map);
//转发到显示的jsp页面
request.getRequestDispatcher("/showfilename.jsp").forward(request, response);
}
//将sourceFile文件夹下的全部文件的名字都存储到map集合中
private
void filenamebasename(File sourceFile, Map<String,String> map) {
//推断此File对象是否是一个文件
if(sourceFile.isFile()){
//假设是就将其名称获取到
String UUIDFileName = sourceFile.getName();
//在获取到用户提交的原名字
String oldFileName = UUIDFileName.substring(UUIDFileName.indexOf("_")+1);
//将其存入到map集合中
map.put(UUIDFileName, oldFileName);
}else{//假设是一个文件文件夹
//就获取到此文件夹的全部子文件和文件夹
File[] sourceFiles = sourceFile.listFiles();
//遍历它们
for(File file : sourceFiles){
//再用递归调用
filenamebasename(file, map);
}
}
}
public
void doPost(HttpServletRequest request, HttpServletResponseresponse)
throws ServletException, IOException {
doGet(request, response);
}
}
显示全部文件的jsp
<%@
page language="java"import="java.util.*"
pageEncoding="UTF-8"%>
<%@
taglib uri="http://java.sun.com/jsp/jstl/core"prefix="c"%>
<!DOCTYPE
HTMLPUBLIC
"-//W3C//DTDHTML 4.01 Transitional//EN">
<html>
<head>
<title>My JSP‘showfilename.jsp‘ starting page</title>
</head>
<%--此jsp文件用于显示map中的全部名称
--%>
<body>
<h1>server中的图片有:</h1>
<%--遍历session中的map集合
--%>
<c:forEach
items="${fileNames }"
var="entry">
<%--输出其用户提交的名字 --%>
${entry.value }
<%--重写url,由于要传的UUID名称
可能有中文,所以要重写此url--%>
<c:url
value="/servlet/Download"var="url">
<%-- 将其UUID名称当传入传入进去 --%>
<c:param
name="UUIDFileName"value="${entry.key }"></c:param>
</c:url>
<%--跳转到处理下载的servlet --%>
<a
href="${url }">下载</a>
<br/>
</c:forEach>
</body>
</html>
处理下载请求的servlet
public
class Download extends HttpServlet {
public
void doGet(HttpServletRequest request, HttpServletResponseresponse)
throws ServletException, IOException {
response.setContentType("text/html; charser=utf-8");
response.setCharacterEncoding("utf-8");
request.setCharacterEncoding("utf-8");
//获取到UUIDFileName
String UUIDFileName =(String)request.getParameter("UUIDFileName");
//将其转换为utf-8编码(由于前面在重写url时。已经将參数转换为了ISO-8859-1)
UUIDFileName = new String(UUIDFileName.getBytes("ISO-8859-1"),"utf-8");
//获取到存储文件(files)文件夹的绝对路径
String sourceFileName = this.getServletContext().getRealPath("/WEB-INF/files");
//通过 UUID名称
得到此文件的数据。在加上此UUID名称,那么就是此文件的绝对路径了
String filePath = getFilePath(sourceFileName,UUIDFileName)+"/"+UUIDFileName;
//通过这个绝对路径创建一个File对象
File file = new File(filePath);
//推断此文件在硬盘上是否存在
if(!file.exists()){
//假设不存在就输入下面字符串,并返回
response.getOutputStream().write("要下载的资源已经不在了!".getBytes("utf-8"));
return;
}
//假设有这个文件
//通过UUID名称
将用提交的原。名称获取到
String oldFileName =UUIDFileName.substring(UUIDFileName.indexOf("_")+1);
//设置此文件为下载。并设置下载的名称;由于下载的名称有可能是中文,所以须要URL加密
response.setHeader("Content-Disposition",
"attachment;filename="+URLEncoder.encode(oldFileName,"UTF-8"));
//通过此文件的创建一个输入流
InputStream in = new FileInputStream(file);
//获取到response对象的字节输出流
OutputStream out = response.getOutputStream();
//将输入流中的数据,放入到输出流中
byte[] b =
new byte[1024];
int len = -1;
while((len=in.read(b))>0){
out.write(b,0,len);
}
//关闭输入流
in.close();
}
//通过UUID文件名称,获取到此文件的路径
private String getFilePath(String sourceFileName,StringUUIDFileName) {
//获取到此文件名称的hash值
int hash = UUIDFileName.hashCode();
//获取到此hash值的二进制的最后4位
int dir1 = hash&0xf;
//获取到此hash值的二进制的最后5-8位
int dir2 = (hash&0xf0)>>4;
//就获取到了此文件的存储文件夹了
sourceFileName +=
"/"+dir1+"/"+dir2;
//返回此文件夹的绝对路径
return sourceFileName;
}
public
void doPost(HttpServletRequest request, HttpServletResponseresponse)
throws ServletException, IOException {
doGet(request, response);
}
}
Email邮件开发
邮件开发用到的协议:SMTP、POP、RFC822、MIME
SMTP: Simle Message Transfer Protocal 简单的传输协议。发送邮件时使用的协议.描写叙述了数据该怎样表示, 默认使用的port:25
POP3: Post Office Protocal 邮局协议,接收邮件时使用的协议,默认使用的port:110
手动发邮件:
能够在cmd中执行:telnet SMTPserver名port号
//telnet是cmd中的一个client(和浏览器一样 仅仅只是它是一个文本浏览器,不能查看图片)
演示样例,连接QQ邮箱:telnet smtp.qq.com587 //假设telnet命令无法使用。就在 控制面板\程序和功能\启用或关闭windows功能 中 打开Telnetclient功能
邮箱server的地址:
如:163邮箱的地址 接收邮件server(POPserver): pop.163.com 发送邮件server(SMTPserver):smtp.163.com
QQ邮箱的地址
POP3server(port995):pop.qq.com
SMTPserver(port465或587):smtp.qq.com
备注:不论什么邮箱的SMTP和POP3server地址都会在帮助中client中找到
邮件发送流程图:
SMTP协议(简单的传输协议,发送邮件时使用。默认使用的port:25
)
ehlo主机名 //向server交互(相当于打开一个入口)
authlogin //server返回此语句 表示让输入用户和password
输入base64编码后的账号和password //经过base64编码后的username和password
mailfrom: <[email protected]> //发件人邮箱
rcptto: <[email protected]> //接收人邮箱
data //表示内容的開始
内容 //内容(正文)
. //‘.’号代表邮件内容的结束(但要单独占一行)
quit //退出
发送邮件内容所遵循的格式:RFC822规范
邮件分为邮件头和邮件体,两者用空行(就是中间再空一行)切割
邮件头:
from //发送者
to //接收者
subject //主题
cc、bcc //cc是抄送(就是给别人也发),bcc是密送(就是秘密的发给谁)
邮件体:
邮件内容
POP3协议(接收邮件时使用的协议。默认使用的port:110
)
user username //pop是属于收邮件的协议。登录时的用户和password不须要base64加密
pass password //password
stat //统计全部邮件的信息
list 邮件编号 //返回某一个邮件的统计信息
quit //退出
手动发邮件过程(QQ邮箱。一条一条的在cmd中运行)
telnetsmtp.qq.com 587
ehloy460 //与server交互
STARTTLS //
authlogin //登录命名
MTAyNzM2MDY5NQ== //这是username的base64编码
bGFuMTM2NjgxMjU1MTchIw== //这是password的base64编码
mailfrom:<[email protected]> //发件者
rcptto:<[email protected]> //收件者
data //表示要发送数据
form:[email protected] //发送者
to:[email protected] //接收者
subject:这是标题 //标题
cc:[email protected] //cc给谁(能够不写)
//必需要有个空行
这是内容 //邮件的内容
. //内容结束符
quit //退出
手动接收邮件
telnetpop.qq.com 995
user1773123249 //pop是属于收邮件的协议,登录时的用户和password不须要base64加密
pass邮箱password //password
stat //统计全部邮件的信息
list1 //返回某一个邮件的统计信息
retr1 //显示某一个邮件的内容
quit //退出
在java中发邮件
//java手动发送电子邮件(用的136邮箱)
public
class JavaSendEmail {
public
static void main(String[] args)
throws Exception {
Socket socket = new Socket("smtp.163.com", 25);//连接136邮箱的smtpserver。并获取到server对象
InputStream in = socket.getInputStream();//获取服务用于返回数据的输入流
InputStreamReader readerIn = newInputStreamReader(in);//将其转换为字符输入流
BufferedReader bufRea = new BufferedReader(readerIn);//由于这里要一行一行的取数据,所以要转换为bufferedReader字符输入缓冲流
//获取向server提交数据的字节输出流
OutputStream out = socket.getOutputStream();
System.out.println(bufRea.readLine());//输出server返回的第一句话
out.write("ehloy460\r\n".getBytes());//与server交互
//server返回全部操作命令
System.out.println(bufRea.readLine());
System.out.println(bufRea.readLine());
System.out.println(bufRea.readLine());
System.out.println(bufRea.readLine());
System.out.println(bufRea.readLine());
System.out.println(bufRea.readLine());
System.out.println(bufRea.readLine());
//向server提交
我要登录
out.write("authlogin\r\n".getBytes());
//server返回
表示
请输入通过base64编码过后的username
System.out.println(bufRea.readLine());
//向server提交username
out.write("MTM2NjgxMjU1MTdAMTYzLmNvbQ==\r\n".getBytes());
//server返回
表示
请输入通过base64编码过后的password
System.out.println(bufRea.readLine());
//向server提交password
out.write("bGFuMTM2NjgxMjU1MTchIw==\r\n".getBytes());
//server返回数据。意思是输入发件者
System.out.println(bufRea.readLine());
//向server提交发件者
out.write("mailfrom:<[email protected]>\r\n".getBytes());
//server返回数据,意思是输入接收者
System.out.println(bufRea.readLine());
//向server提交收件者
out.write("rcptto:<[email protected]>\r\n".getBytes());
//输出server返回的数据,
System.out.println(bufRea.readLine());
//向server提交data,表示要发送数据
out.write("data\r\n".getBytes());
System.out.println(bufRea.readLine());
//发件的内容必须遵循:RFC822规范
//提交发件者
out.write("form:[email protected]\r\n".getBytes());
//提交接收者
out.write("to:[email protected]\r\n".getBytes());
//提交标题
out.write("subject:这是标题\r\n".getBytes());
//提交一个空行
out.write("\r\n".getBytes());
//提交邮件的内容
out.write("这是内容.\r\n".getBytes());
//提交.
表示数据提交完毕,注意此处的.表示完毕发送邮件,而且此.必须单独占一行
out.write(".\r\n".getBytes());
//关闭socket对象
socket.close();
}
}
用java中JavaMail工具来发送邮件
须要导入mail.jar(注意:Javamail的API依赖jaf (javaActivation Framewrk)框架,还须要导入jaf的jar包,假设用的是JDK1.6及以上版本号就不须要导入此包了)
JavaMail中的几大对象
Session //代表邮件的环境
Message //代表邮件对象
BodyPart //代表复杂邮件中的每一部分
Multipart //描写叙述由多个BodyPart组成的邮件的关系
javaMail中须要通过Properties文件配置环境
下面是Properties文件的key的说明
属性名 |
属性类型 |
说明 |
mail.stmp.host |
String |
SMTPserver地址,如smtp.sina.com.cn |
mail.stmp.port |
int |
SMTPserverport号,默觉得25 |
mail.stmp.auth |
boolean |
SMTPserver是否须要用户认证。默觉得false |
mail.stmp.user |
String |
SMTP默认的登陆username |
mail.stmp.from |
String |
默认的邮件发送源地址 |
mail.stmp.socketFactory.class |
String |
socket工厂类类名,通过设置该属性能够覆盖提供者默认的实现,必须实现javax.net.SocketFactory接口 |
mail.stmp.socketFactory.port |
int |
指定socket工厂类所用的port号,假设没有规定。则使用默认的port号 |
mail.smtp.socketFactory.fallback |
boolean |
设置为true时,当使用指定的socket类创建socket失败后,将使用java.net.Socket创建socket,默觉得true |
mail.stmp.timeout |
int |
I/O连接超时时间。单位为毫秒。默觉得永不超时 |
代码演示样例:
//终于版(仅仅能通过QQserver进行发送带图片和附件的邮件,用163的server发送邮会被163的垃圾邮件过滤器拦截,并导致发送不成功)
//发送一个复杂的邮件
public
static void main(String[] args)
throws Exception{
Properties props = new Properties();//配置环境
//设置发送邮件的环境的配置文件(假设仅仅将邮件保存到本地,就不须要设置此环境)
props.setProperty("mail.transport.protocol",
"smtp");//发送邮件所使用的协议
props.setProperty("mail.host",
"smtp.qq.com");//发送server的server地址
props.setProperty("mail.stmp.socketFactory.port",
"25");//设置server的port号,能够不设置,由于默认也为25
props.setProperty("mail.smtp.auth",
"true");//请求身份验证
props.setProperty("mail.debug",
"true");//调试模式(在控制台输出与server交互的过程)
//通过上面的props创建一个环境对象
Session session = Session.getDefaultInstance(props);
//通过环境变量对象创建一个邮件对象
MimeMessage message = new MimeMessage(session);
//创建一个发送对象
Address from = new InternetAddress("[email protected]");
message.setFrom(from);//将送对象加入到邮件中
//创建一个接收对象
Address to = new InternetAddress("[email protected]");
//Message.RecipientType类中还有几个常量如:CC,BCC等,表示抄送和密送
message.setRecipient(Message.RecipientType.TO, to);//将收件者加入到邮件中
message.setSubject("这是通过javamail发送的一封复杂邮件");
//创建一个内容对象,因为存储文本
MimeBodyPart txt = new MimeBodyPart();
//给此内容中加入值。并设置其打开方式及编码格式
txt.setContent("这是一封复杂的邮件。含有附件和图片等:<img src=‘cid:image‘>这是图片",
"text/html;charset=utf-8");
//创建一个内容对象,用于存储图片
MimeBodyPart img = new MimeBodyPart();
//创建一个数据源,图片的数据源
DataSource imgDs = new FileDataSource("C:\\Users\\hcl\\Desktop\\美女.jpg");
//通过数据源创建一个数据处理对象
DataHandler imgDh = new DataHandler(imgDs);
//将数据处理对象加入到内容对象中
img.setDataHandler(imgDh);
//设置这个内容的id,好方便上面的内容引用
img.setContentID("image");
img.setHeader("Content-Type",
"image/jpeg;");
//创建一个描写叙述上面文本和图片关系的对象
MimeMultipart mm1 = new MimeMultipart();
mm1.addBodyPart(txt);//在关系中加入txt文本内容对象
mm1.addBodyPart(img);//在关系中加入img图片内容对象
mm1.setSubType("related");//设置正文与图片之间的关系
//将上面的mm1关系看着一体。所以创建一个新的
内容
对象将上面的mm1关键封装成一个总体
MimeBodyPart txtImg = new MimeBodyPart();//创建一个内容对象,用于存放上面txt和img内容(仅仅须要存入上面的txt和img的关系就OK(mm1这个关系))
txtImg.setContent(mm1);//将mm1关系加入到此内容中
//创建一个内容对象。用于存放附件
MimeBodyPart annex = new MimeBodyPart();
//创建一个数据源,附件的数据源
DataSource annexDs = new FileDataSource("C:\\Users\\hcl\\Desktop\\附件.zip");
//创建一个数据处理对象
DataHandler annexDh = new DataHandler(annexDs);
//将数据处理对象加入到内容对象中
annex.setDataHandler(annexDh);
//将数据处理对象的文件名获取出来
String fileName = annexDh.getName();
//进行编码,以防止附件名中文乱码
fileName = MimeUtility.encodeText(fileName);
//将编码号的文件名重写设置到内容对象中
annex.setFileName(fileName);
//创建一个描写叙述附件和上面txtImg内容对象的关系的对象
MimeMultipart mm2 = new MimeMultipart();
mm2.addBodyPart(txtImg);//在此关系中,加入txtImg内容对象
mm2.addBodyPart(annex);//在此关系中,加入
附件
内容对象
mm2.setSubType("mixed");//设置正文与附件之间的关系
//这样整个关键就创建出来了。然后再加入到邮件对象中就ok
message.setContent(mm2);
message.saveChanges();//保存以上的改动
//创建发送邮件的对象
Transport t = session.getTransport();
//设置邮箱的账号和password
t.connect("[email protected]",
"lan13668125517!#");
//将邮件对象加入到发送对象中,并设置发送的对象message.getAllRecipients()是获取message对象中全部的发送对象
t.sendMessage(message, message.getAllRecipients());
//关闭
t.close();
}