使用JSP/Servlet技术开发新闻发布系统
第一章 动态网页开发基础
1. 动态网页的优势:
a) 交互性– 响应客户端的请求并回发
b) 自动更新– 自动生成的HTML代码而无须依次手动编写
c) 随机性– 不同客户访问同一网站时会产生不同的效果
2. 动态网页与静态网页不能相互替代;
3. 两种架构模式的应用程序:
a) C/S架构:
Client/Server(客户端/服务器)应用程序,本地安装的软件通过Internet进行通信,应用系统在客户机上;
安全性:安全性较高,适用于对于安全性较高的场合。
b) B/S架构:
(Browser/Server)应用程序,系统界面由浏览器展示,应用系统在应用服务器上;
安全性:较C/S架构的应用程序较低,适用于对于安全性不高的场合。
4. B/S架构的工作原理:
5. URL全称为“统一资源定位符”。
6. URL的组成:URL由协议、服务器域名或IP地址、端口号、路径组成。
7. 压缩版的Tomcat修改端口号的方法:
在Tomcat安装目录/conf目录下的server.xml下,修改<Connector>节点的port属性值即可。(默认:8080);
8. Tomcat的目录结构:
当运行Tomcat时,总是先加载运行classes文件夹内的文件在去加载lib目录下的类。
9. 配置某个项目的起始页的方法:项目目录/WEB-INF的web.xml文件中修改/增加<welcome-file>标签的内容即可。内容即某个JSP页面的完全名称;
10. 何为JSP?
JSP就是在HTML中嵌入Java脚本语言;
11. JSP的执行过程:
执行过程详解:
当用户点击操作了JSP页面时,等于向服务器发出了请求,这时Web容器接收到请求后,首先对JSP页面进行转换,转换为Java代码;
之后对Java源码进行编译,编译成.class字节码文件;
这时,容器会执行已经翻译好的class文件,在内部执行,并将结果转换为HTML代码返回到客户端予以显示。
12. JSP页面的组成:
静态内容、指令、表达式、小脚本、声明、标准动作和注释等。
JSP的注释在客户机查看源码时无法看到,而普通的HTML注释依旧可以看到;
Page指令用于导入java包,且多个属性之间使用逗号隔开;
小脚本指在HTML页面中嵌入的<% %>间的代码片段;
表达式指对数据的表示,语法为:<%=Java表达式 %>
13. Tomcat部署、运行项目的注意事项:
a) 项目名绝对不能含有中文;
b) 若项目在系统盘,则需要给此项目的所有关联文件夹都设置权限为EveryOne或当前登入的系统账户;
第二章 JSP数据交互(一)
1. JSP的内置对象对照表
JSP内置对象的常用方法及其说明
内置对象
方法名称
方法说明
request
String getParameter(String name)
根据表单名称获取数据
String[] getParameterValues(String name)
获取一组相同名称的控件数据
void setCharacterEncoding(String CharSet)
设置请求的编码方式
RequestDispatcher getRequestDispatcher(String path)
可以使用forward转发请求
response
void AddCookie(Cookie cookie)
在客户端添加Cookie
void setContentType(String type)
设置HTTP的内容类型
void setCharacterEncoding(String CharSet)
设置响应的编码方式
void sendRedirect(String location)
重定向URL路径
session
void setAttribute(String Key,Object Value)
以K-V的形式保存到session中
Object getAttribute(String Key)
通过Key获取session保存的对象
void removeAttribute(String Key)
从session删除制定Key的session对象
void invalidate()
手动设置session失效
String getId()
获取session Id
application
void setAttribute(String Key,Object Value)
以K-V的形式保存到application中
Object getAttribute(String Key)
通过Key获取application保存的对象
String RealPath(String path)
返回相对路径的真实路径
out
String print()/println()
向HTML页面输出文本信息
2. Request对象用于处理客户端请求的数据
例:String[] channels = request.getParameterValues("channel");//提交页面有共同name为“channel”的元素radiobutton
//取值时使用增强型for循环便利数组集合即可取出
if(channels != null) {
for (String channel : channels) {
out.print(channel+" ");
}
}
3. 两种设置中文字符集编码的方式:
a) 方式一:在项目Java代码中设置:【推荐使用】
request.setCharacterEncoding("UTF-8");
b) 方式二:设置Tomcat安装目录/conf/下的server.xml文件的<Connector>节点的属性。
增加URIEncoding属性,值为“UTF-8”。
4. Response对象用于响应客户的请求并向客户端输出信息。
工作原理:
5. 转发与重定向的区别:
转发与重定向
名称
JSP对应对象
方法
功能
过程
作用场合
转发
request
getRequestDispatcher.().forward(request,response)
页面跳转,并转发两个对象(客户端URL不变)
等同于同一个请求,对象信息保留
服务器端
重定向
response
sendRedirect()
单纯的页面跳转(客户端URL改变)
等于两次请求,对象信息丢失
客户端
6. Session用于保存用户来访时的每一次会话,作用域在服务器端。
当用户打开浏览器访问网站时,session即建立,当关闭浏览器即结束会话。
若Internet Explorer浏览器正在访问session,但此时又开了一个InternetExplorer浏览器,则此时两个浏览器间的session id则是一样的;
若Internet Explorer浏览器正在访问session,但此时又开了一个GoogleChrome浏览器,则此时两个浏览器间的session id则是不同的;
若360浏览器正在访问session,但此时将内核由Chrome内核切换到IE内核,则此时两个浏览器间的sessionid则是一样的;
最后总结:session的每次会话过程是基于浏览器种类的。
7. Session的失效有三种情况:
情况一:浏览操作结束,浏览器关闭后session对象自动销毁;
情况二:手动调用方法实现session失效(常用于网站用户的“注销”功能)。
手动设置失效即调用session的setMaxInactiveInterval([生效秒数,超过此秒数后session对象失效])手动设置失效时间;另一种方法是在项目的WEB-INF/web.xml中加入以下标签:
<session-config>
<session-timeout>[设置生效分钟数,超过此分钟数后session对象失效]</session-timeout>
</session-config>
第二章 JSP数据交互(二)
1. Application对象相当于系统的“全局变量”,且在一个服务器上的一个Web应用只有一个application对象,这样可以使所有用户的数据共享。
此前的session可看作是“私有变量”,而application则可看作是“公有变量”。
使用application对象统计网站的访问人数:
<%
Integer count = (Integer)application.getAttribute("count");
if (count != null) {
count += 1;
} else {
count = 1;
}
application.setAttribute("count",count);
%>
<%
Integer i = (Integer)application.getAttribute("count");
out.print("统计访问量:目前有"+ i + "个人访问过本网站。");
%>
2. 对象的作用域
分为四种作用域:
Page作用域、request作用域、session作用域和application作用域。
Page作用域仅对当前页面有效,转发或重定向到其他页面时page对象失效;
Request作用域在页面间转发时有效;
session作用域在一次会话期间有效;
application作用域面向整个Web应用程序,只要服务器不关闭则一直生效,当服务器重启或关闭时application对象销毁。
3. Cookie
Cookie就是在客户端以字符串保存的文件。
4. Cookie的操作
a) 写入Cookie:
Response.addCookie(new Cookie);
例:
response.addCookie(new Cookie("username","Jack"));
response.addCookie(newCookie("password", "123456"));
b) 读取Cookie:
读取时返回的是Cookie的对象数组,是以K-V的形式保存的。
例:
Cookie[]cookies = request.getCookies();
String user = "";
String pwd = "";
if (cookies != null) {
for (int i = 0; i< cookies.length; i++) {
if(cookies[i].getName().equals("username")) {
user= cookies[i].getValue();
} else if(cookies[i].getName().equals("password")) {
pwd= cookies[i].getValue();
}
}
}
5. Cookie的有效期:
调用Cookie对象的setMaxAge([设置有效时间,单位为秒])。
6. Cookie与session的对比:
Cookie与session的比较
Cookie
session
作用位置
客户端
服务器端
值的类型
String
Object
持久性
长期保存
会话结束即销毁
适于保存的信息
非重要信息
较重要信息
7. JDBC访问数据库的步骤:
加载JDBC驱动;
与数据库建立连接;
发送SQL语句;
处理返回结果;
关闭各种连接,释放所用的资源。
注意:Object…params表示params这个参数的数组长度是可变的,称为“可变长参数”。此形参的位置可以穿入若干个实参。
三元运算符:
语法:
[布尔类型的表达式]?[结果为true时的值]:[结果为false时的值]
8. JavaBean
何为JavaBean?
JavaBean其实就是一种编程思想,本质是一个Java类,并不是一个组件。目的就是封装数据和封装业务,可跨平台重用,提高安全性,提高编码效率。
特点:
属性私有;
访问属性的getter和setter方法公有。
9. 补充:
a) Application内置对象的getRealpath()方法返回相对路径的真实物理路径。
第五章 使用分层实现业务处理
1. JNDI
Java Naming and Directory Interface(Java命名与目录接口),用于查找、访问各种资源的接口。
2. JNDI的配置
修改位于Tomcat安装目录/conf/context.xml文件,在<Context>节点下增加如下信息:
<Environment name=”tjndi” value=”hello JNDI”type=”java.lang.String” />
参数解释:
Name表示环境名称,对应于java:comp/env/的名称;
Value表示要返回的参数值;
3. JNDI的使用:
Contextctx = new InitialContext(); //使用上下文接口实例化一个初始化上下文对象
//调用上下文对象的lookup()方法按照指定的名称检索对象,并将其转换为String类型的数据进行输出
String testjndi = (String)ctx.lookup("java:comp/env/tjndi");
out.print("JNDI:" +testjndi);
注意:对象名必须以“java:comp/env/”开头,后面跟在context.xml文件中配置的环境名称。
4. JNDI与application对象的对比:
JNDI与application的对比
JNDI
application内置对象
可见性
服务器上的所有Web应用
服务器上的某一个Web应用
5. 数据库连接池
作用是分配、管理和释放数据库连接。
工作原理:
初始化阶段:
创建一定数量的数据库连接并放入连接池的空闲池中,且无论是否在用或不在用,数量都会创建为这么多;
使用阶段:
使用时,系统进入空闲池中检查还有没有空闲连接,有则分配后进入使用;否则再去检查是否达到了最大连接数,没有达到则会创建一个数据库连接以供使用,若达到了最大连接数就需等待了,如果超时则返回null值。当达到数据库连接池所能提供的最大连接数量时,若在向连接池请求连接,则将加入等待。
6. 使用数据库连接池与JNDI获得数据库连接,访问数据库:
a) 数据源的配置:
首先配置Tomcat安装目录/conf/context.xml的<Environment>标签;
之后配置Tomcat安装目录/conf/context.xml的< Resource>标签,内容如下:
<Resource name="Actor"auth="Container" type="javax.sql.DataSource"maxActive="100" maxIdle="30" maxWait="10000"username="MSFT" password="123"driverClassName="com.microsoft.sqlserver.jdbc.SQLServerDriver" url="jdbc:sqlserver://localhost:1433;DatabaseName=Chapter03_Demo11"/>
含义解释:
Name表示Resource的名字;
Auth表示由谁来管理Resource(Container表示由容器创建和管理;Application表示由Web应用创建和管理);
Type表示Resource所用到的类名;
maxActive表示在使用的最大数据库活动连接数;
maxIdle表示连接池处于空闲的最大数据库连接数量(0表示数量随意,不受限);
maxWait表示连接池处于空闲的最长时间(单位:毫秒;1表示一直在等待)
username表示数据库的有效用户名
password表示数据库的有效登录密码
driverClassName表示要用到的Java驱动类
url表示数据库的连接字符串
b) 项目中web.xml的配置:
在<web-app>标签中添加如下标签属性:
<resource-ref>
// description标签表示对此数据源的描述
<description>newsDataSource</description>
// res-ref-name标签表示引用Resource的名字,对应于context.xml中<Resource>标签的name属性值
<res-ref-name>jdbc/news</res-ref-name>
// res-type表示引用Resource的类名
<res-type>javax.sql.DataSource</res-type>
// res-auth表示由谁来创建和管理Resource,对应于context.xml中<Resource>标签的auth属性值
<res-auth>Container</res-auth>
</resource-ref>
7. 三层架构
a) 层层间的关系:
表示层依赖业务逻辑层,业务逻辑层依赖数据访问层;
上一层依赖下一层,且不跨层调用;
下一层不依赖上一层。
8. 三层架构开发的优势:
a) 职责划分清晰;
b) 无损替换;
c) 复用代码;
d) 高内聚,低耦合。
9. 补充:
a) Application对象仅用于一个Web应用中访问,因为毕竟application对象是存储在内存中的,想要在一个项目中跨项目访问另一个项目的application对象是访问不到的,因为各个项目之间是相互独立的;
b) JNDI通过名称将资源与服务进行关联,类似于Map集合的K-V存储形式;
c) JNDI与ASP.NET中的Web.config的<connectionString>的作用类似,将数据库连接字符串使用外部配置文件保存起来,在更改数据库连接之后无需再次编译就可正常运行;
d) 数据源(DataSource)负责创建和管理数据库连接,而Tomcat则会从上下文(Context)中取出某一个连接放入连接池中以供应用程序所使用;
e) 数据库连接是由Tomcat容器管理的;
f) 获取DataSource(数据源)对象的过程:
第六章 JSP开发业务应用
1. 分页
a) 原理:
每次翻页时只从数据库中检索出本页要显示的数据而不是全部查询出来。
b) 实现:
i. 确定每页要显示几条数据;
ii. 计算出总共要显示多少页;
iii. 编写SQL语句
packagecom.Entity;
importjava.util.List;
/**
* 分页类
*
* @author Cortana for ThinkPad
*
*/
public classSplitPage {
/**
* 总共要显示多少页
*/
private int totalPageCount = 1;
/**
* 每页显示多少条数据
*/
private int pageSize = 0;
/**
* 全部的数据数量
*/
private int totalCount = 0;
/**
* 当前为第几页
*/
private int currPageNo = 1;
List<News> newsList;
public int getTotalCount() {
return totalCount;
}
//设置全部的数据条数
public void setTotalCount(inttotalCount) {
if (totalCount > 0) {
this.totalCount =totalCount;
//总页数的计算方法:
若总记录数除以每页显示的固定数据量可以除尽,则要显示的全部页数就是它俩的商,因为能除尽代表每页显示固定的数据条数正好可以在当前显示的总页数内容得下;
若总记录数除以每页显示的固定数据量除不尽,则要显示的全部页数就是它俩的商+1,因为能除不尽代表每页显示固定的数据条数不足以在当前显示的总页数内全部显示,还需要再额外加1页才能全部显示完
totalPageCount =this.totalCount % pageSize == 0 ? (this.totalCount / pageSize)
:this.totalCount / pageSize + 1;
}
}
public int getTotalPageCount() {
return totalPageCount;
}
public void setTotalPageCount(inttotalPageCount) {
this.totalPageCount =totalPageCount;
}
public int getPageSize() {
return pageSize;
}
public void setPageSize(int pageSize) {
if (pageSize > 0) {
this.pageSize =pageSize;
}
}
public int getCurrPageNo() {
if (totalPageCount == 0) {
return 0;
}
return currPageNo;
}
public void setCurrPageNo(intcurrPageNo) {
if (this.currPageNo > 0) {
this.currPageNo =currPageNo;
}
}
public List<News> getNewsList() {
return newsList;
}
public voidsetNewsList(List<News> newsList) {
this.newsList = newsList;
}
}
c) 理解分页SQL语句:
select top 3 * from NewsList where ArticleId not in
(select top ((1-1)*3) ArticleId from NewsList)
解释:
外层查询:在NewsList表中查询所有结果的前三列;
内层子查询:在NewsList表中查询出前0个文章编号的数据;
那么此时,按照当前语句来讲,子查询应无返回结果,外层查询查询到了前三条数据,那么not in中又没有数据,所以第一次等于子查询没有实际用处,即正常显示前三条数据,结果如下:
分段查询:
合并查询:
第二次查询:
那么此时,按照当前语句来讲,子查询应无返回结果,外层查询查询到了前三条数据,那么not in中又没有数据,所以第一次等于子查询没有实际用处,即正常显示前三条数据,结果如下:
分段查询:
可能会问为什么第二次查询的时候,分开查询怎么新闻的序号都是1、2、3而第一条SQL语句的查询结果不是6条呢?不应该是查询出前6条然后1、2、3条除外才对吗?就像下图:
此时要注意了,因为语句中使用not in进行条件约束,当首先执行子查询select top ((2-1)*3) ArticleId from NewsList时,查询出的结果为1、2、3,再执行外层的查询时,就会将1、2、3这三条数据过滤掉,从而就会查询出后三条数据来,一定要理解SQL语句的执行顺序,线执行子查询,根据子查询的条件在执行外层查询时就会根据关键字进行过滤。
分页SQL语句示意:
2. 使用Commons-FileUpload组件实现文件上传
a) 组件:
commons-fileupload-1.2.1.jar – 用于客户端向服务器发送文件数据
commons-io-1.3.2.jar – 用于服务器写入客户端上传过来的文件数据
b) 表单的属性设置:
设置<form>标签的encType属性值为二进制数据类型(multipart/form-data),且提交方式(method)必须设为“post”而不是“get”方式。
c) 使用file控件选择文件:
<input type=”file” />
这样,就是一个“打开文件”的对话框了。
d) 上传文件需要用到的commons-fileupload组件的API:
使用commons-fileupload组件上传文件需要用到的API
ServletUpload类
FileItem接口
FileItemFactory接口
用途
文件上传
封装单个表单字段
将获得的FileItem对象保存至服务器硬盘
实现类
无
DiskFileItem类
DiskFileItemFactory类
e) 上传文件并控制文件类型和大小的代码:
request.setCharacterEncoding("UTF-8");
//上传的文件名
String uploadFileName ="";
//表单字段元素的name属性值
String fieldName ="";
//请求信息中的内容是否是MultiPart类型
boolean isMultipart =ServletFileUpload.isMultipartContent(request);
String uploadFilePath =request.getSession().getServletContext()
.getRealPath("upload/");
//创建临时文件目录路径
File tempPatchFile = newFile("e:\\temp\\buffer\\");
//判断临时目录是否存在
//不存在,则调用mkdirs()方法创建目录
if (!tempPatchFile.exists()){
tempPatchFile.mkdirs();
}
if (isMultipart) {
DiskFileItemFactoryfactory = new DiskFileItemFactory();
//设置缓冲区大小(单位:KB)
factory.setSizeThreshold(4096);
//设置上传文件用到的临时文件存放路径
factory.setRepository(tempPatchFile);
ServletFileUploadupload = new ServletFileUpload(factory);
//设置单个文件的最大限制
upload.setFileSizeMax(1024* 30);
try {
//解析form表单中所有的文件
List<FileItem>items = upload.parseRequest(request);
Iterator<FileItem>iter = items.iterator();
while(iter.hasNext()) {
FileItemitem = (FileItem) iter.next();
if(item.isFormField()) {
fieldName= item.getFieldName();
if(fieldName.equals("user")) {
out.print(item.getString("UTF-8")
+"上传了文件。<br />");
}
}else {
StringfileName = item.getName();
List<String>fileType = Arrays.asList("gif", "bmp",
"jpg");
Stringext = fileName.substring(fileName
.lastIndexOf(".")+ 1);
if(!fileType.contains(ext)) {
out.print("上传失败,文件类型只能是gif、bmp、jpg");
}else {
if(fileName != null && !fileName.equals("")) {
FilefullFile = new File(item.getName());
FilesaveFile = new File(uploadFilePath,
fullFile.getName());
item.write(saveFile);
uploadFileName= fullFile.getName();
out.print("上传成功后的文件名是:"+ uploadFileName
+",文件大小是:" + item.getSize()
+"Bytes!");
}
}
}
}
} catch(FileUploadBase.FileSizeLimitExceededException ex) {
out.print("上传失败,文件太大,单个文件的最大限制是:" + upload.getFileSizeMax()
+"Bytes!");
} catch (Exceptione) {
e.printStackTrace();
}
}
3. 补充:
a) Select count(*) from [表名]
Select count(1) from [表名]
两者的执行效率,后者比前者效率高;
b) Commons-FileUpload组件其实是通过二进制字节流的方式传输文件的。
第7章 Servlet基础
1. 在JSP未出现之前,都使用Servlet编写Java Web应用程序。
2. JSP与Servlet的执行过程:
a) JSP的执行过程:
此时,JSP作为向客户展示的页面;
b) Servlet的执行过程:
此时,Servlet生成的HTML代码作为向客户展示的页面。
3. Servlet继承于HttpServlet,共有以下几种方法需要实现:
a) 构造函数;
b) Init()方法;
c) doGet()方法;
d) doPost()方法;
e) destroy()方法;
当初次执行Servlet时,init()方法被执行,若再次请求,则不再执行init()方法,因为初始化方法仅执行一次;
之后根据上以页面的提交方式选择执行doGet()方法还是doPost()方法;
当停止Tomcat服务或重启、关闭服务器时,执行destroy()方法进行回收。
注意:destroy()方法只是指明哪些资源可以回收,而不是由destroy()方法直接进行回收。
4. Servlet常用API:
Servlet常用API接口
API名称
作用
方法
方法说明
Servlet接口
void init(ServletConfig config)
初始化Servlet
void service(ServletRequest req,ServletResponse res)
处理客户端请求
void destroy()
释放Servlet所用资源
GenericServlet抽象类
String getInitParameter(String name)
返回名为name的初始化参数值
HttpServlet抽象类
void service(ServletRequest req,ServletResponse res)
处理客户端请求(调用GenericServlet中的service()方法)
void doXXX(HttpServletRequest req,HttpServletResponse res)
根据提交方式调用不同的请求方法
ServletContext对象
操作一个Web应用的上下文
String getInitParameter(String name)
获取名为name的初始化参数值(初始化参数在web.xml的<context-param>节点内设置)
void setAttribute(String name,Object object)
设置名为name的属性
Object getAttribute(String name)
获取名为name的属性
String getRealPath(String path)
返回参数所代表的目录的物理路径
ServletConfig接口
读取Servlet配置
省略……
省略……
ServletRequest与HttpServletRequest接口
获取客户端的请求
void setAttribute(String name,Object object)
在请求中设置名为name的属性值
Object getAttribute(String name)
获取名为name的属性
void removeAttribute(String name)
清除请求中名为name的属性
ServletResponse与HttpServletResponse接口
向客户端发送响应数据
PrintWriter getWriter()
向客户端发送文本
String getCharacterEncoding()
返回响应的字符编码
void setCharacterEncoding()
设置发送的字符编码
ServletRequest与HttpServletRequest接口具有在JSP中request对象相同的方法,前两者是后者的扩展;
ServletResponse与HttpServletResponse接口具有在JSP中response对象相同的方法,前两者是后者的扩展。
5. Servlet的映射
在项目的web.xml文件中写入如下标签:
<servlet>
//此Servlet的描述【非必须】
<description>HelloServlet</description>
<display-name>HelloServlet</display-name>
//此Servlet的名字
<servlet-name>HelloServlet</servlet-name>
//Servlet的完全限定名(Servlet对应于项目的位置,有包名则需要写包名)
<servlet-class>HelloServlet</servlet-class>
</servlet>
//Servlet的映射
<servlet-mapping>
//Servlet映射到的名字(对应于上方设置的Servlet的名字)
<servlet-name>HelloServlet</servlet-name>
//Servlet的映射路径(可通过浏览器地址栏输入进行访问)
<url-pattern>/servlet/HelloServlet</url-pattern>
</servlet-mapping>
6. Servlet的初始化参数:
在项目的web.xml文件中写入如下标签:
<servlet>
<init-param>
//初始化参数的名字
<param-name>initParam</param-name>
//初始化参数对应的值
<param-value>HelloServlet</param-value>
</init-param>
</servlet>
此时在Servlet文件的doGet()方法中获取:
StringinitParam = getInitParameter("initParam");
System.out.println(initParam);
7. Servlet的上下文参数:
在项目的web.xml文件中写入如下标签:
<web-app>
<context-param>
<param-name>contextParam</param-name>
<param-value>HelloServlet</param-value>
</context-param>
</web-app>
此时在Servlet文件的doGet()方法中获取:
StringcontextParam = this.getServletContext().getInitParameter(
"contextParam");
System.out.println("系统初始化参数:"+ contextParam);
8. 补充:
a) Servlet擅长于流程控制和业务处理;JSP擅长于数据的展示。可以在实际开发中将JSP的Control页面都换成Servlet类进行业务处理。
b) Servlet的体系结构:
c) Servlet的生命周期:
d) 在Servlet中获取上一页提交的表单元素的值与JSP的方法相同;
e) 若要以普通超链接的方式转到Servlet文件上,则超链接的href属性则应写web.xml文件中配置Servlet的url-pattern节点的属性路径;
f) 使用超链接时,Servlet默认调用doGet()方法,点击提交按钮时,若method的方式为“post”,则Servlet调用doPost()方法。